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