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