From cd56f812e09fdd8f8322c5c28cbc4423a74b9a0a Mon Sep 17 00:00:00 2001 From: fmalita Date: Mon, 14 Sep 2015 13:31:18 -0700 Subject: [PATCH] SkImageSource 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 | 80 +++++++++++++++++ gyp/effects.gypi | 2 + include/effects/SkImageSource.h | 48 +++++++++++ src/core/SkPictureData.cpp | 38 +-------- src/core/SkReadBuffer.cpp | 42 +++++++++ src/core/SkReadBuffer.h | 3 + src/effects/SkImageSource.cpp | 118 ++++++++++++++++++++++++++ src/ports/SkGlobalInitialization_chromium.cpp | 2 + src/ports/SkGlobalInitialization_default.cpp | 2 + tests/ImageFilterTest.cpp | 26 ++++++ 10 files changed, 324 insertions(+), 37 deletions(-) create mode 100644 gm/imagesource.cpp create mode 100644 include/effects/SkImageSource.h create mode 100644 src/effects/SkImageSource.cpp diff --git a/gm/imagesource.cpp b/gm/imagesource.cpp new file mode 100644 index 0000000..f4574d2 --- /dev/null +++ b/gm/imagesource.cpp @@ -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 imageSource(SkImageSource::Create(fImage)); + SkAutoTUnref imageSourceSrcRect( + SkImageSource::Create(fImage, srcRect, srcRect, kHigh_SkFilterQuality)); + SkAutoTUnref imageSourceSrcRectDstRect( + SkImageSource::Create(fImage, srcRect, dstRect, kHigh_SkFilterQuality)); + SkAutoTUnref 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 fImage; + typedef GM INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +DEF_GM( return new ImageSourceGM; ) diff --git a/gyp/effects.gypi b/gyp/effects.gypi index 0539424..188be80 100644 --- a/gyp/effects.gypi +++ b/gyp/effects.gypi @@ -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', @@ -100,6 +101,7 @@ '<(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 index 0000000..56c6a6e --- /dev/null +++ b/include/effects/SkImageSource.h @@ -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 fImage; + SkRect fSrcRect, fDstRect; + SkFilterQuality fFilterQuality; + + typedef SkImageFilter INHERITED; +}; + +#endif diff --git a/src/core/SkPictureData.cpp b/src/core/SkPictureData.cpp index 9d497f6..9ed98cf 100644 --- a/src/core/SkPictureData.cpp +++ b/src/core/SkPictureData.cpp @@ -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 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, diff --git a/src/core/SkReadBuffer.cpp b/src/core/SkReadBuffer.cpp index 00e3e2c..4a1d44f 100644 --- a/src/core/SkReadBuffer.cpp +++ b/src/core/SkReadBuffer.cpp @@ -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 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(); diff --git a/src/core/SkReadBuffer.h b/src/core/SkReadBuffer.h index 06b5a77..3d6dd36 100644 --- a/src/core/SkReadBuffer.h +++ b/src/core/SkReadBuffer.h @@ -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 index 0000000..8f8c72b --- /dev/null +++ b/src/effects/SkImageSource.cpp @@ -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 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 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 diff --git a/src/ports/SkGlobalInitialization_chromium.cpp b/src/ports/SkGlobalInitialization_chromium.cpp index 04ca841..e0c7d19 100644 --- a/src/ports/SkGlobalInitialization_chromium.cpp +++ b/src/ports/SkGlobalInitialization_chromium.cpp @@ -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) diff --git a/src/ports/SkGlobalInitialization_default.cpp b/src/ports/SkGlobalInitialization_default.cpp index 0d05c1d..73e4f5a 100644 --- a/src/ports/SkGlobalInitialization_default.cpp +++ b/src/ports/SkGlobalInitialization_default.cpp @@ -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) diff --git a/tests/ImageFilterTest.cpp b/tests/ImageFilterTest.cpp index 8c32066..8444bd3 100644 --- a/tests/ImageFilterTest.cpp +++ b/tests/ImageFilterTest.cpp @@ -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 surface(SkSurface::NewRasterN32Premul(10, 10)); + surface->getCanvas()->clear(SK_ColorGREEN); + SkAutoTUnref image(surface->newImageSnapshot()); + SkAutoTUnref filter(SkImageSource::Create(image)); + + SkAutoTUnref data(SkValidatingSerializeFlattenable(filter)); + SkAutoTUnref flattenable(SkValidatingDeserializeFlattenable( + data->data(), data->size(), SkImageFilter::GetFlattenableType())); + SkImageFilter* unflattenedFilter = static_cast(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) { -- 2.7.4