/* * Copyright 2014 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "gm/gm.h" #include "include/core/SkBitmap.h" #include "include/core/SkCanvas.h" #include "include/core/SkColor.h" #include "include/core/SkFilterQuality.h" #include "include/core/SkImage.h" #include "include/core/SkImageFilter.h" #include "include/core/SkMatrix.h" #include "include/core/SkPaint.h" #include "include/core/SkPath.h" #include "include/core/SkPicture.h" #include "include/core/SkPictureRecorder.h" #include "include/core/SkPoint.h" #include "include/core/SkRRect.h" #include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" #include "include/core/SkSize.h" #include "include/core/SkString.h" #include "include/core/SkSurface.h" #include "include/core/SkTypes.h" #include "include/effects/SkImageFilters.h" #include "include/private/SkTArray.h" #include namespace skiagm { // Each method of this type must draw its geometry inside 'r' using 'p' typedef void(*drawMth)(SkCanvas* canvas, const SkRect& r, const SkPaint& p); static void draw_rect(SkCanvas* canvas, const SkRect& r, const SkPaint& p) { canvas->drawRect(r, p); } static void draw_oval(SkCanvas* canvas, const SkRect& r, const SkPaint& p) { canvas->drawOval(r, p); } static void draw_rrect(SkCanvas* canvas, const SkRect& r, const SkPaint& p) { SkScalar xRad = r.width() / 4.0f; SkScalar yRad = r.height() / 4.0f; SkRRect rr; rr.setRectXY(r, xRad, yRad); canvas->drawRRect(rr, p); } static void draw_drrect(SkCanvas* canvas, const SkRect& r, const SkPaint& p) { SkScalar xRad = r.width() / 4.0f; SkScalar yRad = r.height() / 4.0f; SkRRect outer; outer.setRectXY(r, xRad, yRad); SkRRect inner = outer; inner.inset(xRad, yRad); canvas->drawDRRect(outer, inner, p); } static void draw_path(SkCanvas* canvas, const SkRect& r, const SkPaint& p) { SkPath path; path.moveTo(r.fLeft, r.fTop); path.lineTo(r.fLeft, r.fBottom); path.lineTo(r.fRight, r.fBottom); path.close(); canvas->drawPath(path, p); } static void draw_points(SkCanvas* canvas, const SkRect& r, const SkPaint& p) { SkPoint pts0[2] = { { r.fLeft, r.fTop }, { r.fRight, r.fBottom } }; SkPoint pts1[2] = { { r.fLeft, r.fBottom }, { r.fRight, r.fTop } }; canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts0, p); canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts1, p); } static void draw_bitmap(SkCanvas* canvas, const SkRect& r, const SkPaint& p) { SkBitmap bm; bm.allocN32Pixels(64, 64); SkCanvas temp(bm); temp.clear(SK_ColorMAGENTA); canvas->drawBitmapRect(bm, r, &p); } constexpr drawMth gDrawMthds[] = { draw_rect, draw_oval, draw_rrect, draw_drrect, draw_path, draw_points, draw_bitmap }; static void add_paint(SkTArray* paints, sk_sp filter) { SkPaint& p = paints->push_back(); p.setImageFilter(std::move(filter)); SkASSERT(p.canComputeFastBounds()); } // Create a selection of imagefilter-based paints to test static void create_paints(SkTArray* paints, sk_sp source) { { SkMatrix scale; scale.setScale(2.0f, 2.0f); sk_sp scaleMIF( SkImageFilters::MatrixTransform(scale, kLow_SkFilterQuality, source)); add_paint(paints, std::move(scaleMIF)); } { SkMatrix rot; rot.setRotate(-33.3f); sk_sp rotMIF( SkImageFilters::MatrixTransform(rot, kLow_SkFilterQuality, source)); add_paint(paints, std::move(rotMIF)); } { SkRect src = SkRect::MakeXYWH(20, 20, 10, 10); SkRect dst = SkRect::MakeXYWH(30, 30, 30, 30); sk_sp tileIF(SkImageFilters::Tile(src, dst, nullptr)); add_paint(paints, std::move(tileIF)); } { sk_sp dsif = SkImageFilters::DropShadow(10.0f, 10.0f, 3.0f, 3.0f, SK_ColorRED, source); add_paint(paints, std::move(dsif)); } { sk_sp dsif = SkImageFilters::DropShadowOnly(27.0f, 27.0f, 3.0f, 3.0f, SK_ColorRED, source); add_paint(paints, std::move(dsif)); } add_paint(paints, SkImageFilters::Blur(3, 3, source)); add_paint(paints, SkImageFilters::Offset(15, 15, source)); } // This GM visualizes the fast bounds for various combinations of geometry // and image filter class ImageFilterFastBoundGM : public GM { public: ImageFilterFastBoundGM() { this->setBGColor(0xFFCCCCCC); } protected: static constexpr int kTileWidth = 100; static constexpr int kTileHeight = 100; static constexpr int kNumVertTiles = 7; static constexpr int kNumXtraCols = 2; SkString onShortName() override { return SkString("filterfastbounds"); } SkISize onISize() override { return SkISize::Make((SK_ARRAY_COUNT(gDrawMthds) + kNumXtraCols) * kTileWidth, kNumVertTiles * kTileHeight); } static void draw_geom_with_paint(drawMth draw, const SkIPoint& off, SkCanvas* canvas, const SkPaint& p) { SkPaint redStroked; redStroked.setColor(SK_ColorRED); redStroked.setStyle(SkPaint::kStroke_Style); SkPaint blueStroked; blueStroked.setColor(SK_ColorBLUE); blueStroked.setStyle(SkPaint::kStroke_Style); const SkRect r = SkRect::MakeLTRB(20, 20, 30, 30); SkRect storage; canvas->save(); canvas->translate(SkIntToScalar(off.fX), SkIntToScalar(off.fY)); canvas->scale(1.5f, 1.5f); const SkRect& fastBound = p.computeFastBounds(r, &storage); canvas->save(); canvas->clipRect(fastBound); (*draw)(canvas, r, p); canvas->restore(); canvas->drawRect(r, redStroked); canvas->drawRect(fastBound, blueStroked); canvas->restore(); } static void draw_savelayer_with_paint(const SkIPoint& off, SkCanvas* canvas, const SkPaint& p) { SkPaint redStroked; redStroked.setColor(SK_ColorRED); redStroked.setStyle(SkPaint::kStroke_Style); SkPaint blueStroked; blueStroked.setColor(SK_ColorBLUE); blueStroked.setStyle(SkPaint::kStroke_Style); const SkRect bounds = SkRect::MakeWH(10, 10); SkRect storage; canvas->save(); canvas->translate(30, 30); canvas->translate(SkIntToScalar(off.fX), SkIntToScalar(off.fY)); canvas->scale(1.5f, 1.5f); const SkRect& fastBound = p.computeFastBounds(bounds, &storage); canvas->saveLayer(&fastBound, &p); canvas->restore(); canvas->drawRect(bounds, redStroked); canvas->drawRect(fastBound, blueStroked); canvas->restore(); } void onDraw(SkCanvas* canvas) override { SkPaint blackFill; //----------- // Normal paints (no source) SkTArray paints; create_paints(&paints, nullptr); //----------- // Paints with a PictureImageFilter as a source sk_sp pic; { SkPictureRecorder rec; SkCanvas* c = rec.beginRecording(10, 10); c->drawRect(SkRect::MakeWH(10, 10), blackFill); pic = rec.finishRecordingAsPicture(); } SkTArray pifPaints; create_paints(&pifPaints, SkImageFilters::Picture(pic)); //----------- // Paints with a SkImageSource as a source auto surface(SkSurface::MakeRasterN32Premul(10, 10)); { SkPaint p; SkCanvas* temp = surface->getCanvas(); temp->clear(SK_ColorYELLOW); p.setColor(SK_ColorBLUE); temp->drawRect(SkRect::MakeLTRB(5, 5, 10, 10), p); p.setColor(SK_ColorGREEN); temp->drawRect(SkRect::MakeLTRB(5, 0, 10, 5), p); } sk_sp image(surface->makeImageSnapshot()); sk_sp imageSource(SkImageFilters::Image(std::move(image))); SkTArray bmsPaints; create_paints(&bmsPaints, std::move(imageSource)); //----------- SkASSERT(paints.count() == kNumVertTiles); SkASSERT(paints.count() == pifPaints.count()); SkASSERT(paints.count() == bmsPaints.count()); // horizontal separators for (int i = 1; i < paints.count(); ++i) { canvas->drawLine(0, i*SkIntToScalar(kTileHeight), SkIntToScalar((SK_ARRAY_COUNT(gDrawMthds) + kNumXtraCols)*kTileWidth), i*SkIntToScalar(kTileHeight), blackFill); } // vertical separators for (int i = 0; i < (int)SK_ARRAY_COUNT(gDrawMthds) + kNumXtraCols; ++i) { canvas->drawLine(SkIntToScalar(i * kTileWidth), 0, SkIntToScalar(i * kTileWidth), SkIntToScalar(paints.count() * kTileWidth), blackFill); } // A column of saveLayers with PictureImageFilters for (int i = 0; i < pifPaints.count(); ++i) { draw_savelayer_with_paint(SkIPoint::Make(0, i*kTileHeight), canvas, pifPaints[i]); } // A column of saveLayers with BitmapSources for (int i = 0; i < pifPaints.count(); ++i) { draw_savelayer_with_paint(SkIPoint::Make(kTileWidth, i*kTileHeight), canvas, bmsPaints[i]); } // Multiple columns with different geometry for (int i = 0; i < (int)SK_ARRAY_COUNT(gDrawMthds); ++i) { for (int j = 0; j < paints.count(); ++j) { draw_geom_with_paint(*gDrawMthds[i], SkIPoint::Make((i+kNumXtraCols) * kTileWidth, j*kTileHeight), canvas, paints[j]); } } } private: typedef GM INHERITED; }; ////////////////////////////////////////////////////////////////////////////// DEF_GM(return new ImageFilterFastBoundGM;) }