Add ImageToColorSpace helper in SkImageFilter
authorBrian Osman <brianosman@google.com>
Thu, 29 Dec 2016 14:18:20 +0000 (09:18 -0500)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Thu, 29 Dec 2016 18:35:44 +0000 (18:35 +0000)
Share this logic among a couple filters that need it. This also fixes a
bug that showed up in the morhpology GM for GPU color space configs.

Re-land of https://skia-review.googlesource.com/c/6475/

BUG=skia:

Change-Id: If7b8d892cf89fa030bae68bdd3c03118f290f032
Reviewed-on: https://skia-review.googlesource.com/6484
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>

include/core/SkImageFilter.h
src/core/SkImageFilter.cpp
src/effects/SkMatrixConvolutionImageFilter.cpp
src/effects/SkMorphologyImageFilter.cpp

index c2d7261..86e12e1 100644 (file)
@@ -386,6 +386,15 @@ protected:
      */
     Context mapContext(const Context& ctx) const;
 
+#if SK_SUPPORT_GPU
+    /**
+     *  Returns a version of the passed-in image (possibly the original), that is in a colorspace
+     *  with the same gamut as the one from the OutputProperties. This allows filters that do many
+     *  texture samples to guarantee that any color space conversion has happened before running.
+     */
+    static sk_sp<SkSpecialImage> ImageToColorSpace(SkSpecialImage* src, const OutputProperties&);
+#endif
+
 private:
     friend class SkGraphics;
     static void PurgeCache();
index 2ec441f..7965091 100644 (file)
@@ -8,6 +8,7 @@
 #include "SkImageFilter.h"
 
 #include "SkCanvas.h"
+#include "SkColorSpace_Base.h"
 #include "SkFuzzLogging.h"
 #include "SkImageFilterCache.h"
 #include "SkLocalMatrixImageFilter.h"
@@ -344,6 +345,35 @@ bool SkImageFilter::applyCropRect(const Context& ctx, const SkIRect& srcBounds,
     return dstBounds->intersect(ctx.clipBounds());
 }
 
+#if SK_SUPPORT_GPU
+sk_sp<SkSpecialImage> SkImageFilter::ImageToColorSpace(SkSpecialImage* src,
+                                                       const OutputProperties& outProps) {
+    // There are several conditions that determine if we actually need to convert the source to the
+    // destination's color space. Rather than duplicate that logic here, just try to make an xform
+    // object. If that produces something, then both are tagged, and the source is in a different
+    // gamut than the dest. There is some overhead to making the xform, but those are cached, and
+    // if we get one back, that means we're about to use it during the conversion anyway.
+    sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(src->getColorSpace(),
+                                                                       outProps.colorSpace());
+
+    if (!colorSpaceXform) {
+        // No xform needed, just return the original image
+        return sk_ref_sp(src);
+    }
+
+    sk_sp<SkSpecialSurface> surf(src->makeSurface(outProps,
+                                                  SkISize::Make(src->width(), src->height())));
+    if (!surf) {
+        return sk_ref_sp(src);
+    }
+
+    SkCanvas* canvas = surf->getCanvas();
+    SkASSERT(canvas);
+    src->draw(canvas, 0, 0, nullptr);
+    return surf->makeImageSnapshot();
+}
+#endif
+
 // Return a larger (newWidth x newHeight) copy of 'src' with black padding
 // around it.
 static sk_sp<SkSpecialImage> pad_image(SkSpecialImage* src,
index 40d09d6..6af8508 100644 (file)
@@ -10,7 +10,6 @@
 #include "SkColorPriv.h"
 #include "SkReadBuffer.h"
 #include "SkSpecialImage.h"
-#include "SkSpecialSurface.h"
 #include "SkWriteBuffer.h"
 #include "SkRect.h"
 #include "SkUnPreMultiply.h"
@@ -282,23 +281,6 @@ static GrTextureDomain::Mode convert_tilemodes(SkMatrixConvolutionImageFilter::T
     }
     return GrTextureDomain::kIgnore_Mode;
 }
