123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- /*
- * 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.
- *
- * 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.
- *
- * See crbug.com/589769 skbug.com/5405 skbug.com/5406
- */
- #include "gm/gm.h"
- #include "include/core/SkCanvas.h"
- #include "include/core/SkColor.h"
- #include "include/core/SkPaint.h"
- #include "include/core/SkPath.h"
- #include "include/core/SkPathMeasure.h"
- #include "include/core/SkPoint.h"
- #include "include/core/SkRect.h"
- #include "include/core/SkScalar.h"
- #include "src/core/SkPointPriv.h"
- #include <cstddef>
- const SkScalar OVERSTROKE_WIDTH = 500.0f;
- const SkScalar NORMALSTROKE_WIDTH = 3.0f;
- //////// path and paint builders
- SkPaint make_normal_paint() {
- SkPaint p;
- p.setAntiAlias(true);
- p.setStyle(SkPaint::kStroke_Style);
- p.setStrokeWidth(NORMALSTROKE_WIDTH);
- p.setColor(SK_ColorBLUE);
- return p;
- }
- SkPaint make_overstroke_paint() {
- SkPaint p;
- p.setAntiAlias(true);
- p.setStyle(SkPaint::kStroke_Style);
- p.setStrokeWidth(OVERSTROKE_WIDTH);
- return p;
- }
- SkPath quad_path() {
- SkPath path;
- path.moveTo(0, 0);
- path.lineTo(100, 0);
- path.quadTo(50, -40,
- 0, 0);
- path.close();
- return path;
- }
- 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);
- SkPath path;
- path.arcTo(oval, 0, 359, true);
- path.close();
- return path;
- }
- 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);
- SkPointPriv::RotateCCW(&tan);
- 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);
- }
- ///////// quads
- void draw_small_quad(SkCanvas *canvas) {
- // scaled so it's visible
- // canvas->scale(8, 8);
- SkPaint p = make_normal_paint();
- SkPath path = quad_path();
- draw_ribs(canvas, path);
- canvas->drawPath(path, p);
- }
- void draw_large_quad(SkCanvas *canvas) {
- SkPaint p = make_overstroke_paint();
- SkPath path = quad_path();
- canvas->drawPath(path, p);
- draw_ribs(canvas, path);
- }
- void draw_quad_fillpath(SkCanvas *canvas) {
- SkPath path = quad_path();
- SkPaint p = make_overstroke_paint();
- SkPaint fillp = make_normal_paint();
- fillp.setColor(SK_ColorMAGENTA);
- SkPath fillpath;
- p.getFillPath(path, &fillpath);
- canvas->drawPath(fillpath, fillp);
- }
- void draw_stroked_quad(SkCanvas *canvas) {
- canvas->translate(400, 0);
- draw_large_quad(canvas);
- draw_quad_fillpath(canvas);
- }
- ////////// cubics
- void draw_small_cubic(SkCanvas *canvas) {
- SkPaint p = make_normal_paint();
- SkPath path = cubic_path();
- draw_ribs(canvas, path);
- canvas->drawPath(path, p);
- }
- void draw_large_cubic(SkCanvas *canvas) {
- SkPaint p = make_overstroke_paint();
- SkPath path = cubic_path();
- canvas->drawPath(path, p);
- draw_ribs(canvas, path);
- }
- void draw_cubic_fillpath(SkCanvas *canvas) {
- SkPath path = cubic_path();
- SkPaint p = make_overstroke_paint();
- SkPaint fillp = make_normal_paint();
- 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) {
- SkPaint p = make_normal_paint();
- SkPath path = oval_path();
- draw_ribs(canvas, path);
- canvas->drawPath(path, p);
- }
- void draw_large_oval(SkCanvas *canvas) {
- SkPaint p = make_overstroke_paint();
- SkPath path = oval_path();
- canvas->drawPath(path, p);
- draw_ribs(canvas, path);
- }
- void draw_oval_fillpath(SkCanvas *canvas) {
- SkPath path = oval_path();
- SkPaint p = make_overstroke_paint();
- SkPaint fillp = make_normal_paint();
- 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();
- canvas->translate(150.0f * x, 150.0f * y);
- canvas->scale(0.2f, 0.2f);
- canvas->translate(300.0f, 400.0f);
- examples[i](canvas);
- canvas->restore();
- }
- }
|