123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- /*
- * Copyright 2015 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
- #include "gm/gm.h"
- #include "include/core/SkCanvas.h"
- #include "include/core/SkColor.h"
- #include "include/core/SkColorPriv.h"
- #include "include/core/SkImage.h"
- #include "include/core/SkImageInfo.h"
- #include "include/core/SkPaint.h"
- #include "include/core/SkPath.h"
- #include "include/core/SkRect.h"
- #include "include/core/SkRefCnt.h"
- #include "include/core/SkScalar.h"
- #include "include/core/SkString.h"
- #include "include/core/SkSurface.h"
- #include "include/core/SkTypes.h"
- #include "include/utils/SkParsePath.h"
- #include "src/core/SkAutoPixmapStorage.h"
- // GM to test combinations of stroking zero length paths with different caps and other settings
- // Variables:
- // * Antialiasing: On, Off
- // * Caps: Butt, Round, Square
- // * Stroke width: 0, 0.9, 1, 1.1, 15, 25
- // * Path form: M, ML, MLZ, MZ
- // * Path contours: 1 or 2
- // * Path verbs: Line, Quad, Cubic, Conic
- //
- // Each test is drawn to a 50x20 offscreen surface, and expected to produce some number (0 - 2) of
- // visible pieces of cap geometry. These are counted by scanning horizontally for peaks (blobs).
- static bool draw_path_cell(SkCanvas* canvas, SkImage* img, int expectedCaps) {
- // Draw the image
- canvas->drawImage(img, 0, 0);
- int w = img->width(), h = img->height();
- // Read the pixels back
- SkImageInfo info = SkImageInfo::MakeN32Premul(w, h);
- SkAutoPixmapStorage pmap;
- pmap.alloc(info);
- if (!img->readPixels(pmap, 0, 0)) {
- return false;
- }
- // To account for rasterization differences, we scan the middle two rows [y, y+1] of the image
- SkASSERT(h % 2 == 0);
- int y = (h - 1) / 2;
- bool inBlob = false;
- int numBlobs = 0;
- for (int x = 0; x < w; ++x) {
- // We drew white-on-black. We can look for any non-zero value. Just check red.
- // And we care if either row is non-zero, so just add them to simplify everything.
- uint32_t v = SkGetPackedR32(*pmap.addr32(x, y)) + SkGetPackedR32(*pmap.addr32(x, y + 1));
- if (!inBlob && v) {
- ++numBlobs;
- }
- inBlob = SkToBool(v);
- }
- SkPaint outline;
- outline.setStyle(SkPaint::kStroke_Style);
- if (numBlobs == expectedCaps) {
- outline.setColor(0xFF007F00); // Green
- } else if (numBlobs > expectedCaps) {
- outline.setColor(0xFF7F7F00); // Yellow -- more geometry than expected
- } else {
- outline.setColor(0xFF7F0000); // Red -- missing some geometry
- }
- canvas->drawRect(SkRect::MakeWH(w, h), outline);
- return numBlobs == expectedCaps;
- }
- static const SkPaint::Cap kCaps[] = {
- SkPaint::kButt_Cap,
- SkPaint::kRound_Cap,
- SkPaint::kSquare_Cap
- };
- static const SkScalar kWidths[] = { 0.0f, 0.9f, 1.0f, 1.1f, 15.0f, 25.0f };
- // Full set of path structures for single contour case (each primitive with and without a close)
- static const char* kAllVerbs[] = {
- nullptr,
- "z ",
- "l 0 0 ",
- "l 0 0 z ",
- "q 0 0 0 0 ",
- "q 0 0 0 0 z ",
- "c 0 0 0 0 0 0 ",
- "c 0 0 0 0 0 0 z ",
- "a 0 0 0 0 0 0 0 ",
- "a 0 0 0 0 0 0 0 z "
- };
- // Reduced set of path structures for double contour case, to keep total number of cases down
- static const char* kSomeVerbs[] = {
- nullptr,
- "z ",
- "l 0 0 ",
- "l 0 0 z ",
- "q 0 0 0 0 ",
- "q 0 0 0 0 z ",
- };
- static const int kCellWidth = 50;
- static const int kCellHeight = 20;
- static const int kCellPad = 2;
- static const int kNumRows = SK_ARRAY_COUNT(kCaps) * SK_ARRAY_COUNT(kWidths);
- static const int kNumColumns = SK_ARRAY_COUNT(kAllVerbs);
- static const int kTotalWidth = kNumColumns * (kCellWidth + kCellPad) + kCellPad;
- static const int kTotalHeight = kNumRows * (kCellHeight + kCellPad) + kCellPad;
- static const int kDblContourNumColums = SK_ARRAY_COUNT(kSomeVerbs) * SK_ARRAY_COUNT(kSomeVerbs);
- static const int kDblContourTotalWidth = kDblContourNumColums * (kCellWidth + kCellPad) + kCellPad;
- // 50% transparent versions of the colors used for positive/negative triage icons on gold.skia.org
- static const SkColor kFailureRed = 0x7FE7298A;
- static const SkColor kSuccessGreen = 0x7F1B9E77;
- static void draw_zero_length_capped_paths(SkCanvas* canvas, bool aa) {
- canvas->translate(kCellPad, kCellPad);
- SkImageInfo info = canvas->imageInfo().makeWH(kCellWidth, kCellHeight);
- auto surface = canvas->makeSurface(info);
- if (!surface) {
- surface = SkSurface::MakeRasterN32Premul(kCellWidth, kCellHeight);
- }
- SkPaint paint;
- paint.setColor(SK_ColorWHITE);
- paint.setAntiAlias(aa);
- paint.setStyle(SkPaint::kStroke_Style);
- int numFailedTests = 0;
- for (auto cap : kCaps) {
- for (auto width : kWidths) {
- paint.setStrokeCap(cap);
- paint.setStrokeWidth(width);
- canvas->save();
- for (auto verb : kAllVerbs) {
- SkString pathStr;
- pathStr.appendf("M %f %f ", (kCellWidth - 1) * 0.5f, (kCellHeight - 1) * 0.5f);
- if (verb) {
- pathStr.append(verb);
- }
- SkPath path;
- SkParsePath::FromSVGString(pathStr.c_str(), &path);
- surface->getCanvas()->clear(SK_ColorTRANSPARENT);
- surface->getCanvas()->drawPath(path, paint);
- auto img = surface->makeImageSnapshot();
- // All cases should draw one cap, except for butt capped, and dangling moves
- // (without a verb or close), which shouldn't draw anything.
- int expectedCaps = ((SkPaint::kButt_Cap == cap) || !verb) ? 0 : 1;
- if (!draw_path_cell(canvas, img.get(), expectedCaps)) {
- ++numFailedTests;
- }
- canvas->translate(kCellWidth + kCellPad, 0);
- }
- canvas->restore();
- canvas->translate(0, kCellHeight + kCellPad);
- }
- }
- canvas->drawColor(numFailedTests > 0 ? kFailureRed : kSuccessGreen);
- }
- DEF_SIMPLE_GM_BG(zero_length_paths_aa, canvas, kTotalWidth, kTotalHeight, SK_ColorBLACK) {
- draw_zero_length_capped_paths(canvas, true);
- }
- DEF_SIMPLE_GM_BG(zero_length_paths_bw, canvas, kTotalWidth, kTotalHeight, SK_ColorBLACK) {
- draw_zero_length_capped_paths(canvas, false);
- }
- static void draw_zero_length_capped_paths_dbl_contour(SkCanvas* canvas, bool aa) {
- canvas->translate(kCellPad, kCellPad);
- SkImageInfo info = canvas->imageInfo().makeWH(kCellWidth, kCellHeight);
- auto surface = canvas->makeSurface(info);
- if (!surface) {
- surface = SkSurface::MakeRasterN32Premul(kCellWidth, kCellHeight);
- }
- SkPaint paint;
- paint.setColor(SK_ColorWHITE);
- paint.setAntiAlias(aa);
- paint.setStyle(SkPaint::kStroke_Style);
- int numFailedTests = 0;
- for (auto cap : kCaps) {
- for (auto width : kWidths) {
- paint.setStrokeCap(cap);
- paint.setStrokeWidth(width);
- canvas->save();
- for (auto firstVerb : kSomeVerbs) {
- for (auto secondVerb : kSomeVerbs) {
- int expectedCaps = 0;
- SkString pathStr;
- pathStr.append("M 9.5 9.5 ");
- if (firstVerb) {
- pathStr.append(firstVerb);
- ++expectedCaps;
- }
- pathStr.append("M 40.5 9.5 ");
- if (secondVerb) {
- pathStr.append(secondVerb);
- ++expectedCaps;
- }
- SkPath path;
- SkParsePath::FromSVGString(pathStr.c_str(), &path);
- surface->getCanvas()->clear(SK_ColorTRANSPARENT);
- surface->getCanvas()->drawPath(path, paint);
- auto img = surface->makeImageSnapshot();
- if (SkPaint::kButt_Cap == cap) {
- expectedCaps = 0;
- }
- if (!draw_path_cell(canvas, img.get(), expectedCaps)) {
- ++numFailedTests;
- }
- canvas->translate(kCellWidth + kCellPad, 0);
- }
- }
- canvas->restore();
- canvas->translate(0, kCellHeight + kCellPad);
- }
- }
- canvas->drawColor(numFailedTests > 0 ? kFailureRed : kSuccessGreen);
- }
- DEF_SIMPLE_GM_BG(zero_length_paths_dbl_aa, canvas, kDblContourTotalWidth, kTotalHeight,
- SK_ColorBLACK) {
- draw_zero_length_capped_paths_dbl_contour(canvas, true);
- }
- DEF_SIMPLE_GM_BG(zero_length_paths_dbl_bw, canvas, kDblContourTotalWidth, kTotalHeight,
- SK_ColorBLACK) {
- draw_zero_length_capped_paths_dbl_contour(canvas, false);
- }
|