blob: f28d91456308143206a0f77310df60cd8a24f263 [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
62 // TODO: compute these scale factors in the VS
63 // These scale factors adjust the coverage for < 1 pixel wide/high rects
64 builder->fsCodeAppendf("\tfloat wScale = max(1.0, 2.0/(0.5+%s.z));\n",
65 fsRectName);
66 builder->fsCodeAppendf("\tfloat hScale = max(1.0, 2.0/(0.5+%s.w));\n",
67 fsRectName);
68
69 // Compute the coverage for the rect's width
70 builder->fsCodeAppendf("\tfloat coverage = clamp(wScale*(%s.z-abs(%s.x)), 0.0, 1.0);\n",
71 fsRectName,
72 fsRectName);
73
74 // Compute the coverage for the rect's height and merge with the width
75 builder->fsCodeAppendf(
76 "\tcoverage = min(coverage, clamp(hScale*(%s.w-abs(%s.y)), 0.0, 1.0));\n",
77 fsRectName,
78 fsRectName);
79
80 SkString modulate;
81 GrGLSLModulatef<4>(&modulate, inputColor, "coverage");
82 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
83 }
84
85 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
86 return 0;
87 }
88
89 virtual void setData(const GrGLUniformManager& uman, const GrDrawEffect&) SK_OVERRIDE {}
90
91 private:
92 typedef GrGLEffect INHERITED;
93 };
94
95
96private:
97 GrAlignedRectEffect() : GrEffect() {
98 this->addVertexAttrib(kVec4f_GrSLType);
99 }
100
101 virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE { return true; }
102
103 GR_DECLARE_EFFECT_TEST;
104
105 typedef GrEffect INHERITED;
106};
107
108
109GR_DEFINE_EFFECT_TEST(GrAlignedRectEffect);
110
111GrEffectRef* GrAlignedRectEffect::TestCreate(SkMWCRandom* random,
112 GrContext* context,
113 const GrDrawTargetCaps&,
114 GrTexture* textures[]) {
115 return GrAlignedRectEffect::Create();
116}
117
118///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000119class GrGLRectEffect;
120
121/**
skia.committer@gmail.com07d3a652013-04-10 07:01:15 +0000122 * The output of this effect is a modulation of the input color and coverage
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000123 * for an arbitrarily oriented rect. The rect is specified as:
124 * Center of the rect
125 * Unit vector point down the height of the rect
126 * Half width + 0.5
127 * Half height + 0.5
128 * The center and vector are stored in a vec4 varying ("RectEdge") with the
129 * center in the xy components and the vector in the zw components.
130 * The munged width and height are stored in a vec2 varying ("WidthHeight")
131 * with the width in x and the height in y.
132 */
133class GrRectEffect : public GrEffect {
134public:
135 static GrEffectRef* Create() {
bsalomon@google.comd42aca32013-04-23 15:37:27 +0000136 GR_CREATE_STATIC_EFFECT(gRectEffect, GrRectEffect, ());
137 gRectEffect->ref();
138 return gRectEffect;
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000139 }
140
141 virtual ~GrRectEffect() {}
142
143 static const char* Name() { return "RectEdge"; }
144
skia.committer@gmail.com07d3a652013-04-10 07:01:15 +0000145 virtual void getConstantColorComponents(GrColor* color,
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000146 uint32_t* validFlags) const SK_OVERRIDE {
147 *validFlags = 0;
148 }
149
150 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
151 return GrTBackendEffectFactory<GrRectEffect>::getInstance();
152 }
153
154 class GLEffect : public GrGLEffect {
155 public:
156 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
157 : INHERITED (factory) {}
158
159 virtual void emitCode(GrGLShaderBuilder* builder,
160 const GrDrawEffect& drawEffect,
161 EffectKey key,
162 const char* outputColor,
163 const char* inputColor,
164 const TextureSamplerArray& samplers) SK_OVERRIDE {
165 // setup the varying for the center point and the unit vector
166 // that points down the height of the rect
167 const char *vsRectEdgeName, *fsRectEdgeName;
skia.committer@gmail.com07d3a652013-04-10 07:01:15 +0000168 builder->addVarying(kVec4f_GrSLType, "RectEdge",
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000169 &vsRectEdgeName, &fsRectEdgeName);
skia.committer@gmail.com07d3a652013-04-10 07:01:15 +0000170 const SkString* attr0Name =
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000171 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
172 builder->vsCodeAppendf("\t%s = %s;\n", vsRectEdgeName, attr0Name->c_str());
173
174 // setup the varying for width/2+.5 and height/2+.5
175 const char *vsWidthHeightName, *fsWidthHeightName;
skia.committer@gmail.com07d3a652013-04-10 07:01:15 +0000176 builder->addVarying(kVec2f_GrSLType, "WidthHeight",
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000177 &vsWidthHeightName, &fsWidthHeightName);
178 const SkString* attr1Name =
179 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]);
180 builder->vsCodeAppendf("\t%s = %s;\n", vsWidthHeightName, attr1Name->c_str());
181
182 // TODO: compute these scale factors in the VS
183 // These scale factors adjust the coverage for < 1 pixel wide/high rects
skia.committer@gmail.com07d3a652013-04-10 07:01:15 +0000184 builder->fsCodeAppendf("\tfloat wScale = max(1.0, 2.0/(0.5+%s.x));\n",
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000185 fsWidthHeightName);
skia.committer@gmail.com07d3a652013-04-10 07:01:15 +0000186 builder->fsCodeAppendf("\tfloat hScale = max(1.0, 2.0/(0.5+%s.y));\n",
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000187 fsWidthHeightName);
188
189 // Compute the coverage for the rect's width
skia.committer@gmail.com07d3a652013-04-10 07:01:15 +0000190 builder->fsCodeAppendf("\tvec2 offset = %s.xy - %s.xy;\n",
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000191 builder->fragmentPosition(), fsRectEdgeName);
192 builder->fsCodeAppendf("\tfloat perpDot = abs(offset.x * %s.w - offset.y * %s.z);\n",
193 fsRectEdgeName, fsRectEdgeName);
194 builder->fsCodeAppendf("\tfloat coverage = clamp(wScale*(%s.x-perpDot), 0.0, 1.0);\n",
195 fsWidthHeightName);
196
197 // Compute the coverage for the rect's height and merge with the width
198 builder->fsCodeAppendf("\tperpDot = abs(dot(offset, %s.zw));\n",
199 fsRectEdgeName);
200 builder->fsCodeAppendf(
201 "\tcoverage = min(coverage, clamp(hScale*(%s.y-perpDot), 0.0, 1.0));\n",
202 fsWidthHeightName);
203
204 SkString modulate;
bsalomon@google.com018f1792013-04-18 19:36:09 +0000205 GrGLSLModulatef<4>(&modulate, inputColor, "coverage");
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000206 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
207 }
208
209 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
210 return 0;
211 }
212
213 virtual void setData(const GrGLUniformManager& uman, const GrDrawEffect&) SK_OVERRIDE {}
214
215 private:
216 typedef GrGLEffect INHERITED;
217 };
218
219
220private:
robertphillips@google.com59dd7162013-04-09 14:08:15 +0000221 GrRectEffect() : GrEffect() {
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000222 this->addVertexAttrib(kVec4f_GrSLType);
223 this->addVertexAttrib(kVec2f_GrSLType);
commit-bot@chromium.org8d47ddc2013-05-09 14:55:46 +0000224 this->setWillReadFragmentPosition();
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000225 }
226
227 virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE { return true; }
228
229 GR_DECLARE_EFFECT_TEST;
230
231 typedef GrEffect INHERITED;
232};
233
234
235GR_DEFINE_EFFECT_TEST(GrRectEffect);
236
237GrEffectRef* GrRectEffect::TestCreate(SkMWCRandom* random,
238 GrContext* context,
239 const GrDrawTargetCaps&,
240 GrTexture* textures[]) {
241 return GrRectEffect::Create();
242}
243
244///////////////////////////////////////////////////////////////////////////////
245
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000246namespace {
247
robertphillips@google.com42903302013-04-20 12:26:07 +0000248extern const GrVertexAttrib gAARectCoverageAttribs[] = {
249 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
250 {kVec4ub_GrVertexAttribType, sizeof(GrPoint), kCoverage_GrVertexAttribBinding},
251};
252
253extern const GrVertexAttrib gAARectColorAttribs[] = {
254 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
255 {kVec4ub_GrVertexAttribType, sizeof(GrPoint), kColor_GrVertexAttribBinding},
256};
257
258static void set_aa_rect_vertex_attributes(GrDrawState* drawState, bool useCoverage) {
259 if (useCoverage) {
260 drawState->setVertexAttribs<gAARectCoverageAttribs>(SK_ARRAY_COUNT(gAARectCoverageAttribs));
261 } else {
262 drawState->setVertexAttribs<gAARectColorAttribs>(SK_ARRAY_COUNT(gAARectColorAttribs));
263 }
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000264}
265
robertphillips@google.comca47aae2012-12-12 15:58:25 +0000266static void set_inset_fan(GrPoint* pts, size_t stride,
267 const GrRect& r, SkScalar dx, SkScalar dy) {
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000268 pts->setRectFan(r.fLeft + dx, r.fTop + dy,
269 r.fRight - dx, r.fBottom - dy, stride);
270}
271
272};
273
274void GrAARectRenderer::reset() {
275 GrSafeSetNull(fAAFillRectIndexBuffer);
276 GrSafeSetNull(fAAStrokeRectIndexBuffer);
277}
278
robertphillips@google.com6d067302012-12-18 21:47:47 +0000279static const uint16_t gFillAARectIdx[] = {
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000280 0, 1, 5, 5, 4, 0,
281 1, 2, 6, 6, 5, 1,
282 2, 3, 7, 7, 6, 2,
283 3, 0, 4, 4, 7, 3,
284 4, 5, 6, 6, 7, 4,
285};
286
robertphillips@google.com6d067302012-12-18 21:47:47 +0000287static const int kIndicesPerAAFillRect = GR_ARRAY_COUNT(gFillAARectIdx);
288static const int kVertsPerAAFillRect = 8;
289static const int kNumAAFillRectsInIndexBuffer = 256;
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000290
291GrIndexBuffer* GrAARectRenderer::aaFillRectIndexBuffer(GrGpu* gpu) {
robertphillips@google.com6d067302012-12-18 21:47:47 +0000292 static const size_t kAAFillRectIndexBufferSize = kIndicesPerAAFillRect *
293 sizeof(uint16_t) *
294 kNumAAFillRectsInIndexBuffer;
295
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000296 if (NULL == fAAFillRectIndexBuffer) {
robertphillips@google.com6d067302012-12-18 21:47:47 +0000297 fAAFillRectIndexBuffer = gpu->createIndexBuffer(kAAFillRectIndexBufferSize, false);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000298 if (NULL != fAAFillRectIndexBuffer) {
robertphillips@google.com6d067302012-12-18 21:47:47 +0000299 uint16_t* data = (uint16_t*) fAAFillRectIndexBuffer->lock();
300 bool useTempData = (NULL == data);
301 if (useTempData) {
302 data = SkNEW_ARRAY(uint16_t, kNumAAFillRectsInIndexBuffer * kIndicesPerAAFillRect);
303 }
304 for (int i = 0; i < kNumAAFillRectsInIndexBuffer; ++i) {
305 // Each AA filled rect is drawn with 8 vertices and 10 triangles (8 around
306 // the inner rect (for AA) and 2 for the inner rect.
307 int baseIdx = i * kIndicesPerAAFillRect;
308 uint16_t baseVert = (uint16_t)(i * kVertsPerAAFillRect);
309 for (int j = 0; j < kIndicesPerAAFillRect; ++j) {
310 data[baseIdx+j] = baseVert + gFillAARectIdx[j];
311 }
312 }
313 if (useTempData) {
314 if (!fAAFillRectIndexBuffer->updateData(data, kAAFillRectIndexBufferSize)) {
315 GrCrash("Can't get AA Fill Rect indices into buffer!");
316 }
317 SkDELETE_ARRAY(data);
318 } else {
319 fAAFillRectIndexBuffer->unlock();
320 }
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000321 }
322 }
robertphillips@google.com6d067302012-12-18 21:47:47 +0000323
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000324 return fAAFillRectIndexBuffer;
325}
326
robertphillips@google.com6d067302012-12-18 21:47:47 +0000327static const uint16_t gStrokeAARectIdx[] = {
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000328 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
329 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
330 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
331 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
332
333 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
334 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
335 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
336 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
337
338 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
339 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
340 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
341 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
342};
343
344int GrAARectRenderer::aaStrokeRectIndexCount() {
345 return GR_ARRAY_COUNT(gStrokeAARectIdx);
346}
347
348GrIndexBuffer* GrAARectRenderer::aaStrokeRectIndexBuffer(GrGpu* gpu) {
349 if (NULL == fAAStrokeRectIndexBuffer) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000350 fAAStrokeRectIndexBuffer =
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000351 gpu->createIndexBuffer(sizeof(gStrokeAARectIdx), false);
352 if (NULL != fAAStrokeRectIndexBuffer) {
353#if GR_DEBUG
354 bool updated =
355#endif
356 fAAStrokeRectIndexBuffer->updateData(gStrokeAARectIdx,
357 sizeof(gStrokeAARectIdx));
358 GR_DEBUGASSERT(updated);
359 }
360 }
361 return fAAStrokeRectIndexBuffer;
362}
363
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000364void GrAARectRenderer::geometryFillAARect(GrGpu* gpu,
365 GrDrawTarget* target,
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000366 const GrRect& rect,
367 const SkMatrix& combinedMatrix,
robertphillips@google.comafd1cba2013-05-14 19:47:47 +0000368 const GrRect& devRect,
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000369 bool useVertexCoverage) {
jvanverth@google.com9b855c72013-03-01 18:21:22 +0000370 GrDrawState* drawState = target->drawState();
371
robertphillips@google.com42903302013-04-20 12:26:07 +0000372 set_aa_rect_vertex_attributes(drawState, useVertexCoverage);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000373
jvanverth@google.comb75b0a02013-02-05 20:33:30 +0000374 GrDrawTarget::AutoReleaseGeometry geo(target, 8, 0);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000375 if (!geo.succeeded()) {
376 GrPrintf("Failed to get space for vertices!\n");
377 return;
378 }
robertphillips@google.com6d067302012-12-18 21:47:47 +0000379
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000380 GrIndexBuffer* indexBuffer = this->aaFillRectIndexBuffer(gpu);
381 if (NULL == indexBuffer) {
382 GrPrintf("Failed to create index buffer!\n");
383 return;
384 }
385
386 intptr_t verts = reinterpret_cast<intptr_t>(geo.vertices());
jvanverth@google.com9b855c72013-03-01 18:21:22 +0000387 size_t vsize = drawState->getVertexSize();
388 GrAssert(sizeof(GrPoint) + sizeof(GrColor) == vsize);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000389
390 GrPoint* fan0Pos = reinterpret_cast<GrPoint*>(verts);
391 GrPoint* fan1Pos = reinterpret_cast<GrPoint*>(verts + 4 * vsize);
392
robertphillips@google.com908aed82013-05-28 13:16:20 +0000393 SkScalar inset = SkMinScalar(devRect.width(), SK_Scalar1);
394 inset = SK_ScalarHalf * SkMinScalar(inset, devRect.height());
395
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000396 if (combinedMatrix.rectStaysRect()) {
robertphillips@google.comafd1cba2013-05-14 19:47:47 +0000397 // Temporarily #if'ed out. We don't want to pass in the devRect but
398 // right now it is computed in GrContext::apply_aa_to_rect and we don't
399 // want to throw away the work
400#if 0
robertphillips@google.com91b71162013-05-10 14:09:54 +0000401 SkRect devRect;
402 combinedMatrix.mapRect(&devRect, rect);
robertphillips@google.comafd1cba2013-05-14 19:47:47 +0000403#endif
robertphillips@google.com91b71162013-05-10 14:09:54 +0000404
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000405 set_inset_fan(fan0Pos, vsize, devRect, -SK_ScalarHalf, -SK_ScalarHalf);
robertphillips@google.com908aed82013-05-28 13:16:20 +0000406 set_inset_fan(fan1Pos, vsize, devRect, inset, inset);
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000407 } else {
408 // compute transformed (1, 0) and (0, 1) vectors
409 SkVector vec[2] = {
410 { combinedMatrix[SkMatrix::kMScaleX], combinedMatrix[SkMatrix::kMSkewY] },
411 { combinedMatrix[SkMatrix::kMSkewX], combinedMatrix[SkMatrix::kMScaleY] }
412 };
413
414 vec[0].normalize();
415 vec[0].scale(SK_ScalarHalf);
416 vec[1].normalize();
417 vec[1].scale(SK_ScalarHalf);
418
robertphillips@google.com91b71162013-05-10 14:09:54 +0000419 // create the rotated rect
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000420 fan0Pos->setRectFan(rect.fLeft, rect.fTop,
421 rect.fRight, rect.fBottom, vsize);
422 combinedMatrix.mapPointsWithStride(fan0Pos, vsize, 4);
423
robertphillips@google.com91b71162013-05-10 14:09:54 +0000424 // Now create the inset points and then outset the original
425 // rotated points
426
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000427 // TL
skia.committer@gmail.com2fd42c42013-05-03 07:01:00 +0000428 *((SkPoint*)((intptr_t)fan1Pos + 0 * vsize)) =
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000429 *((SkPoint*)((intptr_t)fan0Pos + 0 * vsize)) + vec[0] + vec[1];
430 *((SkPoint*)((intptr_t)fan0Pos + 0 * vsize)) -= vec[0] + vec[1];
431 // BL
skia.committer@gmail.com2fd42c42013-05-03 07:01:00 +0000432 *((SkPoint*)((intptr_t)fan1Pos + 1 * vsize)) =
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000433 *((SkPoint*)((intptr_t)fan0Pos + 1 * vsize)) + vec[0] - vec[1];
434 *((SkPoint*)((intptr_t)fan0Pos + 1 * vsize)) -= vec[0] - vec[1];
435 // BR
skia.committer@gmail.com2fd42c42013-05-03 07:01:00 +0000436 *((SkPoint*)((intptr_t)fan1Pos + 2 * vsize)) =
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000437 *((SkPoint*)((intptr_t)fan0Pos + 2 * vsize)) - vec[0] - vec[1];
438 *((SkPoint*)((intptr_t)fan0Pos + 2 * vsize)) += vec[0] + vec[1];
439 // TR
skia.committer@gmail.com2fd42c42013-05-03 07:01:00 +0000440 *((SkPoint*)((intptr_t)fan1Pos + 3 * vsize)) =
robertphillips@google.com4b140b52013-05-02 17:13:13 +0000441 *((SkPoint*)((intptr_t)fan0Pos + 3 * vsize)) - vec[0] + vec[1];
442 *((SkPoint*)((intptr_t)fan0Pos + 3 * vsize)) += vec[0] - vec[1];
443 }
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000444
445 verts += sizeof(GrPoint);
446 for (int i = 0; i < 4; ++i) {
447 *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
448 }
449
robertphillips@google.com908aed82013-05-28 13:16:20 +0000450 int scale;
451 if (inset < SK_ScalarHalf) {
452 scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
453 SkASSERT(scale >= 0 && scale <= 255);
454 } else {
455 scale = 0xff;
456 }
457
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000458 GrColor innerColor;
skia.committer@gmail.com2fd42c42013-05-03 07:01:00 +0000459 if (useVertexCoverage) {
robertphillips@google.com908aed82013-05-28 13:16:20 +0000460 innerColor = scale | (scale << 8) | (scale << 16) | (scale << 24);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000461 } else {
robertphillips@google.com908aed82013-05-28 13:16:20 +0000462 if (0xff == scale) {
463 innerColor = target->getDrawState().getColor();
464 } else {
465 innerColor = SkAlphaMulQ(target->getDrawState().getColor(), scale);
466 }
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000467 }
468
469 verts += 4 * vsize;
470 for (int i = 0; i < 4; ++i) {
471 *reinterpret_cast<GrColor*>(verts + i * vsize) = innerColor;
472 }
473
474 target->setIndexSourceToBuffer(indexBuffer);
robertphillips@google.com6d067302012-12-18 21:47:47 +0000475 target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1,
476 kVertsPerAAFillRect,
477 kIndicesPerAAFillRect);
bsalomon@google.com0406b9e2013-04-02 21:00:15 +0000478 target->resetIndexSource();
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000479}
480
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000481namespace {
482
483// Rotated
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000484struct RectVertex {
485 GrPoint fPos;
486 GrPoint fCenter;
487 GrPoint fDir;
488 GrPoint fWidthHeight;
489};
490
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000491// Rotated
robertphillips@google.com42903302013-04-20 12:26:07 +0000492extern const GrVertexAttrib gAARectVertexAttribs[] = {
493 { kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding },
494 { kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding },
495 { kVec2f_GrVertexAttribType, 3*sizeof(GrPoint), kEffect_GrVertexAttribBinding }
496};
497
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000498// Axis Aligned
499struct AARectVertex {
500 GrPoint fPos;
501 GrPoint fOffset;
502 GrPoint fWidthHeight;
503};
504
505// Axis Aligned
506extern const GrVertexAttrib gAAAARectVertexAttribs[] = {
507 { kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding },
508 { kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding },
509};
510
robertphillips@google.com42903302013-04-20 12:26:07 +0000511};
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000512
513void GrAARectRenderer::shaderFillAARect(GrGpu* gpu,
514 GrDrawTarget* target,
515 const GrRect& rect,
robertphillips@google.com114eb9e2013-05-10 13:16:13 +0000516 const SkMatrix& combinedMatrix) {
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000517 GrDrawState* drawState = target->drawState();
518
519 SkPoint center = SkPoint::Make(rect.centerX(), rect.centerY());
520 combinedMatrix.mapPoints(&center, 1);
521
522 // compute transformed (0, 1) vector
523 SkVector dir = { combinedMatrix[SkMatrix::kMSkewX], combinedMatrix[SkMatrix::kMScaleY] };
524 dir.normalize();
525
526 // compute transformed (width, 0) and (0, height) vectors
527 SkVector vec[2] = {
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000528 { combinedMatrix[SkMatrix::kMScaleX], combinedMatrix[SkMatrix::kMSkewY] },
529 { combinedMatrix[SkMatrix::kMSkewX], combinedMatrix[SkMatrix::kMScaleY] }
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000530 };
531
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000532 SkScalar newWidth = SkScalarHalf(rect.width() * vec[0].length()) + SK_ScalarHalf;
533 SkScalar newHeight = SkScalarHalf(rect.height() * vec[1].length()) + SK_ScalarHalf;
robertphillips@google.com42903302013-04-20 12:26:07 +0000534 drawState->setVertexAttribs<gAARectVertexAttribs>(SK_ARRAY_COUNT(gAARectVertexAttribs));
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000535 GrAssert(sizeof(RectVertex) == drawState->getVertexSize());
536
537 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
538 if (!geo.succeeded()) {
539 GrPrintf("Failed to get space for vertices!\n");
540 return;
541 }
542
543 RectVertex* verts = reinterpret_cast<RectVertex*>(geo.vertices());
544
545 enum {
546 // the edge effects share this stage with glyph rendering
547 // (kGlyphMaskStage in GrTextContext) && SW path rendering
548 // (kPathMaskStage in GrSWMaskHelper)
549 kEdgeEffectStage = GrPaint::kTotalStages,
550 };
551
552 GrEffectRef* effect = GrRectEffect::Create();
553 static const int kRectAttrIndex = 1;
554 static const int kWidthIndex = 2;
555 drawState->setEffect(kEdgeEffectStage, effect, kRectAttrIndex, kWidthIndex)->unref();
556
557 for (int i = 0; i < 4; ++i) {
558 verts[i].fCenter = center;
559 verts[i].fDir = dir;
560 verts[i].fWidthHeight.fX = newWidth;
561 verts[i].fWidthHeight.fY = newHeight;
562 }
563
robertphillips@google.com114eb9e2013-05-10 13:16:13 +0000564 SkRect devRect;
565 combinedMatrix.mapRect(&devRect, rect);
566
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000567 SkRect devBounds = {
568 devRect.fLeft - SK_ScalarHalf,
569 devRect.fTop - SK_ScalarHalf,
570 devRect.fRight + SK_ScalarHalf,
571 devRect.fBottom + SK_ScalarHalf
572 };
573
574 verts[0].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fTop);
575 verts[1].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fBottom);
576 verts[2].fPos = SkPoint::Make(devBounds.fRight, devBounds.fBottom);
577 verts[3].fPos = SkPoint::Make(devBounds.fRight, devBounds.fTop);
578
579 target->setIndexSourceToBuffer(gpu->getContext()->getQuadIndexBuffer());
580 target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 4, 6);
581 target->resetIndexSource();
582}
583
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000584void GrAARectRenderer::shaderFillAlignedAARect(GrGpu* gpu,
585 GrDrawTarget* target,
586 const GrRect& rect,
robertphillips@google.com114eb9e2013-05-10 13:16:13 +0000587 const SkMatrix& combinedMatrix) {
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000588 GrDrawState* drawState = target->drawState();
589 SkASSERT(combinedMatrix.rectStaysRect());
590
591 drawState->setVertexAttribs<gAAAARectVertexAttribs>(SK_ARRAY_COUNT(gAAAARectVertexAttribs));
592 GrAssert(sizeof(AARectVertex) == drawState->getVertexSize());
593
594 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
595 if (!geo.succeeded()) {
596 GrPrintf("Failed to get space for vertices!\n");
597 return;
598 }
599
600 AARectVertex* verts = reinterpret_cast<AARectVertex*>(geo.vertices());
601
602 enum {
603 // the edge effects share this stage with glyph rendering
604 // (kGlyphMaskStage in GrTextContext) && SW path rendering
605 // (kPathMaskStage in GrSWMaskHelper)
606 kEdgeEffectStage = GrPaint::kTotalStages,
607 };
608
609 GrEffectRef* effect = GrAlignedRectEffect::Create();
610 static const int kOffsetIndex = 1;
611 drawState->setEffect(kEdgeEffectStage, effect, kOffsetIndex)->unref();
612
robertphillips@google.com114eb9e2013-05-10 13:16:13 +0000613 SkRect devRect;
614 combinedMatrix.mapRect(&devRect, rect);
615
robertphillips@google.comb19cb7f2013-05-02 15:37:20 +0000616 SkRect devBounds = {
617 devRect.fLeft - SK_ScalarHalf,
618 devRect.fTop - SK_ScalarHalf,
619 devRect.fRight + SK_ScalarHalf,
620 devRect.fBottom + SK_ScalarHalf
621 };
622
623 GrPoint widthHeight = {
624 SkScalarHalf(devRect.width()) + SK_ScalarHalf,
625 SkScalarHalf(devRect.height()) + SK_ScalarHalf
626 };
627
628 verts[0].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fTop);
629 verts[0].fOffset = SkPoint::Make(-widthHeight.fX, -widthHeight.fY);
630 verts[0].fWidthHeight = widthHeight;
631
632 verts[1].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fBottom);
633 verts[1].fOffset = SkPoint::Make(-widthHeight.fX, widthHeight.fY);
634 verts[1].fWidthHeight = widthHeight;
635
636 verts[2].fPos = SkPoint::Make(devBounds.fRight, devBounds.fBottom);
637 verts[2].fOffset = widthHeight;
638 verts[2].fWidthHeight = widthHeight;
639
640 verts[3].fPos = SkPoint::Make(devBounds.fRight, devBounds.fTop);
641 verts[3].fOffset = SkPoint::Make(widthHeight.fX, -widthHeight.fY);
642 verts[3].fWidthHeight = widthHeight;
643
644 target->setIndexSourceToBuffer(gpu->getContext()->getQuadIndexBuffer());
645 target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 4, 6);
646 target->resetIndexSource();
647}
648
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000649void GrAARectRenderer::strokeAARect(GrGpu* gpu,
bsalomon@google.come7249bd2012-08-16 15:28:54 +0000650 GrDrawTarget* target,
robertphillips@google.com18136d12013-05-10 11:05:58 +0000651 const GrRect& rect,
652 const SkMatrix& combinedMatrix,
robertphillips@google.comafd1cba2013-05-14 19:47:47 +0000653 const GrRect& devRect,
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000654 SkScalar width,
bsalomon@google.come7249bd2012-08-16 15:28:54 +0000655 bool useVertexCoverage) {
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000656 GrVec devStrokeSize;
657 if (width > 0) {
658 devStrokeSize.set(width, width);
659 combinedMatrix.mapVectors(&devStrokeSize, 1);
660 devStrokeSize.setAbs(devStrokeSize);
661 } else {
662 devStrokeSize.set(SK_Scalar1, SK_Scalar1);
663 }
jvanverth@google.com9b855c72013-03-01 18:21:22 +0000664
robertphillips@google.com18136d12013-05-10 11:05:58 +0000665 const SkScalar dx = devStrokeSize.fX;
666 const SkScalar dy = devStrokeSize.fY;
bsalomon@google.com81712882012-11-01 17:12:34 +0000667 const SkScalar rx = SkScalarMul(dx, SK_ScalarHalf);
668 const SkScalar ry = SkScalarMul(dy, SK_ScalarHalf);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000669
robertphillips@google.comafd1cba2013-05-14 19:47:47 +0000670 // Temporarily #if'ed out. We don't want to pass in the devRect but
671 // right now it is computed in GrContext::apply_aa_to_rect and we don't
672 // want to throw away the work
673#if 0
robertphillips@google.com18136d12013-05-10 11:05:58 +0000674 SkRect devRect;
675 combinedMatrix.mapRect(&devRect, rect);
robertphillips@google.comafd1cba2013-05-14 19:47:47 +0000676#endif
robertphillips@google.com18136d12013-05-10 11:05:58 +0000677
bsalomon@google.com81712882012-11-01 17:12:34 +0000678 SkScalar spare;
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000679 {
bsalomon@google.com81712882012-11-01 17:12:34 +0000680 SkScalar w = devRect.width() - dx;
681 SkScalar h = devRect.height() - dy;
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000682 spare = GrMin(w, h);
683 }
684
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000685 GrRect devOutside(devRect);
686 devOutside.outset(rx, ry);
687
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000688 if (spare <= 0) {
skia.committer@gmail.com845220b2013-05-20 11:51:35 +0000689 this->fillAARect(gpu, target, devOutside, SkMatrix::I(),
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000690 devOutside, useVertexCoverage);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000691 return;
692 }
skia.committer@gmail.comf140f182013-03-02 07:01:56 +0000693
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000694 SkRect devInside(devRect);
695 devInside.inset(rx, ry);
696
697 this->geometryStrokeAARect(gpu, target, devOutside, devInside, useVertexCoverage);
698}
699
700void GrAARectRenderer::geometryStrokeAARect(GrGpu* gpu,
701 GrDrawTarget* target,
702 const SkRect& devOutside,
703 const SkRect& devInside,
704 bool useVertexCoverage) {
705 GrDrawState* drawState = target->drawState();
706
robertphillips@google.com42903302013-04-20 12:26:07 +0000707 set_aa_rect_vertex_attributes(drawState, useVertexCoverage);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000708
jvanverth@google.comb75b0a02013-02-05 20:33:30 +0000709 GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000710 if (!geo.succeeded()) {
711 GrPrintf("Failed to get space for vertices!\n");
712 return;
713 }
714 GrIndexBuffer* indexBuffer = this->aaStrokeRectIndexBuffer(gpu);
715 if (NULL == indexBuffer) {
716 GrPrintf("Failed to create index buffer!\n");
717 return;
718 }
719
720 intptr_t verts = reinterpret_cast<intptr_t>(geo.vertices());
jvanverth@google.com9b855c72013-03-01 18:21:22 +0000721 size_t vsize = drawState->getVertexSize();
722 GrAssert(sizeof(GrPoint) + sizeof(GrColor) == vsize);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000723
bsalomon@google.come7249bd2012-08-16 15:28:54 +0000724 // We create vertices for four nested rectangles. There are two ramps from 0 to full
725 // coverage, one on the exterior of the stroke and the other on the interior.
726 // The following pointers refer to the four rects, from outermost to innermost.
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000727 GrPoint* fan0Pos = reinterpret_cast<GrPoint*>(verts);
728 GrPoint* fan1Pos = reinterpret_cast<GrPoint*>(verts + 4 * vsize);
729 GrPoint* fan2Pos = reinterpret_cast<GrPoint*>(verts + 8 * vsize);
730 GrPoint* fan3Pos = reinterpret_cast<GrPoint*>(verts + 12 * vsize);
731
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000732 // outermost
733 set_inset_fan(fan0Pos, vsize, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
734 set_inset_fan(fan1Pos, vsize, devOutside, SK_ScalarHalf, SK_ScalarHalf);
735 set_inset_fan(fan2Pos, vsize, devInside, -SK_ScalarHalf, -SK_ScalarHalf);
736 // innermost
737 set_inset_fan(fan3Pos, vsize, devInside, SK_ScalarHalf, SK_ScalarHalf);
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000738
bsalomon@google.come7249bd2012-08-16 15:28:54 +0000739 // The outermost rect has 0 coverage
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000740 verts += sizeof(GrPoint);
741 for (int i = 0; i < 4; ++i) {
742 *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
743 }
744
bsalomon@google.come7249bd2012-08-16 15:28:54 +0000745 // The inner two rects have full coverage
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000746 GrColor innerColor;
747 if (useVertexCoverage) {
748 innerColor = 0xffffffff;
749 } else {
750 innerColor = target->getDrawState().getColor();
751 }
752 verts += 4 * vsize;
753 for (int i = 0; i < 8; ++i) {
754 *reinterpret_cast<GrColor*>(verts + i * vsize) = innerColor;
755 }
756
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000757 // The innermost rect has 0 coverage
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000758 verts += 8 * vsize;
bsalomon@google.come7249bd2012-08-16 15:28:54 +0000759 for (int i = 0; i < 4; ++i) {
robertphillips@google.comf6747b02012-06-12 00:32:28 +0000760 *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
761 }
762
763 target->setIndexSourceToBuffer(indexBuffer);
764 target->drawIndexed(kTriangles_GrPrimitiveType,
765 0, 0, 16, aaStrokeRectIndexCount());
766}
robertphillips@google.com83d1a682013-05-17 12:50:27 +0000767
768void GrAARectRenderer::fillAANestedRects(GrGpu* gpu,
769 GrDrawTarget* target,
770 const SkRect rects[2],
771 const SkMatrix& combinedMatrix,
772 bool useVertexCoverage) {
773 SkASSERT(combinedMatrix.rectStaysRect());
774 SkASSERT(!rects[1].isEmpty());
775
776 SkRect devOutside, devInside;
777 combinedMatrix.mapRect(&devOutside, rects[0]);
778 // can't call mapRect for devInside since it calls sort
779 combinedMatrix.mapPoints((SkPoint*)&devInside, (const SkPoint*)&rects[1], 2);
780
781 if (devInside.isEmpty()) {
782 this->fillAARect(gpu, target, devOutside, SkMatrix::I(), devOutside, useVertexCoverage);
783 return;
784 }
785
786 this->geometryStrokeAARect(gpu, target, devOutside, devInside, useVertexCoverage);
787}