3 * Copyright 2013 Google Inc.
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
9 // This test only works with the GPU backend.
15 #include "GrContext.h"
16 #include "GrPathUtils.h"
18 #include "SkColorPriv.h"
20 #include "SkGeometry.h"
22 #include "effects/GrBezierEffect.h"
24 // Position & KLM line eq values. These are the vertex attributes for Bezier curves. The last value
25 // of the Vec4f is ignored.
27 extern const GrVertexAttrib kAttribs[] = {
28 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
29 {kVec4f_GrVertexAttribType, sizeof(SkPoint), kEffect_GrVertexAttribBinding}
33 static 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]);
39 * This GM directly exercises effects that draw Bezier curves in the GPU backend.
41 class BezierCubicEffects : public GM {
43 BezierCubicEffects() {
44 this->setBGColor(0xFFFFFFFF);
48 virtual SkString onShortName() SK_OVERRIDE {
49 return SkString("bezier_cubic_effects");
52 virtual SkISize onISize() SK_OVERRIDE {
53 return SkISize::Make(800, 800);
56 virtual uint32_t onGetFlags() const SK_OVERRIDE {
57 // This is a GPU-specific GM.
62 virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
63 GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
67 GrContext* context = rt->getContext();
68 if (NULL == context) {
74 float fKLM[4]; // The last value is ignored. The effect expects a vec4f.
77 static const int kNumCubics = 15;
80 // Mult by 3 for each edge effect type
81 int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumCubics*3)));
82 int numRows = SkScalarCeilToInt(SkIntToScalar(kNumCubics*3) / numCols);
83 SkScalar w = SkIntToScalar(rt->width()) / numCols;
84 SkScalar h = SkIntToScalar(rt->height()) / numRows;
88 for (int i = 0; i < kNumCubics; ++i) {
89 SkPoint baseControlPts[] = {
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)},
93 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
95 for(int edgeType = 0; edgeType < kGrEffectEdgeTypeCnt; ++edgeType) {
96 SkAutoTUnref<GrEffect> effect;
97 { // scope to contain GrTestTarget
99 context->getTestTarget(&tt);
100 if (NULL == tt.target()) {
103 GrEffectEdgeType et = (GrEffectEdgeType)edgeType;
104 effect.reset(GrCubicEffect::Create(et, *tt.target()->caps()));
110 SkScalar x = SkScalarMul(col, w);
111 SkScalar y = SkScalarMul(row, h);
112 SkPoint controlPts[] = {
113 {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
114 {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
115 {x + baseControlPts[2].fX, y + baseControlPts[2].fY},
116 {x + baseControlPts[3].fX, y + baseControlPts[3].fY}
120 SkScalar klmSigns[3];
121 int cnt = GrPathUtils::chopCubicAtLoopIntersection(controlPts,
127 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
128 for (int i = 0; i < 4; ++i) {
129 canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint);
133 polyPaint.setColor(0xffA0A0A0);
134 polyPaint.setStrokeWidth(0);
135 polyPaint.setStyle(SkPaint::kStroke_Style);
136 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 4, controlPts, polyPaint);
138 SkPaint choppedPtPaint;
139 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
141 for (int c = 0; c < cnt; ++c) {
142 SkPoint* pts = chopped + 3 * c;
144 for (int i = 0; i < 4; ++i) {
145 canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint);
152 boundsPaint.setColor(0xff808080);
153 boundsPaint.setStrokeWidth(0);
154 boundsPaint.setStyle(SkPaint::kStroke_Style);
155 canvas->drawRect(bounds, boundsPaint);
158 verts[0].fPosition.setRectFan(bounds.fLeft, bounds.fTop,
159 bounds.fRight, bounds.fBottom,
161 for (int v = 0; v < 4; ++v) {
162 verts[v].fKLM[0] = eval_line(verts[v].fPosition, klmEqs + 0, klmSigns[c]);
163 verts[v].fKLM[1] = eval_line(verts[v].fPosition, klmEqs + 3, klmSigns[c]);
164 verts[v].fKLM[2] = eval_line(verts[v].fPosition, klmEqs + 6, 1.f);
168 context->getTestTarget(&tt);
169 SkASSERT(NULL != tt.target());
170 GrDrawState* drawState = tt.target()->drawState();
171 drawState->setVertexAttribs<kAttribs>(2);
173 drawState->addCoverageEffect(effect, 1);
174 drawState->setRenderTarget(rt);
175 drawState->setColor(0xff000000);
177 tt.target()->setVertexSourceToArray(verts, 4);
178 tt.target()->setIndexSourceToBuffer(context->getQuadIndexBuffer());
179 tt.target()->drawIndexed(kTriangleFan_GrPrimitiveType, 0, 0, 4, 6);
182 if (numCols == col) {
191 typedef GM INHERITED;
194 //////////////////////////////////////////////////////////////////////////////
197 * This GM directly exercises effects that draw Bezier curves in the GPU backend.
199 class BezierConicEffects : public GM {
201 BezierConicEffects() {
202 this->setBGColor(0xFFFFFFFF);
206 virtual SkString onShortName() SK_OVERRIDE {
207 return SkString("bezier_conic_effects");
210 virtual SkISize onISize() SK_OVERRIDE {
211 return SkISize::Make(800, 800);
214 virtual uint32_t onGetFlags() const SK_OVERRIDE {
215 // This is a GPU-specific GM.
216 return kGPUOnly_Flag;
220 virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
221 GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
225 GrContext* context = rt->getContext();
226 if (NULL == context) {
232 float fKLM[4]; // The last value is ignored. The effect expects a vec4f.
235 static const int kNumConics = 10;
238 // Mult by 3 for each edge effect type
239 int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumConics*3)));
240 int numRows = SkScalarCeilToInt(SkIntToScalar(kNumConics*3) / numCols);
241 SkScalar w = SkIntToScalar(rt->width()) / numCols;
242 SkScalar h = SkIntToScalar(rt->height()) / numRows;
246 for (int i = 0; i < kNumConics; ++i) {
247 SkPoint baseControlPts[] = {
248 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
249 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
250 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
252 SkScalar weight = rand.nextRangeF(0.f, 2.f);
253 for(int edgeType = 0; edgeType < kGrEffectEdgeTypeCnt; ++edgeType) {
254 SkAutoTUnref<GrEffect> effect;
255 { // scope to contain GrTestTarget
257 context->getTestTarget(&tt);
258 if (NULL == tt.target()) {
261 GrEffectEdgeType et = (GrEffectEdgeType)edgeType;
262 effect.reset(GrConicEffect::Create(et, *tt.target()->caps()));
268 SkScalar x = SkScalarMul(col, w);
269 SkScalar y = SkScalarMul(row, h);
270 SkPoint controlPts[] = {
271 {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
272 {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
273 {x + baseControlPts[2].fX, y + baseControlPts[2].fY}
277 int cnt = chop_conic(controlPts, dst, weight);
278 GrPathUtils::getConicKLM(controlPts, weight, klmEqs);
281 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
282 for (int i = 0; i < 3; ++i) {
283 canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint);
287 polyPaint.setColor(0xffA0A0A0);
288 polyPaint.setStrokeWidth(0);
289 polyPaint.setStyle(SkPaint::kStroke_Style);
290 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
292 SkPaint choppedPtPaint;
293 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
295 for (int c = 0; c < cnt; ++c) {
296 SkPoint* pts = dst[c].fPts;
297 for (int i = 0; i < 3; ++i) {
298 canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint);
302 //SkPoint bPts[] = {{0.f, 0.f}, {800.f, 800.f}};
303 //bounds.set(bPts, 2);
307 boundsPaint.setColor(0xff808080);
308 boundsPaint.setStrokeWidth(0);
309 boundsPaint.setStyle(SkPaint::kStroke_Style);
310 canvas->drawRect(bounds, boundsPaint);
313 verts[0].fPosition.setRectFan(bounds.fLeft, bounds.fTop,
314 bounds.fRight, bounds.fBottom,
316 for (int v = 0; v < 4; ++v) {
317 verts[v].fKLM[0] = eval_line(verts[v].fPosition, klmEqs + 0, 1.f);
318 verts[v].fKLM[1] = eval_line(verts[v].fPosition, klmEqs + 3, 1.f);
319 verts[v].fKLM[2] = eval_line(verts[v].fPosition, klmEqs + 6, 1.f);
323 context->getTestTarget(&tt);
324 SkASSERT(NULL != tt.target());
325 GrDrawState* drawState = tt.target()->drawState();
326 drawState->setVertexAttribs<kAttribs>(2);
328 drawState->addCoverageEffect(effect, 1);
329 drawState->setRenderTarget(rt);
330 drawState->setColor(0xff000000);
332 tt.target()->setVertexSourceToArray(verts, 4);
333 tt.target()->setIndexSourceToBuffer(context->getQuadIndexBuffer());
334 tt.target()->drawIndexed(kTriangleFan_GrPrimitiveType, 0, 0, 4, 6);
337 if (numCols == col) {
346 // Uses the max curvature function for quads to estimate
347 // where to chop the conic. If the max curvature is not
348 // found along the curve segment it will return 1 and
349 // dst[0] is the original conic. If it returns 2 the dst[0]
350 // and dst[1] are the two new conics.
351 int split_conic(const SkPoint src[3], SkConic dst[2], const SkScalar weight) {
352 SkScalar t = SkFindQuadMaxCurvature(src);
355 dst[0].set(src, weight);
361 conic.set(src, weight);
362 conic.chopAt(t, dst);
368 // Calls split_conic on the entire conic and then once more on each subsection.
369 // Most cases will result in either 1 conic (chop point is not within t range)
370 // or 3 points (split once and then one subsection is split again).
371 int chop_conic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) {
373 int conicCnt = split_conic(src, dstTemp, weight);
375 int conicCnt2 = split_conic(dstTemp[0].fPts, dst, dstTemp[0].fW);
376 conicCnt = conicCnt2 + split_conic(dstTemp[1].fPts, &dst[conicCnt2], dstTemp[1].fW);
383 typedef GM INHERITED;
386 //////////////////////////////////////////////////////////////////////////////
388 * This GM directly exercises effects that draw Bezier quad curves in the GPU backend.
390 class BezierQuadEffects : public GM {
392 BezierQuadEffects() {
393 this->setBGColor(0xFFFFFFFF);
397 virtual SkString onShortName() SK_OVERRIDE {
398 return SkString("bezier_quad_effects");
401 virtual SkISize onISize() SK_OVERRIDE {
402 return SkISize::Make(800, 800);
405 virtual uint32_t onGetFlags() const SK_OVERRIDE {
406 // This is a GPU-specific GM.
407 return kGPUOnly_Flag;
411 virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
412 GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
416 GrContext* context = rt->getContext();
417 if (NULL == context) {
423 float fUV[4]; // The last two values are ignored. The effect expects a vec4f.
426 static const int kNumQuads = 5;
429 int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumQuads*3)));
430 int numRows = SkScalarCeilToInt(SkIntToScalar(kNumQuads*3) / numCols);
431 SkScalar w = SkIntToScalar(rt->width()) / numCols;
432 SkScalar h = SkIntToScalar(rt->height()) / numRows;
436 for (int i = 0; i < kNumQuads; ++i) {
437 SkPoint baseControlPts[] = {
438 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
439 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
440 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
442 for(int edgeType = 0; edgeType < kGrEffectEdgeTypeCnt; ++edgeType) {
443 SkAutoTUnref<GrEffect> effect;
444 { // scope to contain GrTestTarget
446 context->getTestTarget(&tt);
447 if (NULL == tt.target()) {
450 GrEffectEdgeType et = (GrEffectEdgeType)edgeType;
451 effect.reset(GrQuadEffect::Create(et, *tt.target()->caps()));
457 SkScalar x = SkScalarMul(col, w);
458 SkScalar y = SkScalarMul(row, h);
459 SkPoint controlPts[] = {
460 {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
461 {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
462 {x + baseControlPts[2].fX, y + baseControlPts[2].fY}
465 int cnt = SkChopQuadAtMaxCurvature(controlPts, chopped);
468 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
469 for (int i = 0; i < 3; ++i) {
470 canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint);
474 polyPaint.setColor(0xffA0A0A0);
475 polyPaint.setStrokeWidth(0);
476 polyPaint.setStyle(SkPaint::kStroke_Style);
477 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
479 SkPaint choppedPtPaint;
480 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
482 for (int c = 0; c < cnt; ++c) {
483 SkPoint* pts = chopped + 2 * c;
485 for (int i = 0; i < 3; ++i) {
486 canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint);
493 boundsPaint.setColor(0xff808080);
494 boundsPaint.setStrokeWidth(0);
495 boundsPaint.setStyle(SkPaint::kStroke_Style);
496 canvas->drawRect(bounds, boundsPaint);
499 verts[0].fPosition.setRectFan(bounds.fLeft, bounds.fTop,
500 bounds.fRight, bounds.fBottom,
503 GrPathUtils::QuadUVMatrix DevToUV(pts);
504 DevToUV.apply<4, sizeof(Vertex), sizeof(SkPoint)>(verts);
507 context->getTestTarget(&tt);
508 SkASSERT(NULL != tt.target());
509 GrDrawState* drawState = tt.target()->drawState();
510 drawState->setVertexAttribs<kAttribs>(2);
512 drawState->addCoverageEffect(effect, 1);
513 drawState->setRenderTarget(rt);
514 drawState->setColor(0xff000000);
516 tt.target()->setVertexSourceToArray(verts, 4);
517 tt.target()->setIndexSourceToBuffer(context->getQuadIndexBuffer());
518 tt.target()->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 4, 6);
521 if (numCols == col) {
530 typedef GM INHERITED;
533 DEF_GM( return SkNEW(BezierCubicEffects); )
534 DEF_GM( return SkNEW(BezierConicEffects); )
535 DEF_GM( return SkNEW(BezierQuadEffects); )