352 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			352 lines
		
	
	
		
			12 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.
 | |
|  */
 | |
| 
 | |
| #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/SkPathEffect.h"
 | |
| #include "include/core/SkRect.h"
 | |
| #include "include/core/SkScalar.h"
 | |
| #include "include/core/SkTypes.h"
 | |
| #include "include/effects/SkDashPathEffect.h"
 | |
| #include "include/private/SkFloatBits.h"
 | |
| #include "include/private/SkTArray.h"
 | |
| 
 | |
| #include <functional>
 | |
| 
 | |
| #include "include/effects/SkStrokeAndFillPathEffect.h"
 | |
| static void set_strokeandfill(SkPaint* paint) {
 | |
|     SkASSERT(paint->getPathEffect() == nullptr);
 | |
|     paint->setPathEffect(SkStrokeAndFillPathEffect::Make());
 | |
|     paint->setStroke(true);
 | |
| }
 | |
| 
 | |
| constexpr SkScalar kStarts[] = {0.f, 10.f, 30.f, 45.f, 90.f, 165.f, 180.f, 270.f};
 | |
| constexpr SkScalar kSweeps[] = {1.f, 45.f, 90.f, 130.f, 180.f, 184.f, 300.f, 355.f};
 | |
| constexpr SkScalar kDiameter = 40.f;
 | |
| constexpr SkRect kRect = {0.f, 0.f, kDiameter, kDiameter};
 | |
| constexpr int kW = 1000;
 | |
| constexpr int kH = 1000;
 | |
| constexpr SkScalar kPad = 20.f;
 | |
| 
 | |
| void draw_arcs(SkCanvas* canvas, std::function<void(SkPaint*)> configureStyle) {
 | |
|     // Draws grid of arcs with different start/sweep angles in red and their complement arcs in
 | |
|     // blue.
 | |
|     auto drawGrid = [canvas, &configureStyle] (SkScalar x, SkScalar y, bool useCenter, bool aa) {
 | |
|         SkPaint p0;
 | |
|         p0.setColor(SK_ColorRED);
 | |
|         p0.setAntiAlias(aa);
 | |
|         // Set a reasonable stroke width that configureStyle can override.
 | |
|         p0.setStrokeWidth(15.f);
 | |
|         SkPaint p1 = p0;
 | |
|         p1.setColor(SK_ColorBLUE);
 | |
|         // Use alpha so we see magenta on overlap between arc and its complement.
 | |
|         p0.setAlpha(100);
 | |
|         p1.setAlpha(100);
 | |
|         configureStyle(&p0);
 | |
|         configureStyle(&p1);
 | |
| 
 | |
|         canvas->save();
 | |
|         canvas->translate(kPad + x, kPad + y);
 | |
|         for (auto start : kStarts) {
 | |
|             canvas->save();
 | |
|             for (auto sweep : kSweeps) {
 | |
|                 canvas->drawArc(kRect, start, sweep, useCenter, p0);
 | |
|                 canvas->drawArc(kRect, start, -(360.f - sweep), useCenter, p1);
 | |
|                 canvas->translate(kRect.width() + kPad, 0.f);
 | |
|             }
 | |
|             canvas->restore();
 | |
|             canvas->translate(0, kRect.height() + kPad);
 | |
|         }
 | |
|         canvas->restore();
 | |
|     };
 | |
|     // Draw a grids for combo of enabling/disabling aa and using center.
 | |
|     constexpr SkScalar kGridW = kW / 2.f;
 | |
|     constexpr SkScalar kGridH = kH / 2.f;
 | |
|     drawGrid(0.f   , 0.f   , false, false);
 | |
|     drawGrid(kGridW, 0.f   , true , false);
 | |
|     drawGrid(0.f   , kGridH, false, true );
 | |
|     drawGrid(kGridW, kGridH, true , true );
 | |
|     // Draw separators between the grids.
 | |
|     SkPaint linePaint;
 | |
|     linePaint.setAntiAlias(true);
 | |
|     linePaint.setColor(SK_ColorBLACK);
 | |
|     canvas->drawLine(kGridW, 0.f   , kGridW,            SkIntToScalar(kH), linePaint);
 | |
|     canvas->drawLine(0.f   , kGridH, SkIntToScalar(kW), kGridH,            linePaint);
 | |
| }
 | |
| 
 | |
| #define DEF_ARC_GM(name) DEF_SIMPLE_GM(circular_arcs_##name, canvas, kW, kH)
 | |
| 
 | |
| DEF_ARC_GM(fill) {
 | |
|     auto setFill = [] (SkPaint*p) { p->setStroke(false); };
 | |
|     draw_arcs(canvas, setFill);
 | |
| }
 | |
| 
 | |
| DEF_ARC_GM(hairline) {
 | |
|     auto setHairline = [] (SkPaint* p) {
 | |
|         p->setStroke(true);
 | |
|         p->setStrokeWidth(0.f);
 | |
|     };
 | |
|     draw_arcs(canvas, setHairline);
 | |
| }
 | |
| 
 | |
| DEF_ARC_GM(stroke_butt) {
 | |
|     auto setStroke = [](SkPaint* p) {
 | |
|         p->setStroke(true);
 | |
|         p->setStrokeCap(SkPaint::kButt_Cap);
 | |
|     };
 | |
|     draw_arcs(canvas, setStroke);
 | |
| }
 | |
| 
 | |
| DEF_ARC_GM(stroke_square) {
 | |
|     auto setStroke = [] (SkPaint* p) {
 | |
|         p->setStroke(true);
 | |
|         p->setStrokeCap(SkPaint::kSquare_Cap);
 | |
|     };
 | |
|     draw_arcs(canvas, setStroke);
 | |
| }
 | |
| 
 | |
| DEF_ARC_GM(stroke_round) {
 | |
|     auto setStroke = [] (SkPaint* p) {
 | |
|         p->setStroke(true);
 | |
|         p->setStrokeCap(SkPaint::kRound_Cap);
 | |
|     };
 | |
|     draw_arcs(canvas, setStroke);
 | |
| }
 | |
| 
 | |
| DEF_ARC_GM(stroke_and_fill_butt) {
 | |
|     auto setStroke = [] (SkPaint* p) {
 | |
|         set_strokeandfill(p);
 | |
|         p->setStrokeCap(SkPaint::kButt_Cap);
 | |
|     };
 | |
|     draw_arcs(canvas, setStroke);
 | |
| }
 | |
| 
 | |
| DEF_ARC_GM(stroke_and_fill_square) {
 | |
|     auto setStroke = [] (SkPaint* p) {
 | |
|         set_strokeandfill(p);
 | |
|         p->setStrokeCap(SkPaint::kSquare_Cap);
 | |
|     };
 | |
|     draw_arcs(canvas, setStroke);
 | |
| }
 | |
| 
 | |
| DEF_ARC_GM(stroke_and_fill_round) {
 | |
|     auto setStroke = [] (SkPaint* p) {
 | |
|         set_strokeandfill(p);
 | |
|         p->setStrokeCap(SkPaint::kRound_Cap);
 | |
|     };
 | |
|     draw_arcs(canvas, setStroke);
 | |
| }
 | |
| 
 | |
| DEF_SIMPLE_GM(circular_arcs_weird, canvas, 1000, 400) {
 | |
|     constexpr SkScalar kS = 50;
 | |
|     struct Arc {
 | |
|         SkRect   fOval;
 | |
|         SkScalar fStart;
 | |
|         SkScalar fSweep;
 | |
|     };
 | |
|     const Arc noDrawArcs[] = {
 | |
|         // no sweep
 | |
|         {SkRect::MakeWH(kS, kS),  0,  0},
 | |
|         // empty rect in x
 | |
|         {SkRect::MakeWH(-kS, kS), 0, 90},
 | |
|         // empty rect in y
 | |
|         {SkRect::MakeWH(kS, -kS), 0, 90},
 | |
|         // empty rect in x and y
 | |
|         {SkRect::MakeWH( 0,   0), 0, 90},
 | |
|     };
 | |
|     const Arc arcs[] = {
 | |
|         // large start
 | |
|         {SkRect::MakeWH(kS, kS),   810.f,   90.f},
 | |
|         // large negative start
 | |
|         {SkRect::MakeWH(kS, kS),  -810.f,   90.f},
 | |
|         // exactly 360 sweep
 | |
|         {SkRect::MakeWH(kS, kS),     0.f,  360.f},
 | |
|         // exactly -360 sweep
 | |
|         {SkRect::MakeWH(kS, kS),     0.f, -360.f},
 | |
|         // exactly 540 sweep
 | |
|         {SkRect::MakeWH(kS, kS),     0.f,  540.f},
 | |
|         // exactly -540 sweep
 | |
|         {SkRect::MakeWH(kS, kS),     0.f, -540.f},
 | |
|         // generic large sweep and large start
 | |
|         {SkRect::MakeWH(kS, kS),  1125.f,  990.f},
 | |
|     };
 | |
|     SkTArray<SkPaint> paints;
 | |
|     // fill
 | |
|     paints.push_back();
 | |
|     // stroke
 | |
|     paints.push_back().setStroke(true);
 | |
|     paints.back().setStrokeWidth(kS / 6.f);
 | |
|     // hairline
 | |
|     paints.push_back().setStroke(true);
 | |
|     paints.back().setStrokeWidth(0.f);
 | |
|     // stroke and fill
 | |
|     paints.push_back().setStyle(SkPaint::kStrokeAndFill_Style);
 | |
|     paints.back().setStrokeWidth(kS / 6.f);
 | |
|     // dash effect
 | |
|     paints.push_back().setStroke(true);
 | |
|     paints.back().setStrokeWidth(kS / 6.f);
 | |
|     constexpr SkScalar kDashIntervals[] = {kS / 15, 2 * kS / 15};
 | |
|     paints.back().setPathEffect(SkDashPathEffect::Make(kDashIntervals, 2, 0.f));
 | |
| 
 | |
|     canvas->translate(kPad, kPad);
 | |
|     // This loop should draw nothing.
 | |
|     for (auto arc : noDrawArcs) {
 | |
|         for (auto paint : paints) {
 | |
|             paint.setAntiAlias(true);
 | |
|             canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, false, paint);
 | |
|             canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, true, paint);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     SkPaint linePaint;
 | |
|     linePaint.setAntiAlias(true);
 | |
|     linePaint.setColor(SK_ColorRED);
 | |
|     SkScalar midX   = SK_ARRAY_COUNT(arcs) * (kS + kPad) - kPad/2.f;
 | |
|     SkScalar height = paints.count() * (kS + kPad);
 | |
|     canvas->drawLine(midX, -kPad, midX, height, linePaint);
 | |
| 
 | |
|     for (auto paint : paints) {
 | |
|         paint.setAntiAlias(true);
 | |
|         canvas->save();
 | |
|         for (auto arc : arcs) {
 | |
|             canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, false, paint);
 | |
|             canvas->translate(kS + kPad, 0.f);
 | |
|         }
 | |
|         for (auto arc : arcs) {
 | |
|             canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, true, paint);
 | |
|             canvas->translate(kS + kPad, 0.f);
 | |
|         }
 | |
|         canvas->restore();
 | |
|         canvas->translate(0, kS + kPad);
 | |
|     }
 | |
| }
 | |
| 
 | |
| DEF_SIMPLE_GM(onebadarc, canvas, 100, 100) {
 | |
|     SkPathBuilder path;
 | |
|     path.moveTo(SkBits2Float(0x41a00000), SkBits2Float(0x41a00000));  // 20, 20
 | |
|     path.lineTo(SkBits2Float(0x4208918c), SkBits2Float(0x4208918c));  // 34.1421f, 34.1421f
 | |
|     path.conicTo(SkBits2Float(0x41a00000), SkBits2Float(0x42412318),  // 20, 48.2843f
 | |
|             SkBits2Float(0x40bb73a0), SkBits2Float(0x4208918c),       // 5.85786f, 34.1421f
 | |
|             SkBits2Float(0x3f3504f3));                                // 0.707107f
 | |
|     path.quadTo(SkBits2Float(0x40bb73a0), SkBits2Float(0x4208918c),   // 5.85786f, 34.1421f
 | |
|             SkBits2Float(0x40bb73a2), SkBits2Float(0x4208918c));      // 5.85787f, 34.1421f
 | |
|     path.lineTo(SkBits2Float(0x41a00000), SkBits2Float(0x41a00000));  // 20, 20
 | |
|     path.close();
 | |
|     SkPaint p0;
 | |
|     p0.setColor(SK_ColorRED);
 | |
|     p0.setStrokeWidth(15.f);
 | |
|     p0.setStroke(true);
 | |
|     p0.setAlpha(100);
 | |
|     canvas->translate(20, 0);
 | |
|     canvas->drawPath(path.detach(), p0);
 | |
| 
 | |
|     canvas->drawArc(SkRect{60, 0, 100, 40}, 45, 90, true, p0);
 | |
| }
 | |
| 
 | |
| DEF_SIMPLE_GM(crbug_888453, canvas, 480, 150) {
 | |
|     // Two GPU path renderers were using a too-large tolerance when chopping connics to quads.
 | |
|     // This manifested as not-very-round circular arcs at certain radii. All the arcs being drawn
 | |
|     // here should look like circles.
 | |
|     SkPaint fill;
 | |
|     fill.setAntiAlias(true);
 | |
|     SkPaint hairline = fill;
 | |
|     hairline.setStroke(true);
 | |
|     SkPaint stroke = hairline;
 | |
|     stroke.setStrokeWidth(2.0f);
 | |
|     int x = 4;
 | |
|     int y0 = 25, y1 = 75, y2 = 125;
 | |
|     for (int r = 2; r <= 20; ++r) {
 | |
|         canvas->drawArc(SkRect::MakeXYWH(x - r, y0 - r, 2 * r, 2 * r), 0, 360, false, fill);
 | |
|         canvas->drawArc(SkRect::MakeXYWH(x - r, y1 - r, 2 * r, 2 * r), 0, 360, false, hairline);
 | |
|         canvas->drawArc(SkRect::MakeXYWH(x - r, y2 - r, 2 * r, 2 * r), 0, 360, false, stroke);
 | |
|         x += 2 * r + 4;
 | |
|     }
 | |
| }
 | |
| 
 | |
| DEF_SIMPLE_GM(circular_arc_stroke_matrix, canvas, 820, 1090) {
 | |
|     static constexpr SkScalar kRadius = 40.f;
 | |
|     static constexpr SkScalar kStrokeWidth = 5.f;
 | |
|     static constexpr SkScalar kStart = 89.f;
 | |
|     static constexpr SkScalar kSweep = 180.f/SK_ScalarPI; // one radian
 | |
| 
 | |
|     SkTArray<SkMatrix> matrices;
 | |
|     matrices.push_back().setRotate(kRadius, kRadius, 45.f);
 | |
|     matrices.push_back(SkMatrix::I());
 | |
|     matrices.push_back().setAll(-1,  0,  2*kRadius,
 | |
|                                  0,  1,  0,
 | |
|                                  0,  0,  1);
 | |
|     matrices.push_back().setAll( 1,  0,  0,
 | |
|                                  0, -1,  2*kRadius,
 | |
|                                  0,  0,  1);
 | |
|     matrices.push_back().setAll( 1,  0,  0,
 | |
|                                  0, -1,  2*kRadius,
 | |
|                                  0,  0,  1);
 | |
|     matrices.push_back().setAll( 0, -1,  2*kRadius,
 | |
|                                 -1,  0,  2*kRadius,
 | |
|                                  0,  0,  1);
 | |
|     matrices.push_back().setAll( 0, -1,  2*kRadius,
 | |
|                                  1,  0,  0,
 | |
|                                  0,  0,  1);
 | |
|     matrices.push_back().setAll( 0,  1,  0,
 | |
|                                  1,  0,  0,
 | |
|                                  0,  0,  1);
 | |
|     matrices.push_back().setAll( 0,  1,  0,
 | |
|                                 -1,  0,  2*kRadius,
 | |
|                                  0,  0,  1);
 | |
|     int baseMatrixCnt = matrices.count();
 | |
| 
 | |
| 
 | |
|     SkMatrix tinyCW;
 | |
|     tinyCW.setRotate(0.001f, kRadius, kRadius);
 | |
|     for (int i = 0; i < baseMatrixCnt; ++i) {
 | |
|         matrices.push_back().setConcat(matrices[i], tinyCW);
 | |
|     }
 | |
|     SkMatrix tinyCCW;
 | |
|     tinyCCW.setRotate(-0.001f, kRadius, kRadius);
 | |
|     for (int i = 0; i < baseMatrixCnt; ++i) {
 | |
|         matrices.push_back().setConcat(matrices[i], tinyCCW);
 | |
|     }
 | |
|     SkMatrix cw45;
 | |
|     cw45.setRotate(45.f, kRadius, kRadius);
 | |
|     for (int i = 0; i < baseMatrixCnt; ++i) {
 | |
|         matrices.push_back().setConcat(matrices[i], cw45);
 | |
|     }
 | |
| 
 | |
|     int x = 0;
 | |
|     int y = 0;
 | |
|     static constexpr SkScalar kPad = 2*kStrokeWidth;
 | |
|     canvas->translate(kPad, kPad);
 | |
|     auto bounds = SkRect::MakeWH(2*kRadius, 2*kRadius);
 | |
|     for (auto cap : {SkPaint::kRound_Cap, SkPaint::kButt_Cap, SkPaint::kSquare_Cap}) {
 | |
|         for (const auto& m : matrices) {
 | |
|             SkPaint paint;
 | |
|             paint.setStrokeCap(cap);
 | |
|             paint.setAntiAlias(true);
 | |
|             paint.setStroke(true);
 | |
|             paint.setStrokeWidth(kStrokeWidth);
 | |
|             canvas->save();
 | |
|                 canvas->translate(x * (2*kRadius + kPad), y * (2*kRadius + kPad));
 | |
|                 canvas->concat(m);
 | |
|                 paint.setColor(SK_ColorRED);
 | |
|                 paint.setAlpha(0x80);
 | |
|                 canvas->drawArc(bounds, kStart, kSweep, false, paint);
 | |
|                 paint.setColor(SK_ColorBLUE);
 | |
|                 paint.setAlpha(0x80);
 | |
|                 canvas->drawArc(bounds, kStart, kSweep - 360.f, false, paint);
 | |
|             canvas->restore();
 | |
|             ++x;
 | |
|             if (x == baseMatrixCnt) {
 | |
|                 x = 0;
 | |
|                 ++y;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 |