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
This commit is contained in:
fmalita 2015-09-14 13:31:18 -07:00 committed by Commit bot
parent 506c802a3d
commit cd56f812e0
10 changed files with 324 additions and 37 deletions

80
gm/imagesource.cpp Normal file
View File

@ -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; )

View 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',
@ -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',

View File

@ -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

View 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,

View 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();

View 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) {

View File

@ -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

View 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)

View 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)

View 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) {