skia2/gm/path_stroke_with_zero_length.cpp

284 lines
9.6 KiB
C++
Raw Normal View History

/*
* 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)) {
Revert "Reland "Revert "Update GPU DM/nanobench configs for color testing""" This reverts commit d5161b9a62b6912105e7a49d06e5126bf4fa1f75. Reason for revert: This should be okay again. Original change's description: > Reland "Revert "Update GPU DM/nanobench configs for color testing"" > > This reverts commit c0fb3c8b9fea2f60062819e92a1fa7f0a1966fba. > > Reason for revert: Had to revert dependent CL. > Original change's description: > > Revert "Revert "Update GPU DM/nanobench configs for color testing"" > > > > This reverts commit 2a284de7a51c198096a6f34ec1cbfe9118db554c. > > > > Re-landing without esrgb configs. Going to enable those separately, > > as they require some blacklisting. > > > > Bug: skia: > > Change-Id: I2315849d4f5c45c2e5512f84f9f867d02ff6fe30 > > Reviewed-on: https://skia-review.googlesource.com/136589 > > Reviewed-by: Brian Osman <brianosman@google.com> > > Commit-Queue: Brian Osman <brianosman@google.com> > > TBR=mtklein@google.com,bsalomon@google.com,brianosman@google.com > > Change-Id: I942d4235c17100fd933bf9cbfe923e3ae80657b1 > No-Presubmit: true > No-Tree-Checks: true > No-Try: true > Bug: skia: > Reviewed-on: https://skia-review.googlesource.com/137721 > Reviewed-by: Brian Osman <brianosman@google.com> > Commit-Queue: Brian Osman <brianosman@google.com> TBR=mtklein@google.com,bsalomon@google.com,brianosman@google.com # Not skipping CQ checks because original CL landed > 1 day ago. Bug: skia: Change-Id: I280dac49eb74d6c1cbf7c2f52a3f6a68fa2510ae Reviewed-on: https://skia-review.googlesource.com/138301 Reviewed-by: Brian Osman <brianosman@google.com> Commit-Queue: Brian Osman <brianosman@google.com>
2018-06-28 17:52:02 +00:00
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);
}