1 /*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7 #include "tests/Test.h"
8
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkColor.h"
11 #include "include/core/SkImageInfo.h"
12 #include "include/core/SkMatrix.h"
13 #include "include/core/SkPaint.h"
14 #include "include/core/SkPath.h"
15 #include "include/core/SkPathEffect.h"
16 #include "include/core/SkPathTypes.h"
17 #include "include/core/SkRRect.h"
18 #include "include/core/SkRect.h"
19 #include "include/core/SkRefCnt.h"
20 #include "include/core/SkScalar.h"
21 #include "include/core/SkSurface.h"
22 #include "include/core/SkTypes.h"
23 #include "include/effects/SkDashPathEffect.h"
24 #include "include/gpu/GpuTypes.h"
25 #include "include/gpu/ganesh/GrDirectContext.h"
26 #include "include/gpu/ganesh/GrTypes.h"
27 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
28 #include "tests/CtsEnforcement.h"
29
30 #include <initializer_list>
31
32 struct GrContextOptions;
33
test_drawPathEmpty(skiatest::Reporter *,SkCanvas * canvas)34 static void test_drawPathEmpty(skiatest::Reporter*, SkCanvas* canvas) {
35 // Filling an empty path should not crash.
36 SkPaint paint;
37 SkRect emptyRect = SkRect::MakeEmpty();
38 canvas->drawRect(emptyRect, paint);
39 canvas->drawPath(SkPath(), paint);
40 canvas->drawOval(emptyRect, paint);
41 canvas->drawRect(emptyRect, paint);
42 canvas->drawRRect(SkRRect::MakeRect(emptyRect), paint);
43
44 // Stroking an empty path should not crash.
45 paint.setAntiAlias(true);
46 paint.setStyle(SkPaint::kStroke_Style);
47 paint.setColor(SK_ColorGRAY);
48 paint.setStrokeWidth(SkIntToScalar(20));
49 paint.setStrokeJoin(SkPaint::kRound_Join);
50 canvas->drawRect(emptyRect, paint);
51 canvas->drawPath(SkPath(), paint);
52 canvas->drawOval(emptyRect, paint);
53 canvas->drawRect(emptyRect, paint);
54 canvas->drawRRect(SkRRect::MakeRect(emptyRect), paint);
55 }
56
fill_and_stroke(SkCanvas * canvas,const SkPath & p1,const SkPath & p2,sk_sp<SkPathEffect> effect)57 static void fill_and_stroke(SkCanvas* canvas, const SkPath& p1, const SkPath& p2,
58 sk_sp<SkPathEffect> effect) {
59 SkPaint paint;
60 paint.setAntiAlias(true);
61 paint.setPathEffect(effect);
62
63 canvas->drawPath(p1, paint);
64 canvas->drawPath(p2, paint);
65
66 paint.setStyle(SkPaint::kStroke_Style);
67 canvas->drawPath(p1, paint);
68 canvas->drawPath(p2, paint);
69 }
70
test_drawSameRectOvals(skiatest::Reporter *,SkCanvas * canvas)71 static void test_drawSameRectOvals(skiatest::Reporter*, SkCanvas* canvas) {
72 // Drawing ovals with similar bounds but different points order should not crash.
73
74 SkPath oval1, oval2;
75 const SkRect rect = SkRect::MakeWH(100, 50);
76 oval1.addOval(rect, SkPathDirection::kCW);
77 oval2.addOval(rect, SkPathDirection::kCCW);
78
79 fill_and_stroke(canvas, oval1, oval2, nullptr);
80
81 const SkScalar intervals[] = { 1, 1 };
82 fill_and_stroke(canvas, oval1, oval2, SkDashPathEffect::Make(intervals, 2, 0));
83 }
84
DEF_GANESH_TEST_FOR_GL_CONTEXT(GpuDrawPath,reporter,ctxInfo,CtsEnforcement::kNever)85 DEF_GANESH_TEST_FOR_GL_CONTEXT(GpuDrawPath, reporter, ctxInfo, CtsEnforcement::kNever) {
86 for (auto& test_func : { &test_drawPathEmpty, &test_drawSameRectOvals }) {
87 for (auto& sampleCount : {1, 4, 16}) {
88 SkImageInfo info = SkImageInfo::MakeN32Premul(255, 255);
89 auto surface(SkSurfaces::RenderTarget(
90 ctxInfo.directContext(), skgpu::Budgeted::kNo, info, sampleCount, nullptr));
91 if (!surface) {
92 continue;
93 }
94 test_func(reporter, surface->getCanvas());
95 }
96 }
97 }
98
DEF_GANESH_TEST_FOR_ALL_CONTEXTS(GrDrawCollapsedPath,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)99 DEF_GANESH_TEST_FOR_ALL_CONTEXTS(GrDrawCollapsedPath,
100 reporter,
101 ctxInfo,
102 CtsEnforcement::kApiLevel_T) {
103 // From https://bugs.fuchsia.dev/p/fuchsia/issues/detail?id=37330, it's possible for a convex
104 // path to be accepted by AAConvexPathRenderer, then be transformed to something without a
105 // computable first direction by a perspective matrix.
106 SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
107 auto dContext = ctxInfo.directContext();
108 auto surface(SkSurfaces::RenderTarget(dContext, skgpu::Budgeted::kNo, info));
109
110 SkPaint paint;
111 paint.setAntiAlias(true);
112
113 SkPath path;
114 path.moveTo(0, 0);
115 path.lineTo(50, 0);
116 path.lineTo(0, 50);
117 path.close();
118
119 SkMatrix m;
120 m.setAll( 0.966006875f , -0.125156224f , 72.0899811f,
121 -0.00885376986f , -0.112347461f , 64.7121124f,
122 -8.94321693e-06f, -0.00173384184f, 0.998692870f);
123 surface->getCanvas()->setMatrix(m);
124 surface->getCanvas()->drawPath(path, paint);
125 dContext->flushAndSubmit(surface.get(), GrSyncCpu::kNo);
126 }
127
DEF_GANESH_TEST_FOR_ALL_CONTEXTS(PathTest_CrBug1232834,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)128 DEF_GANESH_TEST_FOR_ALL_CONTEXTS(PathTest_CrBug1232834,
129 reporter,
130 ctxInfo,
131 CtsEnforcement::kApiLevel_T) {
132 // AAHairlinePathRenderer chops this path to quads that include infinities (and then NaNs).
133 // It used to trigger asserts, now the degenerate quad segments should cause it to be rejected.
134 SkImageInfo info = SkImageInfo::MakeN32Premul(256, 256);
135 auto dContext = ctxInfo.directContext();
136 auto surface(SkSurfaces::RenderTarget(dContext, skgpu::Budgeted::kNo, info));
137
138 SkPaint paint;
139 paint.setAntiAlias(true);
140 paint.setStyle(SkPaint::kStroke_Style);
141
142 SkPath path;
143 path.moveTo(9.0072E15f, 60);
144 path.cubicTo(0, 3.40282e+38f, 0, 3.40282e+38f, 0, 0);
145
146 surface->getCanvas()->drawPath(path, paint);
147 dContext->flushAndSubmit(surface.get(), GrSyncCpu::kNo);
148 }
149
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(StrokeCircle_Bug356182429,reporter,ctxInfo,CtsEnforcement::kNextRelease)150 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(StrokeCircle_Bug356182429,
151 reporter,
152 ctxInfo,
153 CtsEnforcement::kNextRelease) {
154 SkImageInfo info = SkImageInfo::MakeN32Premul(256, 256);
155 auto dContext = ctxInfo.directContext();
156 auto surface(SkSurfaces::RenderTarget(dContext, skgpu::Budgeted::kNo, info));
157
158 SkPaint paint;
159 paint.setAntiAlias(true);
160 paint.setStyle(SkPaint::kStroke_Style);
161 paint.setStrokeCap(SkPaint::kRound_Cap);
162 paint.setStrokeWidth(15.4375f);
163 paint.setStrokeMiter(2.85207834E9f);
164
165 // This draw ends up in the CircleOp, and asserted because round caps are requested,
166 // but the stroke is ultimately excluded (due to the negative inner stroke radius).
167 // Along the way, several other bad things happen (but they don't appear relevant to the bug):
168 // - MakeArcOp asserts that sweepAngle is non-zero (and it is). After converting degrees to
169 // radians, it flushes to zero, so the sweep angle stored in the ArcParams is zero.
170 // - The radius of the "circle" is tiny, but it *also* flushes to zero when we call
171 // `viewMatrix.mapRadius()`, despite the view matrix being identity. This is a result of
172 // the implementation of mapRadius (computing geometric mean of the lengths of two vectors).
173 SkRect oval = SkRect::MakeLTRB(0, 0, 1.83670992E-40f, 1.21223864E-38f);
174 surface->getCanvas()->drawArc(oval, 8.17909887E-41f, 2.24207754E-44f, false, paint);
175 dContext->flushAndSubmit(surface.get(), GrSyncCpu::kNo);
176 }
177