/* * 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/gm.h" #include "include/core/SkBitmap.h" #include "include/core/SkBlendMode.h" #include "include/core/SkCanvas.h" #include "include/core/SkColor.h" #include "include/core/SkColorPriv.h" #include "include/core/SkColorSpace.h" #include "include/core/SkFont.h" #include "include/core/SkImageInfo.h" #include "include/core/SkMatrix.h" #include "include/core/SkPaint.h" #include "include/core/SkPoint.h" #include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" #include "include/core/SkShader.h" #include "include/core/SkSize.h" #include "include/core/SkString.h" #include "include/core/SkSurface.h" #include "include/core/SkTileMode.h" #include "include/core/SkTypeface.h" #include "include/core/SkTypes.h" #include "include/effects/SkGradientShader.h" #include "tools/ToolUtils.h" #include namespace skiagm { /** * This tests drawing device-covering rects with solid colors and bitmap shaders over a * checkerboard background using different xfermodes. */ class Xfermodes3GM : public GM { public: Xfermodes3GM() { this->setBGColor(ToolUtils::color_to_565(0xFF70D0E0)); } protected: SkString onShortName() override { return SkString("xfermodes3"); } SkISize onISize() override { return SkISize::Make(630, 1215); } void onDraw(SkCanvas* canvas) override { canvas->translate(SkIntToScalar(10), SkIntToScalar(20)); SkFont font(ToolUtils::create_portable_typeface()); SkPaint labelP; constexpr SkColor kSolidColors[] = { SK_ColorTRANSPARENT, SK_ColorBLUE, 0x80808000 }; constexpr SkColor kBmpAlphas[] = { 0xff, 0x80, }; auto tempSurface(this->makeTempSurface(canvas, kSize, kSize)); int test = 0; int x = 0, y = 0; constexpr struct { SkPaint::Style fStyle; SkScalar fWidth; } kStrokes[] = { {SkPaint::kFill_Style, 0}, {SkPaint::kStroke_Style, SkIntToScalar(kSize) / 2}, }; for (size_t s = 0; s < SK_ARRAY_COUNT(kStrokes); ++s) { for (size_t m = 0; m <= (size_t)SkBlendMode::kLastMode; ++m) { SkBlendMode mode = static_cast(m); canvas->drawString(SkBlendMode_Name(mode), SkIntToScalar(x), SkIntToScalar(y + kSize + 3) + font.getSize(), font, labelP); for (size_t c = 0; c < SK_ARRAY_COUNT(kSolidColors); ++c) { SkPaint modePaint; modePaint.setBlendMode(mode); modePaint.setColor(kSolidColors[c]); modePaint.setStyle(kStrokes[s].fStyle); modePaint.setStrokeWidth(kStrokes[s].fWidth); this->drawMode(canvas, x, y, kSize, kSize, modePaint, tempSurface.get()); ++test; x += kSize + 10; if (!(test % kTestsPerRow)) { x = 0; y += kSize + 30; } } for (size_t a = 0; a < SK_ARRAY_COUNT(kBmpAlphas); ++a) { SkPaint modePaint; modePaint.setBlendMode(mode); modePaint.setAlpha(kBmpAlphas[a]); modePaint.setShader(fBmpShader); modePaint.setStyle(kStrokes[s].fStyle); modePaint.setStrokeWidth(kStrokes[s].fWidth); this->drawMode(canvas, x, y, kSize, kSize, modePaint, tempSurface.get()); ++test; x += kSize + 10; if (!(test % kTestsPerRow)) { x = 0; y += kSize + 30; } } } } } private: /** * GrContext has optimizations around full rendertarget draws that can be replaced with clears. * We are trying to test those. We could use saveLayer() to create small SkGpuDevices but * saveLayer() uses the texture cache. This means that the actual render target may be larger * than the layer. Because the clip will contain the layer's bounds, no draws will be full-RT. * So explicitly create a temporary canvas with dimensions exactly the layer size. */ sk_sp makeTempSurface(SkCanvas* baseCanvas, int w, int h) { SkImageInfo baseInfo = baseCanvas->imageInfo(); SkImageInfo info = SkImageInfo::Make(w, h, baseInfo.colorType(), baseInfo.alphaType(), baseInfo.refColorSpace()); return baseCanvas->makeSurface(info); } void drawMode(SkCanvas* canvas, int x, int y, int w, int h, const SkPaint& modePaint, SkSurface* surface) { canvas->save(); canvas->translate(SkIntToScalar(x), SkIntToScalar(y)); SkRect r = SkRect::MakeWH(SkIntToScalar(w), SkIntToScalar(h)); SkCanvas* modeCanvas; if (nullptr == surface) { canvas->saveLayer(&r, nullptr); canvas->clipRect(r); modeCanvas = canvas; } else { modeCanvas = surface->getCanvas(); } SkPaint bgPaint; bgPaint.setAntiAlias(false); bgPaint.setShader(fBGShader); modeCanvas->drawRect(r, bgPaint); modeCanvas->drawRect(r, modePaint); modeCanvas = nullptr; if (nullptr == surface) { canvas->restore(); } else { surface->draw(canvas, 0, 0, nullptr); } r.inset(-SK_ScalarHalf, -SK_ScalarHalf); SkPaint borderPaint; borderPaint.setStyle(SkPaint::kStroke_Style); canvas->drawRect(r, borderPaint); canvas->restore(); } void onOnceBeforeDraw() override { const uint32_t kCheckData[] = { SkPackARGB32(0xFF, 0x42, 0x41, 0x42), SkPackARGB32(0xFF, 0xD6, 0xD3, 0xD6), SkPackARGB32(0xFF, 0xD6, 0xD3, 0xD6), SkPackARGB32(0xFF, 0x42, 0x41, 0x42) }; SkBitmap bg; bg.allocN32Pixels(2, 2, true); memcpy(bg.getPixels(), kCheckData, sizeof(kCheckData)); SkMatrix lm; lm.setScale(SkIntToScalar(kCheckSize), SkIntToScalar(kCheckSize)); fBGShader = bg.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, SkSamplingOptions(), lm); SkPaint bmpPaint; const SkPoint kCenter = { SkIntToScalar(kSize) / 2, SkIntToScalar(kSize) / 2 }; const SkColor kColors[] = { SK_ColorTRANSPARENT, 0x80800000, 0xF020F060, SK_ColorWHITE }; bmpPaint.setShader(SkGradientShader::MakeRadial(kCenter, 3 * SkIntToScalar(kSize) / 4, kColors, nullptr, SK_ARRAY_COUNT(kColors), SkTileMode::kRepeat)); SkBitmap bmp; bmp.allocN32Pixels(kSize, kSize); SkCanvas bmpCanvas(bmp); bmpCanvas.clear(SK_ColorTRANSPARENT); SkRect rect = { SkIntToScalar(kSize) / 8, SkIntToScalar(kSize) / 8, 7 * SkIntToScalar(kSize) / 8, 7 * SkIntToScalar(kSize) / 8}; bmpCanvas.drawRect(rect, bmpPaint); fBmpShader = bmp.makeShader(SkSamplingOptions()); } enum { kCheckSize = 8, kSize = 30, kTestsPerRow = 15, }; sk_sp fBGShader; sk_sp fBmpShader; using INHERITED = GM; }; ////////////////////////////////////////////////////////////////////////////// DEF_GM(return new Xfermodes3GM;) } // namespace skiagm