blob: fb05d19bfb589a3548efab1ac37b75f217443a93 [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
Chris Dalton50e24d72019-02-07 16:20:09 -070018skiagm::DrawResult draw_diff(SkCanvas* canvas, SkImage* imgA, SkImage* imgB, SkString* errorMsg) {
Brian Osman6e3ce402017-05-17 15:10:18 -040019 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 Dalton50e24d72019-02-07 16:20:09 -070037 *errorMsg = "Failed to read pixels.";
38 return skiagm::DrawResult::kFail;
Brian Osman6e3ce402017-05-17 15:10:18 -040039 }
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);
Chris Dalton50e24d72019-02-07 16:20:09 -070098 return skiagm::DrawResult::kOk;
Brian Osman6e3ce402017-05-17 15:10:18 -040099}
100
101namespace {
102typedef std::function<void(SkCanvas*, const SkRect&, const SkPaint&)> ShapeDrawFunc;
103}
104
105/**
106 * Iterates over a variety of rect shapes, paint parameters, and matrices, calling two different
107 * user-supplied draw callbacks. Produces a grid clearly showing if the two callbacks produce the
108 * same visual results in all cases.
109 */
Chris Dalton50e24d72019-02-07 16:20:09 -0700110static skiagm::DrawResult draw_rect_geom_diff_grid(SkCanvas* canvas, ShapeDrawFunc f1,
111 ShapeDrawFunc f2, SkString* errorMsg) {
Brian Osman6e3ce402017-05-17 15:10:18 -0400112 // Variables:
113 // - Fill, hairline, wide stroke
114 // - Axis aligned, rotated, scaled, scaled negative, perspective
115 // - Source geometry (normal, collapsed, inverted)
116 //
117 // Things not (yet?) tested:
118 // - AntiAlias on/off
119 // - StrokeAndFill
120 // - Cap/join
121 // - Anything even more elaborate...
122
123 const SkRect kRects[] = {
124 SkRect::MakeXYWH(10, 10, 30, 30), // Normal
125 SkRect::MakeXYWH(10, 25, 30, 0), // Collapsed
126 SkRect::MakeXYWH(10, 40, 30, -30), // Inverted
127 };
128
129 const struct { SkPaint::Style fStyle; SkScalar fStrokeWidth; } kStyles[] = {
130 { SkPaint::kFill_Style, 0 }, // Filled
131 { SkPaint::kStroke_Style, 0 }, // Hairline
132 { SkPaint::kStroke_Style, 5 }, // Wide stroke
133 };
134
135 SkMatrix mI = SkMatrix::I();
136 SkMatrix mRot;
137 mRot.setRotate(30, 25, 25);
138 SkMatrix mScale;
139 mScale.setScaleTranslate(0.5f, 1, 12.5f, 0);
140 SkMatrix mFlipX;
141 mFlipX.setScaleTranslate(-1, 1, 50, 0);
142 SkMatrix mFlipY;
143 mFlipY.setScaleTranslate(1, -1, 0, 50);
144 SkMatrix mFlipXY;
145 mFlipXY.setScaleTranslate(-1, -1, 50, 50);
146 SkMatrix mPersp;
147 mPersp.setIdentity();
148 mPersp.setPerspY(0.002f);
149
150 const SkMatrix* kMatrices[] = { &mI, &mRot, &mScale, &mFlipX, &mFlipY, &mFlipXY, &mPersp, };
151
152 canvas->translate(10, 10);
153
154 SkImageInfo info = canvas->imageInfo().makeWH(50, 50);
155 auto surface = canvas->makeSurface(info);
Cary Clarka24712e2018-09-05 18:41:40 +0000156 if (!surface) {
157 surface = SkSurface::MakeRasterN32Premul(50, 50);
158 }
Brian Osman6e3ce402017-05-17 15:10:18 -0400159
160 for (const SkRect& rect : kRects) {
161 for (const auto& style : kStyles) {
162 canvas->save();
163
164 for (const SkMatrix* mat : kMatrices) {
165 SkPaint paint;
166 paint.setColor(SK_ColorWHITE);
167 paint.setAntiAlias(true);
168 paint.setStyle(style.fStyle);
169 paint.setStrokeWidth(style.fStrokeWidth);
170
171 // Do first draw
172 surface->getCanvas()->clear(SK_ColorBLACK);
173 surface->getCanvas()->save();
174 surface->getCanvas()->concat(*mat);
175 f1(surface->getCanvas(), rect, paint);
176 surface->getCanvas()->restore();
177 auto imgA = surface->makeImageSnapshot();
178
179 // Do second draw
180 surface->getCanvas()->clear(SK_ColorBLACK);
181 surface->getCanvas()->save();
182 surface->getCanvas()->concat(*mat);
183 f2(surface->getCanvas(), rect, paint);
184 surface->getCanvas()->restore();
185 auto imgB = surface->makeImageSnapshot();
186
Chris Dalton50e24d72019-02-07 16:20:09 -0700187 skiagm::DrawResult drawResult = draw_diff(canvas, imgA.get(), imgB.get(), errorMsg);
188 if (skiagm::DrawResult::kOk != drawResult) {
189 return drawResult;
190 }
Brian Osman6e3ce402017-05-17 15:10:18 -0400191 canvas->translate(160, 0);
192 }
193 canvas->restore();
194 canvas->translate(0, 60);
195 }
196 }
Chris Dalton50e24d72019-02-07 16:20:09 -0700197 return skiagm::DrawResult::kOk;
Brian Osman6e3ce402017-05-17 15:10:18 -0400198}
199
200static const int kNumRows = 9;
201static const int kNumColumns = 7;
202static const int kTotalWidth = kNumColumns * 160 + 10;
203static const int kTotalHeight = kNumRows * 60 + 10;
204
Chris Dalton50e24d72019-02-07 16:20:09 -0700205DEF_SIMPLE_GM_BG_CAN_FAIL(rects_as_paths, canvas, errorMsg, kTotalWidth, kTotalHeight,
206 SK_ColorBLACK) {
Brian Osman6e3ce402017-05-17 15:10:18 -0400207 // Drawing a rect vs. adding it to a path and drawing the path, should produce same results.
208 auto rectDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) {
209 canvas->drawRect(rect, paint);
210 };
211 auto pathDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) {
212 SkPath path;
213 path.addRect(rect);
214 canvas->drawPath(path, paint);
215 };
216
Chris Dalton50e24d72019-02-07 16:20:09 -0700217 return draw_rect_geom_diff_grid(canvas, rectDrawFunc, pathDrawFunc, errorMsg);
Brian Osman6e3ce402017-05-17 15:10:18 -0400218}
219
Chris Dalton50e24d72019-02-07 16:20:09 -0700220DEF_SIMPLE_GM_BG_CAN_FAIL(ovals_as_paths, canvas, errorMsg, kTotalWidth, kTotalHeight,
221 SK_ColorBLACK) {
Brian Osman6e3ce402017-05-17 15:10:18 -0400222 // Drawing an oval vs. adding it to a path and drawing the path, should produce same results.
223 auto ovalDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) {
224 canvas->drawOval(rect, paint);
225 };
226 auto pathDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) {
227 SkPath path;
228 path.addOval(rect);
229 canvas->drawPath(path, paint);
230 };
231
Chris Dalton50e24d72019-02-07 16:20:09 -0700232 return draw_rect_geom_diff_grid(canvas, ovalDrawFunc, pathDrawFunc, errorMsg);
Brian Osman6e3ce402017-05-17 15:10:18 -0400233}
234
Chris Dalton50e24d72019-02-07 16:20:09 -0700235DEF_SIMPLE_GM_BG_CAN_FAIL(arcs_as_paths, canvas, errorMsg, kTotalWidth, kTotalHeight,
236 SK_ColorBLACK) {
Brian Osman6e3ce402017-05-17 15:10:18 -0400237 // Drawing an arc vs. adding it to a path and drawing the path, should produce same results.
238 auto arcDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) {
239 canvas->drawArc(rect, 10, 200, false, paint);
240 };
241 auto pathDrawFunc = [](SkCanvas* canvas, const SkRect& rect, const SkPaint& paint) {
242 SkPath path;
243 path.addArc(rect, 10, 200);
244 canvas->drawPath(path, paint);
245 };
246
Chris Dalton50e24d72019-02-07 16:20:09 -0700247 return draw_rect_geom_diff_grid(canvas, arcDrawFunc, pathDrawFunc, errorMsg);
Brian Osman6e3ce402017-05-17 15:10:18 -0400248}
249
250}