blob: 862c83c184de7cdf6bb2d4e3341934c0800d739a [file] [log] [blame]
Brian Osman6e3ce402017-05-17 15:10:18 -04001/*
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
15namespace skiagm {
16
17static 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 canvas->drawImageRect(imgA, SkRect::MakeXYWH(maxDiffX - 5, maxDiffY - 5, 10, 10),
82 SkRect::MakeXYWH(w, 0, w, h), nullptr);
83 canvas->drawImageRect(imgB, SkRect::MakeXYWH(maxDiffX - 5, maxDiffY - 5, 10, 10),
84 SkRect::MakeXYWH(2 * w, 0, w, h), nullptr);
85
86 // Add lines to separate zoom boxes
87 canvas->drawLine(w, 0, w, h, outline);
88 canvas->drawLine(2 * w, 0, 2 * w, h, outline);
89 }
90
91 // Draw outline of whole test region
92 canvas->drawRect(SkRect::MakeWH(3 * w, h), outline);
93}
94
95namespace {
96typedef std::function<void(SkCanvas*, const SkRect&, const SkPaint&)> ShapeDrawFunc;
97}
98
99/**
100 * Iterates over a variety of rect shapes, paint parameters, and matrices, calling two different
101 * user-supplied draw callbacks. Produces a grid clearly showing if the two callbacks produce the
102 * same visual results in all cases.
103 */
104static void draw_rect_geom_diff_grid(SkCanvas* canvas, ShapeDrawFunc f1, ShapeDrawFunc f2) {
105 // Variables:
106 // - Fill, hairline, wide stroke
107 // - Axis aligned, rotated, scaled, scaled negative, perspective
108 // - Source geometry (normal, collapsed, inverted)
109 //
110 // Things not (yet?) tested:
111 // - AntiAlias on/off
112 // - StrokeAndFill
113 // - Cap/join
114 // - Anything even more elaborate...
115
116 const SkRect kRects[] = {
117 SkRect::MakeXYWH(10, 10, 30, 30), // Normal
118 SkRect::MakeXYWH(10, 25, 30, 0), // Collapsed
119 SkRect::MakeXYWH(10, 40, 30, -30), // Inverted
120 };
121
122 const struct { SkPaint::Style fStyle; SkScalar fStrokeWidth; } kStyles[] = {
123 { SkPaint::kFill_Style, 0 }, // Filled
124 { SkPaint::kStroke_Style, 0 }, // Hairline
125 { SkPaint::kStroke_Style, 5 }, // Wide stroke
126 };
127
128 SkMatrix mI = SkMatrix::I();
129 SkMatrix mRot;
130 mRot.setRotate(30, 25, 25);
131 SkMatrix mScale;
132 mScale.setScaleTranslate(0.5f, 1, 12.5f, 0);
133 SkMatrix mFlipX;
134 mFlipX.setScaleTranslate(-1, 1, 50, 0);
135 SkMatrix mFlipY;
136 mFlipY.setScaleTranslate(1, -1, 0, 50);
137 SkMatrix mFlipXY;
138 mFlipXY.setScaleTranslate(-1, -1, 50, 50);
139 SkMatrix mPersp;
140 mPersp.setIdentity();
141 mPersp.setPerspY(0.002f);
142
143 const SkMatrix* kMatrices[] = { &mI, &mRot, &mScale, &mFlipX, &mFlipY, &mFlipXY, &mPersp, };
144
145 canvas->translate(10, 10);
146
147 SkImageInfo info = canvas->imageInfo().makeWH(50, 50);
148 auto surface = canvas->makeSurface(info);
149 if (!surface) {
150 surface = SkSurface::MakeRasterN32Premul(50, 50);
151 }
152
153 for (const SkRect& rect : kRects) {
154 for (const auto& style : kStyles) {
155 canvas->save();
156
157 for (const SkMatrix* mat : kMatrices) {
158 SkPaint paint;
159 paint.setColor(SK_ColorWHITE);
160 paint.setAntiAlias(true);
161 paint.setStyle(style.fStyle);
162 paint.setStrokeWidth(style.fStrokeWidth);
163
164 // Do first draw
165 surface->getCanvas()->clear(SK_ColorBLACK);
166 surface->getCanvas()->save();
167 surface->getCanvas()->concat(*mat);
168 f1(surface->getCanvas(), rect, paint);
169 surface->getCanvas()->restore();
170 auto imgA = surface->makeImageSnapshot();
171
172 // Do second draw
173 surface->getCanvas()->clear(SK_ColorBLACK);
174 surface->getCanvas()->save();
175 surface->getCanvas()->concat(*mat);
176 f2(surface->getCanvas(), rect, paint);
177 surface->getCanvas()->restore();
178 auto imgB = surface->makeImageSnapshot();
179
180 draw_diff(canvas, imgA.get(), imgB.get());
181 canvas->translate(160, 0);
182 }
183 canvas->restore();
184 canvas->translate(0, 60);
185 }
186 }
187}
188
189static const int kNumRows = 9;
190static const int kNumColumns = 7;
191static const int kTotalWidth = kNumColumns * 160 + 10;
192static const int kTotalHeight = kNumRows * 60 + 10;
193
194DEF_SIMPLE_GM_BG(rects_as_paths, canvas, kTotalWidth, kTotalHeight, SK_ColorBLACK) {
195 // Drawing a rect vs. adding it to a path and drawing the path, should produce same results.
196 auto rectDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) {
197 canvas->drawRect(rect, paint);
198 };
199 auto pathDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) {
200 SkPath path;
201 path.addRect(rect);
202 canvas->drawPath(path, paint);
203 };
204
205 draw_rect_geom_diff_grid(canvas, rectDrawFunc, pathDrawFunc);
206}
207
208DEF_SIMPLE_GM_BG(ovals_as_paths, canvas, kTotalWidth, kTotalHeight, SK_ColorBLACK) {
209 // Drawing an oval vs. adding it to a path and drawing the path, should produce same results.
210 auto ovalDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) {
211 canvas->drawOval(rect, paint);
212 };
213 auto pathDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) {
214 SkPath path;
215 path.addOval(rect);
216 canvas->drawPath(path, paint);
217 };
218
219 draw_rect_geom_diff_grid(canvas, ovalDrawFunc, pathDrawFunc);
220}
221
222DEF_SIMPLE_GM_BG(arcs_as_paths, canvas, kTotalWidth, kTotalHeight, SK_ColorBLACK) {
223 // Drawing an arc vs. adding it to a path and drawing the path, should produce same results.
224 auto arcDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) {
225 canvas->drawArc(rect, 10, 200, false, paint);
226 };
227 auto pathDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) {
228 SkPath path;
229 path.addArc(rect, 10, 200);
230 canvas->drawPath(path, paint);
231 };
232
233 draw_rect_geom_diff_grid(canvas, arcDrawFunc, pathDrawFunc);
234}
235
236}