11cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 21cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger/* 31cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger * Copyright 2011 Google Inc. 41cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger * 51cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger * Use of this source code is governed by a BSD-style license that can be 61cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger * found in the LICENSE file. 71cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger */ 81cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 91cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#include "GrAAHairLinePathRenderer.h" 101cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 111cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#include "GrContext.h" 121cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#include "GrDrawState.h" 131cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#include "GrGpu.h" 141cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#include "GrIndexBuffer.h" 151cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#include "GrPathUtils.h" 161cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#include "SkGeometry.h" 171cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#include "SkTemplates.h" 181cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 191cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergernamespace { 201cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger// quadratics are rendered as 5-sided polys in order to bound the 211cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger// AA stroke around the center-curve. See comments in push_quad_index_buffer and 221cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger// bloat_quad. 231cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerstatic const int kVertsPerQuad = 5; 241cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerstatic const int kIdxsPerQuad = 9; 251cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 261cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerstatic const int kVertsPerLineSeg = 4; 271cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerstatic const int kIdxsPerLineSeg = 6; 281cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 291cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerstatic const int kNumQuadsInIdxBuffer = 256; 301cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerstatic const size_t kQuadIdxSBufize = kIdxsPerQuad * 311cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger sizeof(uint16_t) * 321cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger kNumQuadsInIdxBuffer; 331cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 341cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerbool push_quad_index_data(GrIndexBuffer* qIdxBuffer) { 351cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger uint16_t* data = (uint16_t*) qIdxBuffer->lock(); 361cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger bool tempData = NULL == data; 371cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (tempData) { 381cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger data = new uint16_t[kNumQuadsInIdxBuffer * kIdxsPerQuad]; 391cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 401cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger for (int i = 0; i < kNumQuadsInIdxBuffer; ++i) { 411cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 421cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // Each quadratic is rendered as a five sided polygon. This poly bounds 431cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // the quadratic's bounding triangle but has been expanded so that the 441cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // 1-pixel wide area around the curve is inside the poly. 451cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // If a,b,c are the original control points then the poly a0,b0,c0,c1,a1 461cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // that is rendered would look like this: 471cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // b0 481cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // b 491cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // 501cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // a0 c0 511cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // a c 521cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // a1 c1 531cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // Each is drawn as three triagnles specified by these 9 indices: 541cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger int baseIdx = i * kIdxsPerQuad; 551cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger uint16_t baseVert = (uint16_t)(i * kVertsPerQuad); 561cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger data[0 + baseIdx] = baseVert + 0; // a0 571cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger data[1 + baseIdx] = baseVert + 1; // a1 581cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger data[2 + baseIdx] = baseVert + 2; // b0 591cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger data[3 + baseIdx] = baseVert + 2; // b0 601cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger data[4 + baseIdx] = baseVert + 4; // c1 611cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger data[5 + baseIdx] = baseVert + 3; // c0 621cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger data[6 + baseIdx] = baseVert + 1; // a1 631cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger data[7 + baseIdx] = baseVert + 4; // c1 641cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger data[8 + baseIdx] = baseVert + 2; // b0 651cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 661cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (tempData) { 671cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger bool ret = qIdxBuffer->updateData(data, kQuadIdxSBufize); 681cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger delete[] data; 691cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger return ret; 701cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } else { 711cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger qIdxBuffer->unlock(); 721cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger return true; 731cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 741cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger} 751cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger} 761cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 771cab2921ab279367f8206cdadc9259d12e603548Derek SollenbergerGrPathRenderer* GrAAHairLinePathRenderer::Create(GrContext* context) { 781cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger const GrIndexBuffer* lIdxBuffer = context->getQuadIndexBuffer(); 791cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (NULL == lIdxBuffer) { 801cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger return NULL; 811cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 821cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrGpu* gpu = context->getGpu(); 831cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrIndexBuffer* qIdxBuf = gpu->createIndexBuffer(kQuadIdxSBufize, false); 841cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkAutoTUnref<GrIndexBuffer> qIdxBuffer(qIdxBuf); 851cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (NULL == qIdxBuf || 861cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger !push_quad_index_data(qIdxBuf)) { 871cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger return NULL; 881cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 891cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger return new GrAAHairLinePathRenderer(context, 901cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger lIdxBuffer, 911cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger qIdxBuf); 921cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger} 931cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 941cab2921ab279367f8206cdadc9259d12e603548Derek SollenbergerGrAAHairLinePathRenderer::GrAAHairLinePathRenderer( 951cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger const GrContext* context, 961cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger const GrIndexBuffer* linesIndexBuffer, 971cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger const GrIndexBuffer* quadsIndexBuffer) { 981cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger fLinesIndexBuffer = linesIndexBuffer; 991cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger linesIndexBuffer->ref(); 1001cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger fQuadsIndexBuffer = quadsIndexBuffer; 1011cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger quadsIndexBuffer->ref(); 1021cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger} 1031cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 1041cab2921ab279367f8206cdadc9259d12e603548Derek SollenbergerGrAAHairLinePathRenderer::~GrAAHairLinePathRenderer() { 1051cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger fLinesIndexBuffer->unref(); 1061cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger fQuadsIndexBuffer->unref(); 1071cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger} 1081cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 1091cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergernamespace { 1101cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 1111cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergertypedef SkTArray<SkPoint, true> PtArray; 1121cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#define PREALLOC_PTARRAY(N) SkSTArray<(N),SkPoint, true> 1131cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergertypedef SkTArray<int, true> IntArray; 1141cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 1151cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger// Takes 178th time of logf on Z600 / VC2010 1161cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerint get_float_exp(float x) { 1171cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GR_STATIC_ASSERT(sizeof(int) == sizeof(float)); 1181cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#if GR_DEBUG 1191cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger static bool tested; 1201cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (!tested) { 1211cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger tested = true; 1221cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrAssert(get_float_exp(0.25f) == -2); 1231cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrAssert(get_float_exp(0.3f) == -2); 1241cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrAssert(get_float_exp(0.5f) == -1); 1251cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrAssert(get_float_exp(1.f) == 0); 1261cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrAssert(get_float_exp(2.f) == 1); 1271cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrAssert(get_float_exp(2.5f) == 1); 1281cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrAssert(get_float_exp(8.f) == 3); 1291cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrAssert(get_float_exp(100.f) == 6); 1301cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrAssert(get_float_exp(1000.f) == 9); 1311cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrAssert(get_float_exp(1024.f) == 10); 1321cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrAssert(get_float_exp(3000000.f) == 21); 1331cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 1341cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#endif 1351cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger const int* iptr = (const int*)&x; 1361cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger return (((*iptr) & 0x7f800000) >> 23) - 127; 1371cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger} 1381cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 1391cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger// we subdivide the quads to avoid huge overfill 1401cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger// if it returns -1 then should be drawn as lines 1411cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerint num_quad_subdivs(const SkPoint p[3]) { 1421cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger static const SkScalar gDegenerateToLineTol = SK_Scalar1; 1431cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger static const SkScalar gDegenerateToLineTolSqd = 1441cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkScalarMul(gDegenerateToLineTol, gDegenerateToLineTol); 1451cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 1461cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (p[0].distanceToSqd(p[1]) < gDegenerateToLineTolSqd || 1471cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger p[1].distanceToSqd(p[2]) < gDegenerateToLineTolSqd) { 1481cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger return -1; 1491cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 1501cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 1511cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrScalar dsqd = p[1].distanceToLineBetweenSqd(p[0], p[2]); 1521cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (dsqd < gDegenerateToLineTolSqd) { 1531cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger return -1; 1541cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 1551cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 1561cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (p[2].distanceToLineBetweenSqd(p[1], p[0]) < gDegenerateToLineTolSqd) { 1571cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger return -1; 1581cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 1591cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 1601cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger static const int kMaxSub = 4; 1611cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // tolerance of triangle height in pixels 1621cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // tuned on windows Quadro FX 380 / Z600 1631cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // trade off of fill vs cpu time on verts 1641cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // maybe different when do this using gpu (geo or tess shaders) 1651cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger static const SkScalar gSubdivTol = 175 * SK_Scalar1; 1661cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 1671cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (dsqd <= gSubdivTol*gSubdivTol) { 1681cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger return 0; 1691cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } else { 1701cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // subdividing the quad reduces d by 4. so we want x = log4(d/tol) 1711cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // = log4(d*d/tol*tol)/2 1721cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // = log2(d*d/tol*tol) 1731cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 1741cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#ifdef SK_SCALAR_IS_FLOAT 1751cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // +1 since we're ignoring the mantissa contribution. 1761cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger int log = get_float_exp(dsqd/(gSubdivTol*gSubdivTol)) + 1; 1771cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger log = GrMin(GrMax(0, log), kMaxSub); 1781cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger return log; 1791cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#else 1801cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkScalar log = SkScalarLog(SkScalarDiv(dsqd,gSubdivTol*gSubdivTol)); 1811cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger static const SkScalar conv = SkScalarInvert(SkScalarLog(2)); 1821cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger log = SkScalarMul(log, conv); 1831cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger return GrMin(GrMax(0, SkScalarCeilToInt(log)),kMaxSub); 1841cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#endif 1851cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 1861cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger} 1871cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 1881cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger/** 1891cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger * Generates the lines and quads to be rendered. Lines are always recorded in 1901cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger * device space. We will do a device space bloat to account for the 1pixel 1911cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger * thickness. 1921cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger * Quads are recorded in device space unless m contains 1931cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger * perspective, then in they are in src space. We do this because we will 1941cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger * subdivide large quads to reduce over-fill. This subdivision has to be 1951cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger * performed before applying the perspective matrix. 1961cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger */ 1971cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerint generate_lines_and_quads(const SkPath& path, 1981cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger const SkMatrix& m, 1991cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger const SkVector& translate, 2001cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrIRect clip, 2011cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger PtArray* lines, 2021cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger PtArray* quads, 2031cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger IntArray* quadSubdivCnts) { 2041cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkPath::Iter iter(path, false); 2051cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 2061cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger int totalQuadCount = 0; 2071cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrRect bounds; 2081cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrIRect ibounds; 2091cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 2101cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger bool persp = m.hasPerspective(); 2111cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 2121cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger for (;;) { 2131cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrPoint pts[4]; 2141cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrPoint devPts[4]; 2151cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrPathCmd cmd = (GrPathCmd)iter.next(pts); 2161cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger switch (cmd) { 2171cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger case kMove_PathCmd: 2181cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger break; 2191cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger case kLine_PathCmd: 2201cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkPoint::Offset(pts, 2, translate); 2211cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger m.mapPoints(devPts, pts, 2); 2221cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger bounds.setBounds(devPts, 2); 2231cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger bounds.outset(SK_Scalar1, SK_Scalar1); 2241cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger bounds.roundOut(&ibounds); 2251cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (SkIRect::Intersects(clip, ibounds)) { 2261cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkPoint* pts = lines->push_back_n(2); 2271cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger pts[0] = devPts[0]; 2281cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger pts[1] = devPts[1]; 2291cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 2301cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger break; 2311cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger case kQuadratic_PathCmd: 2321cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkPoint::Offset(pts, 3, translate); 2331cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger m.mapPoints(devPts, pts, 3); 2341cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger bounds.setBounds(devPts, 3); 2351cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger bounds.outset(SK_Scalar1, SK_Scalar1); 2361cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger bounds.roundOut(&ibounds); 2371cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (SkIRect::Intersects(clip, ibounds)) { 2381cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger int subdiv = num_quad_subdivs(devPts); 2391cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrAssert(subdiv >= -1); 2401cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (-1 == subdiv) { 2411cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkPoint* pts = lines->push_back_n(4); 2421cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger pts[0] = devPts[0]; 2431cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger pts[1] = devPts[1]; 2441cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger pts[2] = devPts[1]; 2451cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger pts[3] = devPts[2]; 2461cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } else { 2471cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // when in perspective keep quads in src space 2481cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkPoint* qPts = persp ? pts : devPts; 2491cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkPoint* pts = quads->push_back_n(3); 2501cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger pts[0] = qPts[0]; 2511cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger pts[1] = qPts[1]; 2521cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger pts[2] = qPts[2]; 2531cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger quadSubdivCnts->push_back() = subdiv; 2541cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger totalQuadCount += 1 << subdiv; 2551cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 2561cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 2571cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger break; 2581cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger case kCubic_PathCmd: 2591cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkPoint::Offset(pts, 4, translate); 2601cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger m.mapPoints(devPts, pts, 4); 2611cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger bounds.setBounds(devPts, 4); 2621cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger bounds.outset(SK_Scalar1, SK_Scalar1); 2631cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger bounds.roundOut(&ibounds); 2641cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (SkIRect::Intersects(clip, ibounds)) { 2651cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger PREALLOC_PTARRAY(32) q; 2664f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger // We convert cubics to quadratics (for now). 2674f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger // In perspective have to do conversion in src space. 2681cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (persp) { 2691cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkScalar tolScale = 2701cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrPathUtils::scaleToleranceToSrc(SK_Scalar1, m, 2711cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger path.getBounds()); 2724f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger GrPathUtils::convertCubicToQuads(pts, tolScale, &q); 2731cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } else { 2744f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger GrPathUtils::convertCubicToQuads(devPts, SK_Scalar1, &q); 2751cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 2761cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger for (int i = 0; i < q.count(); i += 3) { 2771cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkPoint* qInDevSpace; 2781cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // bounds has to be calculated in device space, but q is 2791cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // in src space when there is perspective. 2801cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (persp) { 2811cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger m.mapPoints(devPts, &q[i], 3); 2821cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger bounds.setBounds(devPts, 3); 2831cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger qInDevSpace = devPts; 2841cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } else { 2851cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger bounds.setBounds(&q[i], 3); 2861cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger qInDevSpace = &q[i]; 2871cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 2881cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger bounds.outset(SK_Scalar1, SK_Scalar1); 2891cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger bounds.roundOut(&ibounds); 2901cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (SkIRect::Intersects(clip, ibounds)) { 2911cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger int subdiv = num_quad_subdivs(qInDevSpace); 2921cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrAssert(subdiv >= -1); 2931cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (-1 == subdiv) { 2941cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkPoint* pts = lines->push_back_n(4); 2951cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // lines should always be in device coords 2961cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger pts[0] = qInDevSpace[0]; 2971cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger pts[1] = qInDevSpace[1]; 2981cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger pts[2] = qInDevSpace[1]; 2991cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger pts[3] = qInDevSpace[2]; 3001cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } else { 3011cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkPoint* pts = quads->push_back_n(3); 3021cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // q is already in src space when there is no 3031cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // perspective and dev coords otherwise. 3041cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger pts[0] = q[0 + i]; 3051cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger pts[1] = q[1 + i]; 3061cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger pts[2] = q[2 + i]; 3071cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger quadSubdivCnts->push_back() = subdiv; 3081cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger totalQuadCount += 1 << subdiv; 3091cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 3101cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 3111cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 3121cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 3131cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger break; 3141cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger case kClose_PathCmd: 3151cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger break; 3161cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger case kEnd_PathCmd: 3171cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger return totalQuadCount; 3181cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 3191cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 3201cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger} 3211cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 3221cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerstruct Vertex { 3231cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrPoint fPos; 3241cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger union { 3251cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger struct { 3261cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrScalar fA; 3271cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrScalar fB; 3281cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrScalar fC; 3291cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } fLine; 3301cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrVec fQuadCoord; 3311cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger struct { 3321cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrScalar fBogus[4]; 3331cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger }; 3341cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger }; 3351cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger}; 3361cab2921ab279367f8206cdadc9259d12e603548Derek SollenbergerGR_STATIC_ASSERT(sizeof(Vertex) == 3 * sizeof(GrPoint)); 3371cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 3381cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergervoid intersect_lines(const SkPoint& ptA, const SkVector& normA, 3391cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger const SkPoint& ptB, const SkVector& normB, 3401cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkPoint* result) { 3411cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 3421cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkScalar lineAW = -normA.dot(ptA); 3431cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkScalar lineBW = -normB.dot(ptB); 3441cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 3451cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkScalar wInv = SkScalarMul(normA.fX, normB.fY) - 3461cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkScalarMul(normA.fY, normB.fX); 3471cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger wInv = SkScalarInvert(wInv); 3481cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 3491cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger result->fX = SkScalarMul(normA.fY, lineBW) - SkScalarMul(lineAW, normB.fY); 3501cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger result->fX = SkScalarMul(result->fX, wInv); 3511cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 3521cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger result->fY = SkScalarMul(lineAW, normB.fX) - SkScalarMul(normA.fX, lineBW); 3531cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger result->fY = SkScalarMul(result->fY, wInv); 3541cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger} 3551cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 3561cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergervoid bloat_quad(const SkPoint qpts[3], const GrMatrix* toDevice, 3571cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger const GrMatrix* toSrc, Vertex verts[kVertsPerQuad]) { 3581cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrAssert(!toDevice == !toSrc); 3591cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // original quad is specified by tri a,b,c 3601cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkPoint a = qpts[0]; 3611cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkPoint b = qpts[1]; 3621cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkPoint c = qpts[2]; 3631cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 3641cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // this should be in the src space, not dev coords, when we have perspective 3651cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkMatrix DevToUV; 3664f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger GrPathUtils::quadDesignSpaceToUVCoordsMatrix(qpts, &DevToUV); 3671cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 3681cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (toDevice) { 3691cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger toDevice->mapPoints(&a, 1); 3701cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger toDevice->mapPoints(&b, 1); 3711cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger toDevice->mapPoints(&c, 1); 3721cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 3731cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // make a new poly where we replace a and c by a 1-pixel wide edges orthog 3741cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // to edges ab and bc: 3751cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // 3761cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // before | after 3771cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // | b0 3781cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // b | 3791cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // | 3801cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // | a0 c0 3811cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // a c | a1 c1 3821cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // 3831cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // edges a0->b0 and b0->c0 are parallel to original edges a->b and b->c, 3841cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // respectively. 3851cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger Vertex& a0 = verts[0]; 3861cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger Vertex& a1 = verts[1]; 3871cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger Vertex& b0 = verts[2]; 3881cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger Vertex& c0 = verts[3]; 3891cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger Vertex& c1 = verts[4]; 3901cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 3911cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkVector ab = b; 3921cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger ab -= a; 3931cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkVector ac = c; 3941cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger ac -= a; 3951cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkVector cb = b; 3961cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger cb -= c; 3971cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 3981cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // We should have already handled degenerates 3991cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrAssert(ab.length() > 0 && cb.length() > 0); 4001cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 4011cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger ab.normalize(); 4021cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkVector abN; 4031cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger abN.setOrthog(ab, SkVector::kLeft_Side); 4041cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (abN.dot(ac) > 0) { 4051cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger abN.negate(); 4061cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 4071cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 4081cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger cb.normalize(); 4091cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkVector cbN; 4101cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger cbN.setOrthog(cb, SkVector::kLeft_Side); 4111cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (cbN.dot(ac) < 0) { 4121cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger cbN.negate(); 4131cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 4141cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 4151cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger a0.fPos = a; 4161cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger a0.fPos += abN; 4171cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger a1.fPos = a; 4181cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger a1.fPos -= abN; 4191cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 4201cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger c0.fPos = c; 4211cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger c0.fPos += cbN; 4221cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger c1.fPos = c; 4231cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger c1.fPos -= cbN; 4241cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 4251cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger intersect_lines(a0.fPos, abN, c0.fPos, cbN, &b0.fPos); 4261cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 4271cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (toSrc) { 4281cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger toSrc->mapPointsWithStride(&verts[0].fPos, sizeof(Vertex), kVertsPerQuad); 4291cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 4301cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger DevToUV.mapPointsWithStride(&verts[0].fQuadCoord, 4311cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger &verts[0].fPos, sizeof(Vertex), kVertsPerQuad); 4321cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger} 4331cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 4341cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergervoid add_quads(const SkPoint p[3], 4351cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger int subdiv, 4361cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger const GrMatrix* toDevice, 4371cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger const GrMatrix* toSrc, 4381cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger Vertex** vert) { 4391cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrAssert(subdiv >= 0); 4401cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (subdiv) { 4411cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkPoint newP[5]; 4421cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkChopQuadAtHalf(p, newP); 4431cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger add_quads(newP + 0, subdiv-1, toDevice, toSrc, vert); 4441cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger add_quads(newP + 2, subdiv-1, toDevice, toSrc, vert); 4451cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } else { 4461cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger bloat_quad(p, toDevice, toSrc, *vert); 4471cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger *vert += kVertsPerQuad; 4481cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 4491cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger} 4501cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 4511cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergervoid add_line(const SkPoint p[2], 4521cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger int rtHeight, 4531cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger const SkMatrix* toSrc, 4541cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger Vertex** vert) { 4551cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger const SkPoint& a = p[0]; 4561cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger const SkPoint& b = p[1]; 4571cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 4581cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkVector orthVec = b; 4591cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger orthVec -= a; 4601cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 4611cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (orthVec.setLength(SK_Scalar1)) { 4621cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger orthVec.setOrthog(orthVec); 4631cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 4641cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // the values we pass down to the frag shader 4651cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // have to be in y-points-up space; 4661cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkVector normal; 4671cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger normal.fX = orthVec.fX; 4681cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger normal.fY = -orthVec.fY; 4691cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkPoint aYDown; 4701cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger aYDown.fX = a.fX; 4711cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger aYDown.fY = rtHeight - a.fY; 4721cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 4731cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkScalar lineC = -(aYDown.dot(normal)); 4741cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger for (int i = 0; i < kVertsPerLineSeg; ++i) { 4751cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger (*vert)[i].fPos = (i < 2) ? a : b; 4761cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (0 == i || 3 == i) { 4771cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger (*vert)[i].fPos -= orthVec; 4781cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } else { 4791cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger (*vert)[i].fPos += orthVec; 4801cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 4811cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger (*vert)[i].fLine.fA = normal.fX; 4821cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger (*vert)[i].fLine.fB = normal.fY; 4831cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger (*vert)[i].fLine.fC = lineC; 4841cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 4851cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (NULL != toSrc) { 4861cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger toSrc->mapPointsWithStride(&(*vert)->fPos, 4871cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger sizeof(Vertex), 4881cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger kVertsPerLineSeg); 4891cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 4901cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } else { 4911cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // just make it degenerate and likely offscreen 4921cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger (*vert)[0].fPos.set(SK_ScalarMax, SK_ScalarMax); 4931cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger (*vert)[1].fPos.set(SK_ScalarMax, SK_ScalarMax); 4941cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger (*vert)[2].fPos.set(SK_ScalarMax, SK_ScalarMax); 4951cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger (*vert)[3].fPos.set(SK_ScalarMax, SK_ScalarMax); 4961cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 4971cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 4981cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger *vert += kVertsPerLineSeg; 4991cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger} 5001cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 5011cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger} 5021cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 5034f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenbergerbool GrAAHairLinePathRenderer::createGeom(const SkPath& path, 5044f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger const GrVec* translate, 5054f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger GrDrawTarget* target, 5064f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger GrDrawState::StageMask stageMask, 5074f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger int* lineCnt, 5084f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger int* quadCnt) { 5094f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger const GrDrawState& drawState = target->getDrawState(); 5101cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger int rtHeight = drawState.getRenderTarget()->height(); 5111cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 5121cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrIRect clip; 5134f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger if (target->getClip().hasConservativeBounds()) { 5144f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger GrRect clipRect = target->getClip().getConservativeBounds(); 5151cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger clipRect.roundOut(&clip); 5161cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } else { 5171cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger clip.setLargest(); 5181cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 5191cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 5201cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 5211cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrVertexLayout layout = GrDrawTarget::kEdge_VertexLayoutBit; 5221cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger for (int s = 0; s < GrDrawState::kNumStages; ++s) { 5231cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if ((1 << s) & stageMask) { 5241cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s); 5251cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 5261cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 5271cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 5281cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrMatrix viewM = drawState.getViewMatrix(); 5291cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 5301cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger PREALLOC_PTARRAY(128) lines; 5311cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger PREALLOC_PTARRAY(128) quads; 5321cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger IntArray qSubdivs; 5334f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger static const GrVec gZeroVec = {0, 0}; 5344f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger if (NULL == translate) { 5354f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger translate = &gZeroVec; 5364f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger } 5374f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger *quadCnt = generate_lines_and_quads(path, viewM, *translate, clip, 5381cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger &lines, &quads, &qSubdivs); 5391cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 5404f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger *lineCnt = lines.count() / 2; 5414f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger int vertCnt = kVertsPerLineSeg * *lineCnt + kVertsPerQuad * *quadCnt; 5421cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 5431cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrAssert(sizeof(Vertex) == GrDrawTarget::VertexSize(layout)); 5441cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 5451cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger Vertex* verts; 5464f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger if (!target->reserveVertexSpace(layout, vertCnt, (void**)&verts)) { 5471cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger return false; 5481cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 5491cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 5501cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger const GrMatrix* toDevice = NULL; 5511cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger const GrMatrix* toSrc = NULL; 5521cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrMatrix ivm; 5531cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 5541cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (viewM.hasPerspective()) { 5551cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (viewM.invert(&ivm)) { 5561cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger toDevice = &viewM; 5571cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger toSrc = &ivm; 5581cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 5591cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 5601cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 5614f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger for (int i = 0; i < *lineCnt; ++i) { 5621cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger add_line(&lines[2*i], rtHeight, toSrc, &verts); 5631cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 5641cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 5651cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger int unsubdivQuadCnt = quads.count() / 3; 5661cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger for (int i = 0; i < unsubdivQuadCnt; ++i) { 5671cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrAssert(qSubdivs[i] >= 0); 5681cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger add_quads(&quads[3*i], qSubdivs[i], toDevice, toSrc, &verts); 5691cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 5701cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 5711cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger return true; 5721cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger} 5731cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 5744f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenbergerbool GrAAHairLinePathRenderer::canDrawPath(const SkPath& path, 5754f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger GrPathFill fill, 5764f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger const GrDrawTarget* target, 5774f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger bool antiAlias) const { 5784f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger if (fill != kHairLine_PathFill || !antiAlias) { 5794f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger return false; 5804f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger } 5811cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 5824f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger static const uint32_t gReqDerivMask = SkPath::kCubic_SegmentMask | 5834f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger SkPath::kQuad_SegmentMask; 5844f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger if (!target->getCaps().fShaderDerivativeSupport && 5854f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger (gReqDerivMask & path.getSegmentMasks())) { 5864f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger return false; 5871cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 5884f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger return true; 5894f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger} 5901cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 5914f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenbergerbool GrAAHairLinePathRenderer::onDrawPath(const SkPath& path, 5924f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger GrPathFill fill, 5934f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger const GrVec* translate, 5944f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger GrDrawTarget* target, 5954f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger GrDrawState::StageMask stageMask, 5964f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger bool antiAlias) { 5974f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger 5984f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger int lineCnt; 5994f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger int quadCnt; 6004f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger 6014f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger if (!this->createGeom(path, 6024f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger translate, 6034f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger target, 6044f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger stageMask, 6054f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger &lineCnt, 6064f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger &quadCnt)) { 6074f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger return false; 6084f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger } 6094f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger 6104f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger GrDrawState* drawState = target->drawState(); 6111cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 6121cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrDrawTarget::AutoStateRestore asr; 6131cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (!drawState->getViewMatrix().hasPerspective()) { 6144f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger asr.set(target); 6151cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GrMatrix ivm; 6161cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (drawState->getViewInverse(&ivm)) { 6171cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger drawState->preConcatSamplerMatrices(stageMask, ivm); 6181cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 6191cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger drawState->setViewMatrix(GrMatrix::I()); 6201cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 6211cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 6221cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // TODO: See whether rendering lines as degenerate quads improves perf 6231cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // when we have a mix 6244f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger target->setIndexSourceToBuffer(fLinesIndexBuffer); 6251cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger int lines = 0; 6261cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger int nBufLines = fLinesIndexBuffer->maxQuads(); 6274f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger while (lines < lineCnt) { 6284f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger int n = GrMin(lineCnt - lines, nBufLines); 6291cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger drawState->setVertexEdgeType(GrDrawState::kHairLine_EdgeType); 6304f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger target->drawIndexed(kTriangles_PrimitiveType, 6314f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger kVertsPerLineSeg*lines, // startV 6324f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger 0, // startI 6334f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger kVertsPerLineSeg*n, // vCount 6344f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger kIdxsPerLineSeg*n); // iCount 6351cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger lines += n; 6361cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 6371cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 6384f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger target->setIndexSourceToBuffer(fQuadsIndexBuffer); 6391cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger int quads = 0; 6404f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger while (quads < quadCnt) { 6414f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger int n = GrMin(quadCnt - quads, kNumQuadsInIdxBuffer); 6421cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger drawState->setVertexEdgeType(GrDrawState::kHairQuad_EdgeType); 6434f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger target->drawIndexed(kTriangles_PrimitiveType, 6444f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger 4 * lineCnt + kVertsPerQuad*quads, // startV 6454f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger 0, // startI 6464f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger kVertsPerQuad*n, // vCount 6474f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger kIdxsPerQuad*n); // iCount 6481cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger quads += n; 6491cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 6504f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger return true; 6511cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger} 6521cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 653