Add support for SkImageGenerator creating external textures

Copy the SkImageGenerator texture if tiling is needed and
original texture target is GR_GL_TEXTURE_EXTERNAL.

Bug: skia:
Change-Id: I98f5acc3883e2060b1a35f80633b02b08a706107
Reviewed-on: https://skia-review.googlesource.com/18268
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Stan Iliev <stani@google.com>
diff --git a/include/core/SkImageGenerator.h b/include/core/SkImageGenerator.h
index 4514a2d..776df56 100644
--- a/include/core/SkImageGenerator.h
+++ b/include/core/SkImageGenerator.h
@@ -176,7 +176,13 @@
     virtual bool onGetYUV8Planes(const SkYUVSizeInfo&, void*[3] /*planes*/) { return false; }
 
 #if SK_SUPPORT_GPU
-    virtual bool onCanGenerateTexture() const { return false; }
+    enum class TexGenType {
+        kNone,           //image generator does not implement onGenerateTexture
+        kCheap,          //onGenerateTexture is implemented and it is fast (does not render offscreen)
+        kExpensive,      //onGenerateTexture is implemented and it is relatively slow
+    };
+
+    virtual TexGenType onCanGenerateTexture() const { return TexGenType::kNone; }
     virtual sk_sp<GrTextureProxy> onGenerateTexture(GrContext*, const SkImageInfo&,
                                                     const SkIPoint&);   // returns nullptr
 #endif
diff --git a/src/core/SkColorSpaceXformImageGenerator.h b/src/core/SkColorSpaceXformImageGenerator.h
index 953a1db..3bab29f 100644
--- a/src/core/SkColorSpaceXformImageGenerator.h
+++ b/src/core/SkColorSpaceXformImageGenerator.h
@@ -24,6 +24,9 @@
 #if SK_SUPPORT_GPU
     sk_sp<GrTextureProxy> onGenerateTexture(GrContext*, const SkImageInfo&,
                                             const SkIPoint&) override;
+    TexGenType onCanGenerateTexture() const override {
+        return TexGenType::kExpensive;
+    }
 #endif
 
 private:
diff --git a/src/core/SkImageCacherator.h b/src/core/SkImageCacherator.h
index 3587446..e5ed250 100644
--- a/src/core/SkImageCacherator.h
+++ b/src/core/SkImageCacherator.h
@@ -11,6 +11,10 @@
 #include "SkImage.h"
 #include "SkImageInfo.h"
 
+#if SK_SUPPORT_GPU
+#include "GrTextureMaker.h"
+#endif
+
 class GrCaps;
 class GrContext;
 class GrTextureProxy;
@@ -41,11 +45,13 @@
 #if SK_SUPPORT_GPU
     // Returns the texture proxy. If the cacherator is generating the texture and wants to cache it,
     // it should use the passed in key (if the key is valid).
-    virtual sk_sp<GrTextureProxy> lockTextureProxy(GrContext*,
-                                                   const GrUniqueKey& key,
-                                                   SkImage::CachingHint,
-                                                   bool willBeMipped,
-                                                   SkColorSpace* dstColorSpace) = 0;
+    // If "genType" argument equals AllowedTexGenType::kCheap and the texture is not trivial to
+    // construct then refOriginalTextureProxy should return nullptr (for example if texture is made
+    // by drawing into a render target).
+    virtual sk_sp<GrTextureProxy> lockTextureProxy(GrContext*, const GrUniqueKey& key,
+                                                   SkImage::CachingHint, bool willBeMipped,
+                                                   SkColorSpace* dstColorSpace,
+                                                   GrTextureMaker::AllowedTexGenType genType) = 0;
 
     // Returns the color space of the texture that would be returned if you called lockTexture.
     // Separate code path to allow querying of the color space for textures that cached (even
diff --git a/src/core/SkPictureImageGenerator.h b/src/core/SkPictureImageGenerator.h
index 6dba29c..f328e4f 100644
--- a/src/core/SkPictureImageGenerator.h
+++ b/src/core/SkPictureImageGenerator.h
@@ -21,7 +21,7 @@
                      override;
 
 #if SK_SUPPORT_GPU
