• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "SkColorPriv.h"
12 #include "SkImage.h"
13 #include "SkPath.h"
14 #include "SkSurface.h"
15 
16 namespace skiagm {
17 
draw_diff(SkCanvas * canvas,SkImage * imgA,SkImage * imgB)18 static 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
82         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);
89 
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 
99 namespace {
100 typedef 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  */
draw_rect_geom_diff_grid(SkCanvas * canvas,ShapeDrawFunc f1,ShapeDrawFunc f2)108 static 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 
193 static const int kNumRows = 9;
194 static const int kNumColumns = 7;
195 static const int kTotalWidth = kNumColumns * 160 + 10;
196 static const int kTotalHeight = kNumRows * 60 + 10;
197 
DEF_SIMPLE_GM_BG(rects_as_paths,canvas,kTotalWidth,kTotalHeight,SK_ColorBLACK)198 DEF_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 
DEF_SIMPLE_GM_BG(ovals_as_paths,canvas,kTotalWidth,kTotalHeight,SK_ColorBLACK)212 DEF_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 
DEF_SIMPLE_GM_BG(arcs_as_paths,canvas,kTotalWidth,kTotalHeight,SK_ColorBLACK)226 DEF_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 }
241