1 /* 2 * Copyright 2017 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 10 #include "SkAutoPixmapStorage.h" 11 #include "SkImage.h" 12 #include "SkPath.h" 13 #include "SkSurface.h" 14 15 namespace skiagm { 16 17 static void draw_diff(SkCanvas* canvas, SkImage* imgA, SkImage* imgB) { 18 SkASSERT(imgA->dimensions() == imgB->dimensions()); 19 20 int w = imgA->width(), h = imgA->height(); 21 22 // First, draw the two images faintly overlaid 23 SkPaint paint; 24 paint.setAlpha(64); 25 paint.setBlendMode(SkBlendMode::kPlus); 26 canvas->drawImage(imgA, 0, 0, &paint); 27 canvas->drawImage(imgB, 0, 0, &paint); 28 29 // Next, read the pixels back, figure out if there are any differences 30 SkImageInfo info = SkImageInfo::MakeN32Premul(w, h); 31 SkAutoPixmapStorage pmapA; 32 SkAutoPixmapStorage pmapB; 33 pmapA.alloc(info); 34 pmapB.alloc(info); 35 if (!imgA->readPixels(pmapA, 0, 0) || !imgB->readPixels(pmapB, 0, 0)) { 36 return; 37 } 38 39 int maxDiffX = 0, maxDiffY = 0, maxDiff = 0; 40 SkBitmap highlight; 41 highlight.allocN32Pixels(w, h); 42 highlight.eraseColor(SK_ColorTRANSPARENT); 43 44 for (int y = 0; y < h; ++y) { 45 for (int x = 0; x < w; ++x) { 46 uint32_t pixelA = *pmapA.addr32(x, y); 47 uint32_t pixelB = *pmapB.addr32(x, y); 48 if (pixelA != pixelB) { 49 int diff = 50 SkTAbs((int)(SkColorGetR(pixelA) - SkColorGetR(pixelB))) + 51 SkTAbs((int)(SkColorGetG(pixelA) - SkColorGetG(pixelB))) + 52 SkTAbs((int)(SkColorGetB(pixelA) - SkColorGetB(pixelB))) + 53 SkTAbs((int)(SkColorGetA(pixelA) - SkColorGetA(pixelB))); 54 if (diff > maxDiff) { 55 maxDiffX = x; 56 maxDiffY = y; 57 maxDiff = diff; 58 } 59 *highlight.getAddr32(x, y) = SkPackARGB32(0xA0, 0xA0, 0x00, 0x00); 60 } 61 } 62 } 63 64 SkPaint outline; 65 outline.setStyle(SkPaint::kStroke_Style); 66 outline.setColor(maxDiff == 0 ? 0xFF007F00 : 0xFF7F0000); 67 68 if (maxDiff > 0) { 69 // Call extra attention to the region we're going to zoom 70 SkPMColor yellow = SkPackARGB32(0xFF, 0xFF, 0xFF, 0x00); 71 *highlight.getAddr32(maxDiffX, maxDiffY) = yellow; 72 *highlight.getAddr32(SkTMax(maxDiffX - 1, 0), maxDiffY) = yellow; 73 *highlight.getAddr32(maxDiffX, SkTMax(maxDiffY - 1, 0)) = yellow; 74 *highlight.getAddr32(SkTMin(maxDiffX + 1, w - 1), maxDiffY) = yellow; 75 *highlight.getAddr32(maxDiffX, SkTMin(maxDiffY + 1, h - 1)) = yellow; 76 77 // Draw the overlay 78 canvas->drawBitmap(highlight, 0, 0); 79 80 // Draw zoom of largest pixel diff 81 SkBitmap bmpA, bmpB; 82 SkAssertResult(bmpA.installPixels(pmapA)); 83 SkAssertResult(bmpB.installPixels(pmapB)); 84 canvas->drawBitmapRect(bmpA, SkRect::MakeXYWH(maxDiffX - 5, maxDiffY - 5, 10, 10), 85 SkRect::MakeXYWH(w, 0, w, h), nullptr); 86 canvas->drawBitmapRect(bmpB, SkRect::MakeXYWH(maxDiffX - 5, maxDiffY - 5, 10, 10), 87 SkRect::MakeXYWH(2 * w, 0, w, h), nullptr); 88 89 // Add lines to separate zoom boxes 90 canvas->drawLine(w, 0, w, h, outline); 91 canvas->drawLine(2 * w, 0, 2 * w, h, outline); 92 } 93 94 // Draw outline of whole test region 95 canvas->drawRect(SkRect::MakeWH(3 * w, h), outline); 96 } 97 98 namespace { 99 typedef std::function<void(SkCanvas*, const SkRect&, const SkPaint&)> ShapeDrawFunc; 100 } 101 102 /** 103 * Iterates over a variety of rect shapes, paint parameters, and matrices, calling two different 104 * user-supplied draw callbacks. Produces a grid clearly showing if the two callbacks produce the 105 * same visual results in all cases. 106 */ 107 static void draw_rect_geom_diff_grid(SkCanvas* canvas, ShapeDrawFunc f1, ShapeDrawFunc f2) { 108 // Variables: 109 // - Fill, hairline, wide stroke 110 // - Axis aligned, rotated, scaled, scaled negative, perspective 111 // - Source geometry (normal, collapsed, inverted) 112 // 113 // Things not (yet?) tested: 114 // - AntiAlias on/off 115 // - StrokeAndFill 116 // - Cap/join 117 // - Anything even more elaborate... 118 119 const SkRect kRects[] = { 120 SkRect::MakeXYWH(10, 10, 30, 30), // Normal 121 SkRect::MakeXYWH(10, 25, 30, 0), // Collapsed 122 SkRect::MakeXYWH(10, 40, 30, -30), // Inverted 123 }; 124 125 const struct { SkPaint::Style fStyle; SkScalar fStrokeWidth; } kStyles[] = { 126 { SkPaint::kFill_Style, 0 }, // Filled 127 { SkPaint::kStroke_Style, 0 }, // Hairline 128 { SkPaint::kStroke_Style, 5 }, // Wide stroke 129 }; 130 131 SkMatrix mI = SkMatrix::I(); 132 SkMatrix mRot; 133 mRot.setRotate(30, 25, 25); 134 SkMatrix mScale; 135 mScale.setScaleTranslate(0.5f, 1, 12.5f, 0); 136 SkMatrix mFlipX; 137 mFlipX.setScaleTranslate(-1, 1, 50, 0); 138 SkMatrix mFlipY; 139 mFlipY.setScaleTranslate(1, -1, 0, 50); 140 SkMatrix mFlipXY; 141 mFlipXY.setScaleTranslate(-1, -1, 50, 50); 142 SkMatrix mPersp; 143 mPersp.setIdentity(); 144 mPersp.setPerspY(0.002f); 145 146 const SkMatrix* kMatrices[] = { &mI, &mRot, &mScale, &mFlipX, &mFlipY, &mFlipXY, &mPersp, }; 147 148 canvas->translate(10, 10); 149 150 SkImageInfo info = canvas->imageInfo().makeWH(50, 50); 151 auto surface = canvas->makeSurface(info); 152 if (!surface) { 153 surface = SkSurface::MakeRasterN32Premul(50, 50); 154 } 155 156 for (const SkRect& rect : kRects) { 157 for (const auto& style : kStyles) { 158 canvas->save(); 159 160 for (const SkMatrix* mat : kMatrices) { 161 SkPaint paint; 162 paint.setColor(SK_ColorWHITE); 163 paint.setAntiAlias(true); 164 paint.setStyle(style.fStyle); 165 paint.setStrokeWidth(style.fStrokeWidth); 166 167 // Do first draw 168 surface->getCanvas()->clear(SK_ColorBLACK); 169 surface->getCanvas()->save(); 170 surface->getCanvas()->concat(*mat); 171 f1(surface->getCanvas(), rect, paint); 172 surface->getCanvas()->restore(); 173 auto imgA = surface->makeImageSnapshot(); 174 175 // Do second draw 176 surface->getCanvas()->clear(SK_ColorBLACK); 177 surface->getCanvas()->save(); 178 surface->getCanvas()->concat(*mat); 179 f2(surface->getCanvas(), rect, paint); 180 surface->getCanvas()->restore(); 181 auto imgB = surface->makeImageSnapshot(); 182 183 draw_diff(canvas, imgA.get(), imgB.get()); 184 canvas->translate(160, 0); 185 } 186 canvas->restore(); 187 canvas->translate(0, 60); 188 } 189 } 190 } 191 192 static const int kNumRows = 9; 193 static const int kNumColumns = 7; 194 static const int kTotalWidth = kNumColumns * 160 + 10; 195 static const int kTotalHeight = kNumRows * 60 + 10; 196 197 DEF_SIMPLE_GM_BG(rects_as_paths, canvas, kTotalWidth, kTotalHeight, SK_ColorBLACK) { 198 // Drawing a rect vs. adding it to a path and drawing the path, should produce same results. 199 auto rectDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) { 200 canvas->drawRect(rect, paint); 201 }; 202 auto pathDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) { 203 SkPath path; 204 path.addRect(rect); 205 canvas->drawPath(path, paint); 206 }; 207 208 draw_rect_geom_diff_grid(canvas, rectDrawFunc, pathDrawFunc); 209 } 210 211 DEF_SIMPLE_GM_BG(ovals_as_paths, canvas, kTotalWidth, kTotalHeight, SK_ColorBLACK) { 212 // Drawing an oval vs. adding it to a path and drawing the path, should produce same results. 213 auto ovalDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) { 214 canvas->drawOval(rect, paint); 215 }; 216 auto pathDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) { 217 SkPath path; 218 path.addOval(rect); 219 canvas->drawPath(path, paint); 220 }; 221 222 draw_rect_geom_diff_grid(canvas, ovalDrawFunc, pathDrawFunc); 223 } 224 225 DEF_SIMPLE_GM_BG(arcs_as_paths, canvas, kTotalWidth, kTotalHeight, SK_ColorBLACK) { 226 // Drawing an arc vs. adding it to a path and drawing the path, should produce same results. 227 auto arcDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) { 228 canvas->drawArc(rect, 10, 200, false, paint); 229 }; 230 auto pathDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) { 231 SkPath path; 232 path.addArc(rect, 10, 200); 233 canvas->drawPath(path, paint); 234 }; 235 236 draw_rect_geom_diff_grid(canvas, arcDrawFunc, pathDrawFunc); 237 } 238 239 } 240