-    bool onCanGenerateTexture() const override { return true; }
+    TexGenType onCanGenerateTexture() const override { return TexGenType::kExpensive; }
     sk_sp<GrTextureProxy> onGenerateTexture(GrContext*, const SkImageInfo&,
                                             const SkIPoint&) override;
 #endif
diff --git a/src/gpu/GrAHardwareBufferImageGenerator.h b/src/gpu/GrAHardwareBufferImageGenerator.h
index 4a5004b..521fc7c 100644
--- a/src/gpu/GrAHardwareBufferImageGenerator.h
+++ b/src/gpu/GrAHardwareBufferImageGenerator.h
@@ -34,7 +34,7 @@
     bool onIsValid(GrContext*) const override;
 
 #if SK_SUPPORT_GPU
-    bool onCanGenerateTexture() const override { return true; }
+    TexGenType onCanGenerateTexture() const override { return TexGenType::kCheap; }
     sk_sp<GrTextureProxy> onGenerateTexture(GrContext*, const SkImageInfo&,
                                             const SkIPoint&) override;
 #endif
diff --git a/src/gpu/GrBackendTextureImageGenerator.cpp b/src/gpu/GrBackendTextureImageGenerator.cpp
index 02f172b..3a3cd31 100644
--- a/src/gpu/GrBackendTextureImageGenerator.cpp
+++ b/src/gpu/GrBackendTextureImageGenerator.cpp
@@ -102,9 +102,8 @@
     refHelper->unref();
 }
 
-sk_sp<GrTextureProxy> GrBackendTextureImageGenerator::onGenerateTexture(GrContext* context,
-                                                                        const SkImageInfo& info,
-                                                                        const SkIPoint& origin) {
+sk_sp<GrTextureProxy> GrBackendTextureImageGenerator::onGenerateTexture(
+        GrContext* context, const SkImageInfo& info, const SkIPoint& origin) {
     SkASSERT(context);
 
     if (context->contextPriv().getBackend() != fBackendTexture.backend()) {
diff --git a/src/gpu/GrBackendTextureImageGenerator.h b/src/gpu/GrBackendTextureImageGenerator.h
index 2ada687..675c553 100644
--- a/src/gpu/GrBackendTextureImageGenerator.h
+++ b/src/gpu/GrBackendTextureImageGenerator.h
@@ -27,7 +27,7 @@
     bool onIsValid(GrContext*) const override { return true; }
 
 #if SK_SUPPORT_GPU
-    bool onCanGenerateTexture() const override { return true; }
+    TexGenType onCanGenerateTexture() const override { return TexGenType::kCheap; }
     sk_sp<GrTextureProxy> onGenerateTexture(GrContext*, const SkImageInfo&,
                                             const SkIPoint&) override;
 #endif
diff --git a/src/gpu/GrBitmapTextureMaker.cpp b/src/gpu/GrBitmapTextureMaker.cpp
index bde8276..762b72c 100644
--- a/src/gpu/GrBitmapTextureMaker.cpp
+++ b/src/gpu/GrBitmapTextureMaker.cpp
@@ -28,7 +28,12 @@
 }
 
 sk_sp<GrTextureProxy> GrBitmapTextureMaker::refOriginalTextureProxy(bool willBeMipped,
-                                                                    SkColorSpace* dstColorSpace) {
+                                                                    SkColorSpace* dstColorSpace,
+                                                                    AllowedTexGenType onlyIfFast) {
+    if (AllowedTexGenType::kCheap == onlyIfFast) {
+        return nullptr;
+    }
+
     sk_sp<GrTextureProxy> proxy;
 
     if (fOriginalKey.isValid()) {
diff --git a/src/gpu/GrBitmapTextureMaker.h b/src/gpu/GrBitmapTextureMaker.h
index c706962..419649c 100644
--- a/src/gpu/GrBitmapTextureMaker.h
+++ b/src/gpu/GrBitmapTextureMaker.h
@@ -19,7 +19,8 @@
 
 protected:
     sk_sp<GrTextureProxy> refOriginalTextureProxy(bool willBeMipped,
-                                                  SkColorSpace* dstColorSpace) override;
+                                                  SkColorSpace* dstColorSpace,
+                                                  AllowedTexGenType onlyIfFast) override;
 
     void makeCopyKey(const CopyParams& copyParams, GrUniqueKey* copyKey,
                      SkColorSpace* dstColorSpace) override;
diff --git a/src/gpu/GrImageTextureMaker.cpp b/src/gpu/GrImageTextureMaker.cpp
index bbdc9c1..5bf81d2 100644
--- a/src/gpu/GrImageTextureMaker.cpp
+++ b/src/gpu/GrImageTextureMaker.cpp
@@ -26,9 +26,10 @@
 }
 
 sk_sp<GrTextureProxy> GrImageTextureMaker::refOriginalTextureProxy(bool willBeMipped,
-                                                                   SkColorSpace* dstColorSpace) {
+                                                                   SkColorSpace* dstColorSpace,
+                                                                   AllowedTexGenType onlyIfFast) {
     return fCacher->lockTextureProxy(this->context(), fOriginalKey, fCachingHint,
-                                     willBeMipped, dstColorSpace);
+                                     willBeMipped, dstColorSpace, onlyIfFast);
 }
 
 void GrImageTextureMaker::makeCopyKey(const CopyParams& stretch, GrUniqueKey* paramsCopyKey,
diff --git a/src/gpu/GrImageTextureMaker.h b/src/gpu/GrImageTextureMaker.h
index 4f3e8bd..1a72f68 100644
--- a/src/gpu/GrImageTextureMaker.h
+++ b/src/gpu/GrImageTextureMaker.h
@@ -24,7 +24,8 @@
     //       able to efficiently produce a "stretched" texture natively (e.g. picture-backed)
     //          GrTexture* generateTextureForParams(const CopyParams&) override;
     sk_sp<GrTextureProxy> refOriginalTextureProxy(bool willBeMipped,
-                                                  SkColorSpace* dstColorSpace) override;
+                                                  SkColorSpace* dstColorSpace,
+                                                  AllowedTexGenType onlyIfFast) override;
 
     void makeCopyKey(const CopyParams& stretch, GrUniqueKey* paramsCopyKey,
                      SkColorSpace* dstColorSpace) override;
diff --git a/src/gpu/GrTextureMaker.cpp b/src/gpu/GrTextureMaker.cpp
index 4fb0c0d..69e3a6e 100644
--- a/src/gpu/GrTextureMaker.cpp
+++ b/src/gpu/GrTextureMaker.cpp
@@ -26,10 +26,21 @@
         *texColorSpace = this->getColorSpace(dstColorSpace);
     }
 
