blob: 19ba4e298a4596eae73324b793de5261836cb958 [file] [log] [blame]
commit-bot@chromium.org78a10782013-08-21 19:27:48 +00001
2/*
3 * Copyright 2013 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9// This test only works with the GPU backend.
10
11#include "gm.h"
12
commit-bot@chromium.org53a0b6c2013-08-23 18:05:01 +000013#if SK_SUPPORT_GPU
commit-bot@chromium.org78a10782013-08-21 19:27:48 +000014
15#include "GrContext.h"
16#include "GrPathUtils.h"
17#include "GrTest.h"
18#include "SkColorPriv.h"
19#include "SkDevice.h"
commit-bot@chromium.org53a0b6c2013-08-23 18:05:01 +000020#include "SkGeometry.h"
21
22#include "effects/GrBezierEffect.h"
commit-bot@chromium.org78a10782013-08-21 19:27:48 +000023
24// Position & KLM line eq values. These are the vertex attributes for Bezier curves. The last value
25// of the Vec4f is ignored.
26extern const GrVertexAttrib kAttribs[] = {
27 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
28 {kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
29};
30
31static inline SkScalar eval_line(const SkPoint& p, const SkScalar lineEq[3], SkScalar sign) {
32 return sign * (lineEq[0] * p.fX + lineEq[1] * p.fY + lineEq[2]);
33}
34
35namespace skiagm {
36/**
37 * This GM directly exercises effects that draw Bezier curves in the GPU backend.
38 */
commit-bot@chromium.org53a0b6c2013-08-23 18:05:01 +000039class BezierCubicEffects : public GM {
commit-bot@chromium.org78a10782013-08-21 19:27:48 +000040public:
commit-bot@chromium.org53a0b6c2013-08-23 18:05:01 +000041 BezierCubicEffects() {
commit-bot@chromium.org78a10782013-08-21 19:27:48 +000042 this->setBGColor(0xFFFFFFFF);
43 }
44
45protected:
46 virtual SkString onShortName() SK_OVERRIDE {
commit-bot@chromium.org53a0b6c2013-08-23 18:05:01 +000047 return SkString("bezier_cubic_effects");
commit-bot@chromium.org78a10782013-08-21 19:27:48 +000048 }
49
50 virtual SkISize onISize() SK_OVERRIDE {
51 return make_isize(800, 800);
52 }
53
54 virtual uint32_t onGetFlags() const SK_OVERRIDE {
55 // This is a GPU-specific GM.
56 return kGPUOnly_Flag;
57 }
58
59
60 virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
61 SkDevice* device = canvas->getTopDevice();
62 GrRenderTarget* rt = device->accessRenderTarget();
63 if (NULL == rt) {
64 return;
65 }
66 GrContext* context = rt->getContext();
67 if (NULL == context) {
68 return;
69 }
70
71 struct Vertex {
72 SkPoint fPosition;
73 float fKLM[4]; // The last value is ignored. The effect expects a vec4f.
74 };
75
commit-bot@chromium.org53a0b6c2013-08-23 18:05:01 +000076 static const int kNumCubics = 15;
commit-bot@chromium.org78a10782013-08-21 19:27:48 +000077 SkMWCRandom rand;
78
commit-bot@chromium.org53a0b6c2013-08-23 18:05:01 +000079 // Mult by 3 for each edge effect type
80 int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumCubics*3)));
81 int numRows = SkScalarCeilToInt(SkIntToScalar(kNumCubics*3) / numCols);
commit-bot@chromium.org78a10782013-08-21 19:27:48 +000082 SkScalar w = SkIntToScalar(rt->width()) / numCols;
83 SkScalar h = SkIntToScalar(rt->height()) / numRows;
84 int row = 0;
85 int col = 0;
86
87 for (int i = 0; i < kNumCubics; ++i) {
commit-bot@chromium.org53a0b6c2013-08-23 18:05:01 +000088 SkPoint baseControlPts[] = {
89 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
90 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
91 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
92 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
commit-bot@chromium.org78a10782013-08-21 19:27:48 +000093 };
commit-bot@chromium.org53a0b6c2013-08-23 18:05:01 +000094 for(int edgeType = kFillAA_GrBezierEdgeType; edgeType < 3; ++edgeType) {
95 SkScalar x = SkScalarMul(col, w);
96 SkScalar y = SkScalarMul(row, h);
97 SkPoint controlPts[] = {
98 {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
99 {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
100 {x + baseControlPts[2].fX, y + baseControlPts[2].fY},
101 {x + baseControlPts[3].fX, y + baseControlPts[3].fY}
102 };
103 SkPoint chopped[10];
104 SkScalar klmEqs[9];
105 SkScalar klmSigns[3];
106 int cnt = GrPathUtils::chopCubicAtLoopIntersection(controlPts,
107 chopped,
108 klmEqs,
109 klmSigns);
commit-bot@chromium.org78a10782013-08-21 19:27:48 +0000110
commit-bot@chromium.org53a0b6c2013-08-23 18:05:01 +0000111 SkPaint ctrlPtPaint;
112 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
commit-bot@chromium.org78a10782013-08-21 19:27:48 +0000113 for (int i = 0; i < 4; ++i) {
commit-bot@chromium.org53a0b6c2013-08-23 18:05:01 +0000114 canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint);
commit-bot@chromium.org78a10782013-08-21 19:27:48 +0000115 }
116
commit-bot@chromium.org53a0b6c2013-08-23 18:05:01 +0000117 SkPaint polyPaint;
118 polyPaint.setColor(0xffA0A0A0);
119 polyPaint.setStrokeWidth(0);
120 polyPaint.setStyle(SkPaint::kStroke_Style);
121 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 4, controlPts, polyPaint);
commit-bot@chromium.org78a10782013-08-21 19:27:48 +0000122
commit-bot@chromium.org53a0b6c2013-08-23 18:05:01 +0000123 SkPaint choppedPtPaint;
124 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
commit-bot@chromium.org78a10782013-08-21 19:27:48 +0000125
commit-bot@chromium.org53a0b6c2013-08-23 18:05:01 +0000126 for (int c = 0; c < cnt; ++c) {
127 SkPoint* pts = chopped + 3 * c;
128
129 for (int i = 0; i < 4; ++i) {
130 canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint);
131 }
132
133 SkRect bounds;
134 bounds.set(pts, 4);
135
136 SkPaint boundsPaint;
137 boundsPaint.setColor(0xff808080);
138 boundsPaint.setStrokeWidth(0);
139 boundsPaint.setStyle(SkPaint::kStroke_Style);
140 canvas->drawRect(bounds, boundsPaint);
141
142 Vertex verts[4];
143 verts[0].fPosition.setRectFan(bounds.fLeft, bounds.fTop,
144 bounds.fRight, bounds.fBottom,
145 sizeof(Vertex));
146 for (int v = 0; v < 4; ++v) {
147 verts[v].fKLM[0] = eval_line(verts[v].fPosition, klmEqs + 0, klmSigns[c]);
148 verts[v].fKLM[1] = eval_line(verts[v].fPosition, klmEqs + 3, klmSigns[c]);
149 verts[v].fKLM[2] = eval_line(verts[v].fPosition, klmEqs + 6, 1.f);
150 }
151
152 GrTestTarget tt;
153 context->getTestTarget(&tt);
154 if (NULL == tt.target()) {
155 continue;
156 }
157 GrDrawState* drawState = tt.target()->drawState();
158 drawState->setVertexAttribs<kAttribs>(2);
159
160 SkAutoTUnref<GrEffectRef> effect(GrCubicEffect::Create(
161 GrBezierEdgeType(edgeType), *tt.target()->caps()));
162 if (!effect) {
163 continue;
164 }
165 drawState->addCoverageEffect(effect, 1);
166 drawState->setRenderTarget(rt);
167 drawState->setColor(0xff000000);
168
169 tt.target()->setVertexSourceToArray(verts, 4);
170 tt.target()->setIndexSourceToBuffer(context->getQuadIndexBuffer());
171 tt.target()->drawIndexed(kTriangleFan_GrPrimitiveType, 0, 0, 4, 6);
commit-bot@chromium.org78a10782013-08-21 19:27:48 +0000172 }
commit-bot@chromium.org53a0b6c2013-08-23 18:05:01 +0000173 ++col;
174 if (numCols == col) {
175 col = 0;
176 ++row;
commit-bot@chromium.org78a10782013-08-21 19:27:48 +0000177 }
commit-bot@chromium.org78a10782013-08-21 19:27:48 +0000178 }
179 }
180 }
181
182private:
183 typedef GM INHERITED;
184};
185
186//////////////////////////////////////////////////////////////////////////////
187
commit-bot@chromium.org53a0b6c2013-08-23 18:05:01 +0000188/**
189 * This GM directly exercises effects that draw Bezier curves in the GPU backend.
190 */
191class BezierConicEffects : public GM {
192public:
193 BezierConicEffects() {
194 this->setBGColor(0xFFFFFFFF);
195 }
196
197protected:
198 virtual SkString onShortName() SK_OVERRIDE {
199 return SkString("bezier_conic_effects");
200 }
201
202 virtual SkISize onISize() SK_OVERRIDE {
203 return make_isize(800, 800);
204 }
205
206 virtual uint32_t onGetFlags() const SK_OVERRIDE {
207 // This is a GPU-specific GM.
208 return kGPUOnly_Flag;
209 }
210
211
212 virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
213 SkDevice* device = canvas->getTopDevice();
214 GrRenderTarget* rt = device->accessRenderTarget();
215 if (NULL == rt) {
216 return;
217 }
218 GrContext* context = rt->getContext();
219 if (NULL == context) {
220 return;
221 }
222
223 struct Vertex {
224 SkPoint fPosition;
225 float fKLM[4]; // The last value is ignored. The effect expects a vec4f.
226 };
227
228 static const int kNumConics = 10;
229 SkMWCRandom rand;
230
231 // Mult by 3 for each edge effect type
232 int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumConics*3)));
233 int numRows = SkScalarCeilToInt(SkIntToScalar(kNumConics*3) / numCols);
234 SkScalar w = SkIntToScalar(rt->width()) / numCols;
235 SkScalar h = SkIntToScalar(rt->height()) / numRows;
236 int row = 0;
237 int col = 0;
238
239 for (int i = 0; i < kNumConics; ++i) {
240 SkPoint baseControlPts[] = {
241 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
242 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
243 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
244 };
245 SkScalar weight = rand.nextRangeF(0.f, 2.f);
246 for(int edgeType = kFillAA_GrBezierEdgeType; edgeType < 3; ++edgeType) {
247 SkScalar x = SkScalarMul(col, w);
248 SkScalar y = SkScalarMul(row, h);
249 SkPoint controlPts[] = {
250 {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
251 {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
252 {x + baseControlPts[2].fX, y + baseControlPts[2].fY}
253 };
254 SkConic dst[4];
255 SkScalar klmEqs[9];
256 int cnt = chop_conic(controlPts, dst, weight);
257 GrPathUtils::getConicKLM(controlPts, weight, klmEqs);
258
259 SkPaint ctrlPtPaint;
260 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
261 for (int i = 0; i < 3; ++i) {
262 canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint);
263 }
264
265 SkPaint polyPaint;
266 polyPaint.setColor(0xffA0A0A0);
267 polyPaint.setStrokeWidth(0);
268 polyPaint.setStyle(SkPaint::kStroke_Style);
269 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
270
271 SkPaint choppedPtPaint;
272 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
273
274 for (int c = 0; c < cnt; ++c) {
275 SkPoint* pts = dst[c].fPts;
276 for (int i = 0; i < 3; ++i) {
277 canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint);
278 }
279
280 SkRect bounds;
281 //SkPoint bPts[] = {{0.f, 0.f}, {800.f, 800.f}};
282 //bounds.set(bPts, 2);
283 bounds.set(pts, 3);
284
285 SkPaint boundsPaint;
286 boundsPaint.setColor(0xff808080);
287 boundsPaint.setStrokeWidth(0);
288 boundsPaint.setStyle(SkPaint::kStroke_Style);
289 canvas->drawRect(bounds, boundsPaint);
290
291 Vertex verts[4];
292 verts[0].fPosition.setRectFan(bounds.fLeft, bounds.fTop,
293 bounds.fRight, bounds.fBottom,
294 sizeof(Vertex));
295 for (int v = 0; v < 4; ++v) {
296 verts[v].fKLM[0] = eval_line(verts[v].fPosition, klmEqs + 0, 1.f);
297 verts[v].fKLM[1] = eval_line(verts[v].fPosition, klmEqs + 3, 1.f);
298 verts[v].fKLM[2] = eval_line(verts[v].fPosition, klmEqs + 6, 1.f);
299 }
300
301 GrTestTarget tt;
302 context->getTestTarget(&tt);
303 if (NULL == tt.target()) {
304 continue;
305 }
306 GrDrawState* drawState = tt.target()->drawState();
307 drawState->setVertexAttribs<kAttribs>(2);
308
309 SkAutoTUnref<GrEffectRef> effect(GrConicEffect::Create(
310 GrBezierEdgeType(edgeType), *tt.target()->caps()));
311 if (!effect) {
312 continue;
313 }
314 drawState->addCoverageEffect(effect, 1);
315 drawState->setRenderTarget(rt);
316 drawState->setColor(0xff000000);
317
318 tt.target()->setVertexSourceToArray(verts, 4);
319 tt.target()->setIndexSourceToBuffer(context->getQuadIndexBuffer());
320 tt.target()->drawIndexed(kTriangleFan_GrPrimitiveType, 0, 0, 4, 6);
321 }
322 ++col;
323 if (numCols == col) {
324 col = 0;
325 ++row;
326 }
327 }
328 }
329 }
330
331private:
332 // Uses the max curvature function for quads to estimate
333 // where to chop the conic. If the max curvature is not
334 // found along the curve segment it will return 1 and
335 // dst[0] is the original conic. If it returns 2 the dst[0]
336 // and dst[1] are the two new conics.
337 int split_conic(const SkPoint src[3], SkConic dst[2], const SkScalar weight) {
338 SkScalar t = SkFindQuadMaxCurvature(src);
339 if (t == 0) {
340 if (dst) {
341 dst[0].set(src, weight);
342 }
343 return 1;
344 } else {
345 if (dst) {
346 SkConic conic;
347 conic.set(src, weight);
348 conic.chopAt(t, dst);
349 }
350 return 2;
351 }
352 }
353
354 // Calls split_conic on the entire conic and then once more on each subsection.
355 // Most cases will result in either 1 conic (chop point is not within t range)
356 // or 3 points (split once and then one subsection is split again).
357 int chop_conic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) {
358 SkConic dstTemp[2];
359 int conicCnt = split_conic(src, dstTemp, weight);
360 if (2 == conicCnt) {
361 int conicCnt2 = split_conic(dstTemp[0].fPts, dst, dstTemp[0].fW);
362 conicCnt = conicCnt2 + split_conic(dstTemp[1].fPts, &dst[conicCnt2], dstTemp[1].fW);
363 } else {
364 dst[0] = dstTemp[0];
365 }
366 return conicCnt;
367 }
368
369 typedef GM INHERITED;
370};
371
372//////////////////////////////////////////////////////////////////////////////
373/**
374 * This GM directly exercises effects that draw Bezier quad curves in the GPU backend.
375 */
376class BezierQuadEffects : public GM {
377public:
378 BezierQuadEffects() {
379 this->setBGColor(0xFFFFFFFF);
380 }
381
382protected:
383 virtual SkString onShortName() SK_OVERRIDE {
384 return SkString("bezier_quad_effects");
385 }
386
387 virtual SkISize onISize() SK_OVERRIDE {
388 return make_isize(800, 800);
389 }
390
391 virtual uint32_t onGetFlags() const SK_OVERRIDE {
392 // This is a GPU-specific GM.
393 return kGPUOnly_Flag;
394 }
395
396
397 virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
398 SkDevice* device = canvas->getTopDevice();
399 GrRenderTarget* rt = device->accessRenderTarget();
400 if (NULL == rt) {
401 return;
402 }
403 GrContext* context = rt->getContext();
404 if (NULL == context) {
405 return;
406 }
407
408 struct Vertex {
409 SkPoint fPosition;
410 float fUV[4]; // The last two values are ignored. The effect expects a vec4f.
411 };
412
413 static const int kNumQuads = 5;
414 SkMWCRandom rand;
415
416 int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumQuads*3)));
417 int numRows = SkScalarCeilToInt(SkIntToScalar(kNumQuads*3) / numCols);
418 SkScalar w = SkIntToScalar(rt->width()) / numCols;
419 SkScalar h = SkIntToScalar(rt->height()) / numRows;
420 int row = 0;
421 int col = 0;
422
423 for (int i = 0; i < kNumQuads; ++i) {
424 SkPoint baseControlPts[] = {
425 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
426 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
427 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
428 };
429 for(int edgeType = kFillAA_GrBezierEdgeType; edgeType < 3; ++edgeType) {
430 SkScalar x = SkScalarMul(col, w);
431 SkScalar y = SkScalarMul(row, h);
432 SkPoint controlPts[] = {
433 {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
434 {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
435 {x + baseControlPts[2].fX, y + baseControlPts[2].fY}
436 };
437 SkPoint chopped[5];
438 int cnt = SkChopQuadAtMaxCurvature(controlPts, chopped);
439
440 SkPaint ctrlPtPaint;
441 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
442 for (int i = 0; i < 3; ++i) {
443 canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint);
444 }
445
446 SkPaint polyPaint;
447 polyPaint.setColor(0xffA0A0A0);
448 polyPaint.setStrokeWidth(0);
449 polyPaint.setStyle(SkPaint::kStroke_Style);
450 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
451
452 SkPaint choppedPtPaint;
453 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
454
455 for (int c = 0; c < cnt; ++c) {
456 SkPoint* pts = chopped + 2 * c;
457
458 for (int i = 0; i < 3; ++i) {
459 canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint);
460 }
461
462 SkRect bounds;
463 bounds.set(pts, 3);
464
465 SkPaint boundsPaint;
466 boundsPaint.setColor(0xff808080);
467 boundsPaint.setStrokeWidth(0);
468 boundsPaint.setStyle(SkPaint::kStroke_Style);
469 canvas->drawRect(bounds, boundsPaint);
470
471 Vertex verts[4];
472 verts[0].fPosition.setRectFan(bounds.fLeft, bounds.fTop,
473 bounds.fRight, bounds.fBottom,
474 sizeof(Vertex));
475
476 GrPathUtils::QuadUVMatrix DevToUV(pts);
477 DevToUV.apply<4, sizeof(Vertex), sizeof(GrPoint)>(verts);
478
479 GrTestTarget tt;
480 context->getTestTarget(&tt);
481 if (NULL == tt.target()) {
482 continue;
483 }
484 GrDrawState* drawState = tt.target()->drawState();
485 drawState->setVertexAttribs<kAttribs>(2);
486 SkAutoTUnref<GrEffectRef> effect(GrQuadEffect::Create(
487 GrBezierEdgeType(edgeType), *tt.target()->caps()));
488 if (!effect) {
489 continue;
490 }
491 drawState->addCoverageEffect(effect, 1);
492 drawState->setRenderTarget(rt);
493 drawState->setColor(0xff000000);
494
495 tt.target()->setVertexSourceToArray(verts, 4);
496 tt.target()->setIndexSourceToBuffer(context->getQuadIndexBuffer());
497 tt.target()->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 4, 6);
498 }
499 ++col;
500 if (numCols == col) {
501 col = 0;
502 ++row;
503 }
504 }
505 }
506 }
507
508private:
509 typedef GM INHERITED;
510};
511
512DEF_GM( return SkNEW(BezierCubicEffects); )
513DEF_GM( return SkNEW(BezierConicEffects); )
514DEF_GM( return SkNEW(BezierQuadEffects); )
commit-bot@chromium.org78a10782013-08-21 19:27:48 +0000515
516}
517
518#endif