1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2022 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "gm/gm.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPath.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathUtils.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkFloatBits.h"
13*c8dee2aaSAndroid Build Coastguard Worker
get_path()14*c8dee2aaSAndroid Build Coastguard Worker static SkPath get_path() {
15*c8dee2aaSAndroid Build Coastguard Worker SkPath path;
16*c8dee2aaSAndroid Build Coastguard Worker path.setFillType(SkPathFillType::kWinding);
17*c8dee2aaSAndroid Build Coastguard Worker path.moveTo(SkBits2Float(0x45034ec4), SkBits2Float(0x42e7fb80)); // 2100.92f, 115.991f
18*c8dee2aaSAndroid Build Coastguard Worker path.quadTo(SkBits2Float(0x4500f46c),
19*c8dee2aaSAndroid Build Coastguard Worker SkBits2Float(0x43333300),
20*c8dee2aaSAndroid Build Coastguard Worker SkBits2Float(0x4500f46c),
21*c8dee2aaSAndroid Build Coastguard Worker SkBits2Float(0x431f0ec0)); // 2063.28f, 179.199f, 2063.28f, 159.058f
22*c8dee2aaSAndroid Build Coastguard Worker path.quadTo(SkBits2Float(0x4500f46c),
23*c8dee2aaSAndroid Build Coastguard Worker SkBits2Float(0x430ad7c0),
24*c8dee2aaSAndroid Build Coastguard Worker SkBits2Float(0x45019462),
25*c8dee2aaSAndroid Build Coastguard Worker SkBits2Float(0x42fed580)); // 2063.28f, 138.843f, 2073.27f, 127.417f
26*c8dee2aaSAndroid Build Coastguard Worker path.quadTo(SkBits2Float(0x45023458),
27*c8dee2aaSAndroid Build Coastguard Worker SkBits2Float(0x42e7fb80),
28*c8dee2aaSAndroid Build Coastguard Worker SkBits2Float(0x45034ec4),
29*c8dee2aaSAndroid Build Coastguard Worker SkBits2Float(0x42e7fb80)); // 2083.27f, 115.991f, 2100.92f, 115.991f
30*c8dee2aaSAndroid Build Coastguard Worker path.close();
31*c8dee2aaSAndroid Build Coastguard Worker return path;
32*c8dee2aaSAndroid Build Coastguard Worker }
33*c8dee2aaSAndroid Build Coastguard Worker
34*c8dee2aaSAndroid Build Coastguard Worker // Reproduces the underlying problem from skbug.com/12866.
35*c8dee2aaSAndroid Build Coastguard Worker // The path (part of a glyph) was being drawn stroked, and with a perspective matrix.
36*c8dee2aaSAndroid Build Coastguard Worker // The perspective matrix forces a very large resScale when stroking the path.
37*c8dee2aaSAndroid Build Coastguard Worker // The resulting filled path is incorrect. Note that stroking with a smaller resScale works fine.
38*c8dee2aaSAndroid Build Coastguard Worker DEF_SIMPLE_GM(bug12866, canvas, 128, 64) {
39*c8dee2aaSAndroid Build Coastguard Worker SkPaint strokePaint;
40*c8dee2aaSAndroid Build Coastguard Worker strokePaint.setAntiAlias(true);
41*c8dee2aaSAndroid Build Coastguard Worker strokePaint.setStyle(SkPaint::kStroke_Style);
42*c8dee2aaSAndroid Build Coastguard Worker strokePaint.setStrokeWidth(3);
43*c8dee2aaSAndroid Build Coastguard Worker
44*c8dee2aaSAndroid Build Coastguard Worker SkPaint fillPaint;
45*c8dee2aaSAndroid Build Coastguard Worker fillPaint.setAntiAlias(true);
46*c8dee2aaSAndroid Build Coastguard Worker
47*c8dee2aaSAndroid Build Coastguard Worker SkPath strokePath = get_path();
48*c8dee2aaSAndroid Build Coastguard Worker SkPath fillPath;
49*c8dee2aaSAndroid Build Coastguard Worker skpathutils::FillPathWithPaint(strokePath, strokePaint, &fillPath, nullptr, 1200.0f);
50*c8dee2aaSAndroid Build Coastguard Worker
51*c8dee2aaSAndroid Build Coastguard Worker SkRect strokeBounds = strokePath.getBounds();
52*c8dee2aaSAndroid Build Coastguard Worker SkRect fillBounds = fillPath.getBounds();
53*c8dee2aaSAndroid Build Coastguard Worker
54*c8dee2aaSAndroid Build Coastguard Worker // Draw the stroked path. This (internally) uses a resScale of 1.0, and looks good.
55*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
56*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(10 - strokeBounds.fLeft, 10 - strokeBounds.fTop);
57*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPath(strokePath, strokePaint);
58*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
59*c8dee2aaSAndroid Build Coastguard Worker
60*c8dee2aaSAndroid Build Coastguard Worker // With a perspective CTM, it's possible for resScale to become large. Draw the filled
61*c8dee2aaSAndroid Build Coastguard Worker // path produced by the stroker in that situation, which ends up being incorrect.
62*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
63*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(74 - fillBounds.fLeft, 10 - fillBounds.fTop);
64*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPath(fillPath, fillPaint);
65*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
66*c8dee2aaSAndroid Build Coastguard Worker }
67*c8dee2aaSAndroid Build Coastguard Worker
68*c8dee2aaSAndroid Build Coastguard Worker // This is another example of the same underlying bug (recursion limit in the stroker),
69*c8dee2aaSAndroid Build Coastguard Worker // but with cubics, rather than quads.
70*c8dee2aaSAndroid Build Coastguard Worker DEF_SIMPLE_GM(bug40810065, canvas, 256, 512) {
71*c8dee2aaSAndroid Build Coastguard Worker canvas->scale(2.f, 2.f);
72*c8dee2aaSAndroid Build Coastguard Worker
73*c8dee2aaSAndroid Build Coastguard Worker SkPath path1;
74*c8dee2aaSAndroid Build Coastguard Worker path1.moveTo(108.87f, 3.78f);
75*c8dee2aaSAndroid Build Coastguard Worker path1.cubicTo(201.1f, -128.61f, 34.21f, 82.54f, 134.14f, 126.01f);
76*c8dee2aaSAndroid Build Coastguard Worker SkPath path2;
77*c8dee2aaSAndroid Build Coastguard Worker path2.moveTo(108.87f, 3.78f);
78*c8dee2aaSAndroid Build Coastguard Worker path2.cubicTo(201.f, -128.61f, 34.21f, 82.54f, 134.14f, 126.f);
79*c8dee2aaSAndroid Build Coastguard Worker
80*c8dee2aaSAndroid Build Coastguard Worker SkPaint stroke;
81*c8dee2aaSAndroid Build Coastguard Worker stroke.setColor(SK_ColorBLACK);
82*c8dee2aaSAndroid Build Coastguard Worker stroke.setAntiAlias(true);
83*c8dee2aaSAndroid Build Coastguard Worker stroke.setStyle(SkPaint::kStroke_Style);
84*c8dee2aaSAndroid Build Coastguard Worker stroke.setStrokeWidth(1.f);
85*c8dee2aaSAndroid Build Coastguard Worker stroke.setStrokeCap(SkPaint::kRound_Cap);
86*c8dee2aaSAndroid Build Coastguard Worker
87*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
88*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(-75.f, 50.f);
89*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPath(path1, stroke);
90*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
91*c8dee2aaSAndroid Build Coastguard Worker
92*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
93*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(-20.f, 100.f);
94*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPath(path2, stroke);
95*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
96*c8dee2aaSAndroid Build Coastguard Worker }
97*c8dee2aaSAndroid Build Coastguard Worker
98*c8dee2aaSAndroid Build Coastguard Worker // Finally: A repro case that involves conics. This should draw NOTHING. When incorrect, it drew
99*c8dee2aaSAndroid Build Coastguard Worker // a large black rectangle over half of the slide.
100*c8dee2aaSAndroid Build Coastguard Worker DEF_SIMPLE_GM_BG(bug41422450, canvas, 863, 473, SK_ColorWHITE) {
101*c8dee2aaSAndroid Build Coastguard Worker SkM44 mat{1, -0.00000139566271f, 0, -2321738,
102*c8dee2aaSAndroid Build Coastguard Worker 0.000113059919f, 0.0123444516f, 0, -353,
103*c8dee2aaSAndroid Build Coastguard Worker 0, 0, 1, 0,
104*c8dee2aaSAndroid Build Coastguard Worker 0, 0, 0, 1};
105*c8dee2aaSAndroid Build Coastguard Worker canvas->concat(mat);
106*c8dee2aaSAndroid Build Coastguard Worker
107*c8dee2aaSAndroid Build Coastguard Worker SkPath strokePath;
108*c8dee2aaSAndroid Build Coastguard Worker SkRect circle = SkRect::MakeLTRB(-3299135.5f, -12312541.0f, 9897407.0f, 884000.812f);
109*c8dee2aaSAndroid Build Coastguard Worker strokePath.arcTo(circle, 59.9999962f, 59.9999962f, true);
110*c8dee2aaSAndroid Build Coastguard Worker
111*c8dee2aaSAndroid Build Coastguard Worker SkPaint strokePaint;
112*c8dee2aaSAndroid Build Coastguard Worker strokePaint.setStyle(SkPaint::kStroke_Style);
113*c8dee2aaSAndroid Build Coastguard Worker strokePaint.setStrokeWidth(2);
114*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPath(strokePath, strokePaint);
115*c8dee2aaSAndroid Build Coastguard Worker }
116