-
-// Return a copy of 'src' transformed to the output's color space
-static sk_sp<SkSpecialImage> image_to_color_space(SkSpecialImage* src,
-                                                  const SkImageFilter::OutputProperties& outProps) {
-    sk_sp<SkSpecialSurface> surf(src->makeSurface(
-        outProps, SkISize::Make(src->width(), src->height())));
-    if (!surf) {
-        return sk_ref_sp(src);
-    }
-
-    SkCanvas* canvas = surf->getCanvas();
-    SkASSERT(canvas);
-
-    src->draw(canvas, 0, 0, nullptr);
-
-    return surf->makeImageSnapshot();
-}
 #endif
 
 sk_sp<SkSpecialImage> SkMatrixConvolutionImageFilter::onFilterImage(SkSpecialImage* source,
@@ -322,15 +304,11 @@ sk_sp<SkSpecialImage> SkMatrixConvolutionImageFilter::onFilterImage(SkSpecialIma
         fKernelSize.width() * fKernelSize.height() <= MAX_KERNEL_SIZE) {
         GrContext* context = source->getContext();
 
-        // If the input is not yet already in the destination color space, do an explicit up-front
-        // conversion. This is extremely unlikely (maybe even impossible). Typically, applyCropRect
-        // will have called pad_image to account for our dilation of bounds, so the result will
-        // already be moved to the destination color space. If someone makes a filter DAG that
-        // avoids that, then we use this fall-back, which saves us from having to do the xform
-        // during the filter itself.
-        if (input->getColorSpace() != ctx.outputProperties().colorSpace()) {
-            input = image_to_color_space(input.get(), ctx.outputProperties());
-        }
+        // Ensure the input is in the destination color space. Typically applyCropRect will have
+        // called pad_image to account for our dilation of bounds, so the result will already be
+        // moved to the destination color space. If a filter DAG avoids that, then we use this
+        // fall-back, which saves us from having to do the xform during the filter itself.
+        input = ImageToColorSpace(input.get(), ctx.outputProperties());
 
         sk_sp<GrTexture> inputTexture(input->asTextureRef(context));
         SkASSERT(inputTexture);
index 42986ce..8b5cbaa 100644 (file)
@@ -13,7 +13,6 @@
 #include "SkReadBuffer.h"
 #include "SkRect.h"
 #include "SkSpecialImage.h"
-#include "SkSpecialSurface.h"
 #include "SkWriteBuffer.h"
 
 #if SK_SUPPORT_GPU
@@ -493,9 +492,6 @@ static sk_sp<SkSpecialImage> apply_morphology(
     sk_sp<SkColorSpace> colorSpace = sk_ref_sp(outputProperties.colorSpace());
     GrPixelConfig config = GrRenderableConfigForColorSpace(colorSpace.get());
 
-    // We force the inputs to this filter into the destination color space in the calling code.
-    SkASSERT(input->getColorSpace() == colorSpace.get());
-
     // setup new clip
     const GrFixedClip clip(SkIRect::MakeWH(srcTexture->width(), srcTexture->height()));
 
@@ -546,23 +542,6 @@ static sk_sp<SkSpecialImage> apply_morphology(
                                                std::move(srcTexture), std::move(colorSpace),
                                                &input->props());
 }
-
-// Return a copy of 'src' transformed to the output's color space
-static sk_sp<SkSpecialImage> image_to_color_space(SkSpecialImage* src,
-                                                  const SkImageFilter::OutputProperties& outProps) {
-    sk_sp<SkSpecialSurface> surf(src->makeSurface(
-        outProps, SkISize::Make(src->width(), src->height())));
-    if (!surf) {
-        return sk_ref_sp(src);
-    }
-
-    SkCanvas* canvas = surf->getCanvas();
-    SkASSERT(canvas);
-
-    src->draw(canvas, 0, 0, nullptr);
-
-    return surf->makeImageSnapshot();
-}
 #endif
 
 sk_sp<SkSpecialImage> SkMorphologyImageFilter::onFilterImage(SkSpecialImage* source,
@@ -603,15 +582,11 @@ sk_sp<SkSpecialImage> SkMorphologyImageFilter::onFilterImage(SkSpecialImage* sou
     if (source->isTextureBacked()) {
         GrContext* context = source->getContext();
 
-        // If the input is not yet already in the destination color space, do an explicit up-front
-        // conversion. This is extremely unlikely (maybe even impossible). Typically, applyCropRect
-        // will have called pad_image to account for our dilation of bounds, so the result will
-        // already be moved to the destination color space. If someone makes a filter DAG that
-        // avoids that, then we use this fall-back, which saves us from having to do the xform
-        // during the filter itself.
-        if (input->getColorSpace() != ctx.outputProperties().colorSpace()) {
-            input = image_to_color_space(input.get(), ctx.outputProperties());
-        }
+        // Ensure the input is in the destination color space. Typically applyCropRect will have
+        // called pad_image to account for our dilation of bounds, so the result will already be
+        // moved to the destination color space. If a filter DAG avoids that, then we use this
+        // fall-back, which saves us from having to do the xform during the filter itself.
+        input = ImageToColorSpace(input.get(), ctx.outputProperties());
 
         auto type = (kDilate_Op == this->op()) ? GrMorphologyEffect::kDilate_MorphologyType
                                                : GrMorphologyEffect::kErode_MorphologyType;