284 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			284 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * 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/gpu/GrDirectContext.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(GrDirectContext* dContext, 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(dContext, 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 skiagm::DrawResult draw_zero_length_capped_paths(SkCanvas* canvas, bool aa,
 | |
|                                                         SkString* errorMsg) {
 | |
|     auto rContext = canvas->recordingContext();
 | |
|     auto dContext = GrAsDirectContext(rContext);
 | |
| 
 | |
|     if (!dContext && rContext) {
 | |
|         *errorMsg = "Not supported in DDL mode";
 | |
|         return skiagm::DrawResult::kSkip;
 | |
|     }
 | |
| 
 | |
|     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(dContext, canvas, img.get(), expectedCaps)) {
 | |
|                     ++numFailedTests;
 | |
|                 }
 | |
|                 canvas->translate(kCellWidth + kCellPad, 0);
 | |
|             }
 | |
|             canvas->restore();
 | |
|             canvas->translate(0, kCellHeight + kCellPad);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     canvas->drawColor(numFailedTests > 0 ? kFailureRed : kSuccessGreen);
 | |
|     return skiagm::DrawResult::kOk;
 | |
| }
 | |
| 
 | |
| DEF_SIMPLE_GM_BG_CAN_FAIL(zero_length_paths_aa, canvas, errorMsg,
 | |
|                           kTotalWidth, kTotalHeight, SK_ColorBLACK) {
 | |
|     return draw_zero_length_capped_paths(canvas, true, errorMsg);
 | |
| }
 | |
| 
 | |
| DEF_SIMPLE_GM_BG_CAN_FAIL(zero_length_paths_bw, canvas, errorMsg,
 | |
|                           kTotalWidth, kTotalHeight, SK_ColorBLACK) {
 | |
|     return draw_zero_length_capped_paths(canvas, false, errorMsg);
 | |
| }
 | |
| 
 | |
| static skiagm::DrawResult draw_zero_length_capped_paths_dbl_contour(SkCanvas* canvas, bool aa,
 | |
|                                                                     SkString* errorMsg) {
 | |
|     auto rContext = canvas->recordingContext();
 | |
|     auto dContext = GrAsDirectContext(rContext);
 | |
| 
 | |
|     if (!dContext && rContext) {
 | |
|         *errorMsg = "Not supported in DDL mode";
 | |
|         return skiagm::DrawResult::kSkip;
 | |
|     }
 | |
|     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(dContext, canvas, img.get(), expectedCaps)) {
 | |
|                         ++numFailedTests;
 | |
|                     }
 | |
|                     canvas->translate(kCellWidth + kCellPad, 0);
 | |
|                 }
 | |
|             }
 | |
|             canvas->restore();
 | |
|             canvas->translate(0, kCellHeight + kCellPad);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     canvas->drawColor(numFailedTests > 0 ? kFailureRed : kSuccessGreen);
 | |
|     return skiagm::DrawResult::kOk;
 | |
| }
 | |
| 
 | |
| DEF_SIMPLE_GM_BG_CAN_FAIL(zero_length_paths_dbl_aa, canvas, errorMsg,
 | |
|                           kDblContourTotalWidth, kTotalHeight, SK_ColorBLACK) {
 | |
|     return draw_zero_length_capped_paths_dbl_contour(canvas, true, errorMsg);
 | |
| }
 | |
| 
 | |
| DEF_SIMPLE_GM_BG_CAN_FAIL(zero_length_paths_dbl_bw, canvas, errorMsg,
 | |
|                           kDblContourTotalWidth, kTotalHeight, SK_ColorBLACK) {
 | |
|     return draw_zero_length_capped_paths_dbl_contour(canvas, false, errorMsg);
 | |
| }
 |