diff --git a/gm/bitmapsource.cpp b/gm/bitmapsource.cpp new file mode 100644 index 0000000000..d6e98a5bbd --- /dev/null +++ b/gm/bitmapsource.cpp @@ -0,0 +1,91 @@ +/* + * 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 "SkBitmapSource.h" + +// This GM exercises the SkBitmapSource ImageFilter class. + +class BitmapSourceGM : public skiagm::GM { +public: + BitmapSourceGM() { + } + +protected: + virtual SkString onShortName() SK_OVERRIDE { + return SkString("bitmapsource"); + } + + void makeBitmap() { + fBitmap.setConfig(SkBitmap::kARGB_8888_Config, 100, 100); + fBitmap.allocPixels(); + SkBitmapDevice device(fBitmap); + SkCanvas canvas(&device); + canvas.clear(0x00000000); + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(0xFFFFFFFF); + paint.setTextSize(SkIntToScalar(96)); + const char* str = "e"; + canvas.drawText(str, strlen(str), SkIntToScalar(20), SkIntToScalar(70), paint); + } + + virtual SkISize onISize() SK_OVERRIDE { return SkISize::Make(500, 150); } + + virtual void onOnceBeforeDraw() SK_OVERRIDE { + this->makeBitmap(); + } + + 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(); + } + + virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE { + canvas->clear(0x00000000); + { + 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; + fBitmap.getBounds(&bounds); + SkAutoTUnref bitmapSource(new SkBitmapSource(fBitmap)); + SkAutoTUnref bitmapSourceSrcRect(new SkBitmapSource(fBitmap, srcRect, srcRect)); + SkAutoTUnref bitmapSourceSrcRectDstRect(new SkBitmapSource(fBitmap, srcRect, dstRect)); + SkAutoTUnref bitmapSourceDstRectOnly(new SkBitmapSource(fBitmap, bounds, dstRect)); + + // Draw an unscaled bitmap. + fillRectFiltered(canvas, clipRect, bitmapSource); + canvas->translate(SkIntToScalar(100), 0); + + // Draw an unscaled subset of the source bitmap (srcRect -> srcRect). + fillRectFiltered(canvas, clipRect, bitmapSourceSrcRect); + canvas->translate(SkIntToScalar(100), 0); + + // Draw a subset of the bitmap scaled to a destination rect (srcRect -> dstRect). + fillRectFiltered(canvas, clipRect, bitmapSourceSrcRectDstRect); + canvas->translate(SkIntToScalar(100), 0); + + // Draw the entire bitmap scaled to a destination rect (bounds -> dstRect). + fillRectFiltered(canvas, clipRect, bitmapSourceDstRectOnly); + canvas->translate(SkIntToScalar(100), 0); + } + } + +private: + SkBitmap fBitmap; + typedef GM INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +DEF_GM( return new BitmapSourceGM; ) diff --git a/gm/tileimagefilter.cpp b/gm/tileimagefilter.cpp index 9a14344772..48d65ca073 100644 --- a/gm/tileimagefilter.cpp +++ b/gm/tileimagefilter.cpp @@ -70,9 +70,10 @@ protected: void drawClippedBitmap(SkCanvas* canvas, const SkBitmap& bitmap, const SkPaint& paint, SkScalar x, SkScalar y) { canvas->save(); - canvas->clipRect(SkRect::MakeXYWH(x, y, + canvas->translate(x, y); + canvas->clipRect(SkRect::MakeXYWH(0, 0, SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()))); - canvas->drawBitmap(bitmap, x, y, &paint); + canvas->drawBitmap(bitmap, 0, 0, &paint); canvas->restore(); } diff --git a/gm/xfermodeimagefilter.cpp b/gm/xfermodeimagefilter.cpp index 01bfe0ad61..936af46ec6 100644 --- a/gm/xfermodeimagefilter.cpp +++ b/gm/xfermodeimagefilter.cpp @@ -19,12 +19,12 @@ namespace skiagm { class XfermodeImageFilterGM : public GM { public: - XfermodeImageFilterGM() : fInitialized(false) { + XfermodeImageFilterGM(){ this->setBGColor(0xFF000000); } protected: - virtual SkString onShortName() { + virtual SkString onShortName() SK_OVERRIDE { return SkString("xfermodeimagefilter"); } @@ -65,25 +65,35 @@ protected: } } - virtual SkISize onISize() { + virtual SkISize onISize() SK_OVERRIDE { return make_isize(WIDTH, HEIGHT); } - void drawClippedBitmap(SkCanvas* canvas, const SkBitmap& bitmap, const SkPaint& paint, - SkScalar x, SkScalar y) { + static void drawClippedBitmap(SkCanvas* canvas, const SkBitmap& bitmap, const SkPaint& paint, + int x, int y) { canvas->save(); - canvas->clipRect(SkRect::MakeXYWH(x, y, + canvas->translate(SkIntToScalar(x), SkIntToScalar(y)); + canvas->clipRect(SkRect::MakeWH( SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()))); - canvas->drawBitmap(bitmap, x, y, &paint); + canvas->drawBitmap(bitmap, 0, 0, &paint); canvas->restore(); } - virtual void onDraw(SkCanvas* canvas) { - if (!fInitialized) { - make_bitmap(); - make_checkerboard(); - fInitialized = true; - } + static void drawClippedPaint(SkCanvas* canvas, const SkRect& rect, const SkPaint& paint, + int x, int y) { + canvas->save(); + canvas->translate(SkIntToScalar(x), SkIntToScalar(y)); + canvas->clipRect(rect); + canvas->drawPaint(paint); + canvas->restore(); + } + + virtual void onOnceBeforeDraw() SK_OVERRIDE { + make_bitmap(); + make_checkerboard(); + } + + virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE { canvas->clear(0x00000000); SkPaint paint; @@ -130,7 +140,7 @@ protected: SkAutoTUnref filter(SkNEW_ARGS( SkXfermodeImageFilter, (mode, background))); paint.setImageFilter(filter); - drawClippedBitmap(canvas, fBitmap, paint, SkIntToScalar(x), SkIntToScalar(y)); + drawClippedBitmap(canvas, fBitmap, paint, x, y); x += fBitmap.width() + MARGIN; if (x + fBitmap.width() > WIDTH) { x = 0; @@ -141,7 +151,7 @@ protected: SkAutoTUnref mode(SkArithmeticMode::Create(0, SK_Scalar1, SK_Scalar1, 0)); SkAutoTUnref filter(SkNEW_ARGS(SkXfermodeImageFilter, (mode, background))); paint.setImageFilter(filter); - drawClippedBitmap(canvas, fBitmap, paint, SkIntToScalar(x), SkIntToScalar(y)); + drawClippedBitmap(canvas, fBitmap, paint, x, y); x += fBitmap.width() + MARGIN; if (x + fBitmap.width() > WIDTH) { x = 0; @@ -150,12 +160,14 @@ protected: // Test NULL mode filter.reset(SkNEW_ARGS(SkXfermodeImageFilter, (NULL, background))); paint.setImageFilter(filter); - drawClippedBitmap(canvas, fBitmap, paint, SkIntToScalar(x), SkIntToScalar(y)); + drawClippedBitmap(canvas, fBitmap, paint, x, y); x += fBitmap.width() + MARGIN; if (x + fBitmap.width() > WIDTH) { x = 0; y += fBitmap.height() + MARGIN; } + SkRect clipRect = SkRect::MakeWH(SkIntToScalar(fBitmap.width() + 4), + SkIntToScalar(fBitmap.height() + 4)); // Test offsets on SrcMode (uses fixed-function blend) SkAutoTUnref foreground(SkNEW_ARGS(SkBitmapSource, (fBitmap))); SkAutoTUnref offsetForeground(SkNEW_ARGS(SkOffsetImageFilter, @@ -166,13 +178,7 @@ protected: filter.reset(SkNEW_ARGS(SkXfermodeImageFilter, (mode, offsetBackground, offsetForeground))); paint.setImageFilter(filter); - canvas->save(); - canvas->clipRect(SkRect::MakeXYWH(SkIntToScalar(x), - SkIntToScalar(y), - SkIntToScalar(fBitmap.width() + 4), - SkIntToScalar(fBitmap.height() + 4))); - canvas->drawPaint(paint); - canvas->restore(); + drawClippedPaint(canvas, clipRect, paint, x, y); x += fBitmap.width() + MARGIN; if (x + fBitmap.width() > WIDTH) { x = 0; @@ -182,13 +188,7 @@ protected: mode.reset(SkXfermode::Create(SkXfermode::kDarken_Mode)); filter.reset(SkNEW_ARGS(SkXfermodeImageFilter, (mode, offsetBackground, offsetForeground))); paint.setImageFilter(filter); - canvas->save(); - canvas->clipRect(SkRect::MakeXYWH(SkIntToScalar(x), - SkIntToScalar(y), - SkIntToScalar(fBitmap.width() + 4), - SkIntToScalar(fBitmap.height() + 4))); - canvas->drawPaint(paint); - canvas->restore(); + drawClippedPaint(canvas, clipRect, paint, x, y); x += fBitmap.width() + MARGIN; if (x + fBitmap.width() > WIDTH) { x = 0; @@ -203,8 +203,8 @@ protected: { 10, 10, 10, 10}, {-10, -10, -6, -6}}; for (size_t i = 0; i < nbSamples; ++i) { - SkIRect cropRect = SkIRect::MakeXYWH(x + offsets[i][0], - y + offsets[i][1], + SkIRect cropRect = SkIRect::MakeXYWH(offsets[i][0], + offsets[i][1], fBitmap.width() + offsets[i][2], fBitmap.height() + offsets[i][3]); SkImageFilter::CropRect rect(SkRect::Make(cropRect)); @@ -212,13 +212,7 @@ protected: filter.reset(SkNEW_ARGS(SkXfermodeImageFilter, (mode, offsetBackground, offsetForeground, &rect))); paint.setImageFilter(filter); - canvas->save(); - canvas->clipRect(SkRect::MakeXYWH(SkIntToScalar(x), - SkIntToScalar(y), - SkIntToScalar(fBitmap.width() + 4), - SkIntToScalar(fBitmap.height() + 4))); - canvas->drawPaint(paint); - canvas->restore(); + drawClippedPaint(canvas, clipRect, paint, x, y); x += fBitmap.width() + MARGIN; if (x + fBitmap.width() > WIDTH) { x = 0; @@ -227,14 +221,12 @@ protected: } } private: - typedef GM INHERITED; SkBitmap fBitmap, fCheckerboard; - bool fInitialized; + typedef GM INHERITED; }; ////////////////////////////////////////////////////////////////////////////// -static GM* MyFactory(void*) { return new XfermodeImageFilterGM; } -static GMRegistry reg(MyFactory); +DEF_GM( return new XfermodeImageFilterGM; ); } diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi index 149d037b59..e29a702cfb 100644 --- a/gyp/gmslides.gypi +++ b/gyp/gmslides.gypi @@ -21,6 +21,7 @@ '../gm/bitmaprecttest.cpp', '../gm/bitmapscroll.cpp', '../gm/bitmapshader.cpp', + '../gm/bitmapsource.cpp', '../gm/bleed.cpp', '../gm/blurs.cpp', '../gm/blurquickreject.cpp', diff --git a/include/effects/SkBitmapSource.h b/include/effects/SkBitmapSource.h index 138987e701..699186e9f1 100644 --- a/include/effects/SkBitmapSource.h +++ b/include/effects/SkBitmapSource.h @@ -14,6 +14,7 @@ class SK_API SkBitmapSource : public SkImageFilter { public: explicit SkBitmapSource(const SkBitmap& bitmap); + SkBitmapSource(const SkBitmap& bitmap, const SkRect& srcRect, const SkRect& dstRect); SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBitmapSource) @@ -25,6 +26,7 @@ protected: private: SkBitmap fBitmap; + SkRect fSrcRect, fDstRect; typedef SkImageFilter INHERITED; }; diff --git a/src/effects/SkBitmapSource.cpp b/src/effects/SkBitmapSource.cpp index 72f51f8423..ef5ea86925 100644 --- a/src/effects/SkBitmapSource.cpp +++ b/src/effects/SkBitmapSource.cpp @@ -6,24 +6,74 @@ */ #include "SkBitmapSource.h" +#include "SkDevice.h" +#include "SkCanvas.h" +#include "SkFlattenableBuffers.h" +#include "SkValidationUtils.h" SkBitmapSource::SkBitmapSource(const SkBitmap& bitmap) : INHERITED(0, 0), - fBitmap(bitmap) { + fBitmap(bitmap), + fSrcRect(SkRect::MakeWH(SkIntToScalar(bitmap.width()), + SkIntToScalar(bitmap.height()))), + fDstRect(fSrcRect) { +} + +SkBitmapSource::SkBitmapSource(const SkBitmap& bitmap, const SkRect& srcRect, const SkRect& dstRect) + : INHERITED(0, 0), + fBitmap(bitmap), + fSrcRect(srcRect), + fDstRect(dstRect) { } SkBitmapSource::SkBitmapSource(SkFlattenableReadBuffer& buffer) : INHERITED(0, buffer) { fBitmap.unflatten(buffer); + buffer.readRect(&fSrcRect); + buffer.readRect(&fDstRect); + buffer.validate(SkIsValidRect(fSrcRect) && SkIsValidRect(fDstRect)); } void SkBitmapSource::flatten(SkFlattenableWriteBuffer& buffer) const { this->INHERITED::flatten(buffer); fBitmap.flatten(buffer); + buffer.writeRect(fSrcRect); + buffer.writeRect(fDstRect); } -bool SkBitmapSource::onFilterImage(Proxy*, const SkBitmap&, const SkMatrix&, +bool SkBitmapSource::onFilterImage(Proxy* proxy, const SkBitmap&, const SkMatrix& matrix, SkBitmap* result, SkIPoint* offset) { - *result = fBitmap; + SkRect bounds, dstRect; + fBitmap.getBounds(&bounds); + matrix.mapRect(&dstRect, fDstRect); + if (fSrcRect == bounds && dstRect == bounds) { + // No regions cropped out or resized; return entire bitmap. + *result = fBitmap; + return true; + } + SkIRect dstIRect; + dstRect.roundOut(&dstIRect); + + SkAutoTUnref device(proxy->createDevice(dstIRect.width(), dstIRect.height())); + if (NULL == 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 drawBitmapRectToRect asserts + // None filtering when it's translate-only + paint.setFilterLevel( + fSrcRect.width() == dstRect.width() && fSrcRect.height() == dstRect.height() ? + SkPaint::kNone_FilterLevel : SkPaint::kMedium_FilterLevel); + canvas.drawBitmapRectToRect(fBitmap, &fSrcRect, dstRect, &paint); + + *result = device.get()->accessBitmap(false); + offset->fX += dstIRect.fLeft; + offset->fY += dstIRect.fTop; return true; } diff --git a/src/effects/SkTileImageFilter.cpp b/src/effects/SkTileImageFilter.cpp index c5eace0b74..73e5304adb 100644 --- a/src/effects/SkTileImageFilter.cpp +++ b/src/effects/SkTileImageFilter.cpp @@ -24,16 +24,24 @@ bool SkTileImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src, const S return false; } - int w = SkScalarTruncToInt(fDstRect.width()); - int h = SkScalarTruncToInt(fDstRect.height()); + SkRect dstRect; + ctm.mapRect(&dstRect, fDstRect); + int w = SkScalarCeilToInt(dstRect.width()); + int h = SkScalarCeilToInt(dstRect.height()); if (!fSrcRect.width() || !fSrcRect.height() || !w || !h) { return false; } - SkIRect srcRect; - fSrcRect.roundOut(&srcRect); + SkRect srcRect; + ctm.mapRect(&srcRect, fSrcRect); + SkIRect srcIRect; + srcRect.roundOut(&srcIRect); SkBitmap subset; - if (!source.extractSubset(&subset, srcRect)) { + SkIRect bounds; + source.getBounds(&bounds); + if (!srcIRect.intersect(bounds)) { + return true; + } else if (!source.extractSubset(&subset, srcIRect)) { return false; } @@ -41,8 +49,6 @@ bool SkTileImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src, const S if (NULL == device.get()) { return false; } - SkIRect bounds; - source.getBounds(&bounds); SkCanvas canvas(device); SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); @@ -50,7 +56,6 @@ bool SkTileImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src, const S SkAutoTUnref shader(SkShader::CreateBitmapShader(subset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode)); paint.setShader(shader); - SkRect dstRect = fDstRect; dstRect.offset(SkIntToScalar(localOffset.fX), SkIntToScalar(localOffset.fY)); canvas.drawRect(dstRect, paint); *dst = device->accessBitmap(false);