| /* | 
 |  * Copyright 2011 Google Inc. | 
 |  * | 
 |  * Use of this source code is governed by a BSD-style license that can be | 
 |  * found in the LICENSE file. | 
 |  */ | 
 |  | 
 | #include "GrDefaultPathRenderer.h" | 
 |  | 
 | #include "GrContext.h" | 
 | #include "GrDrawState.h" | 
 | #include "GrPathUtils.h" | 
 | #include "SkString.h" | 
 | #include "SkStrokeRec.h" | 
 | #include "SkTrace.h" | 
 |  | 
 |  | 
 | GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport, | 
 |                                              bool stencilWrapOpsSupport) | 
 |     : fSeparateStencil(separateStencilSupport) | 
 |     , fStencilWrapOps(stencilWrapOpsSupport) { | 
 | } | 
 |  | 
 |  | 
 | //////////////////////////////////////////////////////////////////////////////// | 
 | // Stencil rules for paths | 
 |  | 
 | ////// Even/Odd | 
 |  | 
 | GR_STATIC_CONST_SAME_STENCIL(gEOStencilPass, | 
 |     kInvert_StencilOp, | 
 |     kKeep_StencilOp, | 
 |     kAlwaysIfInClip_StencilFunc, | 
 |     0xffff, | 
 |     0xffff, | 
 |     0xffff); | 
 |  | 
 | // ok not to check clip b/c stencil pass only wrote inside clip | 
 | GR_STATIC_CONST_SAME_STENCIL(gEOColorPass, | 
 |     kZero_StencilOp, | 
 |     kZero_StencilOp, | 
 |     kNotEqual_StencilFunc, | 
 |     0xffff, | 
 |     0x0000, | 
 |     0xffff); | 
 |  | 
 | // have to check clip b/c outside clip will always be zero. | 
 | GR_STATIC_CONST_SAME_STENCIL(gInvEOColorPass, | 
 |     kZero_StencilOp, | 
 |     kZero_StencilOp, | 
 |     kEqualIfInClip_StencilFunc, | 
 |     0xffff, | 
 |     0x0000, | 
 |     0xffff); | 
 |  | 
 | ////// Winding | 
 |  | 
 | // when we have separate stencil we increment front faces / decrement back faces | 
 | // when we don't have wrap incr and decr we use the stencil test to simulate | 
 | // them. | 
 |  | 
 | GR_STATIC_CONST_STENCIL(gWindStencilSeparateWithWrap, | 
 |     kIncWrap_StencilOp,             kDecWrap_StencilOp, | 
 |     kKeep_StencilOp,                kKeep_StencilOp, | 
 |     kAlwaysIfInClip_StencilFunc,    kAlwaysIfInClip_StencilFunc, | 
 |     0xffff,                         0xffff, | 
 |     0xffff,                         0xffff, | 
 |     0xffff,                         0xffff); | 
 |  | 
 | // if inc'ing the max value, invert to make 0 | 
 | // if dec'ing zero invert to make all ones. | 
 | // we can't avoid touching the stencil on both passing and | 
 | // failing, so we can't resctrict ourselves to the clip. | 
 | GR_STATIC_CONST_STENCIL(gWindStencilSeparateNoWrap, | 
 |     kInvert_StencilOp,              kInvert_StencilOp, | 
 |     kIncClamp_StencilOp,            kDecClamp_StencilOp, | 
 |     kEqual_StencilFunc,             kEqual_StencilFunc, | 
 |     0xffff,                         0xffff, | 
 |     0xffff,                         0x0000, | 
 |     0xffff,                         0xffff); | 
 |  | 
 | // When there are no separate faces we do two passes to setup the winding rule | 
 | // stencil. First we draw the front faces and inc, then we draw the back faces | 
 | // and dec. These are same as the above two split into the incrementing and | 
 | // decrementing passes. | 
 | GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapInc, | 
 |     kIncWrap_StencilOp, | 
 |     kKeep_StencilOp, | 
 |     kAlwaysIfInClip_StencilFunc, | 
 |     0xffff, | 
 |     0xffff, | 
 |     0xffff); | 
 |  | 
 | GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapDec, | 
 |     kDecWrap_StencilOp, | 
 |     kKeep_StencilOp, | 
 |     kAlwaysIfInClip_StencilFunc, | 
 |     0xffff, | 
 |     0xffff, | 
 |     0xffff); | 
 |  | 
 | GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapInc, | 
 |     kInvert_StencilOp, | 
 |     kIncClamp_StencilOp, | 
 |     kEqual_StencilFunc, | 
 |     0xffff, | 
 |     0xffff, | 
 |     0xffff); | 
 |  | 
 | GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapDec, | 
 |     kInvert_StencilOp, | 
 |     kDecClamp_StencilOp, | 
 |     kEqual_StencilFunc, | 
 |     0xffff, | 
 |     0x0000, | 
 |     0xffff); | 
 |  | 
 | // Color passes are the same whether we use the two-sided stencil or two passes | 
 |  | 
 | GR_STATIC_CONST_SAME_STENCIL(gWindColorPass, | 
 |     kZero_StencilOp, | 
 |     kZero_StencilOp, | 
 |     kNonZeroIfInClip_StencilFunc, | 
 |     0xffff, | 
 |     0x0000, | 
 |     0xffff); | 
 |  | 
 | GR_STATIC_CONST_SAME_STENCIL(gInvWindColorPass, | 
 |     kZero_StencilOp, | 
 |     kZero_StencilOp, | 
 |     kEqualIfInClip_StencilFunc, | 
 |     0xffff, | 
 |     0x0000, | 
 |     0xffff); | 
 |  | 
 | ////// Normal render to stencil | 
 |  | 
 | // Sometimes the default path renderer can draw a path directly to the stencil | 
 | // buffer without having to first resolve the interior / exterior. | 
 | GR_STATIC_CONST_SAME_STENCIL(gDirectToStencil, | 
 |     kZero_StencilOp, | 
 |     kIncClamp_StencilOp, | 
 |     kAlwaysIfInClip_StencilFunc, | 
 |     0xffff, | 
 |     0x0000, | 
 |     0xffff); | 
 |  | 
 | //////////////////////////////////////////////////////////////////////////////// | 
 | // Helpers for drawPath | 
 |  | 
 | #define STENCIL_OFF     0   // Always disable stencil (even when needed) | 
 |  | 
 | static inline bool single_pass_path(const SkPath& path, const SkStrokeRec& stroke) { | 
 | #if STENCIL_OFF | 
 |     return true; | 
 | #else | 
 |     if (!stroke.isHairlineStyle() && !path.isInverseFillType()) { | 
 |         return path.isConvex(); | 
 |     } | 
 |     return false; | 
 | #endif | 
 | } | 
 |  | 
 | GrPathRenderer::StencilSupport GrDefaultPathRenderer::onGetStencilSupport( | 
 |                                                             const SkPath& path, | 
 |                                                             const SkStrokeRec& stroke, | 
 |                                                             const GrDrawTarget*) const { | 
 |     if (single_pass_path(path, stroke)) { | 
 |         return GrPathRenderer::kNoRestriction_StencilSupport; | 
 |     } else { | 
 |         return GrPathRenderer::kStencilOnly_StencilSupport; | 
 |     } | 
 | } | 
 |  | 
 | static inline void append_countour_edge_indices(bool hairLine, | 
 |                                                 uint16_t fanCenterIdx, | 
 |                                                 uint16_t edgeV0Idx, | 
 |                                                 uint16_t** indices) { | 
 |     // when drawing lines we're appending line segments along | 
 |     // the contour. When applying the other fill rules we're | 
 |     // drawing triangle fans around fanCenterIdx. | 
 |     if (!hairLine) { | 
 |         *((*indices)++) = fanCenterIdx; | 
 |     } | 
 |     *((*indices)++) = edgeV0Idx; | 
 |     *((*indices)++) = edgeV0Idx + 1; | 
 | } | 
 |  | 
 | bool GrDefaultPathRenderer::createGeom(const SkPath& path, | 
 |                                        const SkStrokeRec& stroke, | 
 |                                        SkScalar srcSpaceTol, | 
 |                                        GrDrawTarget* target, | 
 |                                        GrPrimitiveType* primType, | 
 |                                        int* vertexCnt, | 
 |                                        int* indexCnt, | 
 |                                        GrDrawTarget::AutoReleaseGeometry* arg) { | 
 |     { | 
 |     SK_TRACE_EVENT0("GrDefaultPathRenderer::createGeom"); | 
 |  | 
 |     SkScalar srcSpaceTolSqd = SkScalarMul(srcSpaceTol, srcSpaceTol); | 
 |     int contourCnt; | 
 |     int maxPts = GrPathUtils::worstCasePointCount(path, &contourCnt, | 
 |                                                   srcSpaceTol); | 
 |  | 
 |     if (maxPts <= 0) { | 
 |         return false; | 
 |     } | 
 |     if (maxPts > ((int)SK_MaxU16 + 1)) { | 
 |         GrPrintf("Path not rendered, too many verts (%d)\n", maxPts); | 
 |         return false; | 
 |     } | 
 |  | 
 |     bool indexed = contourCnt > 1; | 
 |  | 
 |     const bool isHairline = stroke.isHairlineStyle(); | 
 |  | 
 |     int maxIdxs = 0; | 
 |     if (isHairline) { | 
 |         if (indexed) { | 
 |             maxIdxs = 2 * maxPts; | 
 |             *primType = kLines_GrPrimitiveType; | 
 |         } else { | 
 |             *primType = kLineStrip_GrPrimitiveType; | 
 |         } | 
 |     } else { | 
 |         if (indexed) { | 
 |             maxIdxs = 3 * maxPts; | 
 |             *primType = kTriangles_GrPrimitiveType; | 
 |         } else { | 
 |             *primType = kTriangleFan_GrPrimitiveType; | 
 |         } | 
 |     } | 
 |  | 
 |     target->drawState()->setDefaultVertexAttribs(); | 
 |     if (!arg->set(target, maxPts, maxIdxs)) { | 
 |         return false; | 
 |     } | 
 |  | 
 |     uint16_t* idxBase = reinterpret_cast<uint16_t*>(arg->indices()); | 
 |     uint16_t* idx = idxBase; | 
 |     uint16_t subpathIdxStart = 0; | 
 |  | 
 |     GrPoint* base = reinterpret_cast<GrPoint*>(arg->vertices()); | 
 |     SkASSERT(NULL != base); | 
 |     GrPoint* vert = base; | 
 |  | 
 |     GrPoint pts[4]; | 
 |  | 
 |     bool first = true; | 
 |     int subpath = 0; | 
 |  | 
 |     SkPath::Iter iter(path, false); | 
 |  | 
 |     for (;;) { | 
 |         SkPath::Verb verb = iter.next(pts); | 
 |         switch (verb) { | 
 |             case SkPath::kConic_Verb: | 
 |                 SkASSERT(0); | 
 |                 break; | 
 |             case SkPath::kMove_Verb: | 
 |                 if (!first) { | 
 |                     uint16_t currIdx = (uint16_t) (vert - base); | 
 |                     subpathIdxStart = currIdx; | 
 |                     ++subpath; | 
 |                 } | 
 |                 *vert = pts[0]; | 
 |                 vert++; | 
 |                 break; | 
 |             case SkPath::kLine_Verb: | 
 |                 if (indexed) { | 
 |                     uint16_t prevIdx = (uint16_t)(vert - base) - 1; | 
 |                     append_countour_edge_indices(isHairline, subpathIdxStart, | 
 |                                                  prevIdx, &idx); | 
 |                 } | 
 |                 *(vert++) = pts[1]; | 
 |                 break; | 
 |             case SkPath::kQuad_Verb: { | 
 |                 // first pt of quad is the pt we ended on in previous step | 
 |                 uint16_t firstQPtIdx = (uint16_t)(vert - base) - 1; | 
 |                 uint16_t numPts =  (uint16_t) | 
 |                     GrPathUtils::generateQuadraticPoints( | 
 |                             pts[0], pts[1], pts[2], | 
 |                             srcSpaceTolSqd, &vert, | 
 |                             GrPathUtils::quadraticPointCount(pts, srcSpaceTol)); | 
 |                 if (indexed) { | 
 |                     for (uint16_t i = 0; i < numPts; ++i) { | 
 |                         append_countour_edge_indices(isHairline, subpathIdxStart, | 
 |                                                      firstQPtIdx + i, &idx); | 
 |                     } | 
 |                 } | 
 |                 break; | 
 |             } | 
 |             case SkPath::kCubic_Verb: { | 
 |                 // first pt of cubic is the pt we ended on in previous step | 
 |                 uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1; | 
 |                 uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints( | 
 |                                 pts[0], pts[1], pts[2], pts[3], | 
 |                                 srcSpaceTolSqd, &vert, | 
 |                                 GrPathUtils::cubicPointCount(pts, srcSpaceTol)); | 
 |                 if (indexed) { | 
 |                     for (uint16_t i = 0; i < numPts; ++i) { | 
 |                         append_countour_edge_indices(isHairline, subpathIdxStart, | 
 |                                                      firstCPtIdx + i, &idx); | 
 |                     } | 
 |                 } | 
 |                 break; | 
 |             } | 
 |             case SkPath::kClose_Verb: | 
 |                 break; | 
 |             case SkPath::kDone_Verb: | 
 |              // uint16_t currIdx = (uint16_t) (vert - base); | 
 |                 goto FINISHED; | 
 |         } | 
 |         first = false; | 
 |     } | 
 | FINISHED: | 
 |     SkASSERT((vert - base) <= maxPts); | 
 |     SkASSERT((idx - idxBase) <= maxIdxs); | 
 |  | 
 |     *vertexCnt = static_cast<int>(vert - base); | 
 |     *indexCnt = static_cast<int>(idx - idxBase); | 
 |  | 
 |     } | 
 |     return true; | 
 | } | 
 |  | 
 | bool GrDefaultPathRenderer::internalDrawPath(const SkPath& path, | 
 |                                              const SkStrokeRec& stroke, | 
 |                                              GrDrawTarget* target, | 
 |                                              bool stencilOnly) { | 
 |  | 
 |     SkMatrix viewM = target->getDrawState().getViewMatrix(); | 
 |     SkScalar tol = SK_Scalar1; | 
 |     tol = GrPathUtils::scaleToleranceToSrc(tol, viewM, path.getBounds()); | 
 |  | 
 |     int vertexCnt; | 
 |     int indexCnt; | 
 |     GrPrimitiveType primType; | 
 |     GrDrawTarget::AutoReleaseGeometry arg; | 
 |     if (!this->createGeom(path, | 
 |                           stroke, | 
 |                           tol, | 
 |                           target, | 
 |                           &primType, | 
 |                           &vertexCnt, | 
 |                           &indexCnt, | 
 |                           &arg)) { | 
 |         return false; | 
 |     } | 
 |  | 
 |     SkASSERT(NULL != target); | 
 |     GrDrawTarget::AutoStateRestore asr(target, GrDrawTarget::kPreserve_ASRInit); | 
 |     GrDrawState* drawState = target->drawState(); | 
 |     bool colorWritesWereDisabled = drawState->isColorWriteDisabled(); | 
 |     // face culling doesn't make sense here | 
 |     SkASSERT(GrDrawState::kBoth_DrawFace == drawState->getDrawFace()); | 
 |  | 
 |     int                         passCount = 0; | 
 |     const GrStencilSettings*    passes[3]; | 
 |     GrDrawState::DrawFace       drawFace[3]; | 
 |     bool                        reverse = false; | 
 |     bool                        lastPassIsBounds; | 
 |  | 
 |     if (stroke.isHairlineStyle()) { | 
 |         passCount = 1; | 
 |         if (stencilOnly) { | 
 |             passes[0] = &gDirectToStencil; | 
 |         } else { | 
 |             passes[0] = NULL; | 
 |         } | 
 |         lastPassIsBounds = false; | 
 |         drawFace[0] = GrDrawState::kBoth_DrawFace; | 
 |     } else { | 
 |         if (single_pass_path(path, stroke)) { | 
 |             passCount = 1; | 
 |             if (stencilOnly) { | 
 |                 passes[0] = &gDirectToStencil; | 
 |             } else { | 
 |                 passes[0] = NULL; | 
 |             } | 
 |             drawFace[0] = GrDrawState::kBoth_DrawFace; | 
 |             lastPassIsBounds = false; | 
 |         } else { | 
 |             switch (path.getFillType()) { | 
 |                 case SkPath::kInverseEvenOdd_FillType: | 
 |                     reverse = true; | 
 |                     // fallthrough | 
 |                 case SkPath::kEvenOdd_FillType: | 
 |                     passes[0] = &gEOStencilPass; | 
 |                     if (stencilOnly) { | 
 |                         passCount = 1; | 
 |                         lastPassIsBounds = false; | 
 |                     } else { | 
 |                         passCount = 2; | 
 |                         lastPassIsBounds = true; | 
 |                         if (reverse) { | 
 |                             passes[1] = &gInvEOColorPass; | 
 |                         } else { | 
 |                             passes[1] = &gEOColorPass; | 
 |                         } | 
 |                     } | 
 |                     drawFace[0] = drawFace[1] = GrDrawState::kBoth_DrawFace; | 
 |                     break; | 
 |  | 
 |                 case SkPath::kInverseWinding_FillType: | 
 |                     reverse = true; | 
 |                     // fallthrough | 
 |                 case SkPath::kWinding_FillType: | 
 |                     if (fSeparateStencil) { | 
 |                         if (fStencilWrapOps) { | 
 |                             passes[0] = &gWindStencilSeparateWithWrap; | 
 |                         } else { | 
 |                             passes[0] = &gWindStencilSeparateNoWrap; | 
 |                         } | 
 |                         passCount = 2; | 
 |                         drawFace[0] = GrDrawState::kBoth_DrawFace; | 
 |                     } else { | 
 |                         if (fStencilWrapOps) { | 
 |                             passes[0] = &gWindSingleStencilWithWrapInc; | 
 |                             passes[1] = &gWindSingleStencilWithWrapDec; | 
 |                         } else { | 
 |                             passes[0] = &gWindSingleStencilNoWrapInc; | 
 |                             passes[1] = &gWindSingleStencilNoWrapDec; | 
 |                         } | 
 |                         // which is cw and which is ccw is arbitrary. | 
 |                         drawFace[0] = GrDrawState::kCW_DrawFace; | 
 |                         drawFace[1] = GrDrawState::kCCW_DrawFace; | 
 |                         passCount = 3; | 
 |                     } | 
 |                     if (stencilOnly) { | 
 |                         lastPassIsBounds = false; | 
 |                         --passCount; | 
 |                     } else { | 
 |                         lastPassIsBounds = true; | 
 |                         drawFace[passCount-1] = GrDrawState::kBoth_DrawFace; | 
 |                         if (reverse) { | 
 |                             passes[passCount-1] = &gInvWindColorPass; | 
 |                         } else { | 
 |                             passes[passCount-1] = &gWindColorPass; | 
 |                         } | 
 |                     } | 
 |                     break; | 
 |                 default: | 
 |                     SkDEBUGFAIL("Unknown path fFill!"); | 
 |                     return false; | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     SkRect devBounds; | 
 |     GetPathDevBounds(path, drawState->getRenderTarget(), viewM, &devBounds); | 
 |  | 
 |     for (int p = 0; p < passCount; ++p) { | 
 |         drawState->setDrawFace(drawFace[p]); | 
 |         if (NULL != passes[p]) { | 
 |             *drawState->stencil() = *passes[p]; | 
 |         } | 
 |  | 
 |         if (lastPassIsBounds && (p == passCount-1)) { | 
 |             if (!colorWritesWereDisabled) { | 
 |                 drawState->disableState(GrDrawState::kNoColorWrites_StateBit); | 
 |             } | 
 |             SkRect bounds; | 
 |             GrDrawState::AutoViewMatrixRestore avmr; | 
 |             if (reverse) { | 
 |                 SkASSERT(NULL != drawState->getRenderTarget()); | 
 |                 // draw over the dev bounds (which will be the whole dst surface for inv fill). | 
 |                 bounds = devBounds; | 
 |                 SkMatrix vmi; | 
 |                 // mapRect through persp matrix may not be correct | 
 |                 if (!drawState->getViewMatrix().hasPerspective() && | 
 |                     drawState->getViewInverse(&vmi)) { | 
 |                     vmi.mapRect(&bounds); | 
 |                 } else { | 
 |                     avmr.setIdentity(drawState); | 
 |                 } | 
 |             } else { | 
 |                 bounds = path.getBounds(); | 
 |             } | 
 |             GrDrawTarget::AutoGeometryAndStatePush agasp(target, GrDrawTarget::kPreserve_ASRInit); | 
 |             target->drawSimpleRect(bounds, NULL); | 
 |         } else { | 
 |             if (passCount > 1) { | 
 |                 drawState->enableState(GrDrawState::kNoColorWrites_StateBit); | 
 |             } | 
 |             if (indexCnt) { | 
 |                 target->drawIndexed(primType, 0, 0, | 
 |                                     vertexCnt, indexCnt, &devBounds); | 
 |             } else { | 
 |                 target->drawNonIndexed(primType, 0, vertexCnt, &devBounds); | 
 |             } | 
 |         } | 
 |     } | 
 |     return true; | 
 | } | 
 |  | 
 | bool GrDefaultPathRenderer::canDrawPath(const SkPath& path, | 
 |                                         const SkStrokeRec& stroke, | 
 |                                         const GrDrawTarget* target, | 
 |                                         bool antiAlias) const { | 
 |     // this class can draw any path with any fill but doesn't do any anti-aliasing. | 
 |     return (stroke.isFillStyle() || stroke.isHairlineStyle()) && !antiAlias; | 
 | } | 
 |  | 
 | bool GrDefaultPathRenderer::onDrawPath(const SkPath& path, | 
 |                                        const SkStrokeRec& stroke, | 
 |                                        GrDrawTarget* target, | 
 |                                        bool antiAlias) { | 
 |     return this->internalDrawPath(path, | 
 |                                   stroke, | 
 |                                   target, | 
 |                                   false); | 
 | } | 
 |  | 
 | void GrDefaultPathRenderer::onStencilPath(const SkPath& path, | 
 |                                           const SkStrokeRec& stroke, | 
 |                                           GrDrawTarget* target) { | 
 |     SkASSERT(SkPath::kInverseEvenOdd_FillType != path.getFillType()); | 
 |     SkASSERT(SkPath::kInverseWinding_FillType != path.getFillType()); | 
 |     this->internalDrawPath(path, stroke, target, true); | 
 | } |