257 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			257 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * 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.
 | |
|  *
 | |
|  * The old Nvidia Path Renderer used to yield correct results, so a possible
 | |
|  * direction of attack is to use the GPU and a completely different algorithm.
 | |
|  *
 | |
|  * 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/SkPathBuilder.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() {
 | |
|     return SkPathBuilder().moveTo(0, 0)
 | |
|                           .lineTo(100, 0)
 | |
|                           .quadTo(50, -40, 0, 0)
 | |
|                           .close()
 | |
|                           .detach();
 | |
| }
 | |
| 
 | |
| 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);
 | |
| 
 | |
|     return SkPathBuilder().arcTo(oval, 0, 359, true).close().detach();
 | |
| }
 | |
| 
 | |
| 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();
 | |
|     }
 | |
| }
 |