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