blob: 5c24f6e79e649469fa00b061a75000b99cebc117 [file] [log] [blame]
robertphillips@google.comf6747b02012-06-12 00:32:28 +00001/*
2 * Copyright 2012 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
8#include "GrAARectRenderer.h"
9#include "GrRefCnt.h"
10#include "GrGpu.h"
robertphillips@google.comdf3695e2013-04-09 14:01:44 +000011#include "gl/GrGLEffect.h"
12#include "GrTBackendEffectFactory.h"
robertphillips@google.com908aed82013-05-28 13:16:20 +000013#include "SkColorPriv.h"
robertphillips@google.comf6747b02012-06-12 00:32:28 +000014
robertphillips@google.com4d73ac22012-06-13 18:54:08 +000015SK_DEFINE_INST_COUNT(GrAARectRenderer)
robertphillips@google.comf6747b02012-06-12 00:32:28 +000016
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +000017///////////////////////////////////////////////////////////////////////////////
18class GrGLAlignedRectEffect;
19
20// Axis Aligned special case
21class GrAlignedRectEffect : public GrEffect {
22public:
23 static GrEffectRef* Create() {
24 GR_CREATE_STATIC_EFFECT(gAlignedRectEffect, GrAlignedRectEffect, ());
25 gAlignedRectEffect->ref();
26 return gAlignedRectEffect;
27 }
28
29 virtual ~GrAlignedRectEffect() {}
30
31 static const char* Name() { return "AlignedRectEdge"; }
32
33 virtual void getConstantColorComponents(GrColor* color,
34 uint32_t* validFlags) const SK_OVERRIDE {
35 *validFlags = 0;
36 }
37
38 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
39 return GrTBackendEffectFactory<GrAlignedRectEffect>::getInstance();
40 }
41
42 class GLEffect : public GrGLEffect {
43 public:
44 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
45 : INHERITED (factory) {}
46
47 virtual void emitCode(GrGLShaderBuilder* builder,
48 const GrDrawEffect& drawEffect,
49 EffectKey key,
50 const char* outputColor,
51 const char* inputColor,
52 const TextureSamplerArray& samplers) SK_OVERRIDE {
53 // setup the varying for the Axis aligned rect effect
54 // xy -> interpolated offset
55 // zw -> w/2+0.5, h/2+0.5
56 const char *vsRectName, *fsRectName;
57 builder->addVarying(kVec4f_GrSLType, "Rect", &vsRectName, &fsRectName);
58 const SkString* attr0Name =
59 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
60 builder->vsCodeAppendf("\t%s = %s;\n", vsRectName, attr0Name->c_str());
61
commit-bot@chromium.org99e0d082013-06-14 14:58:50 +000062 // TODO: compute all these offsets, spans, and scales in the VS
63 builder->fsCodeAppendf("\tfloat insetW = min(1.0, %s.z) - 0.5;\n", fsRectName);
64 builder->fsCodeAppendf("\tfloat insetH = min(1.0, %s.w) - 0.5;\n", fsRectName);
65 builder->fsCodeAppend("\tfloat outset = 0.5;\n");
66 // For rects > 1 pixel wide and tall the span's are noops (i.e., 1.0). For rects
67 // < 1 pixel wide or tall they serve to normalize the < 1 ramp to a 0 .. 1 range.
68 builder->fsCodeAppend("\tfloat spanW = insetW + outset;\n");
69 builder->fsCodeAppend("\tfloat spanH = insetH + outset;\n");
70 // For rects < 1 pixel wide or tall, these scale factors are used to cap the maximum
71 // value of coverage that is used. In other words it is the coverage that is
72 // used in the interior of the rect after the ramp.
robertphillips@google.com07a05242013-06-14 17:45:30 +000073 builder->fsCodeAppend("\tfloat scaleW = min(1.0, 2.0*insetW/spanW);\n");
74 builder->fsCodeAppend("\tfloat scaleH = min(1.0, 2.0*insetH/spanH);\n");
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +000075
76 // Compute the coverage for the rect's width
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +000077 builder->fsCodeAppendf(
commit-bot@chromium.org99e0d082013-06-14 14:58:50 +000078 "\tfloat coverage = scaleW*clamp((%s.z-abs(%s.x))/spanW, 0.0, 1.0);\n", fsRectName,
79 fsRectName);
80 // Compute the coverage for the rect's height and merge with the width
egdaniel@google.comf1d7de72013-06-14 19:25:53 +000081 builder->fsCodeAppendf(
82 "\tcoverage = coverage*scaleH*clamp((%s.w-abs(%s.y))/spanH, 0.0, 1.0);\n",
83 fsRectName, fsRectName);
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +000084
85 SkString modulate;
86 GrGLSLModulatef<4>(&modulate, inputColor, "coverage");
87 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
88 }
89
90 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
91 return 0;
92 }
93
94 virtual void setData(const GrGLUniformManager& uman, const GrDrawEffect&) SK_OVERRIDE {}
95
96 private:
97 typedef GrGLEffect INHERITED;
98 };
99
100
101private:
102 GrAlignedRectEffect() : GrEffect() {
103 this->addVertexAttrib(kVec4f_GrSLType);
104 }
105
106 virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE { return true; }
107
108 GR_DECLARE_EFFECT_TEST;
109
110 typedef GrEffect INHERITED;
111};
112
113
114GR_DEFINE_EFFECT_TEST(GrAlignedRectEffect);
115
116GrEffectRef* GrAlignedRectEffect::TestCreate(SkMWCRandom* random,
117 GrContext* context,
118 const GrDrawTargetCaps&,
119 GrTexture* textures[]) {
120 return GrAlignedRectEffect::Create();
121}
122
123///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000124class GrGLRectEffect;
125
126/**
skia.committer@gmail.com07d3a652013-04-10 07:01:15 +0000127 * The output of this effect is a modulation of the input color and coverage
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000128 * for an arbitrarily oriented rect. The rect is specified as:
129 * Center of the rect
130 * Unit vector point down the height of the rect
131 * Half width + 0.5
132 * Half height + 0.5
133 * The center and vector are stored in a vec4 varying ("RectEdge") with the
134 * center in the xy components and the vector in the zw components.
135 * The munged width and height are stored in a vec2 varying ("WidthHeight")
136 * with the width in x and the height in y.
137 */
138class GrRectEffect : public GrEffect {
139public:
140 static GrEffectRef* Create() {
bsalomon@google.comd42aca32013-04-23 15:37:27 +0000141 GR_CREATE_STATIC_EFFECT(gRectEffect, GrRectEffect, ());
142 gRectEffect->ref();
143 return gRectEffect;
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000144 }
145
146 virtual ~GrRectEffect() {}
147
148 static const char* Name() { return "RectEdge"; }
149
skia.committer@gmail.com07d3a652013-04-10 07:01:15 +0000150 virtual void getConstantColorComponents(GrColor* color,
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000151 uint32_t* validFlags) const SK_OVERRIDE {
152 *validFlags = 0;
153 }
154
155 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
156 return GrTBackendEffectFactory<GrRectEffect>::getInstance();
157 }
158
159 class GLEffect : public GrGLEffect {
160 public:
161 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
162 : INHERITED (factory) {}
163
164 virtual void emitCode(GrGLShaderBuilder* builder,
165 const GrDrawEffect& drawEffect,
166 EffectKey key,
167 const char* outputColor,
168 const char* inputColor,
169 const TextureSamplerArray& samplers) SK_OVERRIDE {
170 // setup the varying for the center point and the unit vector
171 // that points down the height of the rect
172 const char *vsRectEdgeName, *fsRectEdgeName;
skia.committer@gmail.com07d3a652013-04-10 07:01:15 +0000173 builder->addVarying(kVec4f_GrSLType, "RectEdge",
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000174 &vsRectEdgeName, &fsRectEdgeName);
skia.committer@gmail.com07d3a652013-04-10 07:01:15 +0000175 const SkString* attr0Name =
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000176 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
177 builder->vsCodeAppendf("\t%s = %s;\n", vsRectEdgeName, attr0Name->c_str());
178
179 // setup the varying for width/2+.5 and height/2+.5
180 const char *vsWidthHeightName, *fsWidthHeightName;
skia.committer@gmail.com07d3a652013-04-10 07:01:15 +0000181 builder->addVarying(kVec2f_GrSLType, "WidthHeight",
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000182 &vsWidthHeightName, &fsWidthHeightName);
183 const SkString* attr1Name =
184 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]);
185 builder->vsCodeAppendf("\t%s = %s;\n", vsWidthHeightName, attr1Name->c_str());
186
egdaniel@google.comf1d7de72013-06-14 19:25:53 +0000187 // TODO: compute all these offsets, spans, and scales in the VS
188 builder->fsCodeAppendf("\tfloat insetW = min(1.0, %s.x) - 0.5;\n", fsWidthHeightName);
189 builder->fsCodeAppendf("\tfloat insetH = min(1.0, %s.y) - 0.5;\n", fsWidthHeightName);
190 builder->fsCodeAppend("\tfloat outset = 0.5;\n");
191 // For rects > 1 pixel wide and tall the span's are noops (i.e., 1.0). For rects
192 // < 1 pixel wide or tall they serve to normalize the < 1 ramp to a 0 .. 1 range.
193 builder->fsCodeAppend("\tfloat spanW = insetW + outset;\n");
194 builder->fsCodeAppend("\tfloat spanH = insetH + outset;\n");
195 // For rects < 1 pixel wide or tall, these scale factors are used to cap the maximum
196 // value of coverage that is used. In other words it is the coverage that is
197 // used in the interior of the rect after the ramp.
198 builder->fsCodeAppend("\tfloat scaleW = min(1.0, 2.0*insetW/spanW);\n");
199 builder->fsCodeAppend("\tfloat scaleH = min(1.0, 2.0*insetH/spanH);\n");
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000200
201 // Compute the coverage for the rect's width
skia.committer@gmail.com07d3a652013-04-10 07:01:15 +0000202 builder->fsCodeAppendf("\tvec2 offset = %s.xy - %s.xy;\n",
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000203 builder->fragmentPosition(), fsRectEdgeName);
204 builder->fsCodeAppendf("\tfloat perpDot = abs(offset.x * %s.w - offset.y * %s.z);\n",
205 fsRectEdgeName, fsRectEdgeName);
egdaniel@google.comf1d7de72013-06-14 19:25:53 +0000206 builder->fsCodeAppendf(
207 "\tfloat coverage = scaleW*clamp((%s.x-perpDot)/spanW, 0.0, 1.0);\n",
208 fsWidthHeightName);
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000209
210 // Compute the coverage for the rect's height and merge with the width
211 builder->fsCodeAppendf("\tperpDot = abs(dot(offset, %s.zw));\n",
212 fsRectEdgeName);
213 builder->fsCodeAppendf(
egdaniel@google.comf1d7de72013-06-14 19:25:53 +0000214 "\tcoverage = coverage*scaleH*clamp((%s.y-perpDot)/spanH, 0.0, 1.0);\n",
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000215 fsWidthHeightName);
216
217 SkString modulate;
bsalomon@google.com018f1792013-04-18 19:36:09 +0000218 GrGLSLModulatef<4>(&modulate, inputColor, "coverage");
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000219 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
220 }
221
222 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
223 return 0;
224 }
225
226 virtual void setData(const GrGLUniformManager& uman, const GrDrawEffect&) SK_OVERRIDE {}
227
228 private:
229 typedef GrGLEffect INHERITED;
230 };
231
232
233private:
robertphillips@google.com59dd7162013-04-09 14:08:15 +0000234 GrRectEffect() : GrEffect() {
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000235 this->addVertexAttrib(kVec4f_GrSLType);
236 this->addVertexAttrib(kVec2f_GrSLType);
commit-bot@chromium.org8d47ddc2013-05-09 14:55:46 +0000237 this->setWillReadFragmentPosition();
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000238 }
239
240 virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE { return true; }
241
242 GR_DECLARE_EFFECT_TEST;
243
244 typedef GrEffect INHERITED;
245};
246
247
248GR_DEFINE_EFFECT_TEST(GrRectEffect);
249
250GrEffectRef* GrRectEffect::TestCreate(SkMWCRandom* random,
251 GrContext* context,
252 const GrDrawTargetCaps&,
253 GrTexture* textures[]) {
254 return GrRectEffect::Create();
255}
256
257///////////////////////////////////////////////////////////////////////////////
258
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000259namespace {
260
robertphillips@google.com42903302013-04-20 12:26:07 +0000261extern const GrVertexAttrib gAARectCoverageAttribs[] = {
262 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
263 {kVec4ub_GrVertexAttribType, sizeof(GrPoint), kCoverage_GrVertexAttribBinding},
264};
265
266extern const GrVertexAttrib gAARectColorAttribs[] = {
267 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
268 {kVec4ub_GrVertexAttribType, sizeof(GrPoint), kColor_GrVertexAttribBinding},
269};
270
271static void set_aa_rect_vertex_attributes(GrDrawState* drawState, bool useCoverage) {
272 if (useCoverage) {
273 drawState->setVertexAttribs<gAARectCoverageAttribs>(SK_ARRAY_COUNT(gAARectCoverageAttribs));
274 } else {
275 drawState->setVertexAttribs<gAARectColorAttribs>(SK_ARRAY_COUNT(gAARectColorAttribs));
276 }
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000277}
278
robertphillips@google.comca47aae2012-12-12 15:58:25 +0000279static void set_inset_fan(GrPoint* pts, size_t stride,
280 const GrRect& r, SkScalar dx, SkScalar dy) {
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000281 pts->setRectFan(r.fLeft + dx, r.fTop + dy,
282 r.fRight - dx, r.fBottom - dy, stride);
283}
284
285};
286
287void GrAARectRenderer::reset() {
288 GrSafeSetNull(fAAFillRectIndexBuffer);
289 GrSafeSetNull(fAAStrokeRectIndexBuffer);
290}
291
robertphillips@google.com6d067302012-12-18 21:47:47 +0000292static const uint16_t gFillAARectIdx[] = {
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000293 0, 1, 5, 5, 4, 0,
294 1, 2, 6, 6, 5, 1,
295 2, 3, 7, 7, 6, 2,
296 3, 0, 4, 4, 7, 3,
297 4, 5, 6, 6, 7, 4,
298};
299
robertphillips@google.com6d067302012-12-18 21:47:47 +0000300static const int kIndicesPerAAFillRect = GR_ARRAY_COUNT(gFillAARectIdx);
301static const int kVertsPerAAFillRect = 8;
302static const int kNumAAFillRectsInIndexBuffer = 256;
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000303
304GrIndexBuffer* GrAARectRenderer::aaFillRectIndexBuffer(GrGpu* gpu) {
robertphillips@google.com6d067302012-12-18 21:47:47 +0000305 static const size_t kAAFillRectIndexBufferSize = kIndicesPerAAFillRect *
306 sizeof(uint16_t) *
307 kNumAAFillRectsInIndexBuffer;
308
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000309 if (NULL == fAAFillRectIndexBuffer) {
robertphillips@google.com6d067302012-12-18 21:47:47 +0000310 fAAFillRectIndexBuffer = gpu->createIndexBuffer(kAAFillRectIndexBufferSize, false);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000311 if (NULL != fAAFillRectIndexBuffer) {
robertphillips@google.com6d067302012-12-18 21:47:47 +0000312 uint16_t* data = (uint16_t*) fAAFillRectIndexBuffer->lock();
313 bool useTempData = (NULL == data);
314 if (useTempData) {
315 data = SkNEW_ARRAY(uint16_t, kNumAAFillRectsInIndexBuffer * kIndicesPerAAFillRect);
316 }
317 for (int i = 0; i < kNumAAFillRectsInIndexBuffer; ++i) {
318 // Each AA filled rect is drawn with 8 vertices and 10 triangles (8 around
319 // the inner rect (for AA) and 2 for the inner rect.
320 int baseIdx = i * kIndicesPerAAFillRect;
321 uint16_t baseVert = (uint16_t)(i * kVertsPerAAFillRect);
322 for (int j = 0; j < kIndicesPerAAFillRect; ++j) {
323 data[baseIdx+j] = baseVert + gFillAARectIdx[j];
324 }
325 }
326 if (useTempData) {
327 if (!fAAFillRectIndexBuffer->updateData(data, kAAFillRectIndexBufferSize)) {
328 GrCrash("Can't get AA Fill Rect indices into buffer!");
329 }
330 SkDELETE_ARRAY(data);
331 } else {
332 fAAFillRectIndexBuffer->unlock();
333 }
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000334 }
335 }
robertphillips@google.com6d067302012-12-18 21:47:47 +0000336
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000337 return fAAFillRectIndexBuffer;
338}
339
robertphillips@google.com6d067302012-12-18 21:47:47 +0000340static const uint16_t gStrokeAARectIdx[] = {
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000341 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
342 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
343 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
344 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
345
346 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
347 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
348 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
349 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
350
351 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
352 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
353 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
354 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
355};
356
357int GrAARectRenderer::aaStrokeRectIndexCount() {
358 return GR_ARRAY_COUNT(gStrokeAARectIdx);
359}
360
361GrIndexBuffer* GrAARectRenderer::aaStrokeRectIndexBuffer(GrGpu* gpu) {
362 if (NULL == fAAStrokeRectIndexBuffer) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000363 fAAStrokeRectIndexBuffer =
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000364 gpu->createIndexBuffer(sizeof(gStrokeAARectIdx), false);
365 if (NULL != fAAStrokeRectIndexBuffer) {
366#if GR_DEBUG
367 bool updated =
368#endif
369 fAAStrokeRectIndexBuffer->updateData(gStrokeAARectIdx,
370 sizeof(gStrokeAARectIdx));
371 GR_DEBUGASSERT(updated);
372 }
373 }
374 return fAAStrokeRectIndexBuffer;
375}
376
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000377void GrAARectRenderer::geometryFillAARect(GrGpu* gpu,
378 GrDrawTarget* target,
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000379 const GrRect& rect,
380 const SkMatrix& combinedMatrix,
robertphillips@google.comafd1cba2013-05-14 19:47:47 +0000381 const GrRect& devRect,
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000382 bool useVertexCoverage) {
jvanverth@google.com9b855c72013-03-01 18:21:22 +0000383 GrDrawState* drawState = target->drawState();
384
robertphillips@google.com42903302013-04-20 12:26:07 +0000385 set_aa_rect_vertex_attributes(drawState, useVertexCoverage);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000386
jvanverth@google.comb75b0a02013-02-05 20:33:30 +0000387 GrDrawTarget::AutoReleaseGeometry geo(target, 8, 0);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000388 if (!geo.succeeded()) {
389 GrPrintf("Failed to get space for vertices!\n");
390 return;
391 }
robertphillips@google.com6d067302012-12-18 21:47:47 +0000392
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000393 GrIndexBuffer* indexBuffer = this->aaFillRectIndexBuffer(gpu);
394 if (NULL == indexBuffer) {
395 GrPrintf("Failed to create index buffer!\n");
396 return;
397 }
398
399 intptr_t verts = reinterpret_cast<intptr_t>(geo.vertices());
jvanverth@google.com9b855c72013-03-01 18:21:22 +0000400 size_t vsize = drawState->getVertexSize();
401 GrAssert(sizeof(GrPoint) + sizeof(GrColor) == vsize);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000402
403 GrPoint* fan0Pos = reinterpret_cast<GrPoint*>(verts);
404 GrPoint* fan1Pos = reinterpret_cast<GrPoint*>(verts + 4 * vsize);
405
robertphillips@google.com908aed82013-05-28 13:16:20 +0000406 SkScalar inset = SkMinScalar(devRect.width(), SK_Scalar1);
407 inset = SK_ScalarHalf * SkMinScalar(inset, devRect.height());
408
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000409 if (combinedMatrix.rectStaysRect()) {
robertphillips@google.comafd1cba2013-05-14 19:47:47 +0000410 // Temporarily #if'ed out. We don't want to pass in the devRect but
411 // right now it is computed in GrContext::apply_aa_to_rect and we don't
412 // want to throw away the work
413#if 0
robertphillips@google.com91b71162013-05-10 14:09:54 +0000414 SkRect devRect;
415 combinedMatrix.mapRect(&devRect, rect);
robertphillips@google.comafd1cba2013-05-14 19:47:47 +0000416#endif
robertphillips@google.com91b71162013-05-10 14:09:54 +0000417
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000418 set_inset_fan(fan0Pos, vsize, devRect, -SK_ScalarHalf, -SK_ScalarHalf);
robertphillips@google.com908aed82013-05-28 13:16:20 +0000419 set_inset_fan(fan1Pos, vsize, devRect, inset, inset);
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000420 } else {
421 // compute transformed (1, 0) and (0, 1) vectors
422 SkVector vec[2] = {
423 { combinedMatrix[SkMatrix::kMScaleX], combinedMatrix[SkMatrix::kMSkewY] },
424 { combinedMatrix[SkMatrix::kMSkewX], combinedMatrix[SkMatrix::kMScaleY] }
425 };
426
427 vec[0].normalize();
428 vec[0].scale(SK_ScalarHalf);
429 vec[1].normalize();
430 vec[1].scale(SK_ScalarHalf);
431
robertphillips@google.com91b71162013-05-10 14:09:54 +0000432 // create the rotated rect
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000433 fan0Pos->setRectFan(rect.fLeft, rect.fTop,
434 rect.fRight, rect.fBottom, vsize);
435 combinedMatrix.mapPointsWithStride(fan0Pos, vsize, 4);
436
robertphillips@google.com91b71162013-05-10 14:09:54 +0000437 // Now create the inset points and then outset the original
438 // rotated points
439
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000440 // TL
skia.committer@gmail.com2fd42c42013-05-03 07:01:00 +0000441 *((SkPoint*)((intptr_t)fan1Pos + 0 * vsize)) =
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000442 *((SkPoint*)((intptr_t)fan0Pos + 0 * vsize)) + vec[0] + vec[1];
443 *((SkPoint*)((intptr_t)fan0Pos + 0 * vsize)) -= vec[0] + vec[1];
444 // BL
skia.committer@gmail.com2fd42c42013-05-03 07:01:00 +0000445 *((SkPoint*)((intptr_t)fan1Pos + 1 * vsize)) =
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000446 *((SkPoint*)((intptr_t)fan0Pos + 1 * vsize)) + vec[0] - vec[1];
447 *((SkPoint*)((intptr_t)fan0Pos + 1 * vsize)) -= vec[0] - vec[1];
448 // BR
skia.committer@gmail.com2fd42c42013-05-03 07:01:00 +0000449 *((SkPoint*)((intptr_t)fan1Pos + 2 * vsize)) =
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000450 *((SkPoint*)((intptr_t)fan0Pos + 2 * vsize)) - vec[0] - vec[1];
451 *((SkPoint*)((intptr_t)fan0Pos + 2 * vsize)) += vec[0] + vec[1];
452 // TR
skia.committer@gmail.com2fd42c42013-05-03 07:01:00 +0000453 *((SkPoint*)((intptr_t)fan1Pos + 3 * vsize)) =
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000454 *((SkPoint*)((intptr_t)fan0Pos + 3 * vsize)) - vec[0] + vec[1];
455 *((SkPoint*)((intptr_t)fan0Pos + 3 * vsize)) += vec[0] - vec[1];
456 }
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000457
458 verts += sizeof(GrPoint);
459 for (int i = 0; i < 4; ++i) {
460 *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
461 }
462
robertphillips@google.com908aed82013-05-28 13:16:20 +0000463 int scale;
464 if (inset < SK_ScalarHalf) {
465 scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
466 SkASSERT(scale >= 0 && scale <= 255);
467 } else {
468 scale = 0xff;
469 }
470
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000471 GrColor innerColor;
skia.committer@gmail.com2fd42c42013-05-03 07:01:00 +0000472 if (useVertexCoverage) {
robertphillips@google.com353f0972013-06-28 17:57:06 +0000473 innerColor = GrColorPackRGBA(scale, scale, scale, scale);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000474 } else {
robertphillips@google.com353f0972013-06-28 17:57:06 +0000475 innerColor = SkAlphaMulQ(target->getDrawState().getColor(), scale);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000476 }
477
478 verts += 4 * vsize;
479 for (int i = 0; i < 4; ++i) {
480 *reinterpret_cast<GrColor*>(verts + i * vsize) = innerColor;
481 }
482
483 target->setIndexSourceToBuffer(indexBuffer);
robertphillips@google.com6d067302012-12-18 21:47:47 +0000484 target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1,
485 kVertsPerAAFillRect,
486 kIndicesPerAAFillRect);
bsalomon@google.com0406b9e2013-04-02 21:00:15 +0000487 target->resetIndexSource();
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000488}
489
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000490namespace {
491
492// Rotated
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000493struct RectVertex {
494 GrPoint fPos;
495 GrPoint fCenter;
496 GrPoint fDir;
497 GrPoint fWidthHeight;
498};
499
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000500// Rotated
robertphillips@google.com42903302013-04-20 12:26:07 +0000501extern const GrVertexAttrib gAARectVertexAttribs[] = {
502 { kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding },
503 { kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding },
504 { kVec2f_GrVertexAttribType, 3*sizeof(GrPoint), kEffect_GrVertexAttribBinding }
505};
506
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000507// Axis Aligned
508struct AARectVertex {
509 GrPoint fPos;
510 GrPoint fOffset;
511 GrPoint fWidthHeight;
512};
513
514// Axis Aligned
515extern const GrVertexAttrib gAAAARectVertexAttribs[] = {
516 { kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding },
517 { kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding },
518};
519
robertphillips@google.com42903302013-04-20 12:26:07 +0000520};
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000521
522void GrAARectRenderer::shaderFillAARect(GrGpu* gpu,
523 GrDrawTarget* target,
524 const GrRect& rect,
robertphillips@google.com114eb9e2013-05-10 13:16:13 +0000525 const SkMatrix& combinedMatrix) {
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000526 GrDrawState* drawState = target->drawState();
527
528 SkPoint center = SkPoint::Make(rect.centerX(), rect.centerY());
529 combinedMatrix.mapPoints(&center, 1);
530
531 // compute transformed (0, 1) vector
532 SkVector dir = { combinedMatrix[SkMatrix::kMSkewX], combinedMatrix[SkMatrix::kMScaleY] };
533 dir.normalize();
534
535 // compute transformed (width, 0) and (0, height) vectors
536 SkVector vec[2] = {
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000537 { combinedMatrix[SkMatrix::kMScaleX], combinedMatrix[SkMatrix::kMSkewY] },
538 { combinedMatrix[SkMatrix::kMSkewX], combinedMatrix[SkMatrix::kMScaleY] }
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000539 };
540
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000541 SkScalar newWidth = SkScalarHalf(rect.width() * vec[0].length()) + SK_ScalarHalf;
542 SkScalar newHeight = SkScalarHalf(rect.height() * vec[1].length()) + SK_ScalarHalf;
robertphillips@google.com42903302013-04-20 12:26:07 +0000543 drawState->setVertexAttribs<gAARectVertexAttribs>(SK_ARRAY_COUNT(gAARectVertexAttribs));
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000544 GrAssert(sizeof(RectVertex) == drawState->getVertexSize());
545
546 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
547 if (!geo.succeeded()) {
548 GrPrintf("Failed to get space for vertices!\n");
549 return;
550 }
551
552 RectVertex* verts = reinterpret_cast<RectVertex*>(geo.vertices());
553
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000554 GrEffectRef* effect = GrRectEffect::Create();
555 static const int kRectAttrIndex = 1;
556 static const int kWidthIndex = 2;
bsalomon@google.comeb6879f2013-06-13 19:34:18 +0000557 drawState->addCoverageEffect(effect, kRectAttrIndex, kWidthIndex)->unref();
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000558
559 for (int i = 0; i < 4; ++i) {
560 verts[i].fCenter = center;
561 verts[i].fDir = dir;
562 verts[i].fWidthHeight.fX = newWidth;
563 verts[i].fWidthHeight.fY = newHeight;
564 }
565
robertphillips@google.com114eb9e2013-05-10 13:16:13 +0000566 SkRect devRect;
567 combinedMatrix.mapRect(&devRect, rect);
568
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000569 SkRect devBounds = {
570 devRect.fLeft - SK_ScalarHalf,
571 devRect.fTop - SK_ScalarHalf,
572 devRect.fRight + SK_ScalarHalf,
573 devRect.fBottom + SK_ScalarHalf
574 };
575
576 verts[0].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fTop);
577 verts[1].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fBottom);
578 verts[2].fPos = SkPoint::Make(devBounds.fRight, devBounds.fBottom);
579 verts[3].fPos = SkPoint::Make(devBounds.fRight, devBounds.fTop);
580
581 target->setIndexSourceToBuffer(gpu->getContext()->getQuadIndexBuffer());
582 target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 4, 6);
583 target->resetIndexSource();
584}
585
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000586void GrAARectRenderer::shaderFillAlignedAARect(GrGpu* gpu,
587 GrDrawTarget* target,
588 const GrRect& rect,
robertphillips@google.com114eb9e2013-05-10 13:16:13 +0000589 const SkMatrix& combinedMatrix) {
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000590 GrDrawState* drawState = target->drawState();
591 SkASSERT(combinedMatrix.rectStaysRect());
592
593 drawState->setVertexAttribs<gAAAARectVertexAttribs>(SK_ARRAY_COUNT(gAAAARectVertexAttribs));
594 GrAssert(sizeof(AARectVertex) == drawState->getVertexSize());
595
596 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
597 if (!geo.succeeded()) {
598 GrPrintf("Failed to get space for vertices!\n");
599 return;
600 }
601
602 AARectVertex* verts = reinterpret_cast<AARectVertex*>(geo.vertices());
603
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000604 GrEffectRef* effect = GrAlignedRectEffect::Create();
605 static const int kOffsetIndex = 1;
bsalomon@google.comeb6879f2013-06-13 19:34:18 +0000606 drawState->addCoverageEffect(effect, kOffsetIndex)->unref();
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000607
robertphillips@google.com114eb9e2013-05-10 13:16:13 +0000608 SkRect devRect;
609 combinedMatrix.mapRect(&devRect, rect);
610
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000611 SkRect devBounds = {
612 devRect.fLeft - SK_ScalarHalf,
613 devRect.fTop - SK_ScalarHalf,
614 devRect.fRight + SK_ScalarHalf,
615 devRect.fBottom + SK_ScalarHalf
616 };
617
618 GrPoint widthHeight = {
619 SkScalarHalf(devRect.width()) + SK_ScalarHalf,
620 SkScalarHalf(devRect.height()) + SK_ScalarHalf
621 };
622
623 verts[0].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fTop);
624 verts[0].fOffset = SkPoint::Make(-widthHeight.fX, -widthHeight.fY);
625 verts[0].fWidthHeight = widthHeight;
626
627 verts[1].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fBottom);
628 verts[1].fOffset = SkPoint::Make(-widthHeight.fX, widthHeight.fY);
629 verts[1].fWidthHeight = widthHeight;
630
631 verts[2].fPos = SkPoint::Make(devBounds.fRight, devBounds.fBottom);
632 verts[2].fOffset = widthHeight;
633 verts[2].fWidthHeight = widthHeight;
634
635 verts[3].fPos = SkPoint::Make(devBounds.fRight, devBounds.fTop);
636 verts[3].fOffset = SkPoint::Make(widthHeight.fX, -widthHeight.fY);
637 verts[3].fWidthHeight = widthHeight;
638
639 target->setIndexSourceToBuffer(gpu->getContext()->getQuadIndexBuffer());
640 target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 4, 6);
641 target->resetIndexSource();
642}
643
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000644void GrAARectRenderer::strokeAARect(GrGpu* gpu,
bsalomon@google.come7249bd2012-08-16 15:28:54 +0000645 GrDrawTarget* target,
robertphillips@google.com18136d12013-05-10 11:05:58 +0000646 const GrRect& rect,
647 const SkMatrix& combinedMatrix,
robertphillips@google.comafd1cba2013-05-14 19:47:47 +0000648 const GrRect& devRect,
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000649 SkScalar width,
bsalomon@google.come7249bd2012-08-16 15:28:54 +0000650 bool useVertexCoverage) {
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000651 GrVec devStrokeSize;
652 if (width > 0) {
653 devStrokeSize.set(width, width);
654 combinedMatrix.mapVectors(&devStrokeSize, 1);
655 devStrokeSize.setAbs(devStrokeSize);
656 } else {
657 devStrokeSize.set(SK_Scalar1, SK_Scalar1);
658 }
jvanverth@google.com9b855c72013-03-01 18:21:22 +0000659
robertphillips@google.com18136d12013-05-10 11:05:58 +0000660 const SkScalar dx = devStrokeSize.fX;
661 const SkScalar dy = devStrokeSize.fY;
bsalomon@google.com81712882012-11-01 17:12:34 +0000662 const SkScalar rx = SkScalarMul(dx, SK_ScalarHalf);
663 const SkScalar ry = SkScalarMul(dy, SK_ScalarHalf);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000664
robertphillips@google.comafd1cba2013-05-14 19:47:47 +0000665 // Temporarily #if'ed out. We don't want to pass in the devRect but
666 // right now it is computed in GrContext::apply_aa_to_rect and we don't
667 // want to throw away the work
668#if 0
robertphillips@google.com18136d12013-05-10 11:05:58 +0000669 SkRect devRect;
670 combinedMatrix.mapRect(&devRect, rect);
robertphillips@google.comafd1cba2013-05-14 19:47:47 +0000671#endif
robertphillips@google.com18136d12013-05-10 11:05:58 +0000672
bsalomon@google.com81712882012-11-01 17:12:34 +0000673 SkScalar spare;
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000674 {
bsalomon@google.com81712882012-11-01 17:12:34 +0000675 SkScalar w = devRect.width() - dx;
676 SkScalar h = devRect.height() - dy;
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000677 spare = GrMin(w, h);
678 }
679
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000680 GrRect devOutside(devRect);
681 devOutside.outset(rx, ry);
682
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000683 if (spare <= 0) {
skia.committer@gmail.com845220b2013-05-20 11:51:35 +0000684 this->fillAARect(gpu, target, devOutside, SkMatrix::I(),
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000685 devOutside, useVertexCoverage);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000686 return;
687 }
skia.committer@gmail.comf140f182013-03-02 07:01:56 +0000688
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000689 SkRect devInside(devRect);
690 devInside.inset(rx, ry);
691
692 this->geometryStrokeAARect(gpu, target, devOutside, devInside, useVertexCoverage);
693}
694
695void GrAARectRenderer::geometryStrokeAARect(GrGpu* gpu,
696 GrDrawTarget* target,
697 const SkRect& devOutside,
698 const SkRect& devInside,
699 bool useVertexCoverage) {
700 GrDrawState* drawState = target->drawState();
701
robertphillips@google.com42903302013-04-20 12:26:07 +0000702 set_aa_rect_vertex_attributes(drawState, useVertexCoverage);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000703
jvanverth@google.comb75b0a02013-02-05 20:33:30 +0000704 GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000705 if (!geo.succeeded()) {
706 GrPrintf("Failed to get space for vertices!\n");
707 return;
708 }
709 GrIndexBuffer* indexBuffer = this->aaStrokeRectIndexBuffer(gpu);
710 if (NULL == indexBuffer) {
711 GrPrintf("Failed to create index buffer!\n");
712 return;
713 }
714
715 intptr_t verts = reinterpret_cast<intptr_t>(geo.vertices());
jvanverth@google.com9b855c72013-03-01 18:21:22 +0000716 size_t vsize = drawState->getVertexSize();
717 GrAssert(sizeof(GrPoint) + sizeof(GrColor) == vsize);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000718
bsalomon@google.come7249bd2012-08-16 15:28:54 +0000719 // We create vertices for four nested rectangles. There are two ramps from 0 to full
720 // coverage, one on the exterior of the stroke and the other on the interior.
721 // The following pointers refer to the four rects, from outermost to innermost.
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000722 GrPoint* fan0Pos = reinterpret_cast<GrPoint*>(verts);
723 GrPoint* fan1Pos = reinterpret_cast<GrPoint*>(verts + 4 * vsize);
724 GrPoint* fan2Pos = reinterpret_cast<GrPoint*>(verts + 8 * vsize);
725 GrPoint* fan3Pos = reinterpret_cast<GrPoint*>(verts + 12 * vsize);
726
robertphillips@google.com353f0972013-06-28 17:57:06 +0000727 // TODO: this only really works if the X & Y margins are the same all around
728 // the rect
729 SkScalar inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight);
730 inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft);
731 inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop);
732 inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom);
733 SkASSERT(inset >= 0);
734
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000735 // outermost
736 set_inset_fan(fan0Pos, vsize, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
robertphillips@google.com353f0972013-06-28 17:57:06 +0000737 // inner two
738 set_inset_fan(fan1Pos, vsize, devOutside, inset, inset);
739 set_inset_fan(fan2Pos, vsize, devInside, -inset, -inset);
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000740 // innermost
741 set_inset_fan(fan3Pos, vsize, devInside, SK_ScalarHalf, SK_ScalarHalf);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000742
bsalomon@google.come7249bd2012-08-16 15:28:54 +0000743 // The outermost rect has 0 coverage
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000744 verts += sizeof(GrPoint);
745 for (int i = 0; i < 4; ++i) {
746 *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
747 }
748
robertphillips@google.com353f0972013-06-28 17:57:06 +0000749 int scale;
750 if (inset < SK_ScalarHalf) {
751 scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
752 SkASSERT(scale >= 0 && scale <= 255);
753 } else {
754 scale = 0xff;
755 }
756
bsalomon@google.come7249bd2012-08-16 15:28:54 +0000757 // The inner two rects have full coverage
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000758 GrColor innerColor;
759 if (useVertexCoverage) {
robertphillips@google.com353f0972013-06-28 17:57:06 +0000760 innerColor = GrColorPackRGBA(scale, scale, scale, scale);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000761 } else {
robertphillips@google.com353f0972013-06-28 17:57:06 +0000762 innerColor = SkAlphaMulQ(target->getDrawState().getColor(), scale);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000763 }
robertphillips@google.com353f0972013-06-28 17:57:06 +0000764
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000765 verts += 4 * vsize;
766 for (int i = 0; i < 8; ++i) {
767 *reinterpret_cast<GrColor*>(verts + i * vsize) = innerColor;
768 }
769
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000770 // The innermost rect has 0 coverage
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000771 verts += 8 * vsize;
bsalomon@google.come7249bd2012-08-16 15:28:54 +0000772 for (int i = 0; i < 4; ++i) {
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000773 *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
774 }
775
776 target->setIndexSourceToBuffer(indexBuffer);
777 target->drawIndexed(kTriangles_GrPrimitiveType,
778 0, 0, 16, aaStrokeRectIndexCount());
779}
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000780
781void GrAARectRenderer::fillAANestedRects(GrGpu* gpu,
782 GrDrawTarget* target,
783 const SkRect rects[2],
784 const SkMatrix& combinedMatrix,
785 bool useVertexCoverage) {
786 SkASSERT(combinedMatrix.rectStaysRect());
787 SkASSERT(!rects[1].isEmpty());
788
789 SkRect devOutside, devInside;
790 combinedMatrix.mapRect(&devOutside, rects[0]);
791 // can't call mapRect for devInside since it calls sort
792 combinedMatrix.mapPoints((SkPoint*)&devInside, (const SkPoint*)&rects[1], 2);
793
794 if (devInside.isEmpty()) {
795 this->fillAARect(gpu, target, devOutside, SkMatrix::I(), devOutside, useVertexCoverage);
796 return;
797 }
798
799 this->geometryStrokeAARect(gpu, target, devOutside, devInside, useVertexCoverage);
800}