SkImageSource
authorfmalita <fmalita@chromium.org>
Mon, 14 Sep 2015 20:31:18 +0000 (13:31 -0700)
committerCommit bot <commit-bot@chromium.org>
Mon, 14 Sep 2015 20:31:18 +0000 (13:31 -0700)
Blink is migrating away from SkBitmaps, so we need an SkImage-based
SkImageFilter source.  This is pretty much a 1-1 equivalent of
SkBitmapSource.

To avoid duplication, relocate the SkImage deserialization logic
from SkPictureData to SkReadBuffer.

R=reed@google.com,robertphillips@google.com,senorblanco@chromium.org

Review URL: https://codereview.chromium.org/1343703005

gm/imagesource.cpp [new file with mode: 0644]
gyp/effects.gypi
include/effects/SkImageSource.h [new file with mode: 0644]
src/core/SkPictureData.cpp
src/core/SkReadBuffer.cpp
src/core/SkReadBuffer.h
src/effects/SkImageSource.cpp [new file with mode: 0644]
src/ports/SkGlobalInitialization_chromium.cpp
src/ports/SkGlobalInitialization_default.cpp
tests/ImageFilterTest.cpp

diff --git a/gm/imagesource.cpp b/gm/imagesource.cpp
new file mode 100644 (file)
index 0000000..f4574d2
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+
+#include "SkImage.h"
+#include "SkImageSource.h"
+
+// This GM exercises the SkImageSource ImageFilter class.
+
+class ImageSourceGM : public skiagm::GM {
+public:
+    ImageSourceGM() { }
+
+protected:
+    SkString onShortName() override {
+        return SkString("imagesource");
+    }
+
+    SkISize onISize() override { return SkISize::Make(500, 150); }
+
+    void onOnceBeforeDraw() override {
+        SkBitmap bm = sk_tool_utils::create_string_bitmap(100, 100, 0xFFFFFFFF, 20, 70, 96, "e");
+        fImage.reset(SkImage::NewFromBitmap(bm));
+    }
+
+    static void FillRectFiltered(SkCanvas* canvas, const SkRect& clipRect, SkImageFilter* filter) {
+        SkPaint paint;
+        paint.setImageFilter(filter);
+        canvas->save();
+        canvas->clipRect(clipRect);
+        canvas->drawPaint(paint);
+        canvas->restore();
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        canvas->clear(SK_ColorBLACK);
+        {
+            SkRect srcRect = SkRect::MakeXYWH(20, 20, 30, 30);
+            SkRect dstRect = SkRect::MakeXYWH(0, 10, 60, 60);
+            SkRect clipRect = SkRect::MakeXYWH(0, 0, 100, 100);
+            SkRect bounds = SkRect::MakeIWH(fImage->width(), fImage->height());
+            SkAutoTUnref<SkImageFilter> imageSource(SkImageSource::Create(fImage));
+            SkAutoTUnref<SkImageFilter> imageSourceSrcRect(
+                SkImageSource::Create(fImage, srcRect, srcRect, kHigh_SkFilterQuality));
+            SkAutoTUnref<SkImageFilter> imageSourceSrcRectDstRect(
+                SkImageSource::Create(fImage, srcRect, dstRect, kHigh_SkFilterQuality));
+            SkAutoTUnref<SkImageFilter> imageSourceDstRectOnly(
+                SkImageSource::Create(fImage, bounds, dstRect, kHigh_SkFilterQuality));
+
+            // Draw an unscaled bitmap.
+            FillRectFiltered(canvas, clipRect, imageSource);
+            canvas->translate(SkIntToScalar(100), 0);
+
+            // Draw an unscaled subset of the source bitmap (srcRect -> srcRect).
+            FillRectFiltered(canvas, clipRect, imageSourceSrcRect);
+            canvas->translate(SkIntToScalar(100), 0);
+
+            // Draw a subset of the bitmap scaled to a destination rect (srcRect -> dstRect).
+            FillRectFiltered(canvas, clipRect, imageSourceSrcRectDstRect);
+            canvas->translate(SkIntToScalar(100), 0);
+
+            // Draw the entire bitmap scaled to a destination rect (bounds -> dstRect).
+            FillRectFiltered(canvas, clipRect, imageSourceDstRectOnly);
+            canvas->translate(SkIntToScalar(100), 0);
+        }
+    }
+
+private:
+    SkAutoTUnref<SkImage> fImage;
+    typedef GM INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+DEF_GM( return new ImageSourceGM; )
index 0539424..188be80 100644 (file)
@@ -39,6 +39,7 @@
     '<(skia_src_path)/effects/SkEmbossMask.h',
     '<(skia_src_path)/effects/SkEmbossMask_Table.h',
     '<(skia_src_path)/effects/SkEmbossMaskFilter.cpp',
+    '<(skia_src_path)/effects/SkImageSource.cpp',
     '<(skia_src_path)/effects/SkGpuBlurUtils.h',
     '<(skia_src_path)/effects/SkGpuBlurUtils.cpp',
     '<(skia_src_path)/effects/SkLayerDrawLooper.cpp',
     '<(skia_include_path)/effects/SkDropShadowImageFilter.h',
     '<(skia_include_path)/effects/SkEmbossMaskFilter.h',
     '<(skia_include_path)/effects/SkGradientShader.h',
+    '<(skia_include_path)/effects/SkImageSource.h',
     '<(skia_include_path)/effects/SkLayerDrawLooper.h',
     '<(skia_include_path)/effects/SkLayerRasterizer.h',
     '<(skia_include_path)/effects/SkLerpXfermode.h',
diff --git a/include/effects/SkImageSource.h b/include/effects/SkImageSource.h
new file mode 100644 (file)
index 0000000..56c6a6e
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkImageSource_DEFINED
+#define SkImageSource_DEFINED
+
+#include "SkImageFilter.h"
+
+class SkImage;
+
+class SK_API SkImageSource : public SkImageFilter {
+public:
+    static SkImageFilter* Create(const SkImage*);
+    static SkImageFilter* Create(const SkImage*,
+                                 const SkRect& srcRect,
+                                 const SkRect& dstRect,
+                                 SkFilterQuality);
+
+    void computeFastBounds(const SkRect& src, SkRect* dst) const override;
+
+    SK_TO_STRING_OVERRIDE()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkImageSource)
+
+protected:
+    void flatten(SkWriteBuffer&) const override;
+
+    bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
+                       SkBitmap* result, SkIPoint* offset) const override;
+
+private:
+    explicit SkImageSource(const SkImage*);
+    SkImageSource(const SkImage*,
+                  const SkRect& srcRect,
+                  const SkRect& dstRect,
+                  SkFilterQuality);
+
+    SkAutoTUnref<const SkImage> fImage;
+    SkRect                      fSrcRect, fDstRect;
+    SkFilterQuality             fFilterQuality;
+
+    typedef SkImageFilter INHERITED;
+};
+
+#endif
index 9d497f6..9ed98cf 100644 (file)
@@ -434,44 +434,8 @@ bool SkPictureData::parseStreamTag(SkStream* stream,
     return true;    // success
 }
 
