blob: 4ad760dcc71ac8233602d807282b8d6df7dddab5 [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)) {
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
Brian Osmanad5a6462017-06-05 13:49:38 -040082 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);
Brian Osman6e3ce402017-05-17 15:10:18 -040089
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
99namespace {
100typedef 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 */
108static 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
193static const int kNumRows = 9;
194static const int kNumColumns = 7;
195static const int kTotalWidth = kNumColumns * 160 + 10;
196static const int kTotalHeight = kNumRows * 60 + 10;
197
198DEF_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
212DEF_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
226DEF_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}