//////////////////////////////////////////////////////////////////////////////// // Create a 8-bit clip mask in alpha bool GrClipMaskManager::createAlphaClipMask(const GrClipData& clipDataIn, GrTexture** result, GrIRect *devResultBounds) { GrAssert(NULL != devResultBounds); GrAssert(kNone_ClipMaskType == fCurrClipMaskType); if (this->clipMaskPreamble(clipDataIn, result, devResultBounds)) { fCurrClipMaskType = kAlpha_ClipMaskType; return true; } // Note: 'resultBounds' is in device (as opposed to canvas) coordinates GrTexture* accum = fAACache.getLastMask(); if (NULL == accum) { fAACache.reset(); return false; } GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit); GrDrawState* drawState = fGpu->drawState(); GrDrawTarget::AutoGeometryPush agp(fGpu); if (0 != devResultBounds->fTop || 0 != devResultBounds->fLeft || 0 != clipDataIn.fOrigin.fX || 0 != clipDataIn.fOrigin.fY) { // if we were able to trim down the size of the mask we need to // offset the paths & rects that will be used to compute it drawState->viewMatrix()->setTranslate( SkIntToScalar(-devResultBounds->fLeft-clipDataIn.fOrigin.fX), SkIntToScalar(-devResultBounds->fTop-clipDataIn.fOrigin.fY)); } bool clearToInside; SkRegion::Op firstOp = SkRegion::kReplace_Op; // suppress warning SkClipStack::Iter iter(*clipDataIn.fClipStack, SkClipStack::Iter::kBottom_IterStart); const SkClipStack::Iter::Clip* clip = process_initial_clip_elements(&iter, *devResultBounds, &clearToInside, &firstOp, clipDataIn); fGpu->clear(NULL, clearToInside ? 0xffffffff : 0x00000000, accum->asRenderTarget()); GrAutoScratchTexture temp; bool first = true; // walk through each clip element and perform its set op for ( ; NULL != clip; clip = iter.next()) { SkRegion::Op op = clip->fOp; if (first) { first = false; op = firstOp; } if (SkRegion::kReplace_Op == op) { // clear the accumulator and draw the new object directly into it fGpu->clear(NULL, 0x00000000, accum->asRenderTarget()); setup_boolean_blendcoeffs(drawState, op); this->drawClipShape(accum, clip, *devResultBounds); } else if (SkRegion::kReverseDifference_Op == op || SkRegion::kIntersect_Op == op) { // there is no point in intersecting a screen filling rectangle. if (SkRegion::kIntersect_Op == op && NULL != clip->fRect && contains(*clip->fRect, *devResultBounds, clipDataIn.fOrigin)) { continue; } getTemp(*devResultBounds, &temp); if (NULL == temp.texture()) { fAACache.reset(); return false; } // clear the temp target & draw into it fGpu->clear(NULL, 0x00000000, temp.texture()->asRenderTarget()); setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op); this->drawClipShape(temp.texture(), clip, *devResultBounds); // TODO: rather than adding these two translations here // compute the bounding box needed to render the texture // into temp if (0 != devResultBounds->fTop || 0 != devResultBounds->fLeft || 0 != clipDataIn.fOrigin.fX || 0 != clipDataIn.fOrigin.fY) { // In order for the merge of the temp clip into the accumulator // to work we need to disable the translation drawState->viewMatrix()->reset(); } // Now draw into the accumulator using the real operation // and the temp buffer as a texture setup_boolean_blendcoeffs(drawState, op); this->drawTexture(accum, temp.texture()); if (0 != devResultBounds->fTop || 0 != devResultBounds->fLeft || 0 != clipDataIn.fOrigin.fX || 0 != clipDataIn.fOrigin.fY) { drawState->viewMatrix()->setTranslate( SkIntToScalar(-devResultBounds->fLeft-clipDataIn.fOrigin.fX), SkIntToScalar(-devResultBounds->fTop-clipDataIn.fOrigin.fY)); } } else { // all the remaining ops can just be directly draw into // the accumulation buffer setup_boolean_blendcoeffs(drawState, op); this->drawClipShape(accum, clip, *devResultBounds); } } *result = accum; fCurrClipMaskType = kAlpha_ClipMaskType; return true; }
//////////////////////////////////////////////////////////////////////////////// // Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device // (as opposed to canvas) coordinates bool GrClipMaskManager::createStencilClipMask(const GrClipData& clipDataIn, const GrIRect& devClipBounds) { 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; } if (stencilBuffer->mustRenderClip(clipDataIn, rt->width(), rt->height())) { stencilBuffer->setLastClip(clipDataIn, rt->width(), rt->height()); // we set the current clip to the bounds so that our recursive // draws are scissored to them. We use the copy of the complex clip // we just stashed on the SB to render from. We set it back after // we finish drawing it into the stencil. const GrClipData* oldClipData = fGpu->getClip(); // The origin of 'newClipData' is (0, 0) so it is okay to place // a device-coordinate bound in 'newClipStack' SkClipStack newClipStack(devClipBounds); GrClipData newClipData; newClipData.fClipStack = &newClipStack; fGpu->setClip(&newClipData); GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit); drawState = fGpu->drawState(); drawState->setRenderTarget(rt); GrDrawTarget::AutoGeometryPush agp(fGpu); if (0 != clipDataIn.fOrigin.fX || 0 != clipDataIn.fOrigin.fY) { // Add the saveLayer's offset to the view matrix rather than // offset each individual draw drawState->viewMatrix()->setTranslate( SkIntToScalar(-clipDataIn.fOrigin.fX), SkIntToScalar(-clipDataIn.fOrigin.fY)); } #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()); bool clearToInside; SkRegion::Op firstOp = SkRegion::kReplace_Op; // suppress warning SkClipStack::Iter iter(*oldClipData->fClipStack, SkClipStack::Iter::kBottom_IterStart); const SkClipStack::Iter::Clip* clip = process_initial_clip_elements(&iter, devRTRect, &clearToInside, &firstOp, clipDataIn); fGpu->clearStencilClip(devClipBounds, clearToInside); bool first = true; // walk through each clip element and perform its set op // with the existing clip. for ( ; NULL != clip; clip = iter.next()) { GrPathFill 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()) { if (clip->fDoAA) { drawState->enableState(GrDrawState::kHWAntialias_StateBit); } else { drawState->disableState(GrDrawState::kHWAntialias_StateBit); } } // Can the clip element be drawn directly to the stencil buffer // with a non-inverted fill rule without extra passes to // resolve in/out status? bool canRenderDirectToStencil = false; SkRegion::Op op = clip->fOp; if (first) { first = false; op = firstOp; } GrPathRenderer* pr = NULL; const SkPath* clipPath = NULL; if (NULL != clip->fRect) { canRenderDirectToStencil = true; fill = kEvenOdd_GrPathFill; fillInverted = false; // there is no point in intersecting a screen filling // rectangle. if (SkRegion::kIntersect_Op == op && contains(*clip->fRect, devRTRect, oldClipData->fOrigin)) { continue; } } else { GrAssert(NULL != clip->fPath); fill = get_path_fill(*clip->fPath); fillInverted = GrIsFillInverted(fill); fill = GrNonInvertedFill(fill); clipPath = clip->fPath; pr = this->getContext()->getPathRenderer(*clipPath, fill, fGpu, false, true); if (NULL == pr) { fGpu->setClip(oldClipData); return false; } canRenderDirectToStencil = !pr->requiresStencilPass(*clipPath, fill, fGpu); } int passes; GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses]; 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 (NULL != clip->fRect) { *drawState->stencil() = gDrawToStencil; fGpu->drawSimpleRect(*clip->fRect, NULL); } else { if (canRenderDirectToStencil) { *drawState->stencil() = gDrawToStencil; pr->drawPath(*clipPath, fill, NULL, fGpu, false); } else { pr->drawPathToStencil(*clipPath, fill, 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 (NULL != clip->fRect) { SET_RANDOM_COLOR fGpu->drawSimpleRect(*clip->fRect, NULL); } else { SET_RANDOM_COLOR pr->drawPath(*clipPath, fill, NULL, fGpu, false); } } else { SET_RANDOM_COLOR // 'devClipBounds' is already in device coordinates so the // translation in the view matrix is inappropriate. // Convert it to canvas space so the drawn rect will // be in the correct location GrRect canvClipBounds; canvClipBounds.set(devClipBounds); device_to_canvas(&canvClipBounds, clipDataIn.fOrigin); fGpu->drawSimpleRect(canvClipBounds, NULL); } } }
bool GrAAHairLinePathRenderer::onDrawPath(const SkPath& path, GrPathFill fill, const GrVec* translate, GrDrawTarget* target, GrDrawState::StageMask stageMask, bool antiAlias) { int lineCnt; int quadCnt; GrDrawTarget::AutoReleaseGeometry arg; if (!this->createGeom(path, translate, target, stageMask, &lineCnt, &quadCnt, &arg)) { return false; } GrDrawTarget::AutoStateRestore asr; GrDrawState* drawState = target->drawState(); if (!drawState->getViewMatrix().hasPerspective()) { // we are going to whack the view matrix to identity to remove // perspective. asr.set(target, GrDrawTarget::kPreserve_ASRInit); drawState = target->drawState(); GrMatrix ivm; if (drawState->getViewInverse(&ivm)) { drawState->preConcatSamplerMatrices(stageMask, ivm); } drawState->viewMatrix()->reset(); } // TODO: See whether rendering lines as degenerate quads improves perf // when we have a mix target->setIndexSourceToBuffer(fLinesIndexBuffer); int lines = 0; int nBufLines = fLinesIndexBuffer->maxQuads(); while (lines < lineCnt) { int n = GrMin(lineCnt - lines, nBufLines); drawState->setVertexEdgeType(GrDrawState::kHairLine_EdgeType); target->drawIndexed(kTriangles_GrPrimitiveType, kVertsPerLineSeg*lines, // startV 0, // startI kVertsPerLineSeg*n, // vCount kIdxsPerLineSeg*n); // iCount lines += n; } target->setIndexSourceToBuffer(fQuadsIndexBuffer); int quads = 0; while (quads < quadCnt) { int n = GrMin(quadCnt - quads, kNumQuadsInIdxBuffer); drawState->setVertexEdgeType(GrDrawState::kHairQuad_EdgeType); target->drawIndexed(kTriangles_GrPrimitiveType, 4 * lineCnt + kVertsPerQuad*quads, // startV 0, // startI kVertsPerQuad*n, // vCount kIdxsPerQuad*n); // iCount quads += n; } return true; }