-    if (!fContext->getGpu()->isACopyNeededForTextureParams(this->width(), this->height(), params,
-                                                           &copyParams, scaleAdjust)) {
-        return this->refOriginalTextureProxy(willBeMipped, dstColorSpace);
+    sk_sp<GrTextureProxy> original(this->refOriginalTextureProxy(willBeMipped, dstColorSpace,
+                                                                 AllowedTexGenType::kCheap));
+    if (original) {
+        if (!fContext->getGpu()->isACopyNeededForTextureParams(original.get(), params, &copyParams,
+                                                               scaleAdjust)) {
+            return original;
+        }
+    } else {
+        if (!fContext->getGpu()->isACopyNeededForTextureParams(this->width(), this->height(),
+                                                               params, &copyParams, scaleAdjust)) {
+            return this->refOriginalTextureProxy(willBeMipped, dstColorSpace,
+                                                 AllowedTexGenType::kAny);
+        }
     }
+
     GrUniqueKey copyKey;
     this->makeCopyKey(copyParams, &copyKey, dstColorSpace);
     if (copyKey.isValid()) {
@@ -39,8 +50,13 @@
         }
     }
 
-    sk_sp<GrTextureProxy> result(this->generateTextureProxyForParams(copyParams, willBeMipped,
-                                                                     dstColorSpace));
+    sk_sp<GrTextureProxy> result;
+    if (original) {
+        result = CopyOnGpu(fContext, std::move(original), nullptr, copyParams);
+    } else {
+        result = this->generateTextureProxyForParams(copyParams, willBeMipped, dstColorSpace);
+    }
+
     if (!result) {
         return nullptr;
     }
