void GrClipMaskManager::mergeMask(GrTexture* dstMask, GrTexture* srcMask, SkRegion::Op op, const GrIRect& dstBound, const GrIRect& srcBound) { GrDrawState* drawState = fGpu->drawState(); GrAssert(NULL != drawState); SkMatrix oldMatrix = drawState->getViewMatrix(); drawState->viewMatrix()->reset(); drawState->setRenderTarget(dstMask->asRenderTarget()); setup_boolean_blendcoeffs(drawState, op); SkMatrix sampleM; sampleM.setIDiv(srcMask->width(), srcMask->height()); drawState->stage(0)->setEffect( GrTextureDomainEffect::Create(srcMask, sampleM, GrTextureDomainEffect::MakeTexelDomain(srcMask, srcBound), GrTextureDomainEffect::kDecal_WrapMode))->unref(); fGpu->drawSimpleRect(SkRect::MakeFromIRect(dstBound), NULL); drawState->disableStage(0); drawState->setViewMatrix(oldMatrix); }
//////////////////////////////////////////////////////////////////////////////// // 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); // The mask we generate is translated so that its upper-left corner is at devResultBounds // upper-left corner in device space. GrIRect maskResultBounds = GrIRect::MakeWH(devResultBounds->width(), devResultBounds->height()); // Set the matrix so that rendered clip elements are transformed from the space of the clip // stack to the alpha-mask. This accounts for both translation due to the clip-origin and the // placement of the mask within the device. SkVector clipToMaskOffset = { SkIntToScalar(-devResultBounds->fLeft - clipDataIn.fOrigin.fX), SkIntToScalar(-devResultBounds->fTop - clipDataIn.fOrigin.fY) }; drawState->viewMatrix()->setTranslate(clipToMaskOffset); 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); // The scratch texture that we are drawing into can be substantially larger than the mask. Only // clear the part that we care about. fGpu->clear(&maskResultBounds, clearToInside ? 0xffffffff : 0x00000000, accum->asRenderTarget()); bool accumClearedToZero = !clearToInside; GrAutoScratchTexture temp; bool first = true; // walk through each clip element and perform its set op for ( ; NULL != clip; clip = iter.nextCombined()) { 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 if (!accumClearedToZero) { fGpu->clear(&maskResultBounds, 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; } // this is the bounds of the clip element in the space of the alpha-mask. The temporary // mask buffer can be substantially larger than the actually clip stack element. We // touch the minimum number of pixels necessary and use decal mode to combine it with // the accumulator GrRect elementMaskBounds = clip->getBounds(); elementMaskBounds.offset(clipToMaskOffset); GrIRect elementMaskIBounds; elementMaskBounds.roundOut(&elementMaskIBounds); // clear the temp target & draw into it fGpu->clear(&elementMaskIBounds, 0x00000000, temp.texture()->asRenderTarget()); setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op); this->drawClipShape(temp.texture(), clip, elementMaskIBounds); // Now draw into the accumulator using the real operation // and the temp buffer as a texture this->mergeMask(accum, temp.texture(), op, maskResultBounds, elementMaskIBounds); } else { // all the remaining ops can just be directly draw into // the accumulation buffer setup_boolean_blendcoeffs(drawState, op); this->drawClipShape(accum, clip, *devResultBounds); } accumClearedToZero = false; } *result = accum; fCurrClipMaskType = kAlpha_ClipMaskType; return true; }
//////////////////////////////////////////////////////////////////////////////// // Create a 8-bit clip mask in alpha bool GrClipMaskManager::createAlphaClipMask(GrGpu* gpu, const GrClip& clipIn, GrTexture** result, GrIRect *resultBounds) { if (this->clipMaskPreamble(gpu, clipIn, result, resultBounds)) { return true; } GrTexture* accum = fAACache.getLastMask(); if (NULL == accum) { fClipMaskInAlpha = false; fAACache.reset(); return false; } GrDrawTarget::AutoStateRestore asr(gpu, GrDrawTarget::kReset_ASRInit); GrDrawState* drawState = gpu->drawState(); GrDrawTarget::AutoGeometryPush agp(gpu); int count = clipIn.getElementCount(); if (0 != resultBounds->fTop || 0 != resultBounds->fLeft) { // 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 GrMatrix m; m.setTranslate(SkIntToScalar(-resultBounds->fLeft), SkIntToScalar(-resultBounds->fTop)); drawState->setViewMatrix(m); } bool clearToInside; SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning int start = process_initial_clip_elements(clipIn, *resultBounds, &clearToInside, &startOp); clear(gpu, accum, clearToInside ? 0xffffffff : 0x00000000); GrAutoScratchTexture temp; // walk through each clip element and perform its set op for (int c = start; c < count; ++c) { SkRegion::Op op = (c == start) ? startOp : clipIn.getOp(c); if (SkRegion::kReplace_Op == op) { // TODO: replace is actually a lot faster then intersection // for this path - refactor the stencil path so it can handle // replace ops and alter GrClip to allow them through // clear the accumulator and draw the new object directly into it clear(gpu, accum, 0x00000000); setup_boolean_blendcoeffs(drawState, op); this->drawClipShape(gpu, accum, clipIn, c); } 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 && kRect_ClipType == clipIn.getElementType(c) && contains(clipIn.getRect(c), *resultBounds)) { continue; } getTemp(*resultBounds, &temp); if (NULL == temp.texture()) { fClipMaskInAlpha = false; fAACache.reset(); return false; } // clear the temp target & draw into it clear(gpu, temp.texture(), 0x00000000); setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op); this->drawClipShape(gpu, temp.texture(), clipIn, c); // TODO: rather than adding these two translations here // compute the bounding box needed to render the texture // into temp if (0 != resultBounds->fTop || 0 != resultBounds->fLeft) { GrMatrix m; m.setTranslate(SkIntToScalar(resultBounds->fLeft), SkIntToScalar(resultBounds->fTop)); drawState->preConcatViewMatrix(m); } // Now draw into the accumulator using the real operation // and the temp buffer as a texture setup_boolean_blendcoeffs(drawState, op); this->drawTexture(gpu, accum, temp.texture()); if (0 != resultBounds->fTop || 0 != resultBounds->fLeft) { GrMatrix m; m.setTranslate(SkIntToScalar(-resultBounds->fLeft), SkIntToScalar(-resultBounds->fTop)); drawState->preConcatViewMatrix(m); } } else { // all the remaining ops can just be directly draw into // the accumulation buffer setup_boolean_blendcoeffs(drawState, op); this->drawClipShape(gpu, accum, clipIn, c); } } *result = accum; return true; }
//////////////////////////////////////////////////////////////////////////////// // 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; }