1 2 /* 3 * Copyright 2013 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 #include "gm.h" 9 #include "SkBitmap.h" 10 #include "SkGradientShader.h" 11 #include "SkXfermode.h" 12 #include "SkColorPriv.h" 13 14 #if SK_SUPPORT_GPU 15 #include "GrContext.h" 16 #include "SkGpuDevice.h" 17 #endif 18 19 namespace skiagm { 20 21 /** 22 * This tests drawing device-covering rects with solid colors and bitmap shaders over a 23 * checkerboard background using different xfermodes. 24 */ 25 class Xfermodes3GM : public GM { 26 public: 27 Xfermodes3GM() {} 28 29 protected: 30 virtual SkString onShortName() SK_OVERRIDE { 31 return SkString("xfermodes3"); 32 } 33 34 virtual SkISize onISize() SK_OVERRIDE { 35 return SkISize::Make(630, 1215); 36 } 37 38 virtual void onDrawBackground(SkCanvas* canvas) SK_OVERRIDE { 39 SkPaint bgPaint; 40 bgPaint.setColor(0xFF70D0E0); 41 canvas->drawPaint(bgPaint); 42 } 43 44 virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE { 45 canvas->translate(SkIntToScalar(10), SkIntToScalar(20)); 46 47 SkPaint labelP; 48 labelP.setAntiAlias(true); 49 50 static const SkColor kSolidColors[] = { 51 SK_ColorTRANSPARENT, 52 SK_ColorBLUE, 53 0x80808000 54 }; 55 56 static const SkColor kBmpAlphas[] = { 57 0xff, 58 0x80, 59 }; 60 61 SkAutoTUnref<SkCanvas> tempCanvas(this->possiblyCreateTempCanvas(canvas, kSize, kSize)); 62 63 int test = 0; 64 int x = 0, y = 0; 65 static const struct { SkPaint::Style fStyle; SkScalar fWidth; } kStrokes[] = { 66 {SkPaint::kFill_Style, 0}, 67 {SkPaint::kStroke_Style, SkIntToScalar(kSize) / 2}, 68 }; 69 for (size_t s = 0; s < SK_ARRAY_COUNT(kStrokes); ++s) { 70 for (size_t m = 0; m <= SkXfermode::kLastMode; ++m) { 71 SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(m); 72 canvas->drawText(SkXfermode::ModeName(mode), 73 strlen(SkXfermode::ModeName(mode)), 74 SkIntToScalar(x), 75 SkIntToScalar(y + kSize + 3) + labelP.getTextSize(), 76 labelP); 77 for (size_t c = 0; c < SK_ARRAY_COUNT(kSolidColors); ++c) { 78 SkPaint modePaint; 79 modePaint.setXfermodeMode(mode); 80 modePaint.setColor(kSolidColors[c]); 81 modePaint.setStyle(kStrokes[s].fStyle); 82 modePaint.setStrokeWidth(kStrokes[s].fWidth); 83 84 this->drawMode(canvas, x, y, kSize, kSize, modePaint, tempCanvas.get()); 85 86 ++test; 87 x += kSize + 10; 88 if (!(test % kTestsPerRow)) { 89 x = 0; 90 y += kSize + 30; 91 } 92 } 93 for (size_t a = 0; a < SK_ARRAY_COUNT(kBmpAlphas); ++a) { 94 SkPaint modePaint; 95 modePaint.setXfermodeMode(mode); 96 modePaint.setAlpha(kBmpAlphas[a]); 97 modePaint.setShader(fBmpShader); 98 modePaint.setStyle(kStrokes[s].fStyle); 99 modePaint.setStrokeWidth(kStrokes[s].fWidth); 100 101 this->drawMode(canvas, x, y, kSize, kSize, modePaint, tempCanvas.get()); 102 103 ++test; 104 x += kSize + 10; 105 if (!(test % kTestsPerRow)) { 106 x = 0; 107 y += kSize + 30; 108 } 109 } 110 } 111 } 112 } 113 114 private: 115 /** 116 * GrContext has optimizations around full rendertarget draws that can be replaced with clears. 117 * We are trying to test those. We could use saveLayer() to create small SkGpuDevices but 118 * saveLayer() uses the texture cache. This means that the actual render target may be larger 119 * than the layer. Because the clip will contain the layer's bounds, no draws will be full-RT. 120 * So when running on a GPU canvas we explicitly create a temporary canvas using a texture with 121 * dimensions exactly matching the layer size. 122 */ 123 SkCanvas* possiblyCreateTempCanvas(SkCanvas* baseCanvas, int w, int h) { 124 SkCanvas* tempCanvas = NULL; 125 #if SK_SUPPORT_GPU 126 GrRenderTarget* rt = baseCanvas->getDevice()->accessRenderTarget(); 127 if (NULL != rt) { 128 GrContext* context = rt->getContext(); 129 GrTextureDesc desc; 130 desc.fWidth = w; 131 desc.fHeight = h; 132 desc.fConfig = rt->config(); 133 desc.fFlags = kRenderTarget_GrTextureFlagBit; 134 SkAutoTUnref<GrSurface> surface(context->createUncachedTexture(desc, NULL, 0)); 135 SkAutoTUnref<SkBaseDevice> device(SkGpuDevice::Create(surface.get())); 136 if (NULL != device.get()) { 137 tempCanvas = SkNEW_ARGS(SkCanvas, (device.get())); 138 } 139 } 140 #endif 141 return tempCanvas; 142 } 143 144 void drawMode(SkCanvas* canvas, 145 int x, int y, int w, int h, 146 const SkPaint& modePaint, SkCanvas* layerCanvas) { 147 canvas->save(); 148 149 canvas->translate(SkIntToScalar(x), SkIntToScalar(y)); 150 151 SkRect r = SkRect::MakeWH(SkIntToScalar(w), SkIntToScalar(h)); 152 153 SkCanvas* modeCanvas; 154 if (NULL == layerCanvas) { 155 canvas->saveLayer(&r, NULL); 156 modeCanvas = canvas; 157 } else { 158 modeCanvas = layerCanvas; 159 } 160 161 SkPaint bgPaint; 162 bgPaint.setAntiAlias(false); 163 bgPaint.setShader(fBGShader); 164 modeCanvas->drawRect(r, bgPaint); 165 modeCanvas->drawRect(r, modePaint); 166 modeCanvas = NULL; 167 168 if (NULL == layerCanvas) { 169 canvas->restore(); 170 } else { 171 SkAutoROCanvasPixels ropixels(layerCanvas); 172 SkBitmap bitmap; 173 if (ropixels.asROBitmap(&bitmap)) { 174 canvas->drawBitmap(bitmap, 0, 0); 175 } 176 } 177 178 r.inset(-SK_ScalarHalf, -SK_ScalarHalf); 179 SkPaint borderPaint; 180 borderPaint.setStyle(SkPaint::kStroke_Style); 181 canvas->drawRect(r, borderPaint); 182 183 canvas->restore(); 184 } 185 186 virtual void onOnceBeforeDraw() SK_OVERRIDE { 187 static const uint32_t kCheckData[] = { 188 SkPackARGB32(0xFF, 0x40, 0x40, 0x40), 189 SkPackARGB32(0xFF, 0xD0, 0xD0, 0xD0), 190 SkPackARGB32(0xFF, 0xD0, 0xD0, 0xD0), 191 SkPackARGB32(0xFF, 0x40, 0x40, 0x40) 192 }; 193 SkBitmap bg; 194 bg.allocN32Pixels(2, 2, true); 195 SkAutoLockPixels bgAlp(bg); 196 memcpy(bg.getPixels(), kCheckData, sizeof(kCheckData)); 197 198 SkMatrix lm; 199 lm.setScale(SkIntToScalar(kCheckSize), SkIntToScalar(kCheckSize)); 200 fBGShader.reset(SkShader::CreateBitmapShader(bg, 201 SkShader::kRepeat_TileMode, 202 SkShader::kRepeat_TileMode, 203 &lm)); 204 205 SkPaint bmpPaint; 206 static const SkPoint kCenter = { SkIntToScalar(kSize) / 2, SkIntToScalar(kSize) / 2 }; 207 static const SkColor kColors[] = { SK_ColorTRANSPARENT, 0x80800000, 208 0xF020F060, SK_ColorWHITE }; 209 bmpPaint.setShader(SkGradientShader::CreateRadial(kCenter, 210 3 * SkIntToScalar(kSize) / 4, 211 kColors, 212 NULL, 213 SK_ARRAY_COUNT(kColors), 214 SkShader::kRepeat_TileMode))->unref(); 215 216 SkBitmap bmp; 217 bmp.allocN32Pixels(kSize, kSize); 218 SkCanvas bmpCanvas(bmp); 219 220 bmpCanvas.clear(SK_ColorTRANSPARENT); 221 SkRect rect = { SkIntToScalar(kSize) / 8, SkIntToScalar(kSize) / 8, 222 7 * SkIntToScalar(kSize) / 8, 7 * SkIntToScalar(kSize) / 8}; 223 bmpCanvas.drawRect(rect, bmpPaint); 224 225 fBmpShader.reset(SkShader::CreateBitmapShader(bmp, 226 SkShader::kClamp_TileMode, 227 SkShader::kClamp_TileMode)); 228 } 229 230 enum { 231 kCheckSize = 8, 232 kSize = 30, 233 kTestsPerRow = 15, 234 }; 235 236 SkAutoTUnref<SkShader> fBGShader; 237 SkAutoTUnref<SkShader> fBmpShader; 238 239 typedef GM INHERITED; 240 }; 241 242 ////////////////////////////////////////////////////////////////////////////// 243 244 DEF_GM(return new Xfermodes3GM;) 245 246 } 247