gfx/skia/trunk/src/gpu/GrAAConvexPathRenderer.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1
michael@0 2 /*
michael@0 3 * Copyright 2012 Google Inc.
michael@0 4 *
michael@0 5 * Use of this source code is governed by a BSD-style license that can be
michael@0 6 * found in the LICENSE file.
michael@0 7 */
michael@0 8
michael@0 9 #include "GrAAConvexPathRenderer.h"
michael@0 10
michael@0 11 #include "GrContext.h"
michael@0 12 #include "GrDrawState.h"
michael@0 13 #include "GrDrawTargetCaps.h"
michael@0 14 #include "GrEffect.h"
michael@0 15 #include "GrPathUtils.h"
michael@0 16 #include "GrTBackendEffectFactory.h"
michael@0 17 #include "SkString.h"
michael@0 18 #include "SkStrokeRec.h"
michael@0 19 #include "SkTrace.h"
michael@0 20
michael@0 21 #include "gl/GrGLEffect.h"
michael@0 22 #include "gl/GrGLSL.h"
michael@0 23 #include "gl/GrGLVertexEffect.h"
michael@0 24
michael@0 25 #include "effects/GrVertexEffect.h"
michael@0 26
michael@0 27 GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
michael@0 28 }
michael@0 29
michael@0 30 struct Segment {
michael@0 31 enum {
michael@0 32 // These enum values are assumed in member functions below.
michael@0 33 kLine = 0,
michael@0 34 kQuad = 1,
michael@0 35 } fType;
michael@0 36
michael@0 37 // line uses one pt, quad uses 2 pts
michael@0 38 GrPoint fPts[2];
michael@0 39 // normal to edge ending at each pt
michael@0 40 GrVec fNorms[2];
michael@0 41 // is the corner where the previous segment meets this segment
michael@0 42 // sharp. If so, fMid is a normalized bisector facing outward.
michael@0 43 GrVec fMid;
michael@0 44
michael@0 45 int countPoints() {
michael@0 46 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
michael@0 47 return fType + 1;
michael@0 48 }
michael@0 49 const SkPoint& endPt() const {
michael@0 50 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
michael@0 51 return fPts[fType];
michael@0 52 };
michael@0 53 const SkPoint& endNorm() const {
michael@0 54 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
michael@0 55 return fNorms[fType];
michael@0 56 };
michael@0 57 };
michael@0 58
michael@0 59 typedef SkTArray<Segment, true> SegmentArray;
michael@0 60
michael@0 61 static void center_of_mass(const SegmentArray& segments, SkPoint* c) {
michael@0 62 SkScalar area = 0;
michael@0 63 SkPoint center = {0, 0};
michael@0 64 int count = segments.count();
michael@0 65 SkPoint p0 = {0, 0};
michael@0 66 if (count > 2) {
michael@0 67 // We translate the polygon so that the first point is at the origin.
michael@0 68 // This avoids some precision issues with small area polygons far away
michael@0 69 // from the origin.
michael@0 70 p0 = segments[0].endPt();
michael@0 71 SkPoint pi;
michael@0 72 SkPoint pj;
michael@0 73 // the first and last iteration of the below loop would compute
michael@0 74 // zeros since the starting / ending point is (0,0). So instead we start
michael@0 75 // at i=1 and make the last iteration i=count-2.
michael@0 76 pj = segments[1].endPt() - p0;
michael@0 77 for (int i = 1; i < count - 1; ++i) {
michael@0 78 pi = pj;
michael@0 79 const SkPoint pj = segments[i + 1].endPt() - p0;
michael@0 80
michael@0 81 SkScalar t = SkScalarMul(pi.fX, pj.fY) - SkScalarMul(pj.fX, pi.fY);
michael@0 82 area += t;
michael@0 83 center.fX += (pi.fX + pj.fX) * t;
michael@0 84 center.fY += (pi.fY + pj.fY) * t;
michael@0 85
michael@0 86 }
michael@0 87 }
michael@0 88 // If the poly has no area then we instead return the average of
michael@0 89 // its points.
michael@0 90 if (SkScalarNearlyZero(area)) {
michael@0 91 SkPoint avg;
michael@0 92 avg.set(0, 0);
michael@0 93 for (int i = 0; i < count; ++i) {
michael@0 94 const SkPoint& pt = segments[i].endPt();
michael@0 95 avg.fX += pt.fX;
michael@0 96 avg.fY += pt.fY;
michael@0 97 }
michael@0 98 SkScalar denom = SK_Scalar1 / count;
michael@0 99 avg.scale(denom);
michael@0 100 *c = avg;
michael@0 101 } else {
michael@0 102 area *= 3;
michael@0 103 area = SkScalarDiv(SK_Scalar1, area);
michael@0 104 center.fX = SkScalarMul(center.fX, area);
michael@0 105 center.fY = SkScalarMul(center.fY, area);
michael@0 106 // undo the translate of p0 to the origin.
michael@0 107 *c = center + p0;
michael@0 108 }
michael@0 109 SkASSERT(!SkScalarIsNaN(c->fX) && !SkScalarIsNaN(c->fY));
michael@0 110 }
michael@0 111
michael@0 112 static void compute_vectors(SegmentArray* segments,
michael@0 113 SkPoint* fanPt,
michael@0 114 SkPath::Direction dir,
michael@0 115 int* vCount,
michael@0 116 int* iCount) {
michael@0 117 center_of_mass(*segments, fanPt);
michael@0 118 int count = segments->count();
michael@0 119
michael@0 120 // Make the normals point towards the outside
michael@0 121 GrPoint::Side normSide;
michael@0 122 if (dir == SkPath::kCCW_Direction) {
michael@0 123 normSide = GrPoint::kRight_Side;
michael@0 124 } else {
michael@0 125 normSide = GrPoint::kLeft_Side;
michael@0 126 }
michael@0 127
michael@0 128 *vCount = 0;
michael@0 129 *iCount = 0;
michael@0 130 // compute normals at all points
michael@0 131 for (int a = 0; a < count; ++a) {
michael@0 132 Segment& sega = (*segments)[a];
michael@0 133 int b = (a + 1) % count;
michael@0 134 Segment& segb = (*segments)[b];
michael@0 135
michael@0 136 const GrPoint* prevPt = &sega.endPt();
michael@0 137 int n = segb.countPoints();
michael@0 138 for (int p = 0; p < n; ++p) {
michael@0 139 segb.fNorms[p] = segb.fPts[p] - *prevPt;
michael@0 140 segb.fNorms[p].normalize();
michael@0 141 segb.fNorms[p].setOrthog(segb.fNorms[p], normSide);
michael@0 142 prevPt = &segb.fPts[p];
michael@0 143 }
michael@0 144 if (Segment::kLine == segb.fType) {
michael@0 145 *vCount += 5;
michael@0 146 *iCount += 9;
michael@0 147 } else {
michael@0 148 *vCount += 6;
michael@0 149 *iCount += 12;
michael@0 150 }
michael@0 151 }
michael@0 152
michael@0 153 // compute mid-vectors where segments meet. TODO: Detect shallow corners
michael@0 154 // and leave out the wedges and close gaps by stitching segments together.
michael@0 155 for (int a = 0; a < count; ++a) {
michael@0 156 const Segment& sega = (*segments)[a];
michael@0 157 int b = (a + 1) % count;
michael@0 158 Segment& segb = (*segments)[b];
michael@0 159 segb.fMid = segb.fNorms[0] + sega.endNorm();
michael@0 160 segb.fMid.normalize();
michael@0 161 // corner wedges
michael@0 162 *vCount += 4;
michael@0 163 *iCount += 6;
michael@0 164 }
michael@0 165 }
michael@0 166
michael@0 167 struct DegenerateTestData {
michael@0 168 DegenerateTestData() { fStage = kInitial; }
michael@0 169 bool isDegenerate() const { return kNonDegenerate != fStage; }
michael@0 170 enum {
michael@0 171 kInitial,
michael@0 172 kPoint,
michael@0 173 kLine,
michael@0 174 kNonDegenerate
michael@0 175 } fStage;
michael@0 176 GrPoint fFirstPoint;
michael@0 177 GrVec fLineNormal;
michael@0 178 SkScalar fLineC;
michael@0 179 };
michael@0 180
michael@0 181 static const SkScalar kClose = (SK_Scalar1 / 16);
michael@0 182 static const SkScalar kCloseSqd = SkScalarMul(kClose, kClose);
michael@0 183
michael@0 184 static void update_degenerate_test(DegenerateTestData* data, const GrPoint& pt) {
michael@0 185 switch (data->fStage) {
michael@0 186 case DegenerateTestData::kInitial:
michael@0 187 data->fFirstPoint = pt;
michael@0 188 data->fStage = DegenerateTestData::kPoint;
michael@0 189 break;
michael@0 190 case DegenerateTestData::kPoint:
michael@0 191 if (pt.distanceToSqd(data->fFirstPoint) > kCloseSqd) {
michael@0 192 data->fLineNormal = pt - data->fFirstPoint;
michael@0 193 data->fLineNormal.normalize();
michael@0 194 data->fLineNormal.setOrthog(data->fLineNormal);
michael@0 195 data->fLineC = -data->fLineNormal.dot(data->fFirstPoint);
michael@0 196 data->fStage = DegenerateTestData::kLine;
michael@0 197 }
michael@0 198 break;
michael@0 199 case DegenerateTestData::kLine:
michael@0 200 if (SkScalarAbs(data->fLineNormal.dot(pt) + data->fLineC) > kClose) {
michael@0 201 data->fStage = DegenerateTestData::kNonDegenerate;
michael@0 202 }
michael@0 203 case DegenerateTestData::kNonDegenerate:
michael@0 204 break;
michael@0 205 default:
michael@0 206 GrCrash("Unexpected degenerate test stage.");
michael@0 207 }
michael@0 208 }
michael@0 209
michael@0 210 static inline bool get_direction(const SkPath& path, const SkMatrix& m, SkPath::Direction* dir) {
michael@0 211 if (!path.cheapComputeDirection(dir)) {
michael@0 212 return false;
michael@0 213 }
michael@0 214 // check whether m reverses the orientation
michael@0 215 SkASSERT(!m.hasPerspective());
michael@0 216 SkScalar det2x2 = SkScalarMul(m.get(SkMatrix::kMScaleX), m.get(SkMatrix::kMScaleY)) -
michael@0 217 SkScalarMul(m.get(SkMatrix::kMSkewX), m.get(SkMatrix::kMSkewY));
michael@0 218 if (det2x2 < 0) {
michael@0 219 *dir = SkPath::OppositeDirection(*dir);
michael@0 220 }
michael@0 221 return true;
michael@0 222 }
michael@0 223
michael@0 224 static inline void add_line_to_segment(const SkPoint& pt,
michael@0 225 SegmentArray* segments,
michael@0 226 SkRect* devBounds) {
michael@0 227 segments->push_back();
michael@0 228 segments->back().fType = Segment::kLine;
michael@0 229 segments->back().fPts[0] = pt;
michael@0 230 devBounds->growToInclude(pt.fX, pt.fY);
michael@0 231 }
michael@0 232
michael@0 233 #ifdef SK_DEBUG
michael@0 234 static inline bool contains_inclusive(const SkRect& rect, const SkPoint& p) {
michael@0 235 return p.fX >= rect.fLeft && p.fX <= rect.fRight && p.fY >= rect.fTop && p.fY <= rect.fBottom;
michael@0 236 }
michael@0 237 #endif
michael@0 238
michael@0 239 static inline void add_quad_segment(const SkPoint pts[3],
michael@0 240 SegmentArray* segments,
michael@0 241 SkRect* devBounds) {
michael@0 242 if (pts[0].distanceToSqd(pts[1]) < kCloseSqd || pts[1].distanceToSqd(pts[2]) < kCloseSqd) {
michael@0 243 if (pts[0] != pts[2]) {
michael@0 244 add_line_to_segment(pts[2], segments, devBounds);
michael@0 245 }
michael@0 246 } else {
michael@0 247 segments->push_back();
michael@0 248 segments->back().fType = Segment::kQuad;
michael@0 249 segments->back().fPts[0] = pts[1];
michael@0 250 segments->back().fPts[1] = pts[2];
michael@0 251 SkASSERT(contains_inclusive(*devBounds, pts[0]));
michael@0 252 devBounds->growToInclude(pts + 1, 2);
michael@0 253 }
michael@0 254 }
michael@0 255
michael@0 256 static inline void add_cubic_segments(const SkPoint pts[4],
michael@0 257 SkPath::Direction dir,
michael@0 258 SegmentArray* segments,
michael@0 259 SkRect* devBounds) {
michael@0 260 SkSTArray<15, SkPoint, true> quads;
michael@0 261 GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, true, dir, &quads);
michael@0 262 int count = quads.count();
michael@0 263 for (int q = 0; q < count; q += 3) {
michael@0 264 add_quad_segment(&quads[q], segments, devBounds);
michael@0 265 }
michael@0 266 }
michael@0 267
michael@0 268 static bool get_segments(const SkPath& path,
michael@0 269 const SkMatrix& m,
michael@0 270 SegmentArray* segments,
michael@0 271 SkPoint* fanPt,
michael@0 272 int* vCount,
michael@0 273 int* iCount,
michael@0 274 SkRect* devBounds) {
michael@0 275 SkPath::Iter iter(path, true);
michael@0 276 // This renderer over-emphasizes very thin path regions. We use the distance
michael@0 277 // to the path from the sample to compute coverage. Every pixel intersected
michael@0 278 // by the path will be hit and the maximum distance is sqrt(2)/2. We don't
michael@0 279 // notice that the sample may be close to a very thin area of the path and
michael@0 280 // thus should be very light. This is particularly egregious for degenerate
michael@0 281 // line paths. We detect paths that are very close to a line (zero area) and
michael@0 282 // draw nothing.
michael@0 283 DegenerateTestData degenerateData;
michael@0 284 SkPath::Direction dir;
michael@0 285 // get_direction can fail for some degenerate paths.
michael@0 286 if (!get_direction(path, m, &dir)) {
michael@0 287 return false;
michael@0 288 }
michael@0 289
michael@0 290 for (;;) {
michael@0 291 GrPoint pts[4];
michael@0 292 SkPath::Verb verb = iter.next(pts);
michael@0 293 switch (verb) {
michael@0 294 case SkPath::kMove_Verb:
michael@0 295 m.mapPoints(pts, 1);
michael@0 296 update_degenerate_test(&degenerateData, pts[0]);
michael@0 297 devBounds->set(pts->fX, pts->fY, pts->fX, pts->fY);
michael@0 298 break;
michael@0 299 case SkPath::kLine_Verb: {
michael@0 300 m.mapPoints(&pts[1], 1);
michael@0 301 update_degenerate_test(&degenerateData, pts[1]);
michael@0 302 add_line_to_segment(pts[1], segments, devBounds);
michael@0 303 break;
michael@0 304 }
michael@0 305 case SkPath::kQuad_Verb:
michael@0 306 m.mapPoints(pts, 3);
michael@0 307 update_degenerate_test(&degenerateData, pts[1]);
michael@0 308 update_degenerate_test(&degenerateData, pts[2]);
michael@0 309 add_quad_segment(pts, segments, devBounds);
michael@0 310 break;
michael@0 311 case SkPath::kCubic_Verb: {
michael@0 312 m.mapPoints(pts, 4);
michael@0 313 update_degenerate_test(&degenerateData, pts[1]);
michael@0 314 update_degenerate_test(&degenerateData, pts[2]);
michael@0 315 update_degenerate_test(&degenerateData, pts[3]);
michael@0 316 add_cubic_segments(pts, dir, segments, devBounds);
michael@0 317 break;
michael@0 318 };
michael@0 319 case SkPath::kDone_Verb:
michael@0 320 if (degenerateData.isDegenerate()) {
michael@0 321 return false;
michael@0 322 } else {
michael@0 323 compute_vectors(segments, fanPt, dir, vCount, iCount);
michael@0 324 return true;
michael@0 325 }
michael@0 326 default:
michael@0 327 break;
michael@0 328 }
michael@0 329 }
michael@0 330 }
michael@0 331
michael@0 332 struct QuadVertex {
michael@0 333 GrPoint fPos;
michael@0 334 GrPoint fUV;
michael@0 335 SkScalar fD0;
michael@0 336 SkScalar fD1;
michael@0 337 };
michael@0 338
michael@0 339 struct Draw {
michael@0 340 Draw() : fVertexCnt(0), fIndexCnt(0) {}
michael@0 341 int fVertexCnt;
michael@0 342 int fIndexCnt;
michael@0 343 };
michael@0 344
michael@0 345 typedef SkTArray<Draw, true> DrawArray;
michael@0 346
michael@0 347 static void create_vertices(const SegmentArray& segments,
michael@0 348 const SkPoint& fanPt,
michael@0 349 DrawArray* draws,
michael@0 350 QuadVertex* verts,
michael@0 351 uint16_t* idxs) {
michael@0 352 Draw* draw = &draws->push_back();
michael@0 353 // alias just to make vert/index assignments easier to read.
michael@0 354 int* v = &draw->fVertexCnt;
michael@0 355 int* i = &draw->fIndexCnt;
michael@0 356
michael@0 357 int count = segments.count();
michael@0 358 for (int a = 0; a < count; ++a) {
michael@0 359 const Segment& sega = segments[a];
michael@0 360 int b = (a + 1) % count;
michael@0 361 const Segment& segb = segments[b];
michael@0 362
michael@0 363 // Check whether adding the verts for this segment to the current draw would cause index
michael@0 364 // values to overflow.
michael@0 365 int vCount = 4;
michael@0 366 if (Segment::kLine == segb.fType) {
michael@0 367 vCount += 5;
michael@0 368 } else {
michael@0 369 vCount += 6;
michael@0 370 }
michael@0 371 if (draw->fVertexCnt + vCount > (1 << 16)) {
michael@0 372 verts += *v;
michael@0 373 idxs += *i;
michael@0 374 draw = &draws->push_back();
michael@0 375 v = &draw->fVertexCnt;
michael@0 376 i = &draw->fIndexCnt;
michael@0 377 }
michael@0 378
michael@0 379 // FIXME: These tris are inset in the 1 unit arc around the corner
michael@0 380 verts[*v + 0].fPos = sega.endPt();
michael@0 381 verts[*v + 1].fPos = verts[*v + 0].fPos + sega.endNorm();
michael@0 382 verts[*v + 2].fPos = verts[*v + 0].fPos + segb.fMid;
michael@0 383 verts[*v + 3].fPos = verts[*v + 0].fPos + segb.fNorms[0];
michael@0 384 verts[*v + 0].fUV.set(0,0);
michael@0 385 verts[*v + 1].fUV.set(0,-SK_Scalar1);
michael@0 386 verts[*v + 2].fUV.set(0,-SK_Scalar1);
michael@0 387 verts[*v + 3].fUV.set(0,-SK_Scalar1);
michael@0 388 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
michael@0 389 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
michael@0 390 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
michael@0 391 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
michael@0 392
michael@0 393 idxs[*i + 0] = *v + 0;
michael@0 394 idxs[*i + 1] = *v + 2;
michael@0 395 idxs[*i + 2] = *v + 1;
michael@0 396 idxs[*i + 3] = *v + 0;
michael@0 397 idxs[*i + 4] = *v + 3;
michael@0 398 idxs[*i + 5] = *v + 2;
michael@0 399
michael@0 400 *v += 4;
michael@0 401 *i += 6;
michael@0 402
michael@0 403 if (Segment::kLine == segb.fType) {
michael@0 404 verts[*v + 0].fPos = fanPt;
michael@0 405 verts[*v + 1].fPos = sega.endPt();
michael@0 406 verts[*v + 2].fPos = segb.fPts[0];
michael@0 407
michael@0 408 verts[*v + 3].fPos = verts[*v + 1].fPos + segb.fNorms[0];
michael@0 409 verts[*v + 4].fPos = verts[*v + 2].fPos + segb.fNorms[0];
michael@0 410
michael@0 411 // we draw the line edge as a degenerate quad (u is 0, v is the
michael@0 412 // signed distance to the edge)
michael@0 413 SkScalar dist = fanPt.distanceToLineBetween(verts[*v + 1].fPos,
michael@0 414 verts[*v + 2].fPos);
michael@0 415 verts[*v + 0].fUV.set(0, dist);
michael@0 416 verts[*v + 1].fUV.set(0, 0);
michael@0 417 verts[*v + 2].fUV.set(0, 0);
michael@0 418 verts[*v + 3].fUV.set(0, -SK_Scalar1);
michael@0 419 verts[*v + 4].fUV.set(0, -SK_Scalar1);
michael@0 420
michael@0 421 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
michael@0 422 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
michael@0 423 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
michael@0 424 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
michael@0 425 verts[*v + 4].fD0 = verts[*v + 4].fD1 = -SK_Scalar1;
michael@0 426
michael@0 427 idxs[*i + 0] = *v + 0;
michael@0 428 idxs[*i + 1] = *v + 2;
michael@0 429 idxs[*i + 2] = *v + 1;
michael@0 430
michael@0 431 idxs[*i + 3] = *v + 3;
michael@0 432 idxs[*i + 4] = *v + 1;
michael@0 433 idxs[*i + 5] = *v + 2;
michael@0 434
michael@0 435 idxs[*i + 6] = *v + 4;
michael@0 436 idxs[*i + 7] = *v + 3;
michael@0 437 idxs[*i + 8] = *v + 2;
michael@0 438
michael@0 439 *v += 5;
michael@0 440 *i += 9;
michael@0 441 } else {
michael@0 442 GrPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
michael@0 443
michael@0 444 GrVec midVec = segb.fNorms[0] + segb.fNorms[1];
michael@0 445 midVec.normalize();
michael@0 446
michael@0 447 verts[*v + 0].fPos = fanPt;
michael@0 448 verts[*v + 1].fPos = qpts[0];
michael@0 449 verts[*v + 2].fPos = qpts[2];
michael@0 450 verts[*v + 3].fPos = qpts[0] + segb.fNorms[0];
michael@0 451 verts[*v + 4].fPos = qpts[2] + segb.fNorms[1];
michael@0 452 verts[*v + 5].fPos = qpts[1] + midVec;
michael@0 453
michael@0 454 SkScalar c = segb.fNorms[0].dot(qpts[0]);
michael@0 455 verts[*v + 0].fD0 = -segb.fNorms[0].dot(fanPt) + c;
michael@0 456 verts[*v + 1].fD0 = 0.f;
michael@0 457 verts[*v + 2].fD0 = -segb.fNorms[0].dot(qpts[2]) + c;
michael@0 458 verts[*v + 3].fD0 = -SK_ScalarMax/100;
michael@0 459 verts[*v + 4].fD0 = -SK_ScalarMax/100;
michael@0 460 verts[*v + 5].fD0 = -SK_ScalarMax/100;
michael@0 461
michael@0 462 c = segb.fNorms[1].dot(qpts[2]);
michael@0 463 verts[*v + 0].fD1 = -segb.fNorms[1].dot(fanPt) + c;
michael@0 464 verts[*v + 1].fD1 = -segb.fNorms[1].dot(qpts[0]) + c;
michael@0 465 verts[*v + 2].fD1 = 0.f;
michael@0 466 verts[*v + 3].fD1 = -SK_ScalarMax/100;
michael@0 467 verts[*v + 4].fD1 = -SK_ScalarMax/100;
michael@0 468 verts[*v + 5].fD1 = -SK_ScalarMax/100;
michael@0 469
michael@0 470 GrPathUtils::QuadUVMatrix toUV(qpts);
michael@0 471 toUV.apply<6, sizeof(QuadVertex), sizeof(GrPoint)>(verts + *v);
michael@0 472
michael@0 473 idxs[*i + 0] = *v + 3;
michael@0 474 idxs[*i + 1] = *v + 1;
michael@0 475 idxs[*i + 2] = *v + 2;
michael@0 476 idxs[*i + 3] = *v + 4;
michael@0 477 idxs[*i + 4] = *v + 3;
michael@0 478 idxs[*i + 5] = *v + 2;
michael@0 479
michael@0 480 idxs[*i + 6] = *v + 5;
michael@0 481 idxs[*i + 7] = *v + 3;
michael@0 482 idxs[*i + 8] = *v + 4;
michael@0 483
michael@0 484 idxs[*i + 9] = *v + 0;
michael@0 485 idxs[*i + 10] = *v + 2;
michael@0 486 idxs[*i + 11] = *v + 1;
michael@0 487
michael@0 488 *v += 6;
michael@0 489 *i += 12;
michael@0 490 }
michael@0 491 }
michael@0 492 }
michael@0 493
michael@0 494 ///////////////////////////////////////////////////////////////////////////////
michael@0 495
michael@0 496 /*
michael@0 497 * Quadratic specified by 0=u^2-v canonical coords. u and v are the first
michael@0 498 * two components of the vertex attribute. Coverage is based on signed
michael@0 499 * distance with negative being inside, positive outside. The edge is specified in
michael@0 500 * window space (y-down). If either the third or fourth component of the interpolated
michael@0 501 * vertex coord is > 0 then the pixel is considered outside the edge. This is used to
michael@0 502 * attempt to trim to a portion of the infinite quad.
michael@0 503 * Requires shader derivative instruction support.
michael@0 504 */
michael@0 505
michael@0 506 class QuadEdgeEffect : public GrVertexEffect {
michael@0 507 public:
michael@0 508
michael@0 509 static GrEffectRef* Create() {
michael@0 510 GR_CREATE_STATIC_EFFECT(gQuadEdgeEffect, QuadEdgeEffect, ());
michael@0 511 gQuadEdgeEffect->ref();
michael@0 512 return gQuadEdgeEffect;
michael@0 513 }
michael@0 514
michael@0 515 virtual ~QuadEdgeEffect() {}
michael@0 516
michael@0 517 static const char* Name() { return "QuadEdge"; }
michael@0 518
michael@0 519 virtual void getConstantColorComponents(GrColor* color,
michael@0 520 uint32_t* validFlags) const SK_OVERRIDE {
michael@0 521 *validFlags = 0;
michael@0 522 }
michael@0 523
michael@0 524 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
michael@0 525 return GrTBackendEffectFactory<QuadEdgeEffect>::getInstance();
michael@0 526 }
michael@0 527
michael@0 528 class GLEffect : public GrGLVertexEffect {
michael@0 529 public:
michael@0 530 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
michael@0 531 : INHERITED (factory) {}
michael@0 532
michael@0 533 virtual void emitCode(GrGLFullShaderBuilder* builder,
michael@0 534 const GrDrawEffect& drawEffect,
michael@0 535 EffectKey key,
michael@0 536 const char* outputColor,
michael@0 537 const char* inputColor,
michael@0 538 const TransformedCoordsArray&,
michael@0 539 const TextureSamplerArray& samplers) SK_OVERRIDE {
michael@0 540 const char *vsName, *fsName;
michael@0 541 const SkString* attrName =
michael@0 542 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
michael@0 543 builder->fsCodeAppendf("\t\tfloat edgeAlpha;\n");
michael@0 544
michael@0 545 SkAssertResult(builder->enableFeature(
michael@0 546 GrGLShaderBuilder::kStandardDerivatives_GLSLFeature));
michael@0 547 builder->addVarying(kVec4f_GrSLType, "QuadEdge", &vsName, &fsName);
michael@0 548
michael@0 549 // keep the derivative instructions outside the conditional
michael@0 550 builder->fsCodeAppendf("\t\tvec2 duvdx = dFdx(%s.xy);\n", fsName);
michael@0 551 builder->fsCodeAppendf("\t\tvec2 duvdy = dFdy(%s.xy);\n", fsName);
michael@0 552 builder->fsCodeAppendf("\t\tif (%s.z > 0.0 && %s.w > 0.0) {\n", fsName, fsName);
michael@0 553 // today we know z and w are in device space. We could use derivatives
michael@0 554 builder->fsCodeAppendf("\t\t\tedgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);\n", fsName,
michael@0 555 fsName);
michael@0 556 builder->fsCodeAppendf ("\t\t} else {\n");
michael@0 557 builder->fsCodeAppendf("\t\t\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n"
michael@0 558 "\t\t\t 2.0*%s.x*duvdy.x - duvdy.y);\n",
michael@0 559 fsName, fsName);
michael@0 560 builder->fsCodeAppendf("\t\t\tedgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName,
michael@0 561 fsName);
michael@0 562 builder->fsCodeAppendf("\t\t\tedgeAlpha = "
michael@0 563 "clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);\n\t\t}\n");
michael@0 564
michael@0 565
michael@0 566 builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
michael@0 567 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("edgeAlpha")).c_str());
michael@0 568
michael@0 569 builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str());
michael@0 570 }
michael@0 571
michael@0 572 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
michael@0 573 return 0x0;
michael@0 574 }
michael@0 575
michael@0 576 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {}
michael@0 577
michael@0 578 private:
michael@0 579 typedef GrGLVertexEffect INHERITED;
michael@0 580 };
michael@0 581
michael@0 582 private:
michael@0 583 QuadEdgeEffect() {
michael@0 584 this->addVertexAttrib(kVec4f_GrSLType);
michael@0 585 }
michael@0 586
michael@0 587 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
michael@0 588 return true;
michael@0 589 }
michael@0 590
michael@0 591 GR_DECLARE_EFFECT_TEST;
michael@0 592
michael@0 593 typedef GrVertexEffect INHERITED;
michael@0 594 };
michael@0 595
michael@0 596 GR_DEFINE_EFFECT_TEST(QuadEdgeEffect);
michael@0 597
michael@0 598 GrEffectRef* QuadEdgeEffect::TestCreate(SkRandom* random,
michael@0 599 GrContext*,
michael@0 600 const GrDrawTargetCaps& caps,
michael@0 601 GrTexture*[]) {
michael@0 602 // Doesn't work without derivative instructions.
michael@0 603 return caps.shaderDerivativeSupport() ? QuadEdgeEffect::Create() : NULL;
michael@0 604 }
michael@0 605
michael@0 606 ///////////////////////////////////////////////////////////////////////////////
michael@0 607
michael@0 608 bool GrAAConvexPathRenderer::canDrawPath(const SkPath& path,
michael@0 609 const SkStrokeRec& stroke,
michael@0 610 const GrDrawTarget* target,
michael@0 611 bool antiAlias) const {
michael@0 612 return (target->caps()->shaderDerivativeSupport() && antiAlias &&
michael@0 613 stroke.isFillStyle() && !path.isInverseFillType() && path.isConvex());
michael@0 614 }
michael@0 615
michael@0 616 namespace {
michael@0 617
michael@0 618 // position + edge
michael@0 619 extern const GrVertexAttrib gPathAttribs[] = {
michael@0 620 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
michael@0 621 {kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
michael@0 622 };
michael@0 623
michael@0 624 };
michael@0 625
michael@0 626 bool GrAAConvexPathRenderer::onDrawPath(const SkPath& origPath,
michael@0 627 const SkStrokeRec&,
michael@0 628 GrDrawTarget* target,
michael@0 629 bool antiAlias) {
michael@0 630
michael@0 631 const SkPath* path = &origPath;
michael@0 632 if (path->isEmpty()) {
michael@0 633 return true;
michael@0 634 }
michael@0 635
michael@0 636 SkMatrix viewMatrix = target->getDrawState().getViewMatrix();
michael@0 637 GrDrawTarget::AutoStateRestore asr;
michael@0 638 if (!asr.setIdentity(target, GrDrawTarget::kPreserve_ASRInit)) {
michael@0 639 return false;
michael@0 640 }
michael@0 641 GrDrawState* drawState = target->drawState();
michael@0 642
michael@0 643 // We use the fact that SkPath::transform path does subdivision based on
michael@0 644 // perspective. Otherwise, we apply the view matrix when copying to the
michael@0 645 // segment representation.
michael@0 646 SkPath tmpPath;
michael@0 647 if (viewMatrix.hasPerspective()) {
michael@0 648 origPath.transform(viewMatrix, &tmpPath);
michael@0 649 path = &tmpPath;
michael@0 650 viewMatrix = SkMatrix::I();
michael@0 651 }
michael@0 652
michael@0 653 QuadVertex *verts;
michael@0 654 uint16_t* idxs;
michael@0 655
michael@0 656 int vCount;
michael@0 657 int iCount;
michael@0 658 enum {
michael@0 659 kPreallocSegmentCnt = 512 / sizeof(Segment),
michael@0 660 kPreallocDrawCnt = 4,
michael@0 661 };
michael@0 662 SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
michael@0 663 SkPoint fanPt;
michael@0 664
michael@0 665 // We can't simply use the path bounds because we may degenerate cubics to quads which produces
michael@0 666 // new control points outside the original convex hull.
michael@0 667 SkRect devBounds;
michael@0 668 if (!get_segments(*path, viewMatrix, &segments, &fanPt, &vCount, &iCount, &devBounds)) {
michael@0 669 return false;
michael@0 670 }
michael@0 671
michael@0 672 // Our computed verts should all be within one pixel of the segment control points.
michael@0 673 devBounds.outset(SK_Scalar1, SK_Scalar1);
michael@0 674
michael@0 675 drawState->setVertexAttribs<gPathAttribs>(SK_ARRAY_COUNT(gPathAttribs));
michael@0 676
michael@0 677 static const int kEdgeAttrIndex = 1;
michael@0 678 GrEffectRef* quadEffect = QuadEdgeEffect::Create();
michael@0 679 drawState->addCoverageEffect(quadEffect, kEdgeAttrIndex)->unref();
michael@0 680
michael@0 681 GrDrawTarget::AutoReleaseGeometry arg(target, vCount, iCount);
michael@0 682 if (!arg.succeeded()) {
michael@0 683 return false;
michael@0 684 }
michael@0 685 SkASSERT(sizeof(QuadVertex) == drawState->getVertexSize());
michael@0 686 verts = reinterpret_cast<QuadVertex*>(arg.vertices());
michael@0 687 idxs = reinterpret_cast<uint16_t*>(arg.indices());
michael@0 688
michael@0 689 SkSTArray<kPreallocDrawCnt, Draw, true> draws;
michael@0 690 create_vertices(segments, fanPt, &draws, verts, idxs);
michael@0 691
michael@0 692 // Check devBounds
michael@0 693 #ifdef SK_DEBUG
michael@0 694 SkRect tolDevBounds = devBounds;
michael@0 695 tolDevBounds.outset(SK_Scalar1 / 10000, SK_Scalar1 / 10000);
michael@0 696 SkRect actualBounds;
michael@0 697 actualBounds.set(verts[0].fPos, verts[1].fPos);
michael@0 698 for (int i = 2; i < vCount; ++i) {
michael@0 699 actualBounds.growToInclude(verts[i].fPos.fX, verts[i].fPos.fY);
michael@0 700 }
michael@0 701 SkASSERT(tolDevBounds.contains(actualBounds));
michael@0 702 #endif
michael@0 703
michael@0 704 int vOffset = 0;
michael@0 705 for (int i = 0; i < draws.count(); ++i) {
michael@0 706 const Draw& draw = draws[i];
michael@0 707 target->drawIndexed(kTriangles_GrPrimitiveType,
michael@0 708 vOffset, // start vertex
michael@0 709 0, // start index
michael@0 710 draw.fVertexCnt,
michael@0 711 draw.fIndexCnt,
michael@0 712 &devBounds);
michael@0 713 vOffset += draw.fVertexCnt;
michael@0 714 }
michael@0 715
michael@0 716 return true;
michael@0 717 }

mercurial