blob: 2c21f09f00043c1cc139b1b3533fddcfe2b2c4e5 [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"
robertphillips@google.comf6747b02012-06-12 00:32:28 +00009#include "GrGpu.h"
robertphillips@google.comdf3695e2013-04-09 14:01:44 +000010#include "gl/GrGLEffect.h"
commit-bot@chromium.org261dc562013-10-04 15:42:56 +000011#include "gl/GrGLVertexEffect.h"
robertphillips@google.comdf3695e2013-04-09 14:01:44 +000012#include "GrTBackendEffectFactory.h"
robertphillips@google.com908aed82013-05-28 13:16:20 +000013#include "SkColorPriv.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000014#include "effects/GrVertexEffect.h"
robertphillips@google.comf6747b02012-06-12 00:32:28 +000015
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +000016///////////////////////////////////////////////////////////////////////////////
17class GrGLAlignedRectEffect;
18
19// Axis Aligned special case
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000020class GrAlignedRectEffect : public GrVertexEffect {
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +000021public:
22 static GrEffectRef* Create() {
23 GR_CREATE_STATIC_EFFECT(gAlignedRectEffect, GrAlignedRectEffect, ());
24 gAlignedRectEffect->ref();
25 return gAlignedRectEffect;
26 }
27
28 virtual ~GrAlignedRectEffect() {}
29
30 static const char* Name() { return "AlignedRectEdge"; }
31
32 virtual void getConstantColorComponents(GrColor* color,
33 uint32_t* validFlags) const SK_OVERRIDE {
34 *validFlags = 0;
35 }
36
37 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
38 return GrTBackendEffectFactory<GrAlignedRectEffect>::getInstance();
39 }
40
commit-bot@chromium.org261dc562013-10-04 15:42:56 +000041 class GLEffect : public GrGLVertexEffect {
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +000042 public:
43 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
44 : INHERITED (factory) {}
45
commit-bot@chromium.org261dc562013-10-04 15:42:56 +000046 virtual void emitCode(GrGLFullShaderBuilder* builder,
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +000047 const GrDrawEffect& drawEffect,
48 EffectKey key,
49 const char* outputColor,
50 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +000051 const TransformedCoordsArray&,
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +000052 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;
commit-bot@chromium.org261dc562013-10-04 15:42:56 +000057 builder->addVarying(kVec4f_GrSLType, "Rect", &vsRectName, &fsRectName);
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +000058 const SkString* attr0Name =
commit-bot@chromium.org261dc562013-10-04 15:42:56 +000059 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
60 builder->vsCodeAppendf("\t%s = %s;\n", vsRectName, attr0Name->c_str());
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +000061
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
commit-bot@chromium.org824c3462013-10-10 06:30:18 +000085
86 builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
commit-bot@chromium.orga34995e2013-10-23 05:42:03 +000087 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("coverage")).c_str());
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +000088 }
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:
commit-bot@chromium.org261dc562013-10-04 15:42:56 +000097 typedef GrGLVertexEffect INHERITED;
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +000098 };
99
100
101private:
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +0000102 GrAlignedRectEffect() : GrVertexEffect() {
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000103 this->addVertexAttrib(kVec4f_GrSLType);
104 }
105
106 virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE { return true; }
107
108 GR_DECLARE_EFFECT_TEST;
109
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +0000110 typedef GrVertexEffect INHERITED;
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000111};
112
113
114GR_DEFINE_EFFECT_TEST(GrAlignedRectEffect);
115
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000116GrEffectRef* GrAlignedRectEffect::TestCreate(SkRandom* random,
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000117 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 */
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +0000138class GrRectEffect : public GrVertexEffect {
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000139public:
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
commit-bot@chromium.org261dc562013-10-04 15:42:56 +0000159 class GLEffect : public GrGLVertexEffect {
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000160 public:
161 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
162 : INHERITED (factory) {}
163
commit-bot@chromium.org261dc562013-10-04 15:42:56 +0000164 virtual void emitCode(GrGLFullShaderBuilder* builder,
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000165 const GrDrawEffect& drawEffect,
166 EffectKey key,
167 const char* outputColor,
168 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000169 const TransformedCoordsArray&,
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000170 const TextureSamplerArray& samplers) SK_OVERRIDE {
171 // setup the varying for the center point and the unit vector
172 // that points down the height of the rect
173 const char *vsRectEdgeName, *fsRectEdgeName;
commit-bot@chromium.org261dc562013-10-04 15:42:56 +0000174 builder->addVarying(kVec4f_GrSLType, "RectEdge",
175 &vsRectEdgeName, &fsRectEdgeName);
skia.committer@gmail.com07d3a652013-04-10 07:01:15 +0000176 const SkString* attr0Name =
commit-bot@chromium.org261dc562013-10-04 15:42:56 +0000177 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
178 builder->vsCodeAppendf("\t%s = %s;\n", vsRectEdgeName, attr0Name->c_str());
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000179
180 // setup the varying for width/2+.5 and height/2+.5
181 const char *vsWidthHeightName, *fsWidthHeightName;
commit-bot@chromium.org261dc562013-10-04 15:42:56 +0000182 builder->addVarying(kVec2f_GrSLType, "WidthHeight",
183 &vsWidthHeightName, &fsWidthHeightName);
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000184 const SkString* attr1Name =
commit-bot@chromium.org261dc562013-10-04 15:42:56 +0000185 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]);
186 builder->vsCodeAppendf("\t%s = %s;\n", vsWidthHeightName, attr1Name->c_str());
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000187
egdaniel@google.comf1d7de72013-06-14 19:25:53 +0000188 // TODO: compute all these offsets, spans, and scales in the VS
189 builder->fsCodeAppendf("\tfloat insetW = min(1.0, %s.x) - 0.5;\n", fsWidthHeightName);
190 builder->fsCodeAppendf("\tfloat insetH = min(1.0, %s.y) - 0.5;\n", fsWidthHeightName);
191 builder->fsCodeAppend("\tfloat outset = 0.5;\n");
192 // For rects > 1 pixel wide and tall the span's are noops (i.e., 1.0). For rects
193 // < 1 pixel wide or tall they serve to normalize the < 1 ramp to a 0 .. 1 range.
194 builder->fsCodeAppend("\tfloat spanW = insetW + outset;\n");
195 builder->fsCodeAppend("\tfloat spanH = insetH + outset;\n");
196 // For rects < 1 pixel wide or tall, these scale factors are used to cap the maximum
197 // value of coverage that is used. In other words it is the coverage that is
198 // used in the interior of the rect after the ramp.
199 builder->fsCodeAppend("\tfloat scaleW = min(1.0, 2.0*insetW/spanW);\n");
200 builder->fsCodeAppend("\tfloat scaleH = min(1.0, 2.0*insetH/spanH);\n");
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000201
202 // Compute the coverage for the rect's width
skia.committer@gmail.com07d3a652013-04-10 07:01:15 +0000203 builder->fsCodeAppendf("\tvec2 offset = %s.xy - %s.xy;\n",
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000204 builder->fragmentPosition(), fsRectEdgeName);
205 builder->fsCodeAppendf("\tfloat perpDot = abs(offset.x * %s.w - offset.y * %s.z);\n",
206 fsRectEdgeName, fsRectEdgeName);
egdaniel@google.comf1d7de72013-06-14 19:25:53 +0000207 builder->fsCodeAppendf(
208 "\tfloat coverage = scaleW*clamp((%s.x-perpDot)/spanW, 0.0, 1.0);\n",
209 fsWidthHeightName);
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000210
211 // Compute the coverage for the rect's height and merge with the width
212 builder->fsCodeAppendf("\tperpDot = abs(dot(offset, %s.zw));\n",
213 fsRectEdgeName);
214 builder->fsCodeAppendf(
egdaniel@google.comf1d7de72013-06-14 19:25:53 +0000215 "\tcoverage = coverage*scaleH*clamp((%s.y-perpDot)/spanH, 0.0, 1.0);\n",
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000216 fsWidthHeightName);
217
commit-bot@chromium.org824c3462013-10-10 06:30:18 +0000218
219 builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
commit-bot@chromium.orga34995e2013-10-23 05:42:03 +0000220 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("coverage")).c_str());
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000221 }
222
223 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
224 return 0;
225 }
226
227 virtual void setData(const GrGLUniformManager& uman, const GrDrawEffect&) SK_OVERRIDE {}
228
229 private:
commit-bot@chromium.org261dc562013-10-04 15:42:56 +0000230 typedef GrGLVertexEffect INHERITED;
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000231 };
232
233
234private:
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +0000235 GrRectEffect() : GrVertexEffect() {
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000236 this->addVertexAttrib(kVec4f_GrSLType);
237 this->addVertexAttrib(kVec2f_GrSLType);
commit-bot@chromium.org8d47ddc2013-05-09 14:55:46 +0000238 this->setWillReadFragmentPosition();
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000239 }
240
241 virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE { return true; }
242
243 GR_DECLARE_EFFECT_TEST;
244
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +0000245 typedef GrVertexEffect INHERITED;
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000246};
247
248
249GR_DEFINE_EFFECT_TEST(GrRectEffect);
250
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000251GrEffectRef* GrRectEffect::TestCreate(SkRandom* random,
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000252 GrContext* context,
253 const GrDrawTargetCaps&,
254 GrTexture* textures[]) {
255 return GrRectEffect::Create();
256}
257
258///////////////////////////////////////////////////////////////////////////////
259
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000260namespace {
261
robertphillips@google.com42903302013-04-20 12:26:07 +0000262extern const GrVertexAttrib gAARectCoverageAttribs[] = {
263 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
264 {kVec4ub_GrVertexAttribType, sizeof(GrPoint), kCoverage_GrVertexAttribBinding},
265};
266
267extern const GrVertexAttrib gAARectColorAttribs[] = {
268 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
269 {kVec4ub_GrVertexAttribType, sizeof(GrPoint), kColor_GrVertexAttribBinding},
270};
271
272static void set_aa_rect_vertex_attributes(GrDrawState* drawState, bool useCoverage) {
273 if (useCoverage) {
274 drawState->setVertexAttribs<gAARectCoverageAttribs>(SK_ARRAY_COUNT(gAARectCoverageAttribs));
275 } else {
276 drawState->setVertexAttribs<gAARectColorAttribs>(SK_ARRAY_COUNT(gAARectColorAttribs));
277 }
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000278}
279
robertphillips@google.comca47aae2012-12-12 15:58:25 +0000280static void set_inset_fan(GrPoint* pts, size_t stride,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000281 const SkRect& r, SkScalar dx, SkScalar dy) {
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000282 pts->setRectFan(r.fLeft + dx, r.fTop + dy,
283 r.fRight - dx, r.fBottom - dy, stride);
284}
285
286};
287
288void GrAARectRenderer::reset() {
commit-bot@chromium.orga4de8c22013-09-09 13:38:37 +0000289 SkSafeSetNull(fAAFillRectIndexBuffer);
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000290 SkSafeSetNull(fAAMiterStrokeRectIndexBuffer);
291 SkSafeSetNull(fAABevelStrokeRectIndexBuffer);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000292}
293
robertphillips@google.com6d067302012-12-18 21:47:47 +0000294static const uint16_t gFillAARectIdx[] = {
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000295 0, 1, 5, 5, 4, 0,
296 1, 2, 6, 6, 5, 1,
297 2, 3, 7, 7, 6, 2,
298 3, 0, 4, 4, 7, 3,
299 4, 5, 6, 6, 7, 4,
300};
301
robertphillips@google.com6d067302012-12-18 21:47:47 +0000302static const int kIndicesPerAAFillRect = GR_ARRAY_COUNT(gFillAARectIdx);
303static const int kVertsPerAAFillRect = 8;
304static const int kNumAAFillRectsInIndexBuffer = 256;
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000305
306GrIndexBuffer* GrAARectRenderer::aaFillRectIndexBuffer(GrGpu* gpu) {
robertphillips@google.com6d067302012-12-18 21:47:47 +0000307 static const size_t kAAFillRectIndexBufferSize = kIndicesPerAAFillRect *
308 sizeof(uint16_t) *
309 kNumAAFillRectsInIndexBuffer;
310
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000311 if (NULL == fAAFillRectIndexBuffer) {
robertphillips@google.com6d067302012-12-18 21:47:47 +0000312 fAAFillRectIndexBuffer = gpu->createIndexBuffer(kAAFillRectIndexBufferSize, false);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000313 if (NULL != fAAFillRectIndexBuffer) {
robertphillips@google.com6d067302012-12-18 21:47:47 +0000314 uint16_t* data = (uint16_t*) fAAFillRectIndexBuffer->lock();
315 bool useTempData = (NULL == data);
316 if (useTempData) {
317 data = SkNEW_ARRAY(uint16_t, kNumAAFillRectsInIndexBuffer * kIndicesPerAAFillRect);
318 }
319 for (int i = 0; i < kNumAAFillRectsInIndexBuffer; ++i) {
320 // Each AA filled rect is drawn with 8 vertices and 10 triangles (8 around
321 // the inner rect (for AA) and 2 for the inner rect.
322 int baseIdx = i * kIndicesPerAAFillRect;
323 uint16_t baseVert = (uint16_t)(i * kVertsPerAAFillRect);
324 for (int j = 0; j < kIndicesPerAAFillRect; ++j) {
325 data[baseIdx+j] = baseVert + gFillAARectIdx[j];
326 }
327 }
328 if (useTempData) {
329 if (!fAAFillRectIndexBuffer->updateData(data, kAAFillRectIndexBufferSize)) {
330 GrCrash("Can't get AA Fill Rect indices into buffer!");
331 }
332 SkDELETE_ARRAY(data);
333 } else {
334 fAAFillRectIndexBuffer->unlock();
335 }
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000336 }
337 }
robertphillips@google.com6d067302012-12-18 21:47:47 +0000338
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000339 return fAAFillRectIndexBuffer;
340}
341
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000342static const uint16_t gMiterStrokeAARectIdx[] = {
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000343 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
344 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
345 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
346 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
347
348 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
349 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
350 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
351 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
352
353 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
354 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
355 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
356 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
357};
358
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000359/**
360 * As in miter-stroke, index = a + b, and a is the current index, b is the shift
361 * from the first index. The index layout:
362 * outer AA line: 0~3, 4~7
363 * outer edge: 8~11, 12~15
364 * inner edge: 16~19
365 * inner AA line: 20~23
366 * Following comes a bevel-stroke rect and its indices:
367 *
368 * 4 7
skia.committer@gmail.com26144182013-11-07 07:02:19 +0000369 * *********************************
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000370 * * ______________________________ *
371 * * / 12 15 \ *
372 * * / \ *
373 * 0 * |8 16_____________________19 11 | * 3
374 * * | | | | *
375 * * | | **************** | | *
376 * * | | * 20 23 * | | *
377 * * | | * * | | *
378 * * | | * 21 22 * | | *
379 * * | | **************** | | *
380 * * | |____________________| | *
381 * 1 * |9 17 18 10| * 2
skia.committer@gmail.com26144182013-11-07 07:02:19 +0000382 * * \ / *
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000383 * * \13 __________________________14/ *
384 * * *
skia.committer@gmail.com26144182013-11-07 07:02:19 +0000385 * **********************************
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000386 * 5 6
387 */
388static const uint16_t gBevelStrokeAARectIdx[] = {
389 // Draw outer AA, from outer AA line to outer edge, shift is 0.
390 0 + 0, 1 + 0, 9 + 0, 9 + 0, 8 + 0, 0 + 0,
391 1 + 0, 5 + 0, 13 + 0, 13 + 0, 9 + 0, 1 + 0,
392 5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
393 6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
394 2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
395 3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
396 7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
397 4 + 0, 0 + 0, 8 + 0, 8 + 0, 12 + 0, 4 + 0,
398
399 // Draw the stroke, from outer edge to inner edge, shift is 8.
400 0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
401 1 + 8, 5 + 8, 9 + 8,
402 5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
403 6 + 8, 2 + 8, 10 + 8,
404 2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
405 3 + 8, 7 + 8, 11 + 8,
406 7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
407 4 + 8, 0 + 8, 8 + 8,
408
409 // Draw the inner AA, from inner edge to inner AA line, shift is 16.
410 0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
411 1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
412 2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
413 3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
414};
415
416int GrAARectRenderer::aaStrokeRectIndexCount(bool miterStroke) {
417 return miterStroke ? GR_ARRAY_COUNT(gMiterStrokeAARectIdx) :
418 GR_ARRAY_COUNT(gBevelStrokeAARectIdx);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000419}
420
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000421GrIndexBuffer* GrAARectRenderer::aaStrokeRectIndexBuffer(GrGpu* gpu, bool miterStroke) {
422 if (miterStroke) {
423 if (NULL == fAAMiterStrokeRectIndexBuffer) {
424 fAAMiterStrokeRectIndexBuffer =
425 gpu->createIndexBuffer(sizeof(gMiterStrokeAARectIdx), false);
426 if (NULL != fAAMiterStrokeRectIndexBuffer) {
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +0000427#ifdef SK_DEBUG
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000428 bool updated =
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000429#endif
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000430 fAAMiterStrokeRectIndexBuffer->updateData(gMiterStrokeAARectIdx,
431 sizeof(gMiterStrokeAARectIdx));
432 GR_DEBUGASSERT(updated);
433 }
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000434 }
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000435 return fAAMiterStrokeRectIndexBuffer;
436 } else {
437 if (NULL == fAABevelStrokeRectIndexBuffer) {
438 fAABevelStrokeRectIndexBuffer =
439 gpu->createIndexBuffer(sizeof(gBevelStrokeAARectIdx), false);
440 if (NULL != fAABevelStrokeRectIndexBuffer) {
441#ifdef SK_DEBUG
442 bool updated =
443#endif
444 fAABevelStrokeRectIndexBuffer->updateData(gBevelStrokeAARectIdx,
445 sizeof(gBevelStrokeAARectIdx));
446 GR_DEBUGASSERT(updated);
447 }
448 }
449 return fAABevelStrokeRectIndexBuffer;
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000450 }
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000451}
452
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000453void GrAARectRenderer::geometryFillAARect(GrGpu* gpu,
454 GrDrawTarget* target,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000455 const SkRect& rect,
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000456 const SkMatrix& combinedMatrix,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000457 const SkRect& devRect,
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000458 bool useVertexCoverage) {
jvanverth@google.com9b855c72013-03-01 18:21:22 +0000459 GrDrawState* drawState = target->drawState();
460
robertphillips@google.com42903302013-04-20 12:26:07 +0000461 set_aa_rect_vertex_attributes(drawState, useVertexCoverage);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000462
jvanverth@google.comb75b0a02013-02-05 20:33:30 +0000463 GrDrawTarget::AutoReleaseGeometry geo(target, 8, 0);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000464 if (!geo.succeeded()) {
465 GrPrintf("Failed to get space for vertices!\n");
466 return;
467 }
robertphillips@google.com6d067302012-12-18 21:47:47 +0000468
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000469 GrIndexBuffer* indexBuffer = this->aaFillRectIndexBuffer(gpu);
470 if (NULL == indexBuffer) {
471 GrPrintf("Failed to create index buffer!\n");
472 return;
473 }
474
475 intptr_t verts = reinterpret_cast<intptr_t>(geo.vertices());
jvanverth@google.com9b855c72013-03-01 18:21:22 +0000476 size_t vsize = drawState->getVertexSize();
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000477 SkASSERT(sizeof(GrPoint) + sizeof(GrColor) == vsize);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000478
479 GrPoint* fan0Pos = reinterpret_cast<GrPoint*>(verts);
480 GrPoint* fan1Pos = reinterpret_cast<GrPoint*>(verts + 4 * vsize);
481
robertphillips@google.com908aed82013-05-28 13:16:20 +0000482 SkScalar inset = SkMinScalar(devRect.width(), SK_Scalar1);
483 inset = SK_ScalarHalf * SkMinScalar(inset, devRect.height());
484
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000485 if (combinedMatrix.rectStaysRect()) {
robertphillips@google.comafd1cba2013-05-14 19:47:47 +0000486 // Temporarily #if'ed out. We don't want to pass in the devRect but
487 // right now it is computed in GrContext::apply_aa_to_rect and we don't
488 // want to throw away the work
489#if 0
robertphillips@google.com91b71162013-05-10 14:09:54 +0000490 SkRect devRect;
491 combinedMatrix.mapRect(&devRect, rect);
robertphillips@google.comafd1cba2013-05-14 19:47:47 +0000492#endif
robertphillips@google.com91b71162013-05-10 14:09:54 +0000493
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000494 set_inset_fan(fan0Pos, vsize, devRect, -SK_ScalarHalf, -SK_ScalarHalf);
robertphillips@google.com908aed82013-05-28 13:16:20 +0000495 set_inset_fan(fan1Pos, vsize, devRect, inset, inset);
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000496 } else {
497 // compute transformed (1, 0) and (0, 1) vectors
498 SkVector vec[2] = {
499 { combinedMatrix[SkMatrix::kMScaleX], combinedMatrix[SkMatrix::kMSkewY] },
500 { combinedMatrix[SkMatrix::kMSkewX], combinedMatrix[SkMatrix::kMScaleY] }
501 };
502
503 vec[0].normalize();
504 vec[0].scale(SK_ScalarHalf);
505 vec[1].normalize();
506 vec[1].scale(SK_ScalarHalf);
507
robertphillips@google.com91b71162013-05-10 14:09:54 +0000508 // create the rotated rect
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000509 fan0Pos->setRectFan(rect.fLeft, rect.fTop,
510 rect.fRight, rect.fBottom, vsize);
511 combinedMatrix.mapPointsWithStride(fan0Pos, vsize, 4);
512
robertphillips@google.com91b71162013-05-10 14:09:54 +0000513 // Now create the inset points and then outset the original
514 // rotated points
515
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000516 // TL
skia.committer@gmail.com2fd42c42013-05-03 07:01:00 +0000517 *((SkPoint*)((intptr_t)fan1Pos + 0 * vsize)) =
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000518 *((SkPoint*)((intptr_t)fan0Pos + 0 * vsize)) + vec[0] + vec[1];
519 *((SkPoint*)((intptr_t)fan0Pos + 0 * vsize)) -= vec[0] + vec[1];
520 // BL
skia.committer@gmail.com2fd42c42013-05-03 07:01:00 +0000521 *((SkPoint*)((intptr_t)fan1Pos + 1 * vsize)) =
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000522 *((SkPoint*)((intptr_t)fan0Pos + 1 * vsize)) + vec[0] - vec[1];
523 *((SkPoint*)((intptr_t)fan0Pos + 1 * vsize)) -= vec[0] - vec[1];
524 // BR
skia.committer@gmail.com2fd42c42013-05-03 07:01:00 +0000525 *((SkPoint*)((intptr_t)fan1Pos + 2 * vsize)) =
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000526 *((SkPoint*)((intptr_t)fan0Pos + 2 * vsize)) - vec[0] - vec[1];
527 *((SkPoint*)((intptr_t)fan0Pos + 2 * vsize)) += vec[0] + vec[1];
528 // TR
skia.committer@gmail.com2fd42c42013-05-03 07:01:00 +0000529 *((SkPoint*)((intptr_t)fan1Pos + 3 * vsize)) =
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000530 *((SkPoint*)((intptr_t)fan0Pos + 3 * vsize)) - vec[0] + vec[1];
531 *((SkPoint*)((intptr_t)fan0Pos + 3 * vsize)) += vec[0] - vec[1];
532 }
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000533
534 verts += sizeof(GrPoint);
535 for (int i = 0; i < 4; ++i) {
536 *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
537 }
538
robertphillips@google.com908aed82013-05-28 13:16:20 +0000539 int scale;
540 if (inset < SK_ScalarHalf) {
541 scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
542 SkASSERT(scale >= 0 && scale <= 255);
543 } else {
544 scale = 0xff;
545 }
546
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000547 GrColor innerColor;
skia.committer@gmail.com2fd42c42013-05-03 07:01:00 +0000548 if (useVertexCoverage) {
robertphillips@google.com353f0972013-06-28 17:57:06 +0000549 innerColor = GrColorPackRGBA(scale, scale, scale, scale);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000550 } else {
robertphillips@google.comc111ce22013-07-01 13:10:10 +0000551 if (0xff == scale) {
skia.committer@gmail.com0d55dd72013-07-02 07:00:59 +0000552 innerColor = target->getDrawState().getColor();
553 } else {
554 innerColor = SkAlphaMulQ(target->getDrawState().getColor(), scale);
555 }
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000556 }
557
558 verts += 4 * vsize;
559 for (int i = 0; i < 4; ++i) {
560 *reinterpret_cast<GrColor*>(verts + i * vsize) = innerColor;
561 }
562
563 target->setIndexSourceToBuffer(indexBuffer);
robertphillips@google.com6d067302012-12-18 21:47:47 +0000564 target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1,
565 kVertsPerAAFillRect,
566 kIndicesPerAAFillRect);
bsalomon@google.com0406b9e2013-04-02 21:00:15 +0000567 target->resetIndexSource();
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000568}
569
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000570namespace {
571
572// Rotated
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000573struct RectVertex {
574 GrPoint fPos;
575 GrPoint fCenter;
576 GrPoint fDir;
577 GrPoint fWidthHeight;
578};
579
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000580// Rotated
robertphillips@google.com42903302013-04-20 12:26:07 +0000581extern const GrVertexAttrib gAARectVertexAttribs[] = {
582 { kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding },
583 { kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding },
584 { kVec2f_GrVertexAttribType, 3*sizeof(GrPoint), kEffect_GrVertexAttribBinding }
585};
586
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000587// Axis Aligned
588struct AARectVertex {
589 GrPoint fPos;
590 GrPoint fOffset;
591 GrPoint fWidthHeight;
592};
593
594// Axis Aligned
595extern const GrVertexAttrib gAAAARectVertexAttribs[] = {
596 { kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding },
597 { kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding },
598};
599
robertphillips@google.com42903302013-04-20 12:26:07 +0000600};
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000601
602void GrAARectRenderer::shaderFillAARect(GrGpu* gpu,
603 GrDrawTarget* target,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000604 const SkRect& rect,
robertphillips@google.com114eb9e2013-05-10 13:16:13 +0000605 const SkMatrix& combinedMatrix) {
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000606 GrDrawState* drawState = target->drawState();
607
608 SkPoint center = SkPoint::Make(rect.centerX(), rect.centerY());
609 combinedMatrix.mapPoints(&center, 1);
610
611 // compute transformed (0, 1) vector
612 SkVector dir = { combinedMatrix[SkMatrix::kMSkewX], combinedMatrix[SkMatrix::kMScaleY] };
613 dir.normalize();
614
615 // compute transformed (width, 0) and (0, height) vectors
616 SkVector vec[2] = {
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000617 { combinedMatrix[SkMatrix::kMScaleX], combinedMatrix[SkMatrix::kMSkewY] },
618 { combinedMatrix[SkMatrix::kMSkewX], combinedMatrix[SkMatrix::kMScaleY] }
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000619 };
620
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000621 SkScalar newWidth = SkScalarHalf(rect.width() * vec[0].length()) + SK_ScalarHalf;
622 SkScalar newHeight = SkScalarHalf(rect.height() * vec[1].length()) + SK_ScalarHalf;
robertphillips@google.com42903302013-04-20 12:26:07 +0000623 drawState->setVertexAttribs<gAARectVertexAttribs>(SK_ARRAY_COUNT(gAARectVertexAttribs));
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000624 SkASSERT(sizeof(RectVertex) == drawState->getVertexSize());
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000625
626 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
627 if (!geo.succeeded()) {
628 GrPrintf("Failed to get space for vertices!\n");
629 return;
630 }
631
632 RectVertex* verts = reinterpret_cast<RectVertex*>(geo.vertices());
633
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000634 GrEffectRef* effect = GrRectEffect::Create();
635 static const int kRectAttrIndex = 1;
636 static const int kWidthIndex = 2;
bsalomon@google.comeb6879f2013-06-13 19:34:18 +0000637 drawState->addCoverageEffect(effect, kRectAttrIndex, kWidthIndex)->unref();
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000638
639 for (int i = 0; i < 4; ++i) {
640 verts[i].fCenter = center;
641 verts[i].fDir = dir;
642 verts[i].fWidthHeight.fX = newWidth;
643 verts[i].fWidthHeight.fY = newHeight;
644 }
645
robertphillips@google.com114eb9e2013-05-10 13:16:13 +0000646 SkRect devRect;
647 combinedMatrix.mapRect(&devRect, rect);
648
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000649 SkRect devBounds = {
650 devRect.fLeft - SK_ScalarHalf,
651 devRect.fTop - SK_ScalarHalf,
652 devRect.fRight + SK_ScalarHalf,
653 devRect.fBottom + SK_ScalarHalf
654 };
655
656 verts[0].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fTop);
657 verts[1].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fBottom);
658 verts[2].fPos = SkPoint::Make(devBounds.fRight, devBounds.fBottom);
659 verts[3].fPos = SkPoint::Make(devBounds.fRight, devBounds.fTop);
660
661 target->setIndexSourceToBuffer(gpu->getContext()->getQuadIndexBuffer());
662 target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 4, 6);
663 target->resetIndexSource();
664}
665
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000666void GrAARectRenderer::shaderFillAlignedAARect(GrGpu* gpu,
667 GrDrawTarget* target,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000668 const SkRect& rect,
robertphillips@google.com114eb9e2013-05-10 13:16:13 +0000669 const SkMatrix& combinedMatrix) {
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000670 GrDrawState* drawState = target->drawState();
671 SkASSERT(combinedMatrix.rectStaysRect());
672
673 drawState->setVertexAttribs<gAAAARectVertexAttribs>(SK_ARRAY_COUNT(gAAAARectVertexAttribs));
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000674 SkASSERT(sizeof(AARectVertex) == drawState->getVertexSize());
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000675
676 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
677 if (!geo.succeeded()) {
678 GrPrintf("Failed to get space for vertices!\n");
679 return;
680 }
681
682 AARectVertex* verts = reinterpret_cast<AARectVertex*>(geo.vertices());
683
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000684 GrEffectRef* effect = GrAlignedRectEffect::Create();
685 static const int kOffsetIndex = 1;
bsalomon@google.comeb6879f2013-06-13 19:34:18 +0000686 drawState->addCoverageEffect(effect, kOffsetIndex)->unref();
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000687
robertphillips@google.com114eb9e2013-05-10 13:16:13 +0000688 SkRect devRect;
689 combinedMatrix.mapRect(&devRect, rect);
690
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000691 SkRect devBounds = {
692 devRect.fLeft - SK_ScalarHalf,
693 devRect.fTop - SK_ScalarHalf,
694 devRect.fRight + SK_ScalarHalf,
695 devRect.fBottom + SK_ScalarHalf
696 };
697
698 GrPoint widthHeight = {
699 SkScalarHalf(devRect.width()) + SK_ScalarHalf,
700 SkScalarHalf(devRect.height()) + SK_ScalarHalf
701 };
702
703 verts[0].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fTop);
704 verts[0].fOffset = SkPoint::Make(-widthHeight.fX, -widthHeight.fY);
705 verts[0].fWidthHeight = widthHeight;
706
707 verts[1].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fBottom);
708 verts[1].fOffset = SkPoint::Make(-widthHeight.fX, widthHeight.fY);
709 verts[1].fWidthHeight = widthHeight;
710
711 verts[2].fPos = SkPoint::Make(devBounds.fRight, devBounds.fBottom);
712 verts[2].fOffset = widthHeight;
713 verts[2].fWidthHeight = widthHeight;
714
715 verts[3].fPos = SkPoint::Make(devBounds.fRight, devBounds.fTop);
716 verts[3].fOffset = SkPoint::Make(widthHeight.fX, -widthHeight.fY);
717 verts[3].fWidthHeight = widthHeight;
718
719 target->setIndexSourceToBuffer(gpu->getContext()->getQuadIndexBuffer());
720 target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 4, 6);
721 target->resetIndexSource();
722}
723
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000724void GrAARectRenderer::strokeAARect(GrGpu* gpu,
bsalomon@google.come7249bd2012-08-16 15:28:54 +0000725 GrDrawTarget* target,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000726 const SkRect& rect,
robertphillips@google.com18136d12013-05-10 11:05:58 +0000727 const SkMatrix& combinedMatrix,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000728 const SkRect& devRect,
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000729 const SkStrokeRec* stroke,
bsalomon@google.come7249bd2012-08-16 15:28:54 +0000730 bool useVertexCoverage) {
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000731 GrVec devStrokeSize;
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000732 SkScalar width = stroke->getWidth();
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000733 if (width > 0) {
734 devStrokeSize.set(width, width);
735 combinedMatrix.mapVectors(&devStrokeSize, 1);
736 devStrokeSize.setAbs(devStrokeSize);
737 } else {
738 devStrokeSize.set(SK_Scalar1, SK_Scalar1);
739 }
jvanverth@google.com9b855c72013-03-01 18:21:22 +0000740
robertphillips@google.com18136d12013-05-10 11:05:58 +0000741 const SkScalar dx = devStrokeSize.fX;
742 const SkScalar dy = devStrokeSize.fY;
bsalomon@google.com81712882012-11-01 17:12:34 +0000743 const SkScalar rx = SkScalarMul(dx, SK_ScalarHalf);
744 const SkScalar ry = SkScalarMul(dy, SK_ScalarHalf);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000745
robertphillips@google.comafd1cba2013-05-14 19:47:47 +0000746 // Temporarily #if'ed out. We don't want to pass in the devRect but
747 // right now it is computed in GrContext::apply_aa_to_rect and we don't
748 // want to throw away the work
749#if 0
robertphillips@google.com18136d12013-05-10 11:05:58 +0000750 SkRect devRect;
751 combinedMatrix.mapRect(&devRect, rect);
robertphillips@google.comafd1cba2013-05-14 19:47:47 +0000752#endif
robertphillips@google.com18136d12013-05-10 11:05:58 +0000753
bsalomon@google.com81712882012-11-01 17:12:34 +0000754 SkScalar spare;
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000755 {
bsalomon@google.com81712882012-11-01 17:12:34 +0000756 SkScalar w = devRect.width() - dx;
757 SkScalar h = devRect.height() - dy;
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000758 spare = GrMin(w, h);
759 }
760
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000761 SkRect devOutside(devRect);
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000762 devOutside.outset(rx, ry);
763
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000764 bool miterStroke = true;
765 // small miter limit means right angles show bevel...
766 if (stroke->getJoin() != SkPaint::kMiter_Join || stroke->getMiter() < SK_ScalarSqrt2) {
767 miterStroke = false;
768 }
769
770 if (spare <= 0 && miterStroke) {
skia.committer@gmail.com845220b2013-05-20 11:51:35 +0000771 this->fillAARect(gpu, target, devOutside, SkMatrix::I(),
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000772 devOutside, useVertexCoverage);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000773 return;
774 }
skia.committer@gmail.comf140f182013-03-02 07:01:56 +0000775
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000776 SkRect devInside(devRect);
777 devInside.inset(rx, ry);
778
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000779 SkRect devOutsideAssist(devRect);
780
781 // For bevel-stroke, use 2 SkRect instances(devOutside and devOutsideAssist)
782 // to draw the outer of the rect. Because there are 8 vertices on the outer
skia.committer@gmail.com26144182013-11-07 07:02:19 +0000783 // edge, while vertex number of inner edge is 4, the same as miter-stroke.
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000784 if (!miterStroke) {
785 devOutside.inset(0, ry);
786 devOutsideAssist.outset(0, ry);
787 }
788
789 this->geometryStrokeAARect(gpu, target, devOutside, devOutsideAssist,
790 devInside, useVertexCoverage, miterStroke);
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000791}
792
793void GrAARectRenderer::geometryStrokeAARect(GrGpu* gpu,
794 GrDrawTarget* target,
795 const SkRect& devOutside,
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000796 const SkRect& devOutsideAssist,
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000797 const SkRect& devInside,
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000798 bool useVertexCoverage,
799 bool miterStroke) {
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000800 GrDrawState* drawState = target->drawState();
801
robertphillips@google.com42903302013-04-20 12:26:07 +0000802 set_aa_rect_vertex_attributes(drawState, useVertexCoverage);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000803
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000804 int innerVertexNum = 4;
805 int outerVertexNum = miterStroke ? 4 : 8;
806 int totalVertexNum = (outerVertexNum + innerVertexNum) * 2;
807
808 GrDrawTarget::AutoReleaseGeometry geo(target, totalVertexNum, 0);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000809 if (!geo.succeeded()) {
810 GrPrintf("Failed to get space for vertices!\n");
811 return;
812 }
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000813 GrIndexBuffer* indexBuffer = this->aaStrokeRectIndexBuffer(gpu, miterStroke);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000814 if (NULL == indexBuffer) {
815 GrPrintf("Failed to create index buffer!\n");
816 return;
817 }
818
819 intptr_t verts = reinterpret_cast<intptr_t>(geo.vertices());
jvanverth@google.com9b855c72013-03-01 18:21:22 +0000820 size_t vsize = drawState->getVertexSize();
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000821 SkASSERT(sizeof(GrPoint) + sizeof(GrColor) == vsize);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000822
bsalomon@google.come7249bd2012-08-16 15:28:54 +0000823 // We create vertices for four nested rectangles. There are two ramps from 0 to full
824 // coverage, one on the exterior of the stroke and the other on the interior.
825 // The following pointers refer to the four rects, from outermost to innermost.
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000826 GrPoint* fan0Pos = reinterpret_cast<GrPoint*>(verts);
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000827 GrPoint* fan1Pos = reinterpret_cast<GrPoint*>(verts + outerVertexNum * vsize);
828 GrPoint* fan2Pos = reinterpret_cast<GrPoint*>(verts + 2 * outerVertexNum * vsize);
829 GrPoint* fan3Pos = reinterpret_cast<GrPoint*>(verts + (2 * outerVertexNum + innerVertexNum) * vsize);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000830
robertphillips@google.comc111ce22013-07-01 13:10:10 +0000831#ifndef SK_IGNORE_THIN_STROKED_RECT_FIX
robertphillips@google.com353f0972013-06-28 17:57:06 +0000832 // TODO: this only really works if the X & Y margins are the same all around
833 // the rect
834 SkScalar inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight);
835 inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft);
836 inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop);
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000837 if (miterStroke) {
838 inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom);
839 } else {
840 inset = SK_ScalarHalf * SkMinScalar(inset, devOutsideAssist.fBottom - devInside.fBottom);
841 }
robertphillips@google.com353f0972013-06-28 17:57:06 +0000842 SkASSERT(inset >= 0);
robertphillips@google.comc111ce22013-07-01 13:10:10 +0000843#else
844 SkScalar inset = SK_ScalarHalf;
845#endif
robertphillips@google.com353f0972013-06-28 17:57:06 +0000846
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000847 if (miterStroke) {
848 // outermost
849 set_inset_fan(fan0Pos, vsize, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
850 // inner two
851 set_inset_fan(fan1Pos, vsize, devOutside, inset, inset);
852 set_inset_fan(fan2Pos, vsize, devInside, -inset, -inset);
853 // innermost
854 set_inset_fan(fan3Pos, vsize, devInside, SK_ScalarHalf, SK_ScalarHalf);
855 } else {
856 GrPoint* fan0AssistPos = reinterpret_cast<GrPoint*>(verts + 4 * vsize);
857 GrPoint* fan1AssistPos = reinterpret_cast<GrPoint*>(verts + (outerVertexNum + 4) * vsize);
858 // outermost
859 set_inset_fan(fan0Pos, vsize, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
860 set_inset_fan(fan0AssistPos, vsize, devOutsideAssist, -SK_ScalarHalf, -SK_ScalarHalf);
861 // outer one of the inner two
862 set_inset_fan(fan1Pos, vsize, devOutside, inset, inset);
863 set_inset_fan(fan1AssistPos, vsize, devOutsideAssist, inset, inset);
864 // inner one of the inner two
865 set_inset_fan(fan2Pos, vsize, devInside, -inset, -inset);
866 // innermost
867 set_inset_fan(fan3Pos, vsize, devInside, SK_ScalarHalf, SK_ScalarHalf);
868 }
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000869
bsalomon@google.come7249bd2012-08-16 15:28:54 +0000870 // The outermost rect has 0 coverage
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000871 verts += sizeof(GrPoint);
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000872 for (int i = 0; i < outerVertexNum; ++i) {
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000873 *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
874 }
875
robertphillips@google.com353f0972013-06-28 17:57:06 +0000876 int scale;
877 if (inset < SK_ScalarHalf) {
878 scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
879 SkASSERT(scale >= 0 && scale <= 255);
880 } else {
881 scale = 0xff;
882 }
883
bsalomon@google.come7249bd2012-08-16 15:28:54 +0000884 // The inner two rects have full coverage
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000885 GrColor innerColor;
886 if (useVertexCoverage) {
robertphillips@google.com353f0972013-06-28 17:57:06 +0000887 innerColor = GrColorPackRGBA(scale, scale, scale, scale);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000888 } else {
robertphillips@google.comc111ce22013-07-01 13:10:10 +0000889 if (0xff == scale) {
skia.committer@gmail.com0d55dd72013-07-02 07:00:59 +0000890 innerColor = target->getDrawState().getColor();
891 } else {
robertphillips@google.comc111ce22013-07-01 13:10:10 +0000892 innerColor = SkAlphaMulQ(target->getDrawState().getColor(), scale);
893 }
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000894 }
robertphillips@google.com353f0972013-06-28 17:57:06 +0000895
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000896 verts += outerVertexNum * vsize;
897 for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) {
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000898 *reinterpret_cast<GrColor*>(verts + i * vsize) = innerColor;
899 }
900
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000901 // The innermost rect has 0 coverage
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000902 verts += (outerVertexNum + innerVertexNum) * vsize;
903 for (int i = 0; i < innerVertexNum; ++i) {
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000904 *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
905 }
906
907 target->setIndexSourceToBuffer(indexBuffer);
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000908 target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0,
909 totalVertexNum, aaStrokeRectIndexCount(miterStroke));
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000910}
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000911
912void GrAARectRenderer::fillAANestedRects(GrGpu* gpu,
913 GrDrawTarget* target,
914 const SkRect rects[2],
915 const SkMatrix& combinedMatrix,
916 bool useVertexCoverage) {
917 SkASSERT(combinedMatrix.rectStaysRect());
918 SkASSERT(!rects[1].isEmpty());
919
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000920 SkRect devOutside, devOutsideAssist, devInside;
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000921 combinedMatrix.mapRect(&devOutside, rects[0]);
922 // can't call mapRect for devInside since it calls sort
923 combinedMatrix.mapPoints((SkPoint*)&devInside, (const SkPoint*)&rects[1], 2);
924
925 if (devInside.isEmpty()) {
926 this->fillAARect(gpu, target, devOutside, SkMatrix::I(), devOutside, useVertexCoverage);
927 return;
928 }
929
commit-bot@chromium.org6006d0f2013-11-06 10:08:21 +0000930 this->geometryStrokeAARect(gpu, target, devOutside, devOutsideAssist,
931 devInside, useVertexCoverage, true);
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000932}