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 make_isize(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, SkCanvas::kARGB_ClipLayer_SaveFlag); 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 SkBitmap bitmap = layerCanvas->getDevice()->accessBitmap(false); 172 canvas->drawBitmap(bitmap, 0, 0); 173 } 174 175 r.inset(-SK_ScalarHalf, -SK_ScalarHalf); 176 SkPaint borderPaint; 177 borderPaint.setStyle(SkPaint::kStroke_Style); 178 canvas->drawRect(r, borderPaint); 179 180 canvas->restore(); 181 } 182 183 virtual void onOnceBeforeDraw() SK_OVERRIDE { 184 static const uint32_t kCheckData[] = { 185 SkPackARGB32(0xFF, 0x40, 0x40, 0x40), 186 SkPackARGB32(0xFF, 0xD0, 0xD0, 0xD0), 187 SkPackARGB32(0xFF, 0xD0, 0xD0, 0xD0), 188 SkPackARGB32(0xFF, 0x40, 0x40, 0x40) 189 }; 190 SkBitmap bg; 191 bg.setConfig(SkBitmap::kARGB_8888_Config, 2, 2, 0, kOpaque_SkAlphaType); 192 bg.allocPixels(); 193 SkAutoLockPixels bgAlp(bg); 194 memcpy(bg.getPixels(), kCheckData, sizeof(kCheckData)); 195 196 fBGShader.reset(SkShader::CreateBitmapShader(bg, 197 SkShader::kRepeat_TileMode, 198 SkShader::kRepeat_TileMode)); 199 SkMatrix lm; 200 lm.setScale(SkIntToScalar(kCheckSize), SkIntToScalar(kCheckSize)); 201 fBGShader->setLocalMatrix(lm); 202 203 SkPaint bmpPaint; 204 static const SkPoint kCenter = { SkIntToScalar(kSize) / 2, SkIntToScalar(kSize) / 2 }; 205 static const SkColor kColors[] = { SK_ColorTRANSPARENT, 0x80800000, 206 0xF020F060, SK_ColorWHITE }; 207 bmpPaint.setShader(SkGradientShader::CreateRadial(kCenter, 208 3 * SkIntToScalar(kSize) / 4, 209 kColors, 210 NULL, 211 SK_ARRAY_COUNT(kColors), 212 SkShader::kRepeat_TileMode))->unref(); 213 214 SkBitmap bmp; 215 bmp.setConfig(SkBitmap::kARGB_8888_Config, kSize, kSize); 216 bmp.allocPixels(); 217 SkCanvas bmpCanvas(bmp); 218 219 bmpCanvas.clear(SK_ColorTRANSPARENT); 220 SkRect rect = { SkIntToScalar(kSize) / 8, SkIntToScalar(kSize) / 8, 221 7 * SkIntToScalar(kSize) / 8, 7 * SkIntToScalar(kSize) / 8}; 222 bmpCanvas.drawRect(rect, bmpPaint); 223 224 fBmpShader.reset(SkShader::CreateBitmapShader(bmp, 225 SkShader::kClamp_TileMode, 226 SkShader::kClamp_TileMode)); 227 } 228 229 enum { 230 kCheckSize = 8, 231 kSize = 30, 232 kTestsPerRow = 15, 233 }; 234 235 SkAutoTUnref<SkShader> fBGShader; 236 SkAutoTUnref<SkShader> fBmpShader; 237 238 typedef GM INHERITED; 239 }; 240 241 ////////////////////////////////////////////////////////////////////////////// 242 243 DEF_GM(return new Xfermodes3GM;) 244 245 } 246