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