@@ -106,7 +122,8 @@
 sk_sp<GrTextureProxy> GrTextureMaker::generateTextureProxyForParams(const CopyParams& copyParams,
                                                                     bool willBeMipped,
                                                                     SkColorSpace* dstColorSpace) {
-    sk_sp<GrTextureProxy> original(this->refOriginalTextureProxy(willBeMipped, dstColorSpace));
+    sk_sp<GrTextureProxy> original(this->refOriginalTextureProxy(willBeMipped, dstColorSpace,
+                                                                 AllowedTexGenType::kAny));
     if (!original) {
         return nullptr;
     }
diff --git a/src/gpu/GrTextureMaker.h b/src/gpu/GrTextureMaker.h
index 909d348..9e1e0dd 100644
--- a/src/gpu/GrTextureMaker.h
+++ b/src/gpu/GrTextureMaker.h
@@ -16,6 +16,8 @@
  */
 class GrTextureMaker : public GrTextureProducer {
 public:
+    enum class AllowedTexGenType : bool { kCheap, kAny };
+
     /**
      *  Returns a texture that is safe for use with the params. If the size of the returned texture
      *  does not match width()/height() then the contents of the original must be scaled to fit
@@ -44,9 +46,13 @@
     /**
      *  Return the maker's "original" texture. It is the responsibility of the maker to handle any
      *  caching of the original if desired.
+     *  If "genType" argument equals AllowedTexGenType::kCheap and the texture is not trivial to
+     *  construct then refOriginalTextureProxy should return nullptr (for example if texture is made
+     *  by drawing into a render target).
      */
     virtual sk_sp<GrTextureProxy> refOriginalTextureProxy(bool willBeMipped,
-                                                          SkColorSpace* dstColorSpace) = 0;
+                                                          SkColorSpace* dstColorSpace,
+                                                          AllowedTexGenType genType) = 0;
 
     /**
      *  Returns the color space of the maker's "original" texture, assuming it was retrieved with
diff --git a/src/image/SkImage_Lazy.cpp b/src/image/SkImage_Lazy.cpp
index 980db1d..7fe3236 100644
--- a/src/image/SkImage_Lazy.cpp
+++ b/src/image/SkImage_Lazy.cpp
@@ -109,7 +109,8 @@
                                            const GrUniqueKey& key,
                                            SkImage::CachingHint,
                                            bool willBeMipped,
-                                           SkColorSpace* dstColorSpace) override;
+                                           SkColorSpace* dstColorSpace,
+                                           GrTextureMaker::AllowedTexGenType genType) override;
 
     // Returns the color space of the texture that would be returned if you called lockTexture.
     // Separate code path to allow querying of the color space for textures that cached (even
@@ -563,7 +564,7 @@
 bool SkImage_Lazy::onCanLazyGenerateOnGPU() const {
 #if SK_SUPPORT_GPU
     ScopedGenerator generator(fSharedGenerator);
-    return generator->onCanGenerateTexture();
+    return SkImageGenerator::TexGenType::kNone != generator->onCanGenerateTexture();
 #else
     return false;
 #endif
@@ -687,7 +688,8 @@
                                                      const GrUniqueKey& origKey,
                                                      SkImage::CachingHint chint,
                                                      bool willBeMipped,
-                                                     SkColorSpace* dstColorSpace) {
+                                                     SkColorSpace* dstColorSpace,
+                                                     GrTextureMaker::AllowedTexGenType genType) {
     // Values representing the various texture lock paths we can take. Used for logging the path
     // taken to a histogram.
     enum LockTexturePath {
@@ -726,6 +728,10 @@
     // 2. Ask the generator to natively create one
     {
         ScopedGenerator generator(fSharedGenerator);
+        if (GrTextureMaker::AllowedTexGenType::kCheap == genType &&
+                SkImageGenerator::TexGenType::kCheap != generator->onCanGenerateTexture()) {
+            return nullptr;
+        }
         if (sk_sp<GrTextureProxy> proxy = generator->generateTexture(ctx, cacheInfo, fOrigin)) {
             SK_HISTOGRAM_ENUMERATION("LockTexturePath", kNative_LockTexturePath,
                                      kLockTexturePathCount);