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