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 "SkColorPriv.h"
     12 #include "SkImage.h"
     13 #include "SkPath.h"
     14 #include "SkSurface.h"
     15 
     16 namespace skiagm {
     17 
     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  */
    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 
    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 
    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 
    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