1 /* 2 * Copyright 2011 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "gm.h" 9 #include "SkCanvas.h" 10 #include "SkPath.h" 11 12 static void test_quadstroke(SkCanvas* canvas) { 13 SkPath path; 14 path.moveTo(6, 0); 15 path.quadTo(150, 150, 0, 6); 16 17 SkPaint paint; 18 19 paint.setAntiAlias(true); 20 paint.setStyle(SkPaint::kStroke_Style); 21 canvas->translate(20, 20); 22 23 #if 1 24 canvas->drawPath(path, paint); 25 canvas->translate(100, 0); 26 #endif 27 28 paint.setStrokeWidth(1.01f); 29 canvas->drawPath(path, paint); 30 } 31 32 static void draw_conic(SkCanvas* canvas, SkScalar weight, const SkPaint& paint) { 33 SkPath path; 34 path.moveTo(100, 100); 35 path.conicTo(300, 100, 300, 300, weight); 36 canvas->drawPath(path, paint); 37 } 38 39 static void test_conic(SkCanvas* canvas) { 40 SkPaint paint; 41 paint.setAntiAlias(true); 42 paint.setStyle(SkPaint::kStroke_Style); 43 44 static const struct { 45 SkScalar fWeight; 46 SkColor fColor; 47 } gRec[] = { 48 { 2 , SK_ColorRED }, 49 { 1 , SK_ColorGREEN }, 50 { 0.5f, SK_ColorBLUE }, 51 }; 52 53 for (SkScalar width = 0; width <= 20; width += 20) { 54 canvas->save(); 55 paint.setStrokeWidth(width); 56 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { 57 paint.setColor(gRec[i].fColor); 58 draw_conic(canvas, gRec[i].fWeight, paint); 59 canvas->translate(-30, 30); 60 } 61 canvas->restore(); 62 canvas->translate(300, 0); 63 } 64 } 65 66 #include "SkGradientShader.h" 67 static void test_shallow_gradient(SkCanvas* canvas, SkScalar width, SkScalar height) { 68 SkColor colors[] = { 0xFF7F7F7F, 0xFF7F7F7F, 0xFF000000 }; 69 SkScalar pos[] = { 0, 0.35f, SK_Scalar1 }; 70 SkPoint pts[] = { { 0, 0 }, { width, height } }; 71 SkShader* s = SkGradientShader::CreateLinear(pts, colors, pos, 72 SK_ARRAY_COUNT(colors), 73 SkShader::kClamp_TileMode); 74 SkPaint paint; 75 paint.setShader(s)->unref(); 76 canvas->drawPaint(paint); 77 } 78 79 #include "SkDashPathEffect.h" 80 static void test_giant_dash(SkCanvas* canvas) { 81 SkPaint paint; 82 const SkScalar intervals[] = { SK_Scalar1, SK_Scalar1 }; 83 84 paint.setStrokeWidth(2); 85 paint.setPathEffect(new SkDashPathEffect(intervals, 2, 0))->unref(); 86 87 SkScalar big = 500 * 1000; 88 89 canvas->drawLine(10, 10, big, 10, paint); 90 canvas->drawLine(-big, 20, 500, 20, paint); 91 canvas->drawLine(-big, 30, big, 30, paint); 92 93 const SkScalar intervals2[] = { 20, 5, 10, 5 }; 94 paint.setPathEffect(new SkDashPathEffect(intervals2, 4, 17))->unref(); 95 96 canvas->translate(0, 40); 97 SkScalar x = -500; 98 SkScalar width = 3173; 99 for (int i = 0; i < 40; ++i) { 100 if (i > 10) 101 canvas->drawLine(x, 0, x + width, 0, paint); 102 x += 1; 103 canvas->translate(0, 4); 104 } 105 } 106 107 108 109 // Reproduces bug found here: http://jsfiddle.net/R8Cu5/1/ 110 // 111 #include "SkGradientShader.h" 112 static void test_grad(SkCanvas* canvas) { 113 SkPoint pts[] = { 114 { 478.544067f, -84.2041016f }, 115 { 602.455933f, 625.204102f }, 116 }; 117 SkColor colors[] = { SK_ColorBLACK, SK_ColorBLACK, SK_ColorRED, SK_ColorRED }; 118 SkScalar pos[] = { 0, 0.3f, 0.3f, 1.0f }; 119 SkShader* s = SkGradientShader::CreateLinear(pts, colors, pos, 4, SkShader::kClamp_TileMode); 120 SkPaint p; 121 p.setShader(s)->unref(); 122 canvas->drawPaint(p); 123 } 124 125 static SkCanvas* MakeCanvas(const SkIRect& bounds) { 126 SkBitmap bm; 127 bm.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(), bounds.height()); 128 bm.allocPixels(); 129 bm.eraseColor(SK_ColorTRANSPARENT); 130 131 SkCanvas* canvas = new SkCanvas(bm); 132 canvas->translate(-SkIntToScalar(bounds.fLeft), -SkIntToScalar(bounds.fTop)); 133 return canvas; 134 } 135 136 #ifdef SK_DEBUG 137 static void GetBitmap(const SkCanvas* canvas, SkBitmap* bm) { 138 *bm = canvas->getDevice()->accessBitmap(false); 139 } 140 #endif 141 142 static void compare_canvas(const SkCanvas* a, const SkCanvas* b) { 143 #ifdef SK_DEBUG 144 SkBitmap bma, bmb; 145 GetBitmap(a, &bma); 146 GetBitmap(b, &bmb); 147 148 SkASSERT(bma.width() == bmb.width()); 149 SkASSERT(bma.height() == bmb.height()); 150 151 bma.lockPixels(); 152 bmb.lockPixels(); 153 for (int y = 0; y < bma.height(); ++y) { 154 const SkPMColor* rowa = bma.getAddr32(0, y); 155 const SkPMColor* rowb = bmb.getAddr32(0, y); 156 SkASSERT(!memcmp(rowa, rowb, bma.width() << 2)); 157 158 for (int x = 1; x < bma.width() - 1; ++x) { 159 SkASSERT(0xFF000000 == rowa[x]); 160 SkASSERT(0xFF000000 == rowb[x]); 161 } 162 } 163 #endif 164 } 165 166 static void drawRectAsPath(SkCanvas* canvas, const SkRect& r, const SkPaint& p) { 167 SkPath path; 168 path.addRect(r); 169 canvas->drawPath(path, p); 170 } 171 172 static void test_maskFromPath(const SkPath& path) { 173 SkIRect bounds; 174 path.getBounds().roundOut(&bounds); 175 176 SkPaint paint; 177 paint.setAntiAlias(true); 178 179 SkAutoTUnref<SkCanvas> path_canvas(MakeCanvas(bounds)); 180 path_canvas->drawPath(path, paint); 181 182 SkAutoTUnref<SkCanvas> rect_canvas(MakeCanvas(bounds)); 183 drawRectAsPath(rect_canvas, path.getBounds(), paint); 184 185 compare_canvas(path_canvas, rect_canvas); 186 } 187 188 static void test_mask() { 189 for (int i = 1; i <= 20; ++i) { 190 const SkScalar dx = SK_Scalar1 / i; 191 const SkRect constr = SkRect::MakeWH(dx, SkIntToScalar(2)); 192 for (int n = 2; n < 20; ++n) { 193 SkPath path; 194 path.setFillType(SkPath::kEvenOdd_FillType); 195 SkRect r = constr; 196 while (r.fRight < SkIntToScalar(4)) { 197 path.addRect(r); 198 r.offset(dx, 0); 199 } 200 test_maskFromPath(path); 201 } 202 } 203 } 204 205 /** Draw a 2px border around the target, then red behind the target; 206 set the clip to match the target, then draw >> the target in blue. 207 */ 208 209 static void draw (SkCanvas* canvas, SkRect& target, int x, int y) { 210 SkPaint borderPaint; 211 borderPaint.setColor(SkColorSetRGB(0x0, 0xDD, 0x0)); 212 borderPaint.setAntiAlias(true); 213 SkPaint backgroundPaint; 214 backgroundPaint.setColor(SkColorSetRGB(0xDD, 0x0, 0x0)); 215 backgroundPaint.setAntiAlias(true); 216 SkPaint foregroundPaint; 217 foregroundPaint.setColor(SkColorSetRGB(0x0, 0x0, 0xDD)); 218 foregroundPaint.setAntiAlias(true); 219 220 canvas->save(); 221 canvas->translate(SkIntToScalar(x), SkIntToScalar(y)); 222 target.inset(SkIntToScalar(-2), SkIntToScalar(-2)); 223 canvas->drawRect(target, borderPaint); 224 target.inset(SkIntToScalar(2), SkIntToScalar(2)); 225 canvas->drawRect(target, backgroundPaint); 226 canvas->clipRect(target, SkRegion::kIntersect_Op, true); 227 target.inset(SkIntToScalar(-4), SkIntToScalar(-4)); 228 canvas->drawRect(target, foregroundPaint); 229 canvas->restore(); 230 } 231 232 static void draw_square (SkCanvas* canvas, int x, int y) { 233 SkRect target (SkRect::MakeWH(10 * SK_Scalar1, 10 * SK_Scalar1)); 234 draw(canvas, target, x, y); 235 } 236 237 static void draw_column (SkCanvas* canvas, int x, int y) { 238 SkRect target (SkRect::MakeWH(1 * SK_Scalar1, 10 * SK_Scalar1)); 239 draw(canvas, target, x, y); 240 } 241 242 static void draw_bar (SkCanvas* canvas, int x, int y) { 243 SkRect target (SkRect::MakeWH(10 * SK_Scalar1, 1 * SK_Scalar1)); 244 draw(canvas, target, x, y); 245 } 246 247 static void draw_rect_tests (SkCanvas* canvas) { 248 draw_square(canvas, 10, 10); 249 draw_column(canvas, 30, 10); 250 draw_bar(canvas, 10, 30); 251 } 252 253 /** 254 Test a set of clipping problems discovered while writing blitAntiRect, 255 and test all the code paths through the clipping blitters. 256 Each region should show as a blue center surrounded by a 2px green 257 border, with no red. 258 */ 259 260 class AAClipGM : public skiagm::GM { 261 public: 262 AAClipGM() { 263 264 } 265 266 protected: 267 virtual SkString onShortName() SK_OVERRIDE { 268 return SkString("aaclip"); 269 } 270 271 virtual SkISize onISize() SK_OVERRIDE { 272 return SkISize::Make(640, 480); 273 } 274 275 virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE { 276 if (false) { test_quadstroke(canvas); return; } 277 if (false) { test_conic(canvas); return; } 278 if (false) { 279 SkRect bounds; 280 canvas->getClipBounds(&bounds); 281 test_shallow_gradient(canvas, bounds.width(), bounds.height()); return; 282 } 283 if (false) { 284 test_giant_dash(canvas); return; 285 } 286 if (false) { 287 test_grad(canvas); return; 288 } 289 if (false) { // avoid bit rot, suppress warning 290 test_mask(); 291 } 292 293 // Initial pixel-boundary-aligned draw 294 draw_rect_tests(canvas); 295 296 // Repeat 4x with .2, .4, .6, .8 px offsets 297 canvas->translate(SK_Scalar1 / 5, SK_Scalar1 / 5); 298 canvas->translate(SkIntToScalar(50), 0); 299 draw_rect_tests(canvas); 300 301 canvas->translate(SK_Scalar1 / 5, SK_Scalar1 / 5); 302 canvas->translate(SkIntToScalar(50), 0); 303 draw_rect_tests(canvas); 304 305 canvas->translate(SK_Scalar1 / 5, SK_Scalar1 / 5); 306 canvas->translate(SkIntToScalar(50), 0); 307 draw_rect_tests(canvas); 308 309 canvas->translate(SK_Scalar1 / 5, SK_Scalar1 / 5); 310 canvas->translate(SkIntToScalar(50), 0); 311 draw_rect_tests(canvas); 312 } 313 314 virtual uint32_t onGetFlags() const { return kSkipPipe_Flag; } 315 316 private: 317 typedef skiagm::GM INHERITED; 318 }; 319 320 DEF_GM( return SkNEW(AAClipGM); ) 321