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
draw_diff(SkCanvas * canvas,SkImage * imgA,SkImage * imgB)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 */
draw_rect_geom_diff_grid(SkCanvas * canvas,ShapeDrawFunc f1,ShapeDrawFunc f2)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
DEF_SIMPLE_GM_BG(rects_as_paths,canvas,kTotalWidth,kTotalHeight,SK_ColorBLACK)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
DEF_SIMPLE_GM_BG(ovals_as_paths,canvas,kTotalWidth,kTotalHeight,SK_ColorBLACK)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
DEF_SIMPLE_GM_BG(arcs_as_paths,canvas,kTotalWidth,kTotalHeight,SK_ColorBLACK)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