Home | History | Annotate | Download | only in gm
      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 
     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  */
    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 
    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 
    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 
    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