2016-08-02 17:35:57 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2016 Google Inc.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This GM exercises stroking of paths with large stroke lengths, which is
|
|
|
|
* referred to as "overstroke" for brevity. In Skia as of 8/2016 we offset
|
|
|
|
* each part of the curve the request amount even if it makes the offsets
|
|
|
|
* overlap and create holes. There is not a really great algorithm for this
|
|
|
|
* and several other 2D graphics engines have the same bug.
|
|
|
|
*
|
2016-08-09 15:53:30 +00:00
|
|
|
* If we run this using Nvidia Path Renderer with:
|
|
|
|
* `path/to/dm --match OverStroke -w gm_out --gpu --config nvpr16`
|
|
|
|
* then we get correct results, so that is a possible direction of attack -
|
|
|
|
* use the GPU and a completely different algorithm to get correctness in
|
|
|
|
* Skia.
|
|
|
|
*
|
2016-08-02 17:35:57 +00:00
|
|
|
* See crbug.com/589769 skbug.com/5405 skbug.com/5406
|
|
|
|
*/
|
|
|
|
|
2019-04-23 17:05:21 +00:00
|
|
|
#include "gm/gm.h"
|
2019-05-01 21:28:53 +00:00
|
|
|
#include "include/core/SkCanvas.h"
|
|
|
|
#include "include/core/SkColor.h"
|
2019-04-23 17:05:21 +00:00
|
|
|
#include "include/core/SkPaint.h"
|
2020-08-03 15:02:20 +00:00
|
|
|
#include "include/core/SkPathBuilder.h"
|
2019-04-23 17:05:21 +00:00
|
|
|
#include "include/core/SkPathMeasure.h"
|
2019-05-01 21:28:53 +00:00
|
|
|
#include "include/core/SkPoint.h"
|
|
|
|
#include "include/core/SkRect.h"
|
|
|
|
#include "include/core/SkScalar.h"
|
2019-04-23 17:05:21 +00:00
|
|
|
#include "src/core/SkPointPriv.h"
|
2016-08-09 15:53:30 +00:00
|
|
|
|
2019-05-01 21:28:53 +00:00
|
|
|
#include <cstddef>
|
|
|
|
|
2016-08-09 15:53:30 +00:00
|
|
|
const SkScalar OVERSTROKE_WIDTH = 500.0f;
|
|
|
|
const SkScalar NORMALSTROKE_WIDTH = 3.0f;
|
2016-08-02 17:35:57 +00:00
|
|
|
|
|
|
|
//////// path and paint builders
|
|
|
|
|
2016-08-09 15:53:30 +00:00
|
|
|
SkPaint make_normal_paint() {
|
|
|
|
SkPaint p;
|
|
|
|
p.setAntiAlias(true);
|
|
|
|
p.setStyle(SkPaint::kStroke_Style);
|
|
|
|
p.setStrokeWidth(NORMALSTROKE_WIDTH);
|
|
|
|
p.setColor(SK_ColorBLUE);
|
|
|
|
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2016-08-02 17:35:57 +00:00
|
|
|
SkPaint make_overstroke_paint() {
|
|
|
|
SkPaint p;
|
|
|
|
p.setAntiAlias(true);
|
|
|
|
p.setStyle(SkPaint::kStroke_Style);
|
2016-08-09 15:53:30 +00:00
|
|
|
p.setStrokeWidth(OVERSTROKE_WIDTH);
|
2016-08-02 17:35:57 +00:00
|
|
|
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
SkPath quad_path() {
|
2020-08-26 16:56:51 +00:00
|
|
|
return SkPathBuilder().moveTo(0, 0)
|
|
|
|
.lineTo(100, 0)
|
|
|
|
.quadTo(50, -40, 0, 0)
|
|
|
|
.close()
|
|
|
|
.detach();
|
2016-08-02 17:35:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SkPath cubic_path() {
|
|
|
|
SkPath path;
|
|
|
|
path.moveTo(0, 0);
|
|
|
|
path.cubicTo(25, 75,
|
|
|
|
75, -50,
|
|
|
|
100, 0);
|
|
|
|
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
SkPath oval_path() {
|
|
|
|
SkRect oval = SkRect::MakeXYWH(0, -25, 100, 50);
|
|
|
|
|
2020-08-03 15:02:20 +00:00
|
|
|
return SkPathBuilder().arcTo(oval, 0, 359, true).close().detach();
|
2016-08-02 17:35:57 +00:00
|
|
|
}
|
|
|
|
|
2016-08-09 15:53:30 +00:00
|
|
|
SkPath ribs_path(SkPath path, SkScalar radius) {
|
|
|
|
SkPath ribs;
|
|
|
|
|
|
|
|
const SkScalar spacing = 5.0f;
|
|
|
|
float accum = 0.0f;
|
|
|
|
|
|
|
|
SkPathMeasure meas(path, false);
|
|
|
|
SkScalar length = meas.getLength();
|
|
|
|
SkPoint pos;
|
|
|
|
SkVector tan;
|
|
|
|
while (accum < length) {
|
|
|
|
if (meas.getPosTan(accum, &pos, &tan)) {
|
|
|
|
tan.scale(radius);
|
2017-11-08 16:44:31 +00:00
|
|
|
SkPointPriv::RotateCCW(&tan);
|
2016-08-09 15:53:30 +00:00
|
|
|
|
|
|
|
ribs.moveTo(pos.x() + tan.x(), pos.y() + tan.y());
|
|
|
|
ribs.lineTo(pos.x() - tan.x(), pos.y() - tan.y());
|
|
|
|
}
|
|
|
|
accum += spacing;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ribs;
|
|
|
|
}
|
|
|
|
|
|
|
|
void draw_ribs(SkCanvas *canvas, SkPath path) {
|
|
|
|
SkPath ribs = ribs_path(path, OVERSTROKE_WIDTH/2.0f);
|
|
|
|
SkPaint p = make_normal_paint();
|
|
|
|
p.setStrokeWidth(1);
|
|
|
|
p.setColor(SK_ColorBLUE);
|
|
|
|
p.setColor(SK_ColorGREEN);
|
|
|
|
|
|
|
|
canvas->drawPath(ribs, p);
|
|
|
|
}
|
|
|
|
|
2016-08-02 17:35:57 +00:00
|
|
|
///////// quads
|
|
|
|
|
|
|
|
void draw_small_quad(SkCanvas *canvas) {
|
|
|
|
// scaled so it's visible
|
2016-08-09 15:53:30 +00:00
|
|
|
// canvas->scale(8, 8);
|
2016-08-02 17:35:57 +00:00
|
|
|
|
2016-08-09 15:53:30 +00:00
|
|
|
SkPaint p = make_normal_paint();
|
2016-08-02 17:35:57 +00:00
|
|
|
SkPath path = quad_path();
|
|
|
|
|
2016-08-09 15:53:30 +00:00
|
|
|
draw_ribs(canvas, path);
|
2016-08-02 17:35:57 +00:00
|
|
|
canvas->drawPath(path, p);
|
|
|
|
}
|
|
|
|
|
|
|
|
void draw_large_quad(SkCanvas *canvas) {
|
|
|
|
SkPaint p = make_overstroke_paint();
|
|
|
|
SkPath path = quad_path();
|
|
|
|
|
|
|
|
canvas->drawPath(path, p);
|
2016-08-09 15:53:30 +00:00
|
|
|
draw_ribs(canvas, path);
|
2016-08-02 17:35:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void draw_quad_fillpath(SkCanvas *canvas) {
|
|
|
|
SkPath path = quad_path();
|
|
|
|
SkPaint p = make_overstroke_paint();
|
|
|
|
|
2016-08-09 15:53:30 +00:00
|
|
|
SkPaint fillp = make_normal_paint();
|
2016-08-02 17:35:57 +00:00
|
|
|
fillp.setColor(SK_ColorMAGENTA);
|
|
|
|
|
|
|
|
SkPath fillpath;
|
|
|
|
p.getFillPath(path, &fillpath);
|
|
|
|
|
|
|
|
canvas->drawPath(fillpath, fillp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void draw_stroked_quad(SkCanvas *canvas) {
|
2016-08-09 15:53:30 +00:00
|
|
|
canvas->translate(400, 0);
|
2016-08-02 17:35:57 +00:00
|
|
|
draw_large_quad(canvas);
|
|
|
|
draw_quad_fillpath(canvas);
|
|
|
|
}
|
|
|
|
|
|
|
|
////////// cubics
|
|
|
|
|
|
|
|
void draw_small_cubic(SkCanvas *canvas) {
|
2016-08-09 15:53:30 +00:00
|
|
|
SkPaint p = make_normal_paint();
|
2016-08-02 17:35:57 +00:00
|
|
|
SkPath path = cubic_path();
|
|
|
|
|
2016-08-09 15:53:30 +00:00
|
|
|
draw_ribs(canvas, path);
|
2016-08-02 17:35:57 +00:00
|
|
|
canvas->drawPath(path, p);
|
|
|
|
}
|
|
|
|
|
|
|
|
void draw_large_cubic(SkCanvas *canvas) {
|
|
|
|
SkPaint p = make_overstroke_paint();
|
|
|
|
SkPath path = cubic_path();
|
|
|
|
|
|
|
|
canvas->drawPath(path, p);
|
2016-08-09 15:53:30 +00:00
|
|
|
draw_ribs(canvas, path);
|
2016-08-02 17:35:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void draw_cubic_fillpath(SkCanvas *canvas) {
|
|
|
|
SkPath path = cubic_path();
|
|
|
|
SkPaint p = make_overstroke_paint();
|
|
|
|
|
2016-08-09 15:53:30 +00:00
|
|
|
SkPaint fillp = make_normal_paint();
|
2016-08-02 17:35:57 +00:00
|
|
|
fillp.setColor(SK_ColorMAGENTA);
|
|
|
|
|
|
|
|
SkPath fillpath;
|
|
|
|
p.getFillPath(path, &fillpath);
|
|
|
|
|
|
|
|
canvas->drawPath(fillpath, fillp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void draw_stroked_cubic(SkCanvas *canvas) {
|
|
|
|
canvas->translate(400, 0);
|
|
|
|
draw_large_cubic(canvas);
|
|
|
|
draw_cubic_fillpath(canvas);
|
|
|
|
}
|
|
|
|
|
|
|
|
////////// ovals
|
|
|
|
|
|
|
|
void draw_small_oval(SkCanvas *canvas) {
|
2016-08-09 15:53:30 +00:00
|
|
|
SkPaint p = make_normal_paint();
|
2016-08-02 17:35:57 +00:00
|
|
|
|
|
|
|
SkPath path = oval_path();
|
|
|
|
|
2016-08-09 15:53:30 +00:00
|
|
|
draw_ribs(canvas, path);
|
2016-08-02 17:35:57 +00:00
|
|
|
canvas->drawPath(path, p);
|
|
|
|
}
|
|
|
|
|
|
|
|
void draw_large_oval(SkCanvas *canvas) {
|
|
|
|
SkPaint p = make_overstroke_paint();
|
|
|
|
SkPath path = oval_path();
|
|
|
|
|
|
|
|
canvas->drawPath(path, p);
|
2016-08-09 15:53:30 +00:00
|
|
|
draw_ribs(canvas, path);
|
2016-08-02 17:35:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void draw_oval_fillpath(SkCanvas *canvas) {
|
|
|
|
SkPath path = oval_path();
|
|
|
|
SkPaint p = make_overstroke_paint();
|
|
|
|
|
2016-08-09 15:53:30 +00:00
|
|
|
SkPaint fillp = make_normal_paint();
|
2016-08-02 17:35:57 +00:00
|
|
|
fillp.setColor(SK_ColorMAGENTA);
|
|
|
|
|
|
|
|
SkPath fillpath;
|
|
|
|
p.getFillPath(path, &fillpath);
|
|
|
|
|
|
|
|
canvas->drawPath(fillpath, fillp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void draw_stroked_oval(SkCanvas *canvas) {
|
|
|
|
canvas->translate(400, 0);
|
|
|
|
draw_large_oval(canvas);
|
|
|
|
draw_oval_fillpath(canvas);
|
|
|
|
}
|
|
|
|
|
|
|
|
////////// gm
|
|
|
|
|
|
|
|
void (*examples[])(SkCanvas *canvas) = {
|
|
|
|
draw_small_quad, draw_stroked_quad, draw_small_cubic,
|
|
|
|
draw_stroked_cubic, draw_small_oval, draw_stroked_oval,
|
|
|
|
};
|
|
|
|
|
|
|
|
DEF_SIMPLE_GM(OverStroke, canvas, 500, 500) {
|
|
|
|
const size_t length = sizeof(examples) / sizeof(examples[0]);
|
|
|
|
const size_t width = 2;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < length; i++) {
|
|
|
|
int x = (int)(i % width);
|
|
|
|
int y = (int)(i / width);
|
|
|
|
|
|
|
|
canvas->save();
|
2016-08-09 15:53:30 +00:00
|
|
|
canvas->translate(150.0f * x, 150.0f * y);
|
|
|
|
canvas->scale(0.2f, 0.2f);
|
|
|
|
canvas->translate(300.0f, 400.0f);
|
2016-08-02 17:35:57 +00:00
|
|
|
|
|
|
|
examples[i](canvas);
|
|
|
|
|
|
|
|
canvas->restore();
|
|
|
|
}
|
|
|
|
}
|