From fa77eb1e51b9317ff993d1be504ada173b561e5f Mon Sep 17 00:00:00 2001 From: kkinnunen Date: Thu, 5 Mar 2015 00:39:45 -0800 Subject: [PATCH] Add image as a draw type that can be filtered Add image as a draw type that can be filtered. This is needed when SkImage is added as an object to be drawn so that the draw is forwarded to SkBaseDevice. This would be used in making filters use SkImages. BUG=skia:3388 Review URL: https://codereview.chromium.org/960783003 --- include/core/SkDrawFilter.h | 3 +- src/core/SkCanvas.cpp | 55 ++++++++++++- tests/SkImageTest.cpp | 144 ++++++++++++++++++++++++++++++++++ tools/bench_pictures_main.cpp | 5 ++ 4 files changed, 203 insertions(+), 4 deletions(-) diff --git a/include/core/SkDrawFilter.h b/include/core/SkDrawFilter.h index 52cbba9d20..26771beae5 100644 --- a/include/core/SkDrawFilter.h +++ b/include/core/SkDrawFilter.h @@ -35,10 +35,11 @@ public: kOval_Type, kPath_Type, kText_Type, + kImage_Type, }; enum { - kTypeCount = kText_Type + 1 + kTypeCount = kImage_Type + 1 }; /** diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index a47bd89c05..c0c6f8af3e 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -1848,15 +1848,64 @@ void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { LOOPER_END } -void SkCanvas::onDrawImage(const SkImage* image, SkScalar dx, SkScalar dy, const SkPaint* paint) { +void SkCanvas::onDrawImage(const SkImage* image, SkScalar dx, SkScalar dy, + const SkPaint* paint) { TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()"); - image->draw(this, dx, dy, paint); + + SkRect bounds = SkRect::MakeXYWH(dx, dy, image->width(), image->height()); + if (NULL == paint || paint->canComputeFastBounds()) { + if (NULL != paint) { + paint->computeFastBounds(bounds, &bounds); + } + if (this->quickReject(bounds)) { + return; + } + } + + SkLazyPaint lazy; + if (NULL == paint) { + paint = lazy.init(); + } + + LOOPER_BEGIN(*paint, SkDrawFilter::kImage_Type, &bounds) + + while (iter.next()) { + SkPaint p = looper.paint(); + p.setLooper(NULL); + image->draw(this, dx, dy, &p); + } + + LOOPER_END } void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, const SkPaint* paint) { TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()"); - image->drawRect(this, src, dst, paint); + SkRect storage; + const SkRect* bounds = &dst; + if (NULL == paint || paint->canComputeFastBounds()) { + if (NULL != paint) { + bounds = &paint->computeFastBounds(dst, &storage); + } + if (this->quickReject(*bounds)) { + return; + } + } + + SkLazyPaint lazy; + if (NULL == paint) { + paint = lazy.init(); + } + + LOOPER_BEGIN(*paint, SkDrawFilter::kImage_Type, bounds) + + while (iter.next()) { + SkPaint p = looper.paint(); + p.setLooper(NULL); + image->drawRect(this, src, dst, &p); + } + + LOOPER_END } void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) { diff --git a/tests/SkImageTest.cpp b/tests/SkImageTest.cpp index 80ba776af1..f9684d21aa 100644 --- a/tests/SkImageTest.cpp +++ b/tests/SkImageTest.cpp @@ -6,6 +6,8 @@ */ #include "SkBitmapDevice.h" +#include "SkColor.h" +#include "SkDrawFilter.h" #include "SkImagePriv.h" #include "Test.h" @@ -47,3 +49,145 @@ DEF_TEST(SkImageFromBitmap_extractSubset, reporter) { canvas.readPixels(info, &pixel, 4, gWidth - 5, gWidth - 5); REPORTER_ASSERT(reporter, pixel == SK_ColorTRANSPARENT); } + +namespace { +class TestDrawFilterImage : public SkDrawFilter { +public: + TestDrawFilterImage() + : fFilteredImage(0) + , fFilteredOthers(0) + , fPreventImages(true) + , fPreventOthers(true) { + } + + bool filter(SkPaint*, Type type) SK_OVERRIDE { + if (type == SkDrawFilter::kImage_Type) { + if (fPreventImages) { + fFilteredImage++; + return false; + } + return true; + } + + if (fPreventOthers) { + fFilteredOthers++; + return false; + } + return true; + } + int fFilteredImage; + int fFilteredOthers; + bool fPreventImages; + bool fPreventOthers; +}; +} + +DEF_TEST(SkCanvas_test_draw_filter_image, reporter) { + SkBitmap bitmap; + bitmap.allocN32Pixels(1, 1); + bitmap.eraseColor(SK_ColorTRANSPARENT); + TestDrawFilterImage drawFilter; + SkCanvas canvas(bitmap); + + SkBitmap imageBitmap; + imageBitmap.allocN32Pixels(1, 1); + imageBitmap.eraseColor(SK_ColorGREEN); + SkAutoTUnref image(SkNewImageFromBitmap(imageBitmap, true, NULL)); + canvas.drawImage(image, 0, 0); + + uint32_t pixel; + SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1); + canvas.readPixels(info, &pixel, 4, 0, 0); + REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN); + + canvas.setDrawFilter(&drawFilter); + imageBitmap.eraseColor(SK_ColorRED); + image.reset(SkNewImageFromBitmap(imageBitmap, true, NULL)); + canvas.drawImage(image, 0, 0); + canvas.readPixels(info, &pixel, 4, 0, 0); + REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN); + REPORTER_ASSERT(reporter, drawFilter.fFilteredImage == 1); + REPORTER_ASSERT(reporter, drawFilter.fFilteredOthers == 0); + + // Document a strange case: filtering everything but the images does not work as + // expected. Images go through, but no pixels appear. (This due to SkCanvas::drawImage() using + // SkCanvas::drawBitmap() instead of non-existing SkBaseDevice::drawImage()). + drawFilter.fFilteredImage = 0; + drawFilter.fPreventImages = false; + + canvas.drawImage(image, 0, 0); + canvas.readPixels(info, &pixel, 4, 0, 0); + REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN); + REPORTER_ASSERT(reporter, drawFilter.fFilteredImage == 0); + REPORTER_ASSERT(reporter, drawFilter.fFilteredOthers == 1); +} + +namespace { +/* + * Subclass of looper that just draws once with one pixel offset. + */ +class OnceTestLooper : public SkDrawLooper { +public: + OnceTestLooper() { } + + SkDrawLooper::Context* createContext(SkCanvas*, void* storage) const SK_OVERRIDE { + return SkNEW_PLACEMENT(storage, OnceTestLooperContext()); + } + + size_t contextSize() const SK_OVERRIDE { return sizeof(OnceTestLooperContext); } + +#ifndef SK_IGNORE_TO_STRING + void toString(SkString* str) const SK_OVERRIDE { + str->append("OnceTestLooper:"); + } +#endif + + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(OnceTestLooper); + +private: + class OnceTestLooperContext : public SkDrawLooper::Context { + public: + OnceTestLooperContext() : fCount(0) {} + virtual ~OnceTestLooperContext() {} + + bool next(SkCanvas* canvas, SkPaint*) SK_OVERRIDE { + SkASSERT(fCount <= 1); + canvas->translate(0, 1); + return fCount++ < 1; + } + private: + unsigned fCount; + }; +}; + +SkFlattenable* OnceTestLooper::CreateProc(SkReadBuffer&) { return SkNEW(OnceTestLooper); } +} + +DEF_TEST(SkCanvas_test_draw_looper_image, reporter) { + // Test that drawImage loops with the looper the correct way. At the time of writing, this was + // tricky because drawImage was implemented with drawBitmap. The drawBitmap uses applies the + // looper. + SkBitmap bitmap; + bitmap.allocN32Pixels(10, 10); + bitmap.eraseColor(SK_ColorRED); + OnceTestLooper drawLooper; + SkCanvas canvas(bitmap); + + SkBitmap imageBitmap; + imageBitmap.allocN32Pixels(1, 1); + imageBitmap.eraseColor(SK_ColorGREEN); + SkAutoTUnref image(SkNewImageFromBitmap(imageBitmap, true, NULL)); + SkPaint p; + p.setLooper(&drawLooper); + canvas.drawImage(image, 0, 0, &p); + + uint32_t pixel; + SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1); + canvas.readPixels(info, &pixel, 4, 0, 1); + REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN); + canvas.readPixels(info, &pixel, 4, 0, 0); + REPORTER_ASSERT(reporter, pixel == SK_ColorRED); + canvas.readPixels(info, &pixel, 4, 0, 2); + REPORTER_ASSERT(reporter, pixel == SK_ColorRED); +} + diff --git a/tools/bench_pictures_main.cpp b/tools/bench_pictures_main.cpp index f919812d13..f6d6dea88a 100644 --- a/tools/bench_pictures_main.cpp +++ b/tools/bench_pictures_main.cpp @@ -75,14 +75,19 @@ static char const * const gFilterTypes[] = { "line", "bitmap", "rect", + "rrect", "oval", "path", "text", + "image", "all", }; static const size_t kFilterTypesCount = sizeof(gFilterTypes) / sizeof(gFilterTypes[0]); +SK_COMPILE_ASSERT(kFilterTypesCount - 1u == SkDrawFilter::kTypeCount, + filter_types_list_is_not_exact); + static char const * const gFilterFlags[] = { "antiAlias", "filterBitmap",