-namespace {
-
-// This generator intentionally should always fail on all attempts to get its pixels,
-// simulating a bad or empty codec stream.
-class EmptyImageGenerator final : public SkImageGenerator {
-public:
-    EmptyImageGenerator(const SkImageInfo& info) : INHERITED(info) { }
-
-private:
-    typedef SkImageGenerator INHERITED;
-};
-
-} // anonymous namespace
-
 static const SkImage* create_image_from_buffer(SkReadBuffer& buffer) {
-    int width = buffer.read32();
-    int height = buffer.read32();
-    if (width <= 0 || height <= 0) {    // SkImage never has a zero dimension
-        buffer.validate(false);
-        return nullptr;
-    }
-
-    SkAutoTUnref<SkData> encoded(buffer.readByteArrayAsData());
-    if (encoded->size() == 0) {
-        // The image could not be encoded at serialization time - return an empty placeholder.
-        return SkImage::NewFromGenerator(
-            new EmptyImageGenerator(SkImageInfo::MakeN32Premul(width, height)));
-    }
-
-    int originX = buffer.read32();
-    int originY = buffer.read32();
-    if (originX < 0 || originY < 0) {
-        buffer.validate(false);
-        return nullptr;
-    }
-
-    const SkIRect subset = SkIRect::MakeXYWH(originX, originY, width, height);
-    return SkImage::NewFromEncoded(encoded, &subset);
+    return buffer.readImage();
 }
 
 // Need a shallow wrapper to return const SkPicture* to match the other factories,
index 00e3e2c..4a1d44f 100644 (file)
@@ -8,6 +8,8 @@
 
 #include "SkBitmap.h"
 #include "SkErrorInternals.h"
+#include "SkImage.h"
+#include "SkImageGenerator.h"
 #include "SkReadBuffer.h"
 #include "SkStream.h"
 #include "SkTypeface.h"
@@ -274,6 +276,46 @@ bool SkReadBuffer::readBitmap(SkBitmap* bitmap) {
     return false;
 }
 
+namespace {
+
+// This generator intentionally should always fail on all attempts to get its pixels,
+// simulating a bad or empty codec stream.
+class EmptyImageGenerator final : public SkImageGenerator {
+public:
+    EmptyImageGenerator(const SkImageInfo& info) : INHERITED(info) { }
+
+private:
+    typedef SkImageGenerator INHERITED;
+};
+
+} // anonymous namespace
+
+SkImage* SkReadBuffer::readImage() {
+    int width = this->read32();
+    int height = this->read32();
+    if (width <= 0 || height <= 0) {    // SkImage never has a zero dimension
+        this->validate(false);
+        return nullptr;
+    }
+
+    SkAutoTUnref<SkData> encoded(this->readByteArrayAsData());
+    if (encoded->size() == 0) {
+        // The image could not be encoded at serialization time - return an empty placeholder.
+        return SkImage::NewFromGenerator(
+            new EmptyImageGenerator(SkImageInfo::MakeN32Premul(width, height)));
+    }
+
+    int originX = this->read32();
+    int originY = this->read32();
+    if (originX < 0 || originY < 0) {
+        this->validate(false);
+        return nullptr;
+    }
+
+    const SkIRect subset = SkIRect::MakeXYWH(originX, originY, width, height);
+    return SkImage::NewFromEncoded(encoded, &subset);
+}
+
 SkTypeface* SkReadBuffer::readTypeface() {
 
     uint32_t index = fReader.readU32();
index 06b5a77..3d6dd36 100644 (file)
@@ -26,6 +26,7 @@
 #include "SkXfermode.h"
 
 class SkBitmap;
+class SkImage;
 
 #if defined(SK_DEBUG) && defined(SK_BUILD_FOR_MAC)
     #define DEBUG_NON_DETERMINISTIC_ASSERT
@@ -169,6 +170,8 @@ public:
      */
     bool readBitmap(SkBitmap* bitmap);
 
+    SkImage* readImage();
+
     virtual SkTypeface* readTypeface();
 
     void setBitmapStorage(SkBitmapHeapReader* bitmapStorage) {
diff --git a/src/effects/SkImageSource.cpp b/src/effects/SkImageSource.cpp
new file mode 100644 (file)
index 0000000..8f8c72b
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkImageSource.h"
+
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkImage.h"
+#include "SkReadBuffer.h"
+#include "SkWriteBuffer.h"
+#include "SkString.h"
+
+SkImageFilter* SkImageSource::Create(const SkImage* image) {
+    return image ? SkNEW_ARGS(SkImageSource, (image)) : nullptr;
+}
+
+SkImageFilter* SkImageSource::Create(const SkImage* image,
+                                     const SkRect& srcRect,
+                                     const SkRect& dstRect,
+                                     SkFilterQuality filterQuality) {
+    return image ? SkNEW_ARGS(SkImageSource, (image, srcRect, dstRect, filterQuality)) : nullptr;
+}
+
+SkImageSource::SkImageSource(const SkImage* image)
+    : INHERITED(0, nullptr)
+    , fImage(SkRef(image))
+    , fSrcRect(SkRect::MakeIWH(image->width(), image->height()))
+    , fDstRect(fSrcRect)
+    , fFilterQuality(kHigh_SkFilterQuality) { }
+
+SkImageSource::SkImageSource(const SkImage* image,
+                             const SkRect& srcRect,
+                             const SkRect& dstRect,
+                             SkFilterQuality filterQuality)
+    : INHERITED(0, nullptr)
+    , fImage(SkRef(image))
+    , fSrcRect(srcRect)
+    , fDstRect(dstRect)
+    , fFilterQuality(filterQuality) { }
+
+SkFlattenable* SkImageSource::CreateProc(SkReadBuffer& buffer) {
+    SkFilterQuality filterQuality = (SkFilterQuality)buffer.readInt();
+
+    SkRect src, dst;
+    buffer.readRect(&src);
+    buffer.readRect(&dst);
+
+    SkAutoTUnref<SkImage> image(buffer.readImage());
+    if (!image) {
+        return nullptr;
+    }
+
+    return SkImageSource::Create(image, src, dst, filterQuality);
+}
+
+void SkImageSource::flatten(SkWriteBuffer& buffer) const {
+    buffer.writeInt(fFilterQuality);
+    buffer.writeRect(fSrcRect);
+    buffer.writeRect(fDstRect);
+    buffer.writeImage(fImage);
+}
+
+bool SkImageSource::onFilterImage(Proxy* proxy, const SkBitmap& src, const Context& ctx,
+                                  SkBitmap* result, SkIPoint* offset) const {
+    SkRect dstRect;
+    ctx.ctm().mapRect(&dstRect, fDstRect);
+    SkRect bounds = SkRect::MakeIWH(fImage->width(), fImage->height());
+    if (fSrcRect == bounds && dstRect == bounds) {
+        // No regions cropped out or resized; return entire image.
+        offset->fX = offset->fY = 0;
+        return fImage->asLegacyBitmap(result, SkImage::kRO_LegacyBitmapMode);
+    }
+
+    const SkIRect dstIRect = dstRect.roundOut();
+    SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(dstIRect.width(), dstIRect.height()));
+    if (nullptr == device.get()) {
+        return false;
+    }
+
+    SkCanvas canvas(device.get());
+    SkPaint paint;
+
+    // Subtract off the integer component of the translation (will be applied in loc, below).
+    dstRect.offset(-SkIntToScalar(dstIRect.fLeft), -SkIntToScalar(dstIRect.fTop));
+    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+    // FIXME: this probably shouldn't be necessary, but drawImageRect asserts
+    // None filtering when it's translate-only
+    paint.setFilterQuality(
+        fSrcRect.width() == dstRect.width() && fSrcRect.height() == dstRect.height() ?
+               kNone_SkFilterQuality : fFilterQuality);
+    canvas.drawImageRect(fImage, fSrcRect, dstRect, &paint, SkCanvas::kStrict_SrcRectConstraint);
+
+    *result = device.get()->accessBitmap(false);
+    offset->fX = dstIRect.fLeft;
+    offset->fY = dstIRect.fTop;
+
+    return true;
+}
+
+void SkImageSource::computeFastBounds(const SkRect& src, SkRect* dst) const {
+    *dst = fDstRect;
+}
+
+#ifndef SK_IGNORE_TO_STRING
+void SkImageSource::toString(SkString* str) const {
+    str->appendf("SkImageSource: (");
+    str->appendf("src: (%f,%f,%f,%f) dst: (%f,%f,%f,%f) ",
+                 fSrcRect.fLeft, fSrcRect.fTop, fSrcRect.fRight, fSrcRect.fBottom,
+                 fDstRect.fLeft, fDstRect.fTop, fDstRect.fRight, fDstRect.fBottom);
+    str->appendf("image: (%d,%d)",
+                 fImage->width(), fImage->height());
+    str->append(")");
+}
+#endif
index 04ca841..e0c7d19 100644 (file)
@@ -37,6 +37,7 @@
 #include "SkEmbossMaskFilter.h"
 #include "SkFlattenable.h"
 #include "SkGradientShader.h"
+#include "SkImageSource.h"
 #include "SkLayerDrawLooper.h"
 #include "SkLayerRasterizer.h"
 #include "SkLerpXfermode.h"
@@ -101,6 +102,7 @@ public:
         SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkEmbossMaskFilter)
         SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkEmptyShader)
         SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkErodeImageFilter)
+        SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkImageSource)
         SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLayerDrawLooper)
         SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLayerRasterizer)
         SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLerpXfermode)
index 0d05c1d..73e4f5a 100644 (file)
@@ -33,6 +33,7 @@
 #include "SkEmbossMaskFilter.h"
 #include "SkFlattenable.h"
 #include "SkGradientShader.h"
+#include "SkImageSource.h"
 #include "SkLayerDrawLooper.h"
 #include "SkLayerRasterizer.h"
 #include "SkLerpXfermode.h"
@@ -80,6 +81,7 @@ public:
         SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkEmbossMaskFilter)
         SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkEmptyShader)
         SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkErodeImageFilter)
+        SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkImageSource)
         SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLayerDrawLooper)
         SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLayerRasterizer)
         SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLerpXfermode)
index 8c32066..8444bd3 100644 (file)
@@ -17,6 +17,7 @@
 #include "SkDropShadowImageFilter.h"
 #include "SkFlattenableSerialization.h"
 #include "SkGradientShader.h"
+#include "SkImageSource.h"
 #include "SkLightingImageFilter.h"
 #include "SkMatrixConvolutionImageFilter.h"
 #include "SkMergeImageFilter.h"
@@ -30,6 +31,7 @@
 #include "SkReadBuffer.h"
 #include "SkRect.h"
 #include "SkRectShaderImageFilter.h"
+#include "SkSurface.h"
 #include "SkTableColorFilter.h"
 #include "SkTileImageFilter.h"
 #include "SkXfermodeImageFilter.h"
@@ -1212,6 +1214,30 @@ DEF_TEST(ImageFilterCanComputeFastBounds, reporter) {
     REPORTER_ASSERT(reporter, !forceOpaque->canComputeFastBounds());
 }
 
+// Verify that SkImageSource survives serialization
+DEF_TEST(ImageFilterImageSourceSerialization, reporter) {
+    SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(10, 10));
+    surface->getCanvas()->clear(SK_ColorGREEN);
+    SkAutoTUnref<SkImage> image(surface->newImageSnapshot());
+    SkAutoTUnref<SkImageFilter> filter(SkImageSource::Create(image));
+
+    SkAutoTUnref<SkData> data(SkValidatingSerializeFlattenable(filter));
+    SkAutoTUnref<SkFlattenable> flattenable(SkValidatingDeserializeFlattenable(
+        data->data(), data->size(), SkImageFilter::GetFlattenableType()));
+    SkImageFilter* unflattenedFilter = static_cast<SkImageFilter*>(flattenable.get());
+    REPORTER_ASSERT(reporter, unflattenedFilter);
+
+    SkBitmap bm;
+    bm.allocN32Pixels(10, 10);
+    SkPaint paint;
+    paint.setColor(SK_ColorRED);
+    paint.setImageFilter(unflattenedFilter);
+
+    SkCanvas canvas(bm);
+    canvas.drawRect(SkRect::MakeWH(10, 10), paint);
+    REPORTER_ASSERT(reporter, *bm.getAddr32(0, 0) == SkPreMultiplyColor(SK_ColorGREEN));
+}
+
 #if SK_SUPPORT_GPU
 
 DEF_GPUTEST(ImageFilterCropRectGPU, reporter, factory) {