Home | History | Annotate | Download | only in gm
      1 /*
      2  * Copyright 2015 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 "SkAutoPixmapStorage.h"
      9 #include "SkColorPriv.h"
     10 #include "SkImage.h"
     11 #include "SkParsePath.h"
     12 #include "SkPath.h"
     13 #include "SkSurface.h"
     14 #include "gm.h"
     15 
     16 // GM to test combinations of stroking zero length paths with different caps and other settings
     17 // Variables:
     18 // * Antialiasing: On, Off
     19 // * Caps: Butt, Round, Square
     20 // * Stroke width: 0, 0.9, 1, 1.1, 15, 25
     21 // * Path form: M, ML, MLZ, MZ
     22 // * Path contours: 1 or 2
     23 // * Path verbs: Line, Quad, Cubic, Conic
     24 //
     25 // Each test is drawn to a 50x20 offscreen surface, and expected to produce some number (0 - 2) of
     26 // visible pieces of cap geometry. These are counted by scanning horizontally for peaks (blobs).
     27 
     28 static bool draw_path_cell(SkCanvas* canvas, SkImage* img, int expectedCaps) {
     29     // Draw the image
     30     canvas->drawImage(img, 0, 0);
     31 
     32     int w = img->width(), h = img->height();
     33 
     34     // Read the pixels back
     35     SkImageInfo info = SkImageInfo::MakeN32Premul(w, h);
     36     SkAutoPixmapStorage pmap;
     37     pmap.alloc(info);
     38     if (!img->readPixels(pmap, 0, 0)) {
     39         return false;
     40     }
     41 
     42     // To account for rasterization differences, we scan the middle two rows [y, y+1] of the image
     43     SkASSERT(h % 2 == 0);
     44     int y = (h - 1) / 2;
     45 
     46     bool inBlob = false;
     47     int numBlobs = 0;
     48     for (int x = 0; x < w; ++x) {
     49         // We drew white-on-black. We can look for any non-zero value. Just check red.
     50         // And we care if either row is non-zero, so just add them to simplify everything.
     51         uint32_t v = SkGetPackedR32(*pmap.addr32(x, y)) + SkGetPackedR32(*pmap.addr32(x, y + 1));
     52 
     53         if (!inBlob && v) {
     54             ++numBlobs;
     55         }
     56         inBlob = SkToBool(v);
     57     }
     58 
     59     SkPaint outline;
     60     outline.setStyle(SkPaint::kStroke_Style);
     61     if (numBlobs == expectedCaps) {
     62         outline.setColor(0xFF007F00); // Green
     63     } else if (numBlobs > expectedCaps) {
     64         outline.setColor(0xFF7F7F00); // Yellow -- more geometry than expected
     65     } else {
     66         outline.setColor(0xFF7F0000); // Red -- missing some geometry
     67     }
     68 
     69     canvas->drawRect(SkRect::MakeWH(w, h), outline);
     70     return numBlobs == expectedCaps;
     71 }
     72 
     73 static const SkPaint::Cap kCaps[] = {
     74     SkPaint::kButt_Cap,
     75     SkPaint::kRound_Cap,
     76     SkPaint::kSquare_Cap
     77 };
     78 
     79 static const SkScalar kWidths[] = { 0.0f, 0.9f, 1.0f, 1.1f, 15.0f, 25.0f };
     80 
     81 // Full set of path structures for single contour case (each primitive with and without a close)
     82 static const char* kAllVerbs[] = {
     83     nullptr,
     84     "z ",
     85     "l 0 0 ",
     86     "l 0 0 z ",
     87     "q 0 0 0 0 ",
     88     "q 0 0 0 0 z ",
     89     "c 0 0 0 0 0 0 ",
     90     "c 0 0 0 0 0 0 z ",
     91     "a 0 0 0 0 0 0 0 ",
     92     "a 0 0 0 0 0 0 0 z "
     93 };
     94 
     95 // Reduced set of path structures for double contour case, to keep total number of cases down
     96 static const char* kSomeVerbs[] = {
     97     nullptr,
     98     "z ",
     99     "l 0 0 ",
    100     "l 0 0 z ",
    101     "q 0 0 0 0 ",
    102     "q 0 0 0 0 z ",
    103 };
    104 
    105 static const int kCellWidth = 50;
    106 static const int kCellHeight = 20;
    107 static const int kCellPad = 2;
    108 
    109 static const int kNumRows = SK_ARRAY_COUNT(kCaps) * SK_ARRAY_COUNT(kWidths);
    110 static const int kNumColumns = SK_ARRAY_COUNT(kAllVerbs);
    111 static const int kTotalWidth = kNumColumns * (kCellWidth + kCellPad) + kCellPad;
    112 static const int kTotalHeight = kNumRows * (kCellHeight + kCellPad) + kCellPad;
    113 
    114 static const int kDblContourNumColums = SK_ARRAY_COUNT(kSomeVerbs) * SK_ARRAY_COUNT(kSomeVerbs);
    115 static const int kDblContourTotalWidth = kDblContourNumColums * (kCellWidth + kCellPad) + kCellPad;
    116 
    117 // 50% transparent versions of the colors used for positive/negative triage icons on gold.skia.org
    118 static const SkColor kFailureRed = 0x7FE7298A;
    119 static const SkColor kSuccessGreen = 0x7F1B9E77;
    120 
    121 static void draw_zero_length_capped_paths(SkCanvas* canvas, bool aa) {
    122     canvas->translate(kCellPad, kCellPad);
    123 
    124     SkImageInfo info = canvas->imageInfo().makeWH(kCellWidth, kCellHeight);
    125     auto surface = canvas->makeSurface(info);
    126     if (!surface) {
    127         surface = SkSurface::MakeRasterN32Premul(kCellWidth, kCellHeight);
    128     }
    129 
    130     SkPaint paint;
    131     paint.setColor(SK_ColorWHITE);
    132     paint.setAntiAlias(aa);
    133     paint.setStyle(SkPaint::kStroke_Style);
    134 
    135     int numFailedTests = 0;
    136     for (auto cap : kCaps) {
    137         for (auto width : kWidths) {
    138             paint.setStrokeCap(cap);
    139             paint.setStrokeWidth(width);
    140             canvas->save();
    141 
    142             for (auto verb : kAllVerbs) {
    143                 SkString pathStr;
    144                 pathStr.appendf("M %f %f ", (kCellWidth - 1) * 0.5f, (kCellHeight - 1) * 0.5f);
    145                 if (verb) {
    146                     pathStr.append(verb);
    147                 }
    148 
    149                 SkPath path;
    150                 SkParsePath::FromSVGString(pathStr.c_str(), &path);
    151 
    152                 surface->getCanvas()->clear(SK_ColorTRANSPARENT);
    153                 surface->getCanvas()->drawPath(path, paint);
    154                 auto img = surface->makeImageSnapshot();
    155 
    156                 // All cases should draw one cap, except for butt capped, and dangling moves
    157                 // (without a verb or close), which shouldn't draw anything.
    158                 int expectedCaps = ((SkPaint::kButt_Cap == cap) || !verb) ? 0 : 1;
    159 
    160                 if (!draw_path_cell(canvas, img.get(), expectedCaps)) {
    161                     ++numFailedTests;
    162                 }
    163                 canvas->translate(kCellWidth + kCellPad, 0);
    164             }
    165             canvas->restore();
    166             canvas->translate(0, kCellHeight + kCellPad);
    167         }
    168     }
    169 
    170     canvas->drawColor(numFailedTests > 0 ? kFailureRed : kSuccessGreen);
    171 }
    172 
    173 DEF_SIMPLE_GM_BG(zero_length_paths_aa, canvas, kTotalWidth, kTotalHeight, SK_ColorBLACK) {
    174     draw_zero_length_capped_paths(canvas, true);
    175 }
    176 
    177 DEF_SIMPLE_GM_BG(zero_length_paths_bw, canvas, kTotalWidth, kTotalHeight, SK_ColorBLACK) {
    178     draw_zero_length_capped_paths(canvas, false);
    179 }
    180 
    181 static void draw_zero_length_capped_paths_dbl_contour(SkCanvas* canvas, bool aa) {
    182     canvas->translate(kCellPad, kCellPad);
    183 
    184     SkImageInfo info = canvas->imageInfo().makeWH(kCellWidth, kCellHeight);
    185     auto surface = canvas->makeSurface(info);
    186     if (!surface) {
    187         surface = SkSurface::MakeRasterN32Premul(kCellWidth, kCellHeight);
    188     }
    189 
    190     SkPaint paint;
    191     paint.setColor(SK_ColorWHITE);
    192     paint.setAntiAlias(aa);
    193     paint.setStyle(SkPaint::kStroke_Style);
    194 
    195     int numFailedTests = 0;
    196     for (auto cap : kCaps) {
    197         for (auto width : kWidths) {
    198             paint.setStrokeCap(cap);
    199             paint.setStrokeWidth(width);
    200             canvas->save();
    201 
    202             for (auto firstVerb : kSomeVerbs) {
    203                 for (auto secondVerb : kSomeVerbs) {
    204                     int expectedCaps = 0;
    205 
    206                     SkString pathStr;
    207                     pathStr.append("M 9.5 9.5 ");
    208                     if (firstVerb) {
    209                         pathStr.append(firstVerb);
    210                         ++expectedCaps;
    211                     }
    212                     pathStr.append("M 40.5 9.5 ");
    213                     if (secondVerb) {
    214                         pathStr.append(secondVerb);
    215                         ++expectedCaps;
    216                     }
    217 
    218                     SkPath path;
    219                     SkParsePath::FromSVGString(pathStr.c_str(), &path);
    220 
    221                     surface->getCanvas()->clear(SK_ColorTRANSPARENT);
    222                     surface->getCanvas()->drawPath(path, paint);
    223                     auto img = surface->makeImageSnapshot();
    224 
    225                     if (SkPaint::kButt_Cap == cap) {
    226                         expectedCaps = 0;
    227                     }
    228 
    229                     if (!draw_path_cell(canvas, img.get(), expectedCaps)) {
    230                         ++numFailedTests;
    231                     }
    232                     canvas->translate(kCellWidth + kCellPad, 0);
    233                 }
    234             }
    235             canvas->restore();
    236             canvas->translate(0, kCellHeight + kCellPad);
    237         }
    238     }
    239 
    240     canvas->drawColor(numFailedTests > 0 ? kFailureRed : kSuccessGreen);
    241 }
    242 
    243 DEF_SIMPLE_GM_BG(zero_length_paths_dbl_aa, canvas, kDblContourTotalWidth, kTotalHeight,
    244                  SK_ColorBLACK) {
    245     draw_zero_length_capped_paths_dbl_contour(canvas, true);
    246 }
    247 
    248 DEF_SIMPLE_GM_BG(zero_length_paths_dbl_bw, canvas, kDblContourTotalWidth, kTotalHeight,
    249                  SK_ColorBLACK) {
    250     draw_zero_length_capped_paths_dbl_contour(canvas, false);
    251 }
    252