bool GrClipMaskManager::canStencilAndDrawElement(GrTexture* target, const SkClipStack::Element* element, GrPathRenderer** pr) { GrDrawState* drawState = fGpu->drawState(); drawState->setRenderTarget(target->asRenderTarget()); switch (element->getType()) { case Element::kRect_Type: return true; case Element::kPath_Type: { SkTCopyOnFirstWrite<SkPath> path(element->getPath()); if (path->isInverseFillType()) { path.writable()->toggleInverseFillType(); } SkStroke stroke; stroke.setDoFill(true); GrPathRendererChain::DrawType type = element->isAA() ? GrPathRendererChain::kStencilAndColorAntiAlias_DrawType : GrPathRendererChain::kStencilAndColor_DrawType; *pr = this->getContext()->getPathRenderer(*path, stroke, fGpu, false, type); return NULL != *pr; } default: // something is wrong if we're trying to draw an empty element. GrCrash("Unexpected element type"); return false; } }
bool SkStrokeRec::applyToPath(SkPath* dst, const SkPath& src) const { if (fWidth <= 0) { // hairline or fill return false; } SkStroke stroker; stroker.setCap(fCap); stroker.setJoin(fJoin); stroker.setMiterLimit(fMiterLimit); stroker.setWidth(fWidth); stroker.setDoFill(fStrokeAndFill); stroker.strokePath(src, dst); return true; }
bool GrClipMaskManager::drawElement(GrTexture* target, const SkClipStack::Element* element, GrPathRenderer* pr) { GrDrawState* drawState = fGpu->drawState(); drawState->setRenderTarget(target->asRenderTarget()); switch (element->getType()) { case Element::kRect_Type: // TODO: Do rects directly to the accumulator using a aa-rect GrEffect that covers the // entire mask bounds and writes 0 outside the rect. if (element->isAA()) { getContext()->getAARectRenderer()->fillAARect(fGpu, fGpu, element->getRect(), false); } else { fGpu->drawSimpleRect(element->getRect(), NULL); } return true; case Element::kPath_Type: { SkTCopyOnFirstWrite<SkPath> path(element->getPath()); if (path->isInverseFillType()) { path.writable()->toggleInverseFillType(); } SkStroke stroke; stroke.setDoFill(true); if (NULL == pr) { GrPathRendererChain::DrawType type; type = element->isAA() ? GrPathRendererChain::kColorAntiAlias_DrawType : GrPathRendererChain::kColor_DrawType; pr = this->getContext()->getPathRenderer(*path, stroke, fGpu, false, type); } if (NULL == pr) { return false; } pr->drawPath(element->getPath(), stroke, fGpu, element->isAA()); break; } default: // something is wrong if we're trying to draw an empty element. GrCrash("Unexpected element type"); return false; } return true; }
bool SkStrokeRec::applyToPath(SkPath* dst, const SkPath& src) const { if (fWidth <= 0) { // hairline or fill return false; } SkStroke stroker; stroker.setCap(fCap); stroker.setJoin(fJoin); stroker.setMiterLimit(fMiterLimit); stroker.setWidth(fWidth); stroker.setDoFill(fStrokeAndFill); #if QUAD_STROKE_APPROXIMATION stroker.setError(1); #endif stroker.strokePath(src, dst); return true; }
/* * This method traverses the clip stack to see if the GrSoftwarePathRenderer * will be used on any element. If so, it returns true to indicate that the * entire clip should be rendered in SW and then uploaded en masse to the gpu. */ bool GrClipMaskManager::useSWOnlyPath(const ElementList& elements) { // TODO: generalize this function so that when // a clip gets complex enough it can just be done in SW regardless // of whether it would invoke the GrSoftwarePathRenderer. SkStroke stroke; stroke.setDoFill(true); for (ElementList::Iter iter(elements.headIter()); iter.get(); iter.next()) { const Element* element = iter.get(); // rects can always be drawn directly w/o using the software path // so only paths need to be checked if (Element::kPath_Type == element->getType() && path_needs_SW_renderer(this->getContext(), fGpu, element->getPath(), stroke, element->isAA())) { return true; } } return false; }
bool SkStrokePathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width) { if (fWidth < 0 || fStyle == SkPaint::kFill_Style) return false; if (fStyle == SkPaint::kStroke_Style && fWidth == 0) // hairline { *width = 0; return true; } SkStroke stroke; stroke.setWidth(fWidth); stroke.setMiterLimit(fMiter); stroke.setJoin((SkPaint::Join)fJoin); stroke.setCap((SkPaint::Cap)fCap); stroke.setDoFill(fStyle == SkPaint::kStrokeAndFill_Style); stroke.strokePath(src, dst); return true; }
void SkScalerContext::internalGetPath(const SkGlyph& glyph, SkPath* fillPath, SkPath* devPath, SkMatrix* fillToDevMatrix) { SkPath path; this->getGlyphContext(glyph)->generatePath(glyph, &path); if (fRec.fFrameWidth > 0 || fPathEffect != NULL) { // need the path in user-space, with only the point-size applied // so that our stroking and effects will operate the same way they // would if the user had extracted the path themself, and then // called drawPath SkPath localPath; SkMatrix matrix, inverse; fRec.getMatrixFrom2x2(&matrix); matrix.invert(&inverse); path.transform(inverse, &localPath); // now localPath is only affected by the paint settings, and not the canvas matrix SkScalar width = fRec.fFrameWidth; if (fPathEffect) { SkPath effectPath; if (fPathEffect->filterPath(&effectPath, localPath, &width)) { localPath.swap(effectPath); } } if (width > 0) { SkStroke stroker; SkPath outline; stroker.setWidth(width); stroker.setMiterLimit(fRec.fMiterLimit); stroker.setJoin((SkPaint::Join)fRec.fStrokeJoin); stroker.setDoFill(SkToBool(fRec.fFlags & kFrameAndFill_Flag)); stroker.strokePath(localPath, &outline); localPath.swap(outline); } // now return stuff to the caller if (fillToDevMatrix) { *fillToDevMatrix = matrix; } if (devPath) { localPath.transform(matrix, devPath); } if (fillPath) { fillPath->swap(localPath); } } else { // nothing tricky to do if (fillToDevMatrix) { fillToDevMatrix->reset(); } if (devPath) { if (fillPath == NULL) { devPath->swap(path); } else { *devPath = path; } } if (fillPath) { fillPath->swap(path); } } if (devPath) { devPath->updateBoundsCache(); } if (fillPath) { fillPath->updateBoundsCache(); } }
//////////////////////////////////////////////////////////////////////////////// // Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device // (as opposed to canvas) coordinates bool GrClipMaskManager::createStencilClipMask(InitialState initialState, const ElementList& elements, const SkIRect& clipSpaceIBounds, const SkIPoint& clipSpaceToStencilOffset) { GrAssert(kNone_ClipMaskType == fCurrClipMaskType); GrDrawState* drawState = fGpu->drawState(); GrAssert(drawState->isClipState()); GrRenderTarget* rt = drawState->getRenderTarget(); GrAssert(NULL != rt); // TODO: dynamically attach a SB when needed. GrStencilBuffer* stencilBuffer = rt->getStencilBuffer(); if (NULL == stencilBuffer) { return false; } int32_t genID = elements.tail()->getGenID(); if (stencilBuffer->mustRenderClip(genID, clipSpaceIBounds, clipSpaceToStencilOffset)) { stencilBuffer->setLastClip(genID, clipSpaceIBounds, clipSpaceToStencilOffset); GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit); drawState = fGpu->drawState(); drawState->setRenderTarget(rt); GrDrawTarget::AutoGeometryPush agp(fGpu); // We set the current clip to the bounds so that our recursive draws are scissored to them. SkIRect stencilSpaceIBounds(clipSpaceIBounds); stencilSpaceIBounds.offset(clipSpaceToStencilOffset); GrDrawTarget::AutoClipRestore acr(fGpu, stencilSpaceIBounds); drawState->enableState(GrDrawState::kClip_StateBit); // Set the matrix so that rendered clip elements are transformed from clip to stencil space. SkVector translate = { SkIntToScalar(clipSpaceToStencilOffset.fX), SkIntToScalar(clipSpaceToStencilOffset.fY) }; drawState->viewMatrix()->setTranslate(translate); #if !VISUALIZE_COMPLEX_CLIP drawState->enableState(GrDrawState::kNoColorWrites_StateBit); #endif int clipBit = stencilBuffer->bits(); SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil buffers"); clipBit = (1 << (clipBit-1)); GrIRect devRTRect = GrIRect::MakeWH(rt->width(), rt->height()); fGpu->clearStencilClip(stencilSpaceIBounds, kAllIn_InitialState == initialState); // walk through each clip element and perform its set op // with the existing clip. for (ElementList::Iter iter(elements.headIter()); NULL != iter.get(); iter.next()) { const Element* element = iter.get(); SkPath::FillType fill; bool fillInverted = false; // enabled at bottom of loop drawState->disableState(GrGpu::kModifyStencilClip_StateBit); // if the target is MSAA then we want MSAA enabled when the clip is soft if (rt->isMultisampled()) { drawState->setState(GrDrawState::kHWAntialias_StateBit, element->isAA()); } // This will be used to determine whether the clip shape can be rendered into the // stencil with arbitrary stencil settings. GrPathRenderer::StencilSupport stencilSupport; SkStroke stroke; stroke.setDoFill(true); SkRegion::Op op = element->getOp(); GrPathRenderer* pr = NULL; SkTCopyOnFirstWrite<SkPath> clipPath; if (Element::kRect_Type == element->getType()) { stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport; fill = SkPath::kEvenOdd_FillType; fillInverted = false; } else { GrAssert(Element::kPath_Type == element->getType()); clipPath.init(element->getPath()); fill = clipPath->getFillType(); fillInverted = clipPath->isInverseFillType(); if (fillInverted) { clipPath.writable()->toggleInverseFillType(); fill = clipPath->getFillType(); } pr = this->getContext()->getPathRenderer(*clipPath, stroke, fGpu, false, GrPathRendererChain::kStencilOnly_DrawType, &stencilSupport); if (NULL == pr) { return false; } } int passes; GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses]; bool canRenderDirectToStencil = GrPathRenderer::kNoRestriction_StencilSupport == stencilSupport; bool canDrawDirectToClip; // Given the renderer, the element, // fill rule, and set operation can // we render the element directly to // stencil bit used for clipping. canDrawDirectToClip = GrStencilSettings::GetClipPasses(op, canRenderDirectToStencil, clipBit, fillInverted, &passes, stencilSettings); // draw the element to the client stencil bits if necessary if (!canDrawDirectToClip) { GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil, kIncClamp_StencilOp, kIncClamp_StencilOp, kAlways_StencilFunc, 0xffff, 0x0000, 0xffff); SET_RANDOM_COLOR if (Element::kRect_Type == element->getType()) { *drawState->stencil() = gDrawToStencil; fGpu->drawSimpleRect(element->getRect(), NULL); } else { GrAssert(Element::kPath_Type == element->getType()); if (canRenderDirectToStencil) { *drawState->stencil() = gDrawToStencil; pr->drawPath(*clipPath, stroke, fGpu, false); } else { pr->stencilPath(*clipPath, stroke, fGpu); } } } // now we modify the clip bit by rendering either the clip // element directly or a bounding rect of the entire clip. drawState->enableState(GrGpu::kModifyStencilClip_StateBit); for (int p = 0; p < passes; ++p) { *drawState->stencil() = stencilSettings[p]; if (canDrawDirectToClip) { if (Element::kRect_Type == element->getType()) { SET_RANDOM_COLOR fGpu->drawSimpleRect(element->getRect(), NULL); } else { GrAssert(Element::kPath_Type == element->getType()); SET_RANDOM_COLOR pr->drawPath(*clipPath, stroke, fGpu, false); } } else { SET_RANDOM_COLOR // The view matrix is setup to do clip space -> stencil space translation, so // draw rect in clip space. fGpu->drawSimpleRect(SkRect::MakeFromIRect(clipSpaceIBounds), NULL); } } }