void basic_clear_test(skiatest::Reporter* reporter, GrContext* context, GrPixelConfig config) { GrVkGpu* gpu = static_cast<GrVkGpu*>(context->getGpu()); gpu->discard(NULL); SkAutoTMalloc<GrColor> buffer(25); GrSurfaceDesc surfDesc; surfDesc.fFlags = kRenderTarget_GrSurfaceFlag; surfDesc.fOrigin = kTopLeft_GrSurfaceOrigin; surfDesc.fWidth = 5; surfDesc.fHeight = 5; surfDesc.fConfig = config; surfDesc.fSampleCnt = 0; GrTexture* tex = gpu->createTexture(surfDesc, false, nullptr, 0); SkASSERT(tex); SkASSERT(tex->asRenderTarget()); SkIRect rect = SkIRect::MakeWH(5, 5); gpu->clear(rect, GrColor_TRANSPARENT_BLACK, tex->asRenderTarget()); gpu->readPixels(tex, 0, 0, 5, 5, config, (void*)buffer.get(), 0); REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(buffer.get(), GrColor_TRANSPARENT_BLACK, config, 5, 5)); gpu->clear(rect, GrColor_WHITE, tex->asRenderTarget()); gpu->readPixels(tex, 0, 0, 5, 5, config, (void*)buffer.get(), 0); REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(buffer.get(), GrColor_WHITE, config, 5, 5)); GrColor myColor = GrColorPackRGBA(0xFF, 0x7F, 0x40, 0x20); gpu->clear(rect, myColor, tex->asRenderTarget()); gpu->readPixels(tex, 0, 0, 5, 5, config, (void*)buffer.get(), 0); REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(buffer.get(), myColor, config, 5, 5)); }
static GrRenderTarget* random_render_target(GrTextureProvider* textureProvider, SkRandom* random, const GrCaps* caps) { // setup render target GrTextureParams params; GrSurfaceDesc texDesc; texDesc.fWidth = kRenderTargetWidth; texDesc.fHeight = kRenderTargetHeight; texDesc.fFlags = kRenderTarget_GrSurfaceFlag; texDesc.fConfig = kRGBA_8888_GrPixelConfig; texDesc.fOrigin = random->nextBool() == true ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin; texDesc.fSampleCnt = random->nextBool() == true ? SkTMin(4, caps->maxSampleCount()) : 0; GrUniqueKey key; static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); GrUniqueKey::Builder builder(&key, kDomain, 2); builder[0] = texDesc.fOrigin; builder[1] = texDesc.fSampleCnt; builder.finish(); GrTexture* texture = textureProvider->findAndRefTextureByUniqueKey(key); if (!texture) { texture = textureProvider->createTexture(texDesc, true); if (texture) { textureProvider->assignUniqueKeyToTexture(key, texture); } } return texture ? texture->asRenderTarget() : nullptr; }
GrTexture* GrGpu::createTexture(const GrTextureDesc& desc, const void* srcData, size_t rowBytes) { this->handleDirtyContext(); GrTexture* tex = this->onCreateTexture(desc, srcData, rowBytes); if (NULL != tex && (kRenderTarget_GrTextureFlagBit & desc.fFlags) && !(kNoStencil_GrTextureFlagBit & desc.fFlags)) { GrAssert(NULL != tex->asRenderTarget()); // TODO: defer this and attach dynamically if (!this->attachStencilBufferToRenderTarget(tex->asRenderTarget())) { tex->unref(); return NULL; } } return tex; }
// Create a mask of 'devPath' and place the result in 'mask'. static GrTexture* create_mask_GPU(GrContext* context, const SkRect& maskRect, const SkPath& devPath, const GrStrokeInfo& strokeInfo, bool doAA, int sampleCnt) { GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = SkScalarCeilToInt(maskRect.width()); desc.fHeight = SkScalarCeilToInt(maskRect.height()); desc.fSampleCnt = doAA ? sampleCnt : 0; // We actually only need A8, but it often isn't supported as a // render target so default to RGBA_8888 desc.fConfig = kRGBA_8888_GrPixelConfig; if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, desc.fSampleCnt > 0)) { desc.fConfig = kAlpha_8_GrPixelConfig; } GrTexture* mask = context->textureProvider()->createApproxTexture(desc); if (nullptr == mask) { return nullptr; } SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height()); GrDrawContext* drawContext = context->drawContext(); if (!drawContext) { return nullptr; } drawContext->clear(mask->asRenderTarget(), nullptr, 0x0, true); GrPaint tempPaint; tempPaint.setAntiAlias(doAA); tempPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op); // setup new clip GrClip clip(clipRect); // Draw the mask into maskTexture with the path's top-left at the origin using tempPaint. SkMatrix translate; translate.setTranslate(-maskRect.fLeft, -maskRect.fTop); drawContext->drawPath(mask->asRenderTarget(), clip, tempPaint, translate, devPath, strokeInfo); return mask; }
bool GrDrawTarget::checkDraw(GrPrimitiveType type, int startVertex, int startIndex, int vertexCount, int indexCount) const { const GrDrawState& drawState = this->getDrawState(); #if GR_DEBUG const GeometrySrcState& geoSrc = fGeoSrcStateStack.back(); int maxVertex = startVertex + vertexCount; int maxValidVertex; switch (geoSrc.fVertexSrc) { case kNone_GeometrySrcType: GrCrash("Attempting to draw without vertex src."); case kReserved_GeometrySrcType: // fallthrough case kArray_GeometrySrcType: maxValidVertex = geoSrc.fVertexCount; break; case kBuffer_GeometrySrcType: maxValidVertex = geoSrc.fVertexBuffer->sizeInBytes() / VertexSize(geoSrc.fVertexLayout); break; } if (maxVertex > maxValidVertex) { GrCrash("Drawing outside valid vertex range."); } if (indexCount > 0) { int maxIndex = startIndex + indexCount; int maxValidIndex; switch (geoSrc.fIndexSrc) { case kNone_GeometrySrcType: GrCrash("Attempting to draw indexed geom without index src."); case kReserved_GeometrySrcType: // fallthrough case kArray_GeometrySrcType: maxValidIndex = geoSrc.fIndexCount; break; case kBuffer_GeometrySrcType: maxValidIndex = geoSrc.fIndexBuffer->sizeInBytes() / sizeof(uint16_t); break; } if (maxIndex > maxValidIndex) { GrCrash("Index reads outside valid index range."); } } GrAssert(NULL != drawState.getRenderTarget()); for (int s = 0; s < GrDrawState::kNumStages; ++s) { if (drawState.isStageEnabled(s)) { const GrEffect* effect = drawState.getStage(s).getEffect(); int numTextures = effect->numTextures(); for (int t = 0; t < numTextures; ++t) { GrTexture* texture = effect->texture(t); GrAssert(texture->asRenderTarget() != drawState.getRenderTarget()); } } } #endif if (NULL == drawState.getRenderTarget()) { return false; } return true; }
static SkGrPixelRef* copyToTexturePixelRef(GrTexture* texture, const SkIRect* subset) { if (NULL == texture) { return NULL; } GrContext* context = texture->getContext(); if (NULL == context) { return NULL; } GrTextureDesc desc; SkIPoint pointStorage; SkIPoint* topLeft; if (subset != NULL) { SkASSERT(SkIRect::MakeWH(texture->width(), texture->height()).contains(*subset)); // Create a new texture that is the size of subset. desc.fWidth = subset->width(); desc.fHeight = subset->height(); pointStorage.set(subset->x(), subset->y()); topLeft = &pointStorage; } else { desc.fWidth = texture->width(); desc.fHeight = texture->height(); topLeft = NULL; } desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit; desc.fConfig = texture->config(); SkImageInfo info; if (!GrPixelConfig2ColorType(desc.fConfig, &info.fColorType)) { return NULL; } info.fWidth = desc.fWidth; info.fHeight = desc.fHeight; info.fAlphaType = kPremul_SkAlphaType; GrTexture* dst = context->createUncachedTexture(desc, NULL, 0); if (NULL == dst) { return NULL; } context->copyTexture(texture, dst->asRenderTarget(), topLeft); // TODO: figure out if this is responsible for Chrome canvas errors #if 0 // The render texture we have created (to perform the copy) isn't fully // functional (since it doesn't have a stencil buffer). Release it here // so the caller doesn't try to render to it. // TODO: we can undo this release when dynamic stencil buffer attach/ // detach has been implemented dst->releaseRenderTarget(); #endif SkGrPixelRef* pixelRef = SkNEW_ARGS(SkGrPixelRef, (info, dst)); SkSafeUnref(dst); return pixelRef; }
// Create a mask of 'devPath' and place the result in 'mask'. static GrTexture* create_mask_GPU(GrContext* context, SkRect* maskRect, const SkPath& devPath, const GrStrokeInfo& strokeInfo, bool doAA, int sampleCnt) { // This mask will ultimately be drawn as a non-AA rect (see draw_mask). // Non-AA rects have a bad habit of snapping arbitrarily. Integerize here // so the mask draws in a reproducible manner. *maskRect = SkRect::Make(maskRect->roundOut()); GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = SkScalarCeilToInt(maskRect->width()); desc.fHeight = SkScalarCeilToInt(maskRect->height()); desc.fSampleCnt = doAA ? sampleCnt : 0; // We actually only need A8, but it often isn't supported as a // render target so default to RGBA_8888 desc.fConfig = kRGBA_8888_GrPixelConfig; if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, desc.fSampleCnt > 0)) { desc.fConfig = kAlpha_8_GrPixelConfig; } GrTexture* mask = context->textureProvider()->createApproxTexture(desc); if (nullptr == mask) { return nullptr; } SkRect clipRect = SkRect::MakeWH(maskRect->width(), maskRect->height()); SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(mask->asRenderTarget())); if (!drawContext) { return nullptr; } drawContext->clear(nullptr, 0x0, true); GrPaint tempPaint; tempPaint.setAntiAlias(doAA); tempPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op); // setup new clip GrClip clip(clipRect); // Draw the mask into maskTexture with the path's integerized top-left at // the origin using tempPaint. SkMatrix translate; translate.setTranslate(-maskRect->fLeft, -maskRect->fTop); drawContext->drawPath(clip, tempPaint, translate, devPath, strokeInfo); return mask; }
GrTexture* GrGpu::createPlatformTexture(const GrPlatformTextureDesc& desc) { this->handleDirtyContext(); GrTexture* tex = this->onCreatePlatformTexture(desc); // TODO: defer this and attach dynamically GrRenderTarget* tgt = tex->asRenderTarget(); if (NULL != tgt && !this->attachStencilBufferToRenderTarget(tgt)) { tex->unref(); return NULL; } else { return tex; } }
GrTexture* GrGpu::createTexture(const GrTextureDesc& desc, const void* srcData, size_t rowBytes) { if (!this->caps()->isConfigTexturable(desc.fConfig)) { return NULL; } if ((desc.fFlags & kRenderTarget_GrTextureFlagBit) && !this->caps()->isConfigRenderable(desc.fConfig, desc.fSampleCnt > 0)) { return NULL; } GrTexture *tex = NULL; if (GrPixelConfigIsCompressed(desc.fConfig)) { // We shouldn't be rendering into this SkASSERT((desc.fFlags & kRenderTarget_GrTextureFlagBit) == 0); if (!this->caps()->npotTextureTileSupport() && (!SkIsPow2(desc.fWidth) || !SkIsPow2(desc.fHeight))) { return NULL; } this->handleDirtyContext(); tex = this->onCreateCompressedTexture(desc, srcData); } else { this->handleDirtyContext(); tex = this->onCreateTexture(desc, srcData, rowBytes); if (tex && (kRenderTarget_GrTextureFlagBit & desc.fFlags) && !(kNoStencil_GrTextureFlagBit & desc.fFlags)) { SkASSERT(tex->asRenderTarget()); // TODO: defer this and attach dynamically if (!this->attachStencilBufferToRenderTarget(tex->asRenderTarget())) { tex->unref(); return NULL; } } } return tex; }
GrTexture* GrGpu::wrapBackendTexture(const GrBackendTextureDesc& desc, GrWrapOwnership ownership) { this->handleDirtyContext(); GrTexture* tex = this->onWrapBackendTexture(desc, ownership); if (NULL == tex) { return NULL; } // TODO: defer this and attach dynamically GrRenderTarget* tgt = tex->asRenderTarget(); if (tgt && !this->attachStencilAttachmentToRenderTarget(tgt)) { tex->unref(); return NULL; } else { return tex; } }
GrTexture* GrGpu::createTexture(const GrTextureDesc& desc, const void* srcData, size_t rowBytes) { if (kUnknown_GrPixelConfig == desc.fConfig) { return NULL; } if ((desc.fFlags & kRenderTarget_GrTextureFlagBit) && !this->caps()->isConfigRenderable(desc.fConfig, desc.fSampleCnt > 0)) { return NULL; } this->handleDirtyContext(); GrTexture* tex = this->onCreateTexture(desc, srcData, rowBytes); if (NULL != tex && (kRenderTarget_GrTextureFlagBit & desc.fFlags) && !(kNoStencil_GrTextureFlagBit & desc.fFlags)) { SkASSERT(NULL != tex->asRenderTarget()); // TODO: defer this and attach dynamically if (!this->attachStencilBufferToRenderTarget(tex->asRenderTarget())) { tex->unref(); return NULL; } } return tex; }
static SkGrPixelRef* copy_to_new_texture_pixelref(GrTexture* texture, SkColorType dstCT, SkColorProfileType dstPT, const SkIRect* subset) { if (NULL == texture || kUnknown_SkColorType == dstCT) { return NULL; } GrContext* context = texture->getContext(); if (NULL == context) { return NULL; } GrSurfaceDesc desc; SkIRect srcRect; if (!subset) { desc.fWidth = texture->width(); desc.fHeight = texture->height(); srcRect = SkIRect::MakeWH(texture->width(), texture->height()); } else { SkASSERT(SkIRect::MakeWH(texture->width(), texture->height()).contains(*subset)); // Create a new texture that is the size of subset. desc.fWidth = subset->width(); desc.fHeight = subset->height(); srcRect = *subset; } desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fConfig = SkImageInfo2GrPixelConfig(dstCT, kPremul_SkAlphaType, dstPT); GrTexture* dst = context->createTexture(desc, false, NULL, 0); if (NULL == dst) { return NULL; } // Blink is relying on the above copy being sent to GL immediately in the case when the source // is a WebGL canvas backing store. We could have a TODO to remove this flush flag, but we have // a larger TODO to remove SkGrPixelRef entirely. context->copySurface(dst->asRenderTarget(), texture, srcRect, SkIPoint::Make(0,0), GrContext::kFlushWrites_PixelOp); SkImageInfo info = SkImageInfo::Make(desc.fWidth, desc.fHeight, dstCT, kPremul_SkAlphaType, dstPT); SkGrPixelRef* pixelRef = SkNEW_ARGS(SkGrPixelRef, (info, dst)); SkSafeUnref(dst); return pixelRef; }
GrTexture* GrGpu::wrapBackendTexture(const GrBackendTextureDesc& desc, GrWrapOwnership ownership) { this->handleDirtyContext(); if (!this->caps()->isConfigTexturable(desc.fConfig)) { return nullptr; } if ((desc.fFlags & kRenderTarget_GrBackendTextureFlag) && !this->caps()->isConfigRenderable(desc.fConfig, desc.fSampleCnt > 0)) { return nullptr; } GrTexture* tex = this->onWrapBackendTexture(desc, ownership); if (nullptr == tex) { return nullptr; } // TODO: defer this and attach dynamically GrRenderTarget* tgt = tex->asRenderTarget(); if (tgt && !fContext->resourceProvider()->attachStencilAttachment(tgt)) { tex->unref(); return nullptr; } else { return tex; } }
//////////////////////////////////////////////////////////////////////////////// // Create a 8-bit clip mask in alpha GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t clipStackGenID, InitialState initialState, const ElementList& elements, const SkIRect& clipSpaceIBounds) { GrAssert(kNone_ClipMaskType == fCurrClipMaskType); GrTexture* result; if (this->getMaskTexture(clipStackGenID, clipSpaceIBounds, &result)) { fCurrClipMaskType = kAlpha_ClipMaskType; return result; } if (NULL == result) { fAACache.reset(); return NULL; } GrDrawTarget::AutoGeometryAndStatePush agasp(fGpu, GrDrawTarget::kReset_ASRInit); GrDrawState* drawState = fGpu->drawState(); // The top-left of the mask corresponds to the top-left corner of the bounds. SkVector clipToMaskOffset = { SkIntToScalar(-clipSpaceIBounds.fLeft), SkIntToScalar(-clipSpaceIBounds.fTop) }; // The texture may be larger than necessary, this rect represents the part of the texture // we populate with a rasterization of the clip. SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpaceIBounds.height()); // We're drawing a coverage mask and want coverage to be run through the blend function. drawState->enableState(GrDrawState::kCoverageDrawing_StateBit); // Set the matrix so that rendered clip elements are transformed to mask space from clip space. drawState->viewMatrix()->setTranslate(clipToMaskOffset); // 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(&maskSpaceIBounds, kAllIn_InitialState == initialState ? 0xffffffff : 0x00000000, result->asRenderTarget()); // When we use the stencil in the below loop it is important to have this clip installed. // The second pass that zeros the stencil buffer renders the rect maskSpaceIBounds so the first // pass must not set values outside of this bounds or stencil values outside the rect won't be // cleared. GrDrawTarget::AutoClipRestore acr(fGpu, maskSpaceIBounds); drawState->enableState(GrDrawState::kClip_StateBit); GrAutoScratchTexture temp; // walk through each clip element and perform its set op for (ElementList::Iter iter = elements.headIter(); iter.get(); iter.next()) { const Element* element = iter.get(); SkRegion::Op op = element->getOp(); bool invert = element->isInverseFilled(); if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) { GrPathRenderer* pr = NULL; bool useTemp = !this->canStencilAndDrawElement(result, element, &pr); GrTexture* dst; // 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. GrIRect maskSpaceElementIBounds; if (useTemp) { if (invert) { maskSpaceElementIBounds = maskSpaceIBounds; } else { GrRect elementBounds = element->getBounds(); elementBounds.offset(clipToMaskOffset); elementBounds.roundOut(&maskSpaceElementIBounds); } this->getTemp(maskSpaceIBounds.fRight, maskSpaceIBounds.fBottom, &temp); if (NULL == temp.texture()) { fAACache.reset(); return NULL; } dst = temp.texture(); // clear the temp target and set blend to replace fGpu->clear(&maskSpaceElementIBounds, invert ? 0xffffffff : 0x00000000, dst->asRenderTarget()); setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op); } else { // draw directly into the result with the stencil set to make the pixels affected // by the clip shape be non-zero. dst = result; GR_STATIC_CONST_SAME_STENCIL(kStencilInElement, kReplace_StencilOp, kReplace_StencilOp, kAlways_StencilFunc, 0xffff, 0xffff, 0xffff); drawState->setStencil(kStencilInElement); setup_boolean_blendcoeffs(drawState, op); } drawState->setAlpha(invert ? 0x00 : 0xff); if (!this->drawElement(dst, element, pr)) { fAACache.reset(); return NULL; } if (useTemp) { // Now draw into the accumulator using the real operation and the temp buffer as a // texture this->mergeMask(result, temp.texture(), op, maskSpaceIBounds, maskSpaceElementIBounds); } else { // Draw to the exterior pixels (those with a zero stencil value). drawState->setAlpha(invert ? 0xff : 0x00); GR_STATIC_CONST_SAME_STENCIL(kDrawOutsideElement, kZero_StencilOp, kZero_StencilOp, kEqual_StencilFunc, 0xffff, 0x0000, 0xffff); drawState->setStencil(kDrawOutsideElement); fGpu->drawSimpleRect(clipSpaceIBounds); drawState->disableStencil(); } } else { // all the remaining ops can just be directly draw into the accumulation buffer drawState->setAlpha(0xff); setup_boolean_blendcoeffs(drawState, op); this->drawElement(result, element); } } fCurrClipMaskType = kAlpha_ClipMaskType; return result; }
GrTexture* GaussianBlur(GrContext* context, GrTexture* srcTexture, bool canClobberSrc, const SkRect& rect, bool cropToRect, float sigmaX, float sigmaY) { SkASSERT(context); SkIRect clearRect; int scaleFactorX, radiusX; int scaleFactorY, radiusY; int maxTextureSize = context->caps()->maxTextureSize(); sigmaX = adjust_sigma(sigmaX, maxTextureSize, &scaleFactorX, &radiusX); sigmaY = adjust_sigma(sigmaY, maxTextureSize, &scaleFactorY, &radiusY); SkRect srcRect(rect); scale_rect(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY); srcRect.roundOut(&srcRect); scale_rect(&srcRect, static_cast<float>(scaleFactorX), static_cast<float>(scaleFactorY)); // setup new clip GrClip clip(SkRect::MakeWH(srcRect.width(), srcRect.height())); SkASSERT(kBGRA_8888_GrPixelConfig == srcTexture->config() || kRGBA_8888_GrPixelConfig == srcTexture->config() || kAlpha_8_GrPixelConfig == srcTexture->config()); GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = SkScalarFloorToInt(srcRect.width()); desc.fHeight = SkScalarFloorToInt(srcRect.height()); desc.fConfig = srcTexture->config(); GrTexture* dstTexture; GrTexture* tempTexture; SkAutoTUnref<GrTexture> temp1, temp2; temp1.reset(context->textureProvider()->refScratchTexture( desc, GrTextureProvider::kApprox_ScratchTexMatch)); dstTexture = temp1.get(); if (canClobberSrc) { tempTexture = srcTexture; } else { temp2.reset(context->textureProvider()->refScratchTexture( desc, GrTextureProvider::kApprox_ScratchTexMatch)); tempTexture = temp2.get(); } if (NULL == dstTexture || NULL == tempTexture) { return NULL; } GrDrawContext* drawContext = context->drawContext(); if (!drawContext) { return NULL; } for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) { GrPaint paint; SkMatrix matrix; matrix.setIDiv(srcTexture->width(), srcTexture->height()); SkRect dstRect(srcRect); if (cropToRect && i == 1) { dstRect.offset(-dstRect.fLeft, -dstRect.fTop); SkRect domain; matrix.mapRect(&domain, rect); domain.inset(i < scaleFactorX ? SK_ScalarHalf / srcTexture->width() : 0.0f, i < scaleFactorY ? SK_ScalarHalf / srcTexture->height() : 0.0f); SkAutoTUnref<GrFragmentProcessor> fp( GrTextureDomainEffect::Create( paint.getProcessorDataManager(), srcTexture, matrix, domain, GrTextureDomain::kDecal_Mode, GrTextureParams::kBilerp_FilterMode)); paint.addColorProcessor(fp); } else { GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode); paint.addColorTextureProcessor(srcTexture, matrix, params); } scale_rect(&dstRect, i < scaleFactorX ? 0.5f : 1.0f, i < scaleFactorY ? 0.5f : 1.0f); drawContext->drawNonAARectToRect(dstTexture->asRenderTarget(), clip, paint, SkMatrix::I(), dstRect, srcRect); srcRect = dstRect; srcTexture = dstTexture; SkTSwap(dstTexture, tempTexture); } const SkIRect srcIRect = srcRect.roundOut(); // For really small blurs(Certainly no wider than 5x5 on desktop gpus) it is faster to just // launch a single non separable kernel vs two launches if (sigmaX > 0.0f && sigmaY > 0 && (2 * radiusX + 1) * (2 * radiusY + 1) <= MAX_KERNEL_SIZE) { // We shouldn't be scaling because this is a small size blur SkASSERT((scaleFactorX == scaleFactorY) == 1); SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); convolve_gaussian_2d(drawContext, dstTexture->asRenderTarget(), clip, srcRect, dstRect, srcTexture, radiusX, radiusY, sigmaX, sigmaY, cropToRect, srcIRect); srcTexture = dstTexture; srcRect = dstRect; SkTSwap(dstTexture, tempTexture); } else { if (sigmaX > 0.0f) { if (scaleFactorX > 1) { // Clear out a radius to the right of the srcRect to prevent the // X convolution from reading garbage. clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop, radiusX, srcIRect.height()); drawContext->clear(srcTexture->asRenderTarget(), &clearRect, 0x0, false); } SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); convolve_gaussian(drawContext, dstTexture->asRenderTarget(), clip, srcRect, dstRect, srcTexture, Gr1DKernelEffect::kX_Direction, radiusX, sigmaX, cropToRect); srcTexture = dstTexture; srcRect = dstRect; SkTSwap(dstTexture, tempTexture); } if (sigmaY > 0.0f) { if (scaleFactorY > 1 || sigmaX > 0.0f) { // Clear out a radius below the srcRect to prevent the Y // convolution from reading garbage. clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom, srcIRect.width(), radiusY); drawContext->clear(srcTexture->asRenderTarget(), &clearRect, 0x0, false); } SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); convolve_gaussian(drawContext, dstTexture->asRenderTarget(), clip, srcRect, dstRect, srcTexture, Gr1DKernelEffect::kY_Direction, radiusY, sigmaY, cropToRect); srcTexture = dstTexture; srcRect = dstRect; SkTSwap(dstTexture, tempTexture); } } if (scaleFactorX > 1 || scaleFactorY > 1) { // Clear one pixel to the right and below, to accommodate bilinear // upsampling. clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom, srcIRect.width() + 1, 1); drawContext->clear(srcTexture->asRenderTarget(), &clearRect, 0x0, false); clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop, 1, srcIRect.height()); drawContext->clear(srcTexture->asRenderTarget(), &clearRect, 0x0, false); SkMatrix matrix; matrix.setIDiv(srcTexture->width(), srcTexture->height()); GrPaint paint; // FIXME: this should be mitchell, not bilinear. GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode); paint.addColorTextureProcessor(srcTexture, matrix, params); SkRect dstRect(srcRect); scale_rect(&dstRect, (float) scaleFactorX, (float) scaleFactorY); drawContext->drawNonAARectToRect(dstTexture->asRenderTarget(), clip, paint, SkMatrix::I(), dstRect, srcRect); srcRect = dstRect; srcTexture = dstTexture; SkTSwap(dstTexture, tempTexture); } return SkRef(srcTexture); }
static sk_sp<SkSpecialImage> apply_morphology(GrContext* context, SkSpecialImage* input, const SkIRect& rect, GrMorphologyEffect::MorphologyType morphType, SkISize radius) { SkAutoTUnref<GrTexture> srcTexture(input->asTextureRef(context)); SkASSERT(srcTexture); // setup new clip GrClip clip(SkRect::MakeWH(SkIntToScalar(srcTexture->width()), SkIntToScalar(srcTexture->height()))); SkIRect dstRect = SkIRect::MakeWH(rect.width(), rect.height()); GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = rect.width(); desc.fHeight = rect.height(); desc.fConfig = kSkia8888_GrPixelConfig; SkIRect srcRect = rect; SkASSERT(radius.width() > 0 || radius.height() > 0); if (radius.fWidth > 0) { GrTexture* scratch = context->textureProvider()->createApproxTexture(desc); if (!scratch) { return nullptr; } SkAutoTUnref<GrDrawContext> dstDrawContext( context->drawContext(scratch->asRenderTarget())); if (!dstDrawContext) { return nullptr; } apply_morphology_pass(dstDrawContext, clip, srcTexture, srcRect, dstRect, radius.fWidth, morphType, Gr1DKernelEffect::kX_Direction); SkIRect clearRect = SkIRect::MakeXYWH(dstRect.fLeft, dstRect.fBottom, dstRect.width(), radius.fHeight); GrColor clearColor = GrMorphologyEffect::kErode_MorphologyType == morphType ? SK_ColorWHITE : SK_ColorTRANSPARENT; dstDrawContext->clear(&clearRect, clearColor, false); srcTexture.reset(scratch); srcRect = dstRect; } if (radius.fHeight > 0) { GrTexture* scratch = context->textureProvider()->createApproxTexture(desc); if (!scratch) { return nullptr; } SkAutoTUnref<GrDrawContext> dstDrawContext( context->drawContext(scratch->asRenderTarget())); if (!dstDrawContext) { return nullptr; } apply_morphology_pass(dstDrawContext, clip, srcTexture, srcRect, dstRect, radius.fHeight, morphType, Gr1DKernelEffect::kY_Direction); srcTexture.reset(scratch); } return SkSpecialImage::MakeFromGpu(input->internal_getProxy(), SkIRect::MakeWH(rect.width(), rect.height()), kNeedNewImageUniqueID_SpecialImage, srcTexture, &input->props()); }
//////////////////////////////////////////////////////////////////////////////// // 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; }
bool GrDrawTarget::checkDraw(GrPrimitiveType type, int startVertex, int startIndex, int vertexCount, int indexCount) const { const GrDrawState& drawState = this->getDrawState(); #ifdef SK_DEBUG const GeometrySrcState& geoSrc = fGeoSrcStateStack.back(); int maxVertex = startVertex + vertexCount; int maxValidVertex; switch (geoSrc.fVertexSrc) { case kNone_GeometrySrcType: SkFAIL("Attempting to draw without vertex src."); case kReserved_GeometrySrcType: // fallthrough case kArray_GeometrySrcType: maxValidVertex = geoSrc.fVertexCount; break; case kBuffer_GeometrySrcType: maxValidVertex = static_cast<int>(geoSrc.fVertexBuffer->gpuMemorySize() / geoSrc.fVertexSize); break; } if (maxVertex > maxValidVertex) { SkFAIL("Drawing outside valid vertex range."); } if (indexCount > 0) { int maxIndex = startIndex + indexCount; int maxValidIndex; switch (geoSrc.fIndexSrc) { case kNone_GeometrySrcType: SkFAIL("Attempting to draw indexed geom without index src."); case kReserved_GeometrySrcType: // fallthrough case kArray_GeometrySrcType: maxValidIndex = geoSrc.fIndexCount; break; case kBuffer_GeometrySrcType: maxValidIndex = static_cast<int>(geoSrc.fIndexBuffer->gpuMemorySize() / sizeof(uint16_t)); break; } if (maxIndex > maxValidIndex) { SkFAIL("Index reads outside valid index range."); } } SkASSERT(NULL != drawState.getRenderTarget()); for (int s = 0; s < drawState.numColorStages(); ++s) { const GrEffectRef& effect = *drawState.getColorStage(s).getEffect(); int numTextures = effect->numTextures(); for (int t = 0; t < numTextures; ++t) { GrTexture* texture = effect->texture(t); SkASSERT(texture->asRenderTarget() != drawState.getRenderTarget()); } } for (int s = 0; s < drawState.numCoverageStages(); ++s) { const GrEffectRef& effect = *drawState.getCoverageStage(s).getEffect(); int numTextures = effect->numTextures(); for (int t = 0; t < numTextures; ++t) { GrTexture* texture = effect->texture(t); SkASSERT(texture->asRenderTarget() != drawState.getRenderTarget()); } } SkASSERT(drawState.validateVertexAttribs()); #endif if (NULL == drawState.getRenderTarget()) { return false; } return true; }
void sub_clear_test(skiatest::Reporter* reporter, GrContext* context, GrPixelConfig config) { const int width = 10; const int height = 10; const int subWidth = width/2; const int subHeight = height/2; GrVkGpu* gpu = static_cast<GrVkGpu*>(context->getGpu()); gpu->discard(NULL); SkAutoTMalloc<GrColor> buffer(width * height); SkAutoTMalloc<GrColor> subBuffer(subWidth * subHeight); GrSurfaceDesc surfDesc; surfDesc.fFlags = kRenderTarget_GrSurfaceFlag; surfDesc.fOrigin = kTopLeft_GrSurfaceOrigin; surfDesc.fWidth = width; surfDesc.fHeight = height; surfDesc.fConfig = config; surfDesc.fSampleCnt = 0; GrTexture* tex = gpu->createTexture(surfDesc, false, nullptr, 0); SkASSERT(tex); SkASSERT(tex->asRenderTarget()); SkIRect fullRect = SkIRect::MakeWH(10, 10); gpu->clear(fullRect, GrColor_TRANSPARENT_BLACK, tex->asRenderTarget()); gpu->readPixels(tex, 0, 0, width, height, config, (void*)buffer.get(), 0); REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(buffer.get(), GrColor_TRANSPARENT_BLACK, config, width, height)); SkIRect rect; rect = SkIRect::MakeXYWH(0, 0, subWidth, subHeight); gpu->clear(rect, GrColor_WHITE, tex->asRenderTarget()); rect = SkIRect::MakeXYWH(subWidth, 0, subWidth, subHeight); gpu->clear(rect, GrColor_WHITE, tex->asRenderTarget()); rect = SkIRect::MakeXYWH(0, subHeight, subWidth, subHeight); gpu->clear(rect, GrColor_WHITE, tex->asRenderTarget()); // Should fail since bottom right sub area has not been cleared to white gpu->readPixels(tex, 0, 0, width, height, config, (void*)buffer.get(), 0); REPORTER_ASSERT(reporter, !does_full_buffer_contain_correct_color(buffer.get(), GrColor_WHITE, config, width, height)); rect = SkIRect::MakeXYWH(subWidth, subHeight, subWidth, subHeight); gpu->clear(rect, GrColor_WHITE, tex->asRenderTarget()); gpu->readPixels(tex, 0, 0, width, height, config, (void*)buffer.get(), 0); REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(buffer.get(), GrColor_WHITE, config, width, height)); // Try different colors and that each sub area has correct color GrColor subColor1 = GrColorPackRGBA(0xFF, 0x00, 0x00, 0xFF); GrColor subColor2 = GrColorPackRGBA(0x00, 0xFF, 0x00, 0xFF); GrColor subColor3 = GrColorPackRGBA(0x00, 0x00, 0xFF, 0xFF); GrColor subColor4 = GrColorPackRGBA(0xFF, 0xFF, 0x00, 0xFF); rect = SkIRect::MakeXYWH(0, 0, subWidth, subHeight); gpu->clear(rect, subColor1, tex->asRenderTarget()); rect = SkIRect::MakeXYWH(subWidth, 0, subWidth, subHeight); gpu->clear(rect, subColor2, tex->asRenderTarget()); rect = SkIRect::MakeXYWH(0, subHeight, subWidth, subHeight); gpu->clear(rect, subColor3, tex->asRenderTarget()); rect = SkIRect::MakeXYWH(subWidth, subHeight, subWidth, subHeight); gpu->clear(rect, subColor4, tex->asRenderTarget()); gpu->readPixels(tex, 0, 0, subWidth, subHeight, config, (void*)subBuffer.get(), 0); REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(subBuffer.get(), subColor1, config, subWidth, subHeight)); gpu->readPixels(tex, subWidth, 0, subWidth, subHeight, config, (void*)subBuffer.get(), 0); REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(subBuffer.get(), subColor2, config, subWidth, subHeight)); gpu->readPixels(tex, 0, subHeight, subWidth, subHeight, config, (void*)subBuffer.get(), 0); REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(subBuffer.get(), subColor3, config, subWidth, subHeight)); gpu->readPixels(tex, subWidth, subHeight, subWidth, subHeight, config, (void*)subBuffer.get(), 0); REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(subBuffer.get(), subColor4, config, subWidth, subHeight)); }
GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t elementsGenID, GrReducedClip::InitialState initialState, const GrReducedClip::ElementList& elements, const SkVector& clipToMaskOffset, const SkIRect& clipSpaceIBounds) { GrResourceProvider* resourceProvider = fDrawTarget->cmmAccess().resourceProvider(); GrUniqueKey key; GetClipMaskKey(elementsGenID, clipSpaceIBounds, &key); if (GrTexture* texture = resourceProvider->findAndRefTextureByUniqueKey(key)) { return texture; } SkAutoTUnref<GrTexture> texture(this->createCachedMask( clipSpaceIBounds.width(), clipSpaceIBounds.height(), key, true)); // There's no texture in the cache. Let's try to allocate it then. if (!texture) { return nullptr; } // Set the matrix so that rendered clip elements are transformed to mask space from clip // space. SkMatrix translate; translate.setTranslate(clipToMaskOffset); // The texture may be larger than necessary, this rect represents the part of the texture // we populate with a rasterization of the clip. SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpaceIBounds.height()); // The scratch texture that we are drawing into can be substantially larger than the mask. Only // clear the part that we care about. fDrawTarget->clear(&maskSpaceIBounds, GrReducedClip::kAllIn_InitialState == initialState ? 0xffffffff : 0x00000000, true, texture->asRenderTarget()); // When we use the stencil in the below loop it is important to have this clip installed. // The second pass that zeros the stencil buffer renders the rect maskSpaceIBounds so the first // pass must not set values outside of this bounds or stencil values outside the rect won't be // cleared. GrClip clip(maskSpaceIBounds); SkAutoTUnref<GrTexture> temp; // walk through each clip element and perform its set op for (GrReducedClip::ElementList::Iter iter = elements.headIter(); iter.get(); iter.next()) { const Element* element = iter.get(); SkRegion::Op op = element->getOp(); bool invert = element->isInverseFilled(); if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) { GrPipelineBuilder pipelineBuilder; pipelineBuilder.setClip(clip); GrPathRenderer* pr = nullptr; bool useTemp = !this->canStencilAndDrawElement(&pipelineBuilder, texture, &pr, element); GrTexture* dst; // 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. SkIRect maskSpaceElementIBounds; if (useTemp) { if (invert) { maskSpaceElementIBounds = maskSpaceIBounds; } else { SkRect elementBounds = element->getBounds(); elementBounds.offset(clipToMaskOffset); elementBounds.roundOut(&maskSpaceElementIBounds); } if (!temp) { temp.reset(this->createTempMask(maskSpaceIBounds.fRight, maskSpaceIBounds.fBottom)); if (!temp) { texture->resourcePriv().removeUniqueKey(); return nullptr; } } dst = temp; // clear the temp target and set blend to replace fDrawTarget->clear(&maskSpaceElementIBounds, invert ? 0xffffffff : 0x00000000, true, dst->asRenderTarget()); set_coverage_drawing_xpf(SkRegion::kReplace_Op, invert, &pipelineBuilder); } else { // draw directly into the result with the stencil set to make the pixels affected // by the clip shape be non-zero. dst = texture; GR_STATIC_CONST_SAME_STENCIL(kStencilInElement, kReplace_StencilOp, kReplace_StencilOp, kAlways_StencilFunc, 0xffff, 0xffff, 0xffff); pipelineBuilder.setStencil(kStencilInElement); set_coverage_drawing_xpf(op, invert, &pipelineBuilder); } if (!this->drawElement(&pipelineBuilder, translate, dst, element, pr)) { texture->resourcePriv().removeUniqueKey(); return nullptr; } if (useTemp) { GrPipelineBuilder backgroundPipelineBuilder; backgroundPipelineBuilder.setRenderTarget(texture->asRenderTarget()); // Now draw into the accumulator using the real operation and the temp buffer as a // texture this->mergeMask(&backgroundPipelineBuilder, texture, temp, op, maskSpaceIBounds, maskSpaceElementIBounds); } else { GrPipelineBuilder backgroundPipelineBuilder; backgroundPipelineBuilder.setRenderTarget(texture->asRenderTarget()); set_coverage_drawing_xpf(op, !invert, &backgroundPipelineBuilder); // Draw to the exterior pixels (those with a zero stencil value). GR_STATIC_CONST_SAME_STENCIL(kDrawOutsideElement, kZero_StencilOp, kZero_StencilOp, kEqual_StencilFunc, 0xffff, 0x0000, 0xffff); backgroundPipelineBuilder.setStencil(kDrawOutsideElement); // The color passed in here does not matter since the coverageSetOpXP won't read it. fDrawTarget->drawNonAARect(backgroundPipelineBuilder, GrColor_WHITE, translate, clipSpaceIBounds); } } else { GrPipelineBuilder pipelineBuilder; // all the remaining ops can just be directly draw into the accumulation buffer set_coverage_drawing_xpf(op, false, &pipelineBuilder); // The color passed in here does not matter since the coverageSetOpXP won't read it. this->drawElement(&pipelineBuilder, translate, texture, element); } } return texture.detach(); }
GrTexture* GaussianBlur(GrContext* context, GrTexture* srcTexture, bool canClobberSrc, const SkRect& rect, bool cropToRect, float sigmaX, float sigmaY) { SkASSERT(NULL != context); GrContext::AutoRenderTarget art(context); GrContext::AutoMatrix am; am.setIdentity(context); SkIRect clearRect; int scaleFactorX, radiusX; int scaleFactorY, radiusY; sigmaX = adjust_sigma(sigmaX, &scaleFactorX, &radiusX); sigmaY = adjust_sigma(sigmaY, &scaleFactorY, &radiusY); SkRect srcRect(rect); scale_rect(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY); srcRect.roundOut(); scale_rect(&srcRect, static_cast<float>(scaleFactorX), static_cast<float>(scaleFactorY)); GrContext::AutoClip acs(context, SkRect::MakeWH(srcRect.width(), srcRect.height())); SkASSERT(kBGRA_8888_GrPixelConfig == srcTexture->config() || kRGBA_8888_GrPixelConfig == srcTexture->config() || kAlpha_8_GrPixelConfig == srcTexture->config()); GrTextureDesc desc; desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit; desc.fWidth = SkScalarFloorToInt(srcRect.width()); desc.fHeight = SkScalarFloorToInt(srcRect.height()); desc.fConfig = srcTexture->config(); GrAutoScratchTexture temp1, temp2; GrTexture* dstTexture = temp1.set(context, desc); GrTexture* tempTexture = canClobberSrc ? srcTexture : temp2.set(context, desc); if (NULL == dstTexture || NULL == tempTexture) { return NULL; } for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) { GrPaint paint; SkMatrix matrix; matrix.setIDiv(srcTexture->width(), srcTexture->height()); context->setRenderTarget(dstTexture->asRenderTarget()); SkRect dstRect(srcRect); if (cropToRect && i == 1) { dstRect.offset(-dstRect.fLeft, -dstRect.fTop); SkRect domain; matrix.mapRect(&domain, rect); domain.inset(i < scaleFactorX ? SK_ScalarHalf / srcTexture->width() : 0.0f, i < scaleFactorY ? SK_ScalarHalf / srcTexture->height() : 0.0f); SkAutoTUnref<GrEffectRef> effect(GrTextureDomainEffect::Create( srcTexture, matrix, domain, GrTextureDomainEffect::kDecal_WrapMode, GrTextureParams::kBilerp_FilterMode)); paint.addColorEffect(effect); } else { GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode); paint.addColorTextureEffect(srcTexture, matrix, params); } scale_rect(&dstRect, i < scaleFactorX ? 0.5f : 1.0f, i < scaleFactorY ? 0.5f : 1.0f); context->drawRectToRect(paint, dstRect, srcRect); srcRect = dstRect; srcTexture = dstTexture; SkTSwap(dstTexture, tempTexture); } SkIRect srcIRect; srcRect.roundOut(&srcIRect); if (sigmaX > 0.0f) { if (scaleFactorX > 1) { // Clear out a radius to the right of the srcRect to prevent the // X convolution from reading garbage. clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop, radiusX, srcIRect.height()); context->clear(&clearRect, 0x0, false); } context->setRenderTarget(dstTexture->asRenderTarget()); SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); convolve_gaussian(context, srcRect, dstRect, srcTexture, Gr1DKernelEffect::kX_Direction, radiusX, sigmaX, cropToRect); srcTexture = dstTexture; srcRect = dstRect; SkTSwap(dstTexture, tempTexture); } if (sigmaY > 0.0f) { if (scaleFactorY > 1 || sigmaX > 0.0f) { // Clear out a radius below the srcRect to prevent the Y // convolution from reading garbage. clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom, srcIRect.width(), radiusY); context->clear(&clearRect, 0x0, false); } context->setRenderTarget(dstTexture->asRenderTarget()); SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); convolve_gaussian(context, srcRect, dstRect, srcTexture, Gr1DKernelEffect::kY_Direction, radiusY, sigmaY, cropToRect); srcTexture = dstTexture; srcRect = dstRect; SkTSwap(dstTexture, tempTexture); } if (scaleFactorX > 1 || scaleFactorY > 1) { // Clear one pixel to the right and below, to accommodate bilinear // upsampling. clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom, srcIRect.width() + 1, 1); context->clear(&clearRect, 0x0, false); clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop, 1, srcIRect.height()); context->clear(&clearRect, 0x0, false); SkMatrix matrix; matrix.setIDiv(srcTexture->width(), srcTexture->height()); context->setRenderTarget(dstTexture->asRenderTarget()); GrPaint paint; // FIXME: this should be mitchell, not bilinear. GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode); paint.addColorTextureEffect(srcTexture, matrix, params); SkRect dstRect(srcRect); scale_rect(&dstRect, (float) scaleFactorX, (float) scaleFactorY); context->drawRectToRect(paint, dstRect, srcRect); srcRect = dstRect; srcTexture = dstTexture; SkTSwap(dstTexture, tempTexture); } if (srcTexture == temp1.texture()) { return temp1.detach(); } else if (srcTexture == temp2.texture()) { return temp2.detach(); } else { srcTexture->ref(); return srcTexture; } }
static GrTexture* load_yuv_texture(GrContext* ctx, const GrUniqueKey& optionalKey, const SkBitmap& bm, const GrSurfaceDesc& desc) { // Subsets are not supported, the whole pixelRef is loaded when using YUV decoding SkPixelRef* pixelRef = bm.pixelRef(); if ((NULL == pixelRef) || (pixelRef->info().width() != bm.info().width()) || (pixelRef->info().height() != bm.info().height())) { return NULL; } const bool useCache = optionalKey.isValid(); SkYUVPlanesCache::Info yuvInfo; SkAutoTUnref<SkCachedData> cachedData; SkAutoMalloc storage; if (useCache) { cachedData.reset(SkYUVPlanesCache::FindAndRef(pixelRef->getGenerationID(), &yuvInfo)); } void* planes[3]; if (cachedData.get()) { planes[0] = (void*)cachedData->data(); planes[1] = (uint8_t*)planes[0] + yuvInfo.fSizeInMemory[0]; planes[2] = (uint8_t*)planes[1] + yuvInfo.fSizeInMemory[1]; } else { // Fetch yuv plane sizes for memory allocation. Here, width and height can be // rounded up to JPEG block size and be larger than the image's width and height. if (!pixelRef->getYUV8Planes(yuvInfo.fSize, NULL, NULL, NULL)) { return NULL; } // Allocate the memory for YUV size_t totalSize(0); for (int i = 0; i < 3; ++i) { yuvInfo.fRowBytes[i] = yuvInfo.fSize[i].fWidth; yuvInfo.fSizeInMemory[i] = yuvInfo.fRowBytes[i] * yuvInfo.fSize[i].fHeight; totalSize += yuvInfo.fSizeInMemory[i]; } if (useCache) { cachedData.reset(SkResourceCache::NewCachedData(totalSize)); planes[0] = cachedData->writable_data(); } else { storage.reset(totalSize); planes[0] = storage.get(); } planes[1] = (uint8_t*)planes[0] + yuvInfo.fSizeInMemory[0]; planes[2] = (uint8_t*)planes[1] + yuvInfo.fSizeInMemory[1]; // Get the YUV planes and update plane sizes to actual image size if (!pixelRef->getYUV8Planes(yuvInfo.fSize, planes, yuvInfo.fRowBytes, &yuvInfo.fColorSpace)) { return NULL; } if (useCache) { // Decoding is done, cache the resulting YUV planes SkYUVPlanesCache::Add(pixelRef->getGenerationID(), cachedData, &yuvInfo); } } GrSurfaceDesc yuvDesc; yuvDesc.fConfig = kAlpha_8_GrPixelConfig; SkAutoTUnref<GrTexture> yuvTextures[3]; for (int i = 0; i < 3; ++i) { yuvDesc.fWidth = yuvInfo.fSize[i].fWidth; yuvDesc.fHeight = yuvInfo.fSize[i].fHeight; bool needsExactTexture = (yuvDesc.fWidth != yuvInfo.fSize[0].fWidth) || (yuvDesc.fHeight != yuvInfo.fSize[0].fHeight); if (needsExactTexture) { yuvTextures[i].reset(ctx->textureProvider()->createTexture(yuvDesc, true)); } else { yuvTextures[i].reset(ctx->textureProvider()->createApproxTexture(yuvDesc)); } if (!yuvTextures[i] || !yuvTextures[i]->writePixels(0, 0, yuvDesc.fWidth, yuvDesc.fHeight, yuvDesc.fConfig, planes[i], yuvInfo.fRowBytes[i])) { return NULL; } } GrSurfaceDesc rtDesc = desc; rtDesc.fFlags = rtDesc.fFlags | kRenderTarget_GrSurfaceFlag; GrTexture* result = create_texture_for_bmp(ctx, optionalKey, rtDesc, pixelRef, NULL, 0); if (!result) { return NULL; } GrRenderTarget* renderTarget = result->asRenderTarget(); SkASSERT(renderTarget); GrPaint paint; SkAutoTUnref<GrFragmentProcessor> yuvToRgbProcessor(GrYUVtoRGBEffect::Create(paint.getProcessorDataManager(), yuvTextures[0], yuvTextures[1], yuvTextures[2], yuvInfo.fSize, yuvInfo.fColorSpace)); paint.addColorProcessor(yuvToRgbProcessor); SkRect r = SkRect::MakeWH(SkIntToScalar(yuvInfo.fSize[0].fWidth), SkIntToScalar(yuvInfo.fSize[0].fHeight)); GrDrawContext* drawContext = ctx->drawContext(); if (!drawContext) { return NULL; } drawContext->drawRect(renderTarget, GrClip::WideOpen(), paint, SkMatrix::I(), r); return result; }
bool GrDrawTarget::checkDraw(const GrPipelineBuilder& pipelineBuilder, const GrGeometryProcessor* gp, GrPrimitiveType type, int startVertex, int startIndex, int vertexCount, int indexCount) const { #ifdef SK_DEBUG const GeometrySrcState& geoSrc = fGeoSrcStateStack.back(); int maxVertex = startVertex + vertexCount; int maxValidVertex; switch (geoSrc.fVertexSrc) { case kNone_GeometrySrcType: SkFAIL("Attempting to draw without vertex src."); case kReserved_GeometrySrcType: // fallthrough maxValidVertex = geoSrc.fVertexCount; break; case kBuffer_GeometrySrcType: maxValidVertex = static_cast<int>(geoSrc.fVertexBuffer->gpuMemorySize() / geoSrc.fVertexSize); break; } if (maxVertex > maxValidVertex) { SkFAIL("Drawing outside valid vertex range."); } if (indexCount > 0) { int maxIndex = startIndex + indexCount; int maxValidIndex; switch (geoSrc.fIndexSrc) { case kNone_GeometrySrcType: SkFAIL("Attempting to draw indexed geom without index src."); case kReserved_GeometrySrcType: // fallthrough maxValidIndex = geoSrc.fIndexCount; break; case kBuffer_GeometrySrcType: maxValidIndex = static_cast<int>(geoSrc.fIndexBuffer->gpuMemorySize() / sizeof(uint16_t)); break; } if (maxIndex > maxValidIndex) { SkFAIL("Index reads outside valid index range."); } } SkASSERT(pipelineBuilder.getRenderTarget()); if (gp) { int numTextures = gp->numTextures(); for (int t = 0; t < numTextures; ++t) { GrTexture* texture = gp->texture(t); SkASSERT(texture->asRenderTarget() != pipelineBuilder.getRenderTarget()); } } for (int s = 0; s < pipelineBuilder.numColorFragmentStages(); ++s) { const GrProcessor* effect = pipelineBuilder.getColorFragmentStage(s).processor(); int numTextures = effect->numTextures(); for (int t = 0; t < numTextures; ++t) { GrTexture* texture = effect->texture(t); SkASSERT(texture->asRenderTarget() != pipelineBuilder.getRenderTarget()); } } for (int s = 0; s < pipelineBuilder.numCoverageFragmentStages(); ++s) { const GrProcessor* effect = pipelineBuilder.getCoverageFragmentStage(s).processor(); int numTextures = effect->numTextures(); for (int t = 0; t < numTextures; ++t) { GrTexture* texture = effect->texture(t); SkASSERT(texture->asRenderTarget() != pipelineBuilder.getRenderTarget()); } } #endif if (NULL == pipelineBuilder.getRenderTarget()) { return false; } 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; }
DEF_GPUTEST(ReadWriteAlpha, reporter, factory) { for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) { GrContextFactory::GLContextType glType = static_cast<GrContextFactory::GLContextType>(type); if (!GrContextFactory::IsRenderingGLContext(glType)) { continue; } GrContext* context = factory->get(glType); if (NULL == context) { continue; } unsigned char textureData[X_SIZE][Y_SIZE]; memset(textureData, 0, X_SIZE * Y_SIZE); GrSurfaceDesc desc; // let Skia know we will be using this texture as a render target desc.fFlags = kRenderTarget_GrSurfaceFlag; // it is a single channel texture desc.fConfig = kAlpha_8_GrPixelConfig; desc.fWidth = X_SIZE; desc.fHeight = Y_SIZE; // We are initializing the texture with zeros here GrTexture* texture = context->createTexture(desc, false, textureData, 0); if (!texture) { return; } SkAutoTUnref<GrTexture> au(texture); // create a distinctive texture for (int y = 0; y < Y_SIZE; ++y) { for (int x = 0; x < X_SIZE; ++x) { textureData[x][y] = x*Y_SIZE+y; } } // upload the texture texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig, textureData, 0); unsigned char readback[X_SIZE][Y_SIZE]; // clear readback to something non-zero so we can detect readback failures memset(readback, 0x1, X_SIZE * Y_SIZE); // read the texture back texture->readPixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig, readback, 0); // make sure the original & read back versions match bool match = true; for (int y = 0; y < Y_SIZE; ++y) { for (int x = 0; x < X_SIZE; ++x) { if (textureData[x][y] != readback[x][y]) { match = false; } } } REPORTER_ASSERT(reporter, match); // Now try writing on the single channel texture SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType); SkAutoTUnref<SkBaseDevice> device(SkGpuDevice::Create(texture->asRenderTarget(), &props)); SkCanvas canvas(device); SkPaint paint; const SkRect rect = SkRect::MakeLTRB(-10, -10, X_SIZE + 10, Y_SIZE + 10); paint.setColor(SK_ColorWHITE); canvas.drawRect(rect, paint); texture->readPixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig, readback, 0); match = true; for (int y = 0; y < Y_SIZE; ++y) { for (int x = 0; x < X_SIZE; ++x) { if (0xFF != readback[x][y]) { match = false; } } } REPORTER_ASSERT(reporter, match); } }
// creates a new texture that is the input texture scaled up. If optionalKey is valid it will be // set on the new texture. stretch controls whether the scaling is done using nearest or bilerp // filtering and the size to stretch the texture to. GrTexture* stretch_texture(GrTexture* inputTexture, const Stretch& stretch, SkPixelRef* pixelRef, const GrUniqueKey& optionalKey) { SkASSERT(Stretch::kNone_Type != stretch.fType); GrContext* context = inputTexture->getContext(); SkASSERT(context); const GrCaps* caps = context->caps(); // Either it's a cache miss or the original wasn't cached to begin with. GrSurfaceDesc rtDesc = inputTexture->desc(); rtDesc.fFlags = rtDesc.fFlags | kRenderTarget_GrSurfaceFlag; rtDesc.fWidth = stretch.fWidth; rtDesc.fHeight = stretch.fHeight; rtDesc.fConfig = GrMakePixelConfigUncompressed(rtDesc.fConfig); // If the config isn't renderable try converting to either A8 or an 32 bit config. Otherwise, // fail. if (!caps->isConfigRenderable(rtDesc.fConfig, false)) { if (GrPixelConfigIsAlphaOnly(rtDesc.fConfig)) { if (caps->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { rtDesc.fConfig = kAlpha_8_GrPixelConfig; } else if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) { rtDesc.fConfig = kSkia8888_GrPixelConfig; } else { return NULL; } } else if (kRGB_GrColorComponentFlags == (kRGB_GrColorComponentFlags & GrPixelConfigComponentMask(rtDesc.fConfig))) { if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) { rtDesc.fConfig = kSkia8888_GrPixelConfig; } else { return NULL; } } else { return NULL; } } GrTexture* stretched = create_texture_for_bmp(context, optionalKey, rtDesc, pixelRef, NULL, 0); if (!stretched) { return NULL; } GrPaint paint; // If filtering is not desired then we want to ensure all texels in the resampled image are // copies of texels from the original. GrTextureParams params(SkShader::kClamp_TileMode, Stretch::kBilerp_Type == stretch.fType ? GrTextureParams::kBilerp_FilterMode : GrTextureParams::kNone_FilterMode); paint.addColorTextureProcessor(inputTexture, SkMatrix::I(), params); SkRect rect = SkRect::MakeWH(SkIntToScalar(rtDesc.fWidth), SkIntToScalar(rtDesc.fHeight)); SkRect localRect = SkRect::MakeWH(1.f, 1.f); GrDrawContext* drawContext = context->drawContext(); if (!drawContext) { return NULL; } drawContext->drawNonAARectToRect(stretched->asRenderTarget(), GrClip::WideOpen(), paint, SkMatrix::I(), rect, localRect); return stretched; }
GrTexture* GaussianBlur(GrContext* context, GrTexture* srcTexture, bool canClobberSrc, const SkRect& dstBounds, const SkRect* srcBounds, float sigmaX, float sigmaY, GrTextureProvider::SizeConstraint constraint) { SkASSERT(context); SkIRect clearRect; int scaleFactorX, radiusX; int scaleFactorY, radiusY; int maxTextureSize = context->caps()->maxTextureSize(); sigmaX = adjust_sigma(sigmaX, maxTextureSize, &scaleFactorX, &radiusX); sigmaY = adjust_sigma(sigmaY, maxTextureSize, &scaleFactorY, &radiusY); SkPoint srcOffset = SkPoint::Make(-dstBounds.x(), -dstBounds.y()); SkRect localDstBounds = SkRect::MakeWH(dstBounds.width(), dstBounds.height()); SkRect localSrcBounds; SkRect srcRect; if (srcBounds) { srcRect = localSrcBounds = *srcBounds; srcRect.offset(srcOffset); srcBounds = &localSrcBounds; } else { srcRect = localDstBounds; } scale_rect(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY); srcRect.roundOut(&srcRect); scale_rect(&srcRect, static_cast<float>(scaleFactorX), static_cast<float>(scaleFactorY)); // setup new clip GrClip clip(localDstBounds); SkASSERT(kBGRA_8888_GrPixelConfig == srcTexture->config() || kRGBA_8888_GrPixelConfig == srcTexture->config() || kAlpha_8_GrPixelConfig == srcTexture->config()); GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = SkScalarFloorToInt(dstBounds.width()); desc.fHeight = SkScalarFloorToInt(dstBounds.height()); desc.fConfig = srcTexture->config(); GrTexture* dstTexture; GrTexture* tempTexture; SkAutoTUnref<GrTexture> temp1, temp2; temp1.reset(context->textureProvider()->createTexture(desc, constraint)); dstTexture = temp1.get(); if (canClobberSrc) { tempTexture = srcTexture; } else { temp2.reset(context->textureProvider()->createTexture(desc, constraint)); tempTexture = temp2.get(); } if (nullptr == dstTexture || nullptr == tempTexture) { return nullptr; } SkAutoTUnref<GrDrawContext> srcDrawContext; for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) { GrPaint paint; SkMatrix matrix; matrix.setIDiv(srcTexture->width(), srcTexture->height()); SkRect dstRect(srcRect); if (srcBounds && i == 1) { SkRect domain; matrix.mapRect(&domain, *srcBounds); domain.inset((i < scaleFactorX) ? SK_ScalarHalf / srcTexture->width() : 0.0f, (i < scaleFactorY) ? SK_ScalarHalf / srcTexture->height() : 0.0f); SkAutoTUnref<const GrFragmentProcessor> fp(GrTextureDomainEffect::Create( srcTexture, matrix, domain, GrTextureDomain::kDecal_Mode, GrTextureParams::kBilerp_FilterMode)); paint.addColorFragmentProcessor(fp); srcRect.offset(-srcOffset); srcOffset.set(0, 0); } else { GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode); paint.addColorTextureProcessor(srcTexture, matrix, params); } paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); scale_rect(&dstRect, i < scaleFactorX ? 0.5f : 1.0f, i < scaleFactorY ? 0.5f : 1.0f); SkAutoTUnref<GrDrawContext> dstDrawContext( context->drawContext(dstTexture->asRenderTarget())); if (!dstDrawContext) { return nullptr; } dstDrawContext->fillRectToRect(clip, paint, SkMatrix::I(), dstRect, srcRect); srcDrawContext.swap(dstDrawContext); srcRect = dstRect; srcTexture = dstTexture; SkTSwap(dstTexture, tempTexture); localSrcBounds = srcRect; } // For really small blurs (certainly no wider than 5x5 on desktop gpus) it is faster to just // launch a single non separable kernel vs two launches srcRect = localDstBounds; if (sigmaX > 0.0f && sigmaY > 0.0f && (2 * radiusX + 1) * (2 * radiusY + 1) <= MAX_KERNEL_SIZE) { // We shouldn't be scaling because this is a small size blur SkASSERT((1 == scaleFactorX) && (1 == scaleFactorY)); SkAutoTUnref<GrDrawContext> dstDrawContext( context->drawContext(dstTexture->asRenderTarget())); if (!dstDrawContext) { return nullptr; } convolve_gaussian_2d(dstDrawContext, clip, srcRect, srcOffset, srcTexture, radiusX, radiusY, sigmaX, sigmaY, srcBounds); srcDrawContext.swap(dstDrawContext); srcRect.offsetTo(0, 0); srcTexture = dstTexture; SkTSwap(dstTexture, tempTexture); } else { scale_rect(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY); srcRect.roundOut(&srcRect); const SkIRect srcIRect = srcRect.roundOut(); if (sigmaX > 0.0f) { if (scaleFactorX > 1) { // TODO: if we pass in the source draw context we don't need this here if (!srcDrawContext) { srcDrawContext.reset(context->drawContext(srcTexture->asRenderTarget())); if (!srcDrawContext) { return nullptr; } } // Clear out a radius to the right of the srcRect to prevent the // X convolution from reading garbage. clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop, radiusX, srcIRect.height()); srcDrawContext->clear(&clearRect, 0x0, false); } SkAutoTUnref<GrDrawContext> dstDrawContext( context->drawContext(dstTexture->asRenderTarget())); if (!dstDrawContext) { return nullptr; } convolve_gaussian(dstDrawContext, clip, srcRect, srcTexture, Gr1DKernelEffect::kX_Direction, radiusX, sigmaX, srcBounds, srcOffset); srcDrawContext.swap(dstDrawContext); srcTexture = dstTexture; srcRect.offsetTo(0, 0); SkTSwap(dstTexture, tempTexture); localSrcBounds = srcRect; srcOffset.set(0, 0); } if (sigmaY > 0.0f) { if (scaleFactorY > 1 || sigmaX > 0.0f) { // TODO: if we pass in the source draw context we don't need this here if (!srcDrawContext) { srcDrawContext.reset(context->drawContext(srcTexture->asRenderTarget())); if (!srcDrawContext) { return nullptr; } } // Clear out a radius below the srcRect to prevent the Y // convolution from reading garbage. clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom, srcIRect.width(), radiusY); srcDrawContext->clear(&clearRect, 0x0, false); } SkAutoTUnref<GrDrawContext> dstDrawContext( context->drawContext(dstTexture->asRenderTarget())); if (!dstDrawContext) { return nullptr; } convolve_gaussian(dstDrawContext, clip, srcRect, srcTexture, Gr1DKernelEffect::kY_Direction, radiusY, sigmaY, srcBounds, srcOffset); srcDrawContext.swap(dstDrawContext); srcTexture = dstTexture; srcRect.offsetTo(0, 0); SkTSwap(dstTexture, tempTexture); } } const SkIRect srcIRect = srcRect.roundOut(); if (scaleFactorX > 1 || scaleFactorY > 1) { SkASSERT(srcDrawContext); // Clear one pixel to the right and below, to accommodate bilinear // upsampling. clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom, srcIRect.width() + 1, 1); srcDrawContext->clear(&clearRect, 0x0, false); clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop, 1, srcIRect.height()); srcDrawContext->clear(&clearRect, 0x0, false); SkMatrix matrix; matrix.setIDiv(srcTexture->width(), srcTexture->height()); GrPaint paint; // FIXME: this should be mitchell, not bilinear. GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode); paint.addColorTextureProcessor(srcTexture, matrix, params); paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); SkRect dstRect(srcRect); scale_rect(&dstRect, (float) scaleFactorX, (float) scaleFactorY); SkAutoTUnref<GrDrawContext> dstDrawContext( context->drawContext(dstTexture->asRenderTarget())); if (!dstDrawContext) { return nullptr; } dstDrawContext->fillRectToRect(clip, paint, SkMatrix::I(), dstRect, srcRect); srcDrawContext.swap(dstDrawContext); srcRect = dstRect; srcTexture = dstTexture; SkTSwap(dstTexture, tempTexture); } return SkRef(srcTexture); }