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