|  | 
 | /* | 
 |  * 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 "SkBitmap.h" | 
 | #include "SkGradientShader.h" | 
 | #include "SkXfermode.h" | 
 | #include "SkColorPriv.h" | 
 |  | 
 | #if SK_SUPPORT_GPU | 
 | #include "GrContext.h" | 
 | #include "SkGpuDevice.h" | 
 | #endif | 
 |  | 
 | 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() {} | 
 |  | 
 | protected: | 
 |     virtual SkString onShortName() SK_OVERRIDE { | 
 |         return SkString("xfermodes3"); | 
 |     } | 
 |  | 
 |     virtual SkISize onISize() SK_OVERRIDE { | 
 |         return make_isize(630, 1215); | 
 |     } | 
 |  | 
 |     virtual void onDrawBackground(SkCanvas* canvas) SK_OVERRIDE { | 
 |         SkPaint bgPaint; | 
 |         bgPaint.setColor(0xFF70D0E0); | 
 |         canvas->drawPaint(bgPaint); | 
 |     } | 
 |  | 
 |     virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE { | 
 |         canvas->translate(SkIntToScalar(10), SkIntToScalar(20)); | 
 |  | 
 |         SkPaint labelP; | 
 |         labelP.setAntiAlias(true); | 
 |  | 
 |         static const SkColor kSolidColors[] = { | 
 |             SK_ColorTRANSPARENT, | 
 |             SK_ColorBLUE, | 
 |             0x80808000 | 
 |         }; | 
 |  | 
 |         static const SkColor kBmpAlphas[] = { | 
 |             0xff, | 
 |             0x80, | 
 |         }; | 
 |  | 
 |         SkAutoTUnref<SkCanvas> tempCanvas(this->possiblyCreateTempCanvas(canvas, kSize, kSize)); | 
 |  | 
 |         int test = 0; | 
 |         int x = 0, y = 0; | 
 |         static const 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 <= SkXfermode::kLastMode; ++m) { | 
 |                 SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(m); | 
 |                 canvas->drawText(SkXfermode::ModeName(mode), | 
 |                                  strlen(SkXfermode::ModeName(mode)), | 
 |                                  SkIntToScalar(x), | 
 |                                  SkIntToScalar(y + kSize + 3) + labelP.getTextSize(), | 
 |                                  labelP); | 
 |                 for (size_t c = 0; c < SK_ARRAY_COUNT(kSolidColors); ++c) { | 
 |                     SkPaint modePaint; | 
 |                     modePaint.setXfermodeMode(mode); | 
 |                     modePaint.setColor(kSolidColors[c]); | 
 |                     modePaint.setStyle(kStrokes[s].fStyle); | 
 |                     modePaint.setStrokeWidth(kStrokes[s].fWidth); | 
 |  | 
 |                     this->drawMode(canvas, x, y, kSize, kSize, modePaint, tempCanvas.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.setXfermodeMode(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, tempCanvas.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 when running on a GPU canvas we explicitly create a temporary canvas using a texture with | 
 |      * dimensions exactly matching the layer size. | 
 |      */ | 
 |     SkCanvas* possiblyCreateTempCanvas(SkCanvas* baseCanvas, int w, int h) { | 
 |         SkCanvas* tempCanvas = NULL; | 
 | #if SK_SUPPORT_GPU | 
 |         GrRenderTarget* rt = baseCanvas->getDevice()->accessRenderTarget(); | 
 |         if (NULL != rt) { | 
 |             GrContext* context = rt->getContext(); | 
 |             GrTextureDesc desc; | 
 |             desc.fWidth = w; | 
 |             desc.fHeight = h; | 
 |             desc.fConfig = rt->config(); | 
 |             desc.fFlags = kRenderTarget_GrTextureFlagBit; | 
 |             SkAutoTUnref<GrSurface> surface(context->createUncachedTexture(desc, NULL, 0)); | 
 |             SkAutoTUnref<SkBaseDevice> device(SkGpuDevice::Create(surface.get())); | 
 |             if (NULL != device.get()) { | 
 |                 tempCanvas = SkNEW_ARGS(SkCanvas, (device.get())); | 
 |             } | 
 |         } | 
 | #endif | 
 |         return tempCanvas; | 
 |     } | 
 |  | 
 |     void drawMode(SkCanvas* canvas, | 
 |                   int x, int y, int w, int h, | 
 |                   const SkPaint& modePaint, SkCanvas* layerCanvas) { | 
 |         canvas->save(); | 
 |  | 
 |         canvas->translate(SkIntToScalar(x), SkIntToScalar(y)); | 
 |  | 
 |         SkRect r = SkRect::MakeWH(SkIntToScalar(w), SkIntToScalar(h)); | 
 |  | 
 |         SkCanvas* modeCanvas; | 
 |         if (NULL == layerCanvas) { | 
 |             canvas->saveLayer(&r, NULL); | 
 |             modeCanvas = canvas; | 
 |         } else { | 
 |             modeCanvas = layerCanvas; | 
 |         } | 
 |  | 
 |         SkPaint bgPaint; | 
 |         bgPaint.setAntiAlias(false); | 
 |         bgPaint.setShader(fBGShader); | 
 |         modeCanvas->drawRect(r, bgPaint); | 
 |         modeCanvas->drawRect(r, modePaint); | 
 |         modeCanvas = NULL; | 
 |  | 
 |         if (NULL == layerCanvas) { | 
 |             canvas->restore(); | 
 |         } else { | 
 |             SkAutoROCanvasPixels ropixels(layerCanvas); | 
 |             SkBitmap bitmap; | 
 |             if (ropixels.asROBitmap(&bitmap)) { | 
 |                 canvas->drawBitmap(bitmap, 0, 0); | 
 |             } | 
 |         } | 
 |  | 
 |         r.inset(-SK_ScalarHalf, -SK_ScalarHalf); | 
 |         SkPaint borderPaint; | 
 |         borderPaint.setStyle(SkPaint::kStroke_Style); | 
 |         canvas->drawRect(r, borderPaint); | 
 |  | 
 |         canvas->restore(); | 
 |     } | 
 |  | 
 |     virtual void onOnceBeforeDraw() SK_OVERRIDE { | 
 |         static const uint32_t kCheckData[] = { | 
 |             SkPackARGB32(0xFF, 0x40, 0x40, 0x40), | 
 |             SkPackARGB32(0xFF, 0xD0, 0xD0, 0xD0), | 
 |             SkPackARGB32(0xFF, 0xD0, 0xD0, 0xD0), | 
 |             SkPackARGB32(0xFF, 0x40, 0x40, 0x40) | 
 |         }; | 
 |         SkBitmap bg; | 
 |         bg.allocN32Pixels(2, 2, true); | 
 |         SkAutoLockPixels bgAlp(bg); | 
 |         memcpy(bg.getPixels(), kCheckData, sizeof(kCheckData)); | 
 |  | 
 |         SkMatrix lm; | 
 |         lm.setScale(SkIntToScalar(kCheckSize), SkIntToScalar(kCheckSize)); | 
 |         fBGShader.reset(SkShader::CreateBitmapShader(bg, | 
 |                                                      SkShader::kRepeat_TileMode, | 
 |                                                      SkShader::kRepeat_TileMode, | 
 |                                                      &lm)); | 
 |  | 
 |         SkPaint bmpPaint; | 
 |         static const SkPoint kCenter = { SkIntToScalar(kSize) / 2, SkIntToScalar(kSize) / 2 }; | 
 |         static const SkColor kColors[] = { SK_ColorTRANSPARENT, 0x80800000, | 
 |                                           0xF020F060, SK_ColorWHITE }; | 
 |         bmpPaint.setShader(SkGradientShader::CreateRadial(kCenter, | 
 |                                                           3 * SkIntToScalar(kSize) / 4, | 
 |                                                           kColors, | 
 |                                                           NULL, | 
 |                                                           SK_ARRAY_COUNT(kColors), | 
 |                                                           SkShader::kRepeat_TileMode))->unref(); | 
 |  | 
 |         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.reset(SkShader::CreateBitmapShader(bmp, | 
 |                                                       SkShader::kClamp_TileMode, | 
 |                                                       SkShader::kClamp_TileMode)); | 
 |     } | 
 |  | 
 |     enum { | 
 |         kCheckSize = 8, | 
 |         kSize = 30, | 
 |         kTestsPerRow = 15, | 
 |     }; | 
 |  | 
 |     SkAutoTUnref<SkShader> fBGShader; | 
 |     SkAutoTUnref<SkShader> fBmpShader; | 
 |  | 
 |     typedef GM INHERITED; | 
 | }; | 
 |  | 
 | ////////////////////////////////////////////////////////////////////////////// | 
 |  | 
 | DEF_GM(return new Xfermodes3GM;) | 
 |  | 
 | } |