sk_sp<SkImage> SkImage_Gpu::onMakeColorSpace(sk_sp<SkColorSpace> colorSpace) const { sk_sp<SkColorSpace> srcSpace = fColorSpace ? fColorSpace : SkColorSpace::MakeSRGB(); auto xform = GrNonlinearColorSpaceXformEffect::Make(srcSpace.get(), colorSpace.get()); if (!xform) { return sk_ref_sp(const_cast<SkImage_Gpu*>(this)); } sk_sp<GrRenderTargetContext> renderTargetContext(fContext->makeRenderTargetContext( SkBackingFit::kExact, this->width(), this->height(), kRGBA_8888_GrPixelConfig, nullptr)); if (!renderTargetContext) { return nullptr; } GrPaint paint; paint.setPorterDuffXPFactory(SkBlendMode::kSrc); paint.addColorTextureProcessor(fContext->resourceProvider(), fProxy, nullptr, SkMatrix::I()); paint.addColorFragmentProcessor(std::move(xform)); const SkRect rect = SkRect::MakeIWH(this->width(), this->height()); renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), rect); if (!renderTargetContext->asTextureProxy()) { return nullptr; } // MDB: this call is okay bc we know 'renderTargetContext' was exact return sk_make_sp<SkImage_Gpu>(fContext, kNeedNewImageUniqueID, fAlphaType, renderTargetContext->asTextureProxyRef(), std::move(colorSpace), fBudgeted); }
static bool convert_texture(GrTexture* src, GrDrawContext* dst, int dstW, int dstH, SkYUVColorSpace colorSpace, MakeFPProc proc) { SkScalar xScale = SkIntToScalar(src->width()) / dstW / src->width(); SkScalar yScale = SkIntToScalar(src->height()) / dstH / src->height(); GrTextureParams::FilterMode filter; if (dstW == src->width() && dstW == src->height()) { filter = GrTextureParams::kNone_FilterMode; } else { filter = GrTextureParams::kBilerp_FilterMode; } sk_sp<GrFragmentProcessor> fp( GrSimpleTextureEffect::Make(src, SkMatrix::MakeScale(xScale, yScale), filter)); if (!fp) { return false; } fp = proc(std::move(fp), colorSpace); if (!fp) { return false; } GrPaint paint; paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); paint.addColorFragmentProcessor(std::move(fp)); dst->drawRect(GrNoClip(), paint, SkMatrix::I(), SkRect::MakeIWH(dstW, dstH)); return true; }
sk_sp<GrTextureProxy> GrTextureProducer::CopyOnGpu(GrContext* context, sk_sp<GrTextureProxy> inputProxy, const CopyParams& copyParams, bool dstWillRequireMipMaps) { SkASSERT(context); const SkRect dstRect = SkRect::MakeIWH(copyParams.fWidth, copyParams.fHeight); GrMipMapped mipMapped = dstWillRequireMipMaps ? GrMipMapped::kYes : GrMipMapped::kNo; SkRect localRect = SkRect::MakeWH(inputProxy->width(), inputProxy->height()); bool needsDomain = false; bool resizing = false; if (copyParams.fFilter != GrSamplerState::Filter::kNearest) { bool resizing = localRect.width() != dstRect.width() || localRect.height() != dstRect.height(); needsDomain = resizing && !GrProxyProvider::IsFunctionallyExact(inputProxy.get()); } if (copyParams.fFilter == GrSamplerState::Filter::kNearest && !needsDomain && !resizing && dstWillRequireMipMaps) { sk_sp<GrTextureProxy> proxy = GrCopyBaseMipMapToTextureProxy(context, inputProxy.get()); if (proxy) { return proxy; } } sk_sp<GrRenderTargetContext> copyRTC = context->contextPriv().makeDeferredRenderTargetContextWithFallback( SkBackingFit::kExact, dstRect.width(), dstRect.height(), inputProxy->config(), nullptr, 1, mipMapped, inputProxy->origin()); if (!copyRTC) { return nullptr; } GrPaint paint; if (needsDomain) { const SkRect domain = localRect.makeInset(0.5f, 0.5f); // This would cause us to read values from outside the subset. Surely, the caller knows // better! SkASSERT(copyParams.fFilter != GrSamplerState::Filter::kMipMap); paint.addColorFragmentProcessor( GrTextureDomainEffect::Make(std::move(inputProxy), SkMatrix::I(), domain, GrTextureDomain::kClamp_Mode, copyParams.fFilter)); } else { GrSamplerState samplerState(GrSamplerState::WrapMode::kClamp, copyParams.fFilter); paint.addColorTextureProcessor(std::move(inputProxy), SkMatrix::I(), samplerState); } paint.setPorterDuffXPFactory(SkBlendMode::kSrc); copyRTC->fillRectToRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), dstRect, localRect); return copyRTC->asTextureProxyRef(); }
// skbug.com/5932 static void test_basic_draw_as_src(skiatest::Reporter* reporter, GrContext* context, sk_sp<GrTextureProxy> rectProxy, uint32_t expectedPixelValues[]) { sk_sp<GrRenderTargetContext> rtContext(context->contextPriv().makeDeferredRenderTargetContext( SkBackingFit::kExact, rectProxy->width(), rectProxy->height(), rectProxy->config(), nullptr)); for (auto filter : {GrSamplerState::Filter::kNearest, GrSamplerState::Filter::kBilerp, GrSamplerState::Filter::kMipMap}) { rtContext->clear(nullptr, 0xDDCCBBAA, GrRenderTargetContext::CanClearFullscreen::kYes); auto fp = GrSimpleTextureEffect::Make(rectProxy, SkMatrix::I(), filter); GrPaint paint; paint.setPorterDuffXPFactory(SkBlendMode::kSrc); paint.addColorFragmentProcessor(std::move(fp)); rtContext->drawPaint(GrNoClip(), std::move(paint), SkMatrix::I()); test_read_pixels(reporter, rtContext.get(), expectedPixelValues, "RectangleTexture-basic-draw"); } }
void SkGpuDevice::drawTexture(GrTexture* tex, const SkRect& dst, const SkPaint& paint) { GrPaint grPaint; SkMatrix mat; mat.reset(); if (!SkPaintToGrPaint(this->context(), paint, mat, this->surfaceProps().isGammaCorrect(), &grPaint)) { return; } SkMatrix textureMat; textureMat.reset(); textureMat[SkMatrix::kMScaleX] = 1.0f/dst.width(); textureMat[SkMatrix::kMScaleY] = 1.0f/dst.height(); textureMat[SkMatrix::kMTransX] = -dst.fLeft/dst.width(); textureMat[SkMatrix::kMTransY] = -dst.fTop/dst.height(); grPaint.addColorTextureProcessor(tex, textureMat); fDrawContext->drawRect(GrNoClip(), grPaint, mat, dst); }
void GrDrawTarget::clear(const SkIRect* rect, GrColor color, bool canIgnoreRect, GrRenderTarget* renderTarget) { SkIRect rtRect = SkIRect::MakeWH(renderTarget->width(), renderTarget->height()); SkIRect clippedRect; if (!rect || (canIgnoreRect && this->caps()->fullClearIsFree()) || rect->contains(rtRect)) { rect = &rtRect; } else { clippedRect = *rect; if (!clippedRect.intersect(rtRect)) { return; } rect = &clippedRect; } if (this->caps()->useDrawInsteadOfClear()) { // This works around a driver bug with clear by drawing a rect instead. // The driver will ignore a clear if it is the only thing rendered to a // target before the target is read. if (rect == &rtRect) { this->discard(renderTarget); } GrPipelineBuilder pipelineBuilder; pipelineBuilder.setXPFactory( GrPorterDuffXPFactory::Create(SkXfermode::kSrc_Mode))->unref(); pipelineBuilder.setRenderTarget(renderTarget); SkRect scalarRect = SkRect::Make(*rect); SkAutoTUnref<GrDrawBatch> batch( GrRectBatchFactory::CreateNonAAFill(color, SkMatrix::I(), scalarRect, nullptr, nullptr)); this->drawBatch(pipelineBuilder, GrNoClip(), batch); } else { GrBatch* batch = new GrClearBatch(*rect, color, renderTarget); this->recordBatch(batch); batch->unref(); } }
sk_sp<GrRenderTargetContext> GrCCPRAtlas::finalize(GrOnFlushResourceProvider* onFlushRP, std::unique_ptr<GrDrawOp> atlasOp) { SkASSERT(!fTextureProxy); GrSurfaceDesc desc; desc.fOrigin = GrCCPRCoverageProcessor::kAtlasOrigin; desc.fWidth = fWidth; desc.fHeight = fHeight; desc.fConfig = kAlpha_half_GrPixelConfig; sk_sp<GrRenderTargetContext> rtc = onFlushRP->makeRenderTargetContext(desc, nullptr, nullptr); if (!rtc) { SkDebugf("WARNING: failed to allocate a %ix%i atlas. Some paths will not be drawn.\n", fWidth, fHeight); return nullptr; } SkIRect clearRect = SkIRect::MakeSize(fDrawBounds); rtc->clear(&clearRect, 0, true); rtc->addDrawOp(GrNoClip(), std::move(atlasOp)); fTextureProxy = sk_ref_sp(rtc->asTextureProxy()); return rtc; }
static sk_sp<SkImage> make_from_yuv_textures_copy(GrContext* ctx, SkYUVColorSpace colorSpace, bool nv12, const GrBackendObject yuvTextureHandles[], const SkISize yuvSizes[], GrSurfaceOrigin origin, sk_sp<SkColorSpace> imageColorSpace) { const SkBudgeted budgeted = SkBudgeted::kYes; if (yuvSizes[0].fWidth <= 0 || yuvSizes[0].fHeight <= 0 || yuvSizes[1].fWidth <= 0 || yuvSizes[1].fHeight <= 0) { return nullptr; } if (!nv12 && (yuvSizes[2].fWidth <= 0 || yuvSizes[2].fHeight <= 0)) { return nullptr; } const GrPixelConfig kConfig = nv12 ? kRGBA_8888_GrPixelConfig : kAlpha_8_GrPixelConfig; GrBackendTextureDesc yDesc; yDesc.fConfig = kConfig; yDesc.fOrigin = origin; yDesc.fSampleCnt = 0; yDesc.fTextureHandle = yuvTextureHandles[0]; yDesc.fWidth = yuvSizes[0].fWidth; yDesc.fHeight = yuvSizes[0].fHeight; GrBackendTextureDesc uDesc; uDesc.fConfig = kConfig; uDesc.fOrigin = origin; uDesc.fSampleCnt = 0; uDesc.fTextureHandle = yuvTextureHandles[1]; uDesc.fWidth = yuvSizes[1].fWidth; uDesc.fHeight = yuvSizes[1].fHeight; sk_sp<GrSurfaceProxy> yProxy = GrSurfaceProxy::MakeWrappedBackend(ctx, yDesc); sk_sp<GrSurfaceProxy> uProxy = GrSurfaceProxy::MakeWrappedBackend(ctx, uDesc); sk_sp<GrSurfaceProxy> vProxy; if (nv12) { vProxy = uProxy; } else { GrBackendTextureDesc vDesc; vDesc.fConfig = kConfig; vDesc.fOrigin = origin; vDesc.fSampleCnt = 0; vDesc.fTextureHandle = yuvTextureHandles[2]; vDesc.fWidth = yuvSizes[2].fWidth; vDesc.fHeight = yuvSizes[2].fHeight; vProxy = GrSurfaceProxy::MakeWrappedBackend(ctx, vDesc); } if (!yProxy || !uProxy || !vProxy) { return nullptr; } const int width = yuvSizes[0].fWidth; const int height = yuvSizes[0].fHeight; // Needs to be a render target in order to draw to it for the yuv->rgb conversion. sk_sp<GrRenderTargetContext> renderTargetContext(ctx->makeRenderTargetContext( SkBackingFit::kExact, width, height, kRGBA_8888_GrPixelConfig, std::move(imageColorSpace), 0, origin)); if (!renderTargetContext) { return nullptr; } GrPaint paint; paint.setPorterDuffXPFactory(SkBlendMode::kSrc); paint.addColorFragmentProcessor( GrYUVEffect::MakeYUVToRGB(ctx->resourceProvider(), sk_ref_sp(yProxy->asTextureProxy()), sk_ref_sp(uProxy->asTextureProxy()), sk_ref_sp(vProxy->asTextureProxy()), yuvSizes, colorSpace, nv12)); const SkRect rect = SkRect::MakeIWH(width, height); renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), rect); if (!renderTargetContext->asSurfaceProxy()) { return nullptr; } ctx->contextPriv().flushSurfaceWrites(renderTargetContext->asSurfaceProxy()); // MDB: this call is okay bc we know 'renderTargetContext' was exact return sk_make_sp<SkImage_Gpu>(ctx, kNeedNewImageUniqueID, kOpaque_SkAlphaType, renderTargetContext->asTextureProxyRef(), renderTargetContext->refColorSpace(), budgeted); }
sk_sp<GrTexture> GrYUVProvider::refAsTexture(GrContext* ctx, const GrSurfaceDesc& desc, bool useCache) { SkYUVPlanesCache::Info yuvInfo; void* planes[3]; YUVScoper scoper; if (!scoper.init(this, &yuvInfo, planes, useCache)) { return nullptr; } GrSurfaceDesc yuvDesc; yuvDesc.fConfig = kAlpha_8_GrPixelConfig; SkAutoTUnref<GrTexture> yuvTextures[3]; for (int i = 0; i < 3; i++) { yuvDesc.fWidth = yuvInfo.fSizeInfo.fSizes[i].fWidth; yuvDesc.fHeight = yuvInfo.fSizeInfo.fSizes[i].fHeight; // TODO: why do we need this check? bool needsExactTexture = (yuvDesc.fWidth != yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fWidth) || (yuvDesc.fHeight != yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight); if (needsExactTexture) { yuvTextures[i].reset(ctx->textureProvider()->createTexture(yuvDesc, SkBudgeted::kYes)); } 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.fSizeInfo.fWidthBytes[i])) { return nullptr; } } sk_sp<GrDrawContext> drawContext(ctx->newDrawContext(SkBackingFit::kExact, desc.fWidth, desc.fHeight, desc.fConfig, desc.fSampleCnt)); if (!drawContext) { return nullptr; } GrPaint paint; sk_sp<GrFragmentProcessor> yuvToRgbProcessor( GrYUVEffect::MakeYUVToRGB(yuvTextures[0], yuvTextures[1], yuvTextures[2], yuvInfo.fSizeInfo.fSizes, yuvInfo.fColorSpace, false)); paint.addColorFragmentProcessor(std::move(yuvToRgbProcessor)); // If we're decoding an sRGB image, the result of our linear math on the YUV planes is already // in sRGB. (The encoding is just math on bytes, with no concept of color spaces.) So, we need // to output the results of that math directly to the buffer that we will then consider sRGB. // If we have sRGB write control, we can just tell the HW not to do the Linear -> sRGB step. // Otherwise, we do our shader math to go from YUV -> sRGB, manually convert sRGB -> Linear, // then let the HW convert Linear -> sRGB. if (GrPixelConfigIsSRGB(desc.fConfig)) { if (ctx->caps()->srgbWriteControl()) { paint.setDisableOutputConversionToSRGB(true); } else { paint.addColorFragmentProcessor(GrGammaEffect::Make(2.2f)); } } paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); const SkRect r = SkRect::MakeIWH(yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fWidth, yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight); drawContext->drawRect(GrNoClip(), paint, SkMatrix::I(), r); return drawContext->asTexture(); }
static sk_sp<SkImage> make_from_yuv_textures_copy(GrContext* ctx, SkYUVColorSpace colorSpace, bool nv12, const GrBackendObject yuvTextureHandles[], const SkISize yuvSizes[], GrSurfaceOrigin origin, sk_sp<SkColorSpace> imageColorSpace) { const SkBudgeted budgeted = SkBudgeted::kYes; if (yuvSizes[0].fWidth <= 0 || yuvSizes[0].fHeight <= 0 || yuvSizes[1].fWidth <= 0 || yuvSizes[1].fHeight <= 0) { return nullptr; } if (!nv12 && (yuvSizes[2].fWidth <= 0 || yuvSizes[2].fHeight <= 0)) { return nullptr; } const GrPixelConfig kConfig = nv12 ? kRGBA_8888_GrPixelConfig : kAlpha_8_GrPixelConfig; GrBackendTextureDesc yDesc; yDesc.fConfig = kConfig; yDesc.fOrigin = origin; yDesc.fSampleCnt = 0; yDesc.fTextureHandle = yuvTextureHandles[0]; yDesc.fWidth = yuvSizes[0].fWidth; yDesc.fHeight = yuvSizes[0].fHeight; GrBackendTextureDesc uDesc; uDesc.fConfig = kConfig; uDesc.fOrigin = origin; uDesc.fSampleCnt = 0; uDesc.fTextureHandle = yuvTextureHandles[1]; uDesc.fWidth = yuvSizes[1].fWidth; uDesc.fHeight = yuvSizes[1].fHeight; sk_sp<GrTexture> yTex( ctx->textureProvider()->wrapBackendTexture(yDesc, kBorrow_GrWrapOwnership)); sk_sp<GrTexture> uTex( ctx->textureProvider()->wrapBackendTexture(uDesc, kBorrow_GrWrapOwnership)); sk_sp<GrTexture> vTex; if (nv12) { vTex = uTex; } else { GrBackendTextureDesc vDesc; vDesc.fConfig = kConfig; vDesc.fOrigin = origin; vDesc.fSampleCnt = 0; vDesc.fTextureHandle = yuvTextureHandles[2]; vDesc.fWidth = yuvSizes[2].fWidth; vDesc.fHeight = yuvSizes[2].fHeight; vTex = sk_sp<GrTexture>( ctx->textureProvider()->wrapBackendTexture(vDesc, kBorrow_GrWrapOwnership)); } if (!yTex || !uTex || !vTex) { return nullptr; } const int width = yuvSizes[0].fWidth; const int height = yuvSizes[0].fHeight; // Needs to be a render target in order to draw to it for the yuv->rgb conversion. sk_sp<GrRenderTargetContext> renderTargetContext(ctx->makeRenderTargetContext( SkBackingFit::kExact, width, height, kRGBA_8888_GrPixelConfig, std::move(imageColorSpace), 0, origin)); if (!renderTargetContext) { return nullptr; } GrPaint paint; paint.setPorterDuffXPFactory(SkBlendMode::kSrc); paint.addColorFragmentProcessor( GrYUVEffect::MakeYUVToRGB(yTex.get(), uTex.get(), vTex.get(), yuvSizes, colorSpace, nv12)); const SkRect rect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height)); renderTargetContext->drawRect(GrNoClip(), paint, SkMatrix::I(), rect); ctx->flushSurfaceWrites(renderTargetContext->accessRenderTarget()); return sk_make_sp<SkImage_Gpu>(width, height, kNeedNewImageUniqueID, kOpaque_SkAlphaType, renderTargetContext->asTexture(), sk_ref_sp(renderTargetContext->getColorSpace()), budgeted); }
sk_sp<SkSpecialImage> SkDisplacementMapEffect::onFilterImage(SkSpecialImage* source, const Context& ctx, SkIPoint* offset) const { SkIPoint colorOffset = SkIPoint::Make(0, 0); sk_sp<SkSpecialImage> color(this->filterInput(1, source, ctx, &colorOffset)); if (!color) { return nullptr; } SkIPoint displOffset = SkIPoint::Make(0, 0); sk_sp<SkSpecialImage> displ(this->filterInput(0, source, ctx, &displOffset)); if (!displ) { return nullptr; } const SkIRect srcBounds = SkIRect::MakeXYWH(colorOffset.x(), colorOffset.y(), color->width(), color->height()); // Both paths do bounds checking on color pixel access, we don't need to // pad the color bitmap to bounds here. SkIRect bounds; if (!this->applyCropRect(ctx, srcBounds, &bounds)) { return nullptr; } SkIRect displBounds; displ = this->applyCropRect(ctx, displ.get(), &displOffset, &displBounds); if (!displ) { return nullptr; } if (!bounds.intersect(displBounds)) { return nullptr; } const SkIRect colorBounds = bounds.makeOffset(-colorOffset.x(), -colorOffset.y()); SkVector scale = SkVector::Make(fScale, fScale); ctx.ctm().mapVectors(&scale, 1); #if SK_SUPPORT_GPU if (source->isTextureBacked()) { GrContext* context = source->getContext(); sk_sp<GrTexture> colorTexture(color->asTextureRef(context)); sk_sp<GrTexture> displTexture(displ->asTextureRef(context)); if (!colorTexture || !displTexture) { return nullptr; } GrPaint paint; SkMatrix offsetMatrix = GrCoordTransform::MakeDivByTextureWHMatrix(displTexture.get()); offsetMatrix.preTranslate(SkIntToScalar(colorOffset.fX - displOffset.fX), SkIntToScalar(colorOffset.fY - displOffset.fY)); paint.addColorFragmentProcessor( GrDisplacementMapEffect::Make(fXChannelSelector, fYChannelSelector, scale, displTexture.get(), offsetMatrix, colorTexture.get(), SkISize::Make(color->width(), color->height()))); paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); SkMatrix matrix; matrix.setTranslate(-SkIntToScalar(colorBounds.x()), -SkIntToScalar(colorBounds.y())); sk_sp<GrDrawContext> drawContext(context->newDrawContext(SkBackingFit::kApprox, bounds.width(), bounds.height(), kSkia8888_GrPixelConfig)); if (!drawContext) { return nullptr; } drawContext->drawRect(GrNoClip(), paint, matrix, SkRect::Make(colorBounds)); offset->fX = bounds.left(); offset->fY = bounds.top(); return SkSpecialImage::MakeFromGpu(SkIRect::MakeWH(bounds.width(), bounds.height()), kNeedNewImageUniqueID_SpecialImage, drawContext->asTexture()); } #endif SkBitmap colorBM, displBM; if (!color->getROPixels(&colorBM) || !displ->getROPixels(&displBM)) { return nullptr; } if ((colorBM.colorType() != kN32_SkColorType) || (displBM.colorType() != kN32_SkColorType)) { return nullptr; } SkAutoLockPixels colorLock(colorBM), displLock(displBM); if (!colorBM.getPixels() || !displBM.getPixels()) { return nullptr; } SkImageInfo info = SkImageInfo::MakeN32(bounds.width(), bounds.height(), colorBM.alphaType()); SkBitmap dst; if (!dst.tryAllocPixels(info)) { return nullptr; } SkAutoLockPixels dstLock(dst); computeDisplacement(fXChannelSelector, fYChannelSelector, scale, &dst, displBM, colorOffset - displOffset, colorBM, colorBounds); offset->fX = bounds.left(); offset->fY = bounds.top(); return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()), dst); }
sk_sp<SkImage> SkImage::MakeFromYUVTexturesCopy(GrContext* ctx , SkYUVColorSpace colorSpace, const GrBackendObject yuvTextureHandles[3], const SkISize yuvSizes[3], GrSurfaceOrigin origin) { const SkBudgeted budgeted = SkBudgeted::kYes; if (yuvSizes[0].fWidth <= 0 || yuvSizes[0].fHeight <= 0 || yuvSizes[1].fWidth <= 0 || yuvSizes[1].fHeight <= 0 || yuvSizes[2].fWidth <= 0 || yuvSizes[2].fHeight <= 0) { return nullptr; } static const GrPixelConfig kConfig = kAlpha_8_GrPixelConfig; GrBackendTextureDesc yDesc; yDesc.fConfig = kConfig; yDesc.fOrigin = origin; yDesc.fSampleCnt = 0; yDesc.fTextureHandle = yuvTextureHandles[0]; yDesc.fWidth = yuvSizes[0].fWidth; yDesc.fHeight = yuvSizes[0].fHeight; GrBackendTextureDesc uDesc; uDesc.fConfig = kConfig; uDesc.fOrigin = origin; uDesc.fSampleCnt = 0; uDesc.fTextureHandle = yuvTextureHandles[1]; uDesc.fWidth = yuvSizes[1].fWidth; uDesc.fHeight = yuvSizes[1].fHeight; GrBackendTextureDesc vDesc; vDesc.fConfig = kConfig; vDesc.fOrigin = origin; vDesc.fSampleCnt = 0; vDesc.fTextureHandle = yuvTextureHandles[2]; vDesc.fWidth = yuvSizes[2].fWidth; vDesc.fHeight = yuvSizes[2].fHeight; SkAutoTUnref<GrTexture> yTex(ctx->textureProvider()->wrapBackendTexture( yDesc, kBorrow_GrWrapOwnership)); SkAutoTUnref<GrTexture> uTex(ctx->textureProvider()->wrapBackendTexture( uDesc, kBorrow_GrWrapOwnership)); SkAutoTUnref<GrTexture> vTex(ctx->textureProvider()->wrapBackendTexture( vDesc, kBorrow_GrWrapOwnership)); if (!yTex || !uTex || !vTex) { return nullptr; } const int width = yuvSizes[0].fWidth; const int height = yuvSizes[0].fHeight; // Needs to be a render target in order to draw to it for the yuv->rgb conversion. sk_sp<GrDrawContext> drawContext(ctx->newDrawContext(SkBackingFit::kExact, width, height, kRGBA_8888_GrPixelConfig, 0, origin)); if (!drawContext) { return nullptr; } GrPaint paint; paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); paint.addColorFragmentProcessor(GrYUVEffect::CreateYUVToRGB(yTex, uTex, vTex, yuvSizes, colorSpace))->unref(); const SkRect rect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height)); drawContext->drawRect(GrNoClip(), paint, SkMatrix::I(), rect); ctx->flushSurfaceWrites(drawContext->accessRenderTarget()); return sk_make_sp<SkImage_Gpu>(width, height, kNeedNewImageUniqueID, kOpaque_SkAlphaType, drawContext->asTexture().get(), budgeted); }
static GrTexture* copy_on_gpu(GrTexture* inputTexture, const SkIRect* subset, const CopyParams& copyParams) { SkASSERT(!subset || !subset->isEmpty()); 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 = copyParams.fWidth; rtDesc.fHeight = copyParams.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 nullptr; } } else if (kRGB_GrColorComponentFlags == (kRGB_GrColorComponentFlags & GrPixelConfigComponentMask(rtDesc.fConfig))) { if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) { rtDesc.fConfig = kSkia8888_GrPixelConfig; } else { return nullptr; } } else { return nullptr; } } SkAutoTUnref<GrTexture> copy(context->textureProvider()->createTexture(rtDesc, SkBudgeted::kYes)); if (!copy) { return nullptr; } // TODO: If no scaling is being performed then use copySurface. GrPaint paint; paint.setGammaCorrect(true); // TODO: Initializing these values for no reason cause the compiler is complaining SkScalar sx = 0.f; SkScalar sy = 0.f; if (subset) { sx = 1.f / inputTexture->width(); sy = 1.f / inputTexture->height(); } if (copyParams.fFilter != GrTextureParams::kNone_FilterMode && subset && (subset->width() != copyParams.fWidth || subset->height() != copyParams.fHeight)) { SkRect domain; domain.fLeft = (subset->fLeft + 0.5f) * sx; domain.fTop = (subset->fTop + 0.5f)* sy; domain.fRight = (subset->fRight - 0.5f) * sx; domain.fBottom = (subset->fBottom - 0.5f) * sy; // This would cause us to read values from outside the subset. Surely, the caller knows // better! SkASSERT(copyParams.fFilter != GrTextureParams::kMipMap_FilterMode); paint.addColorFragmentProcessor( GrTextureDomainEffect::Make(inputTexture, SkMatrix::I(), domain, GrTextureDomain::kClamp_Mode, copyParams.fFilter)); } else { GrTextureParams params(SkShader::kClamp_TileMode, copyParams.fFilter); paint.addColorTextureProcessor(inputTexture, SkMatrix::I(), params); } paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); SkRect localRect; if (subset) { localRect = SkRect::Make(*subset); localRect.fLeft *= sx; localRect.fTop *= sy; localRect.fRight *= sx; localRect.fBottom *= sy; } else { localRect = SkRect::MakeWH(1.f, 1.f); } sk_sp<GrDrawContext> drawContext(context->drawContext(sk_ref_sp(copy->asRenderTarget()))); if (!drawContext) { return nullptr; } SkRect dstRect = SkRect::MakeWH(SkIntToScalar(rtDesc.fWidth), SkIntToScalar(rtDesc.fHeight)); drawContext->fillRectToRect(GrNoClip(), paint, SkMatrix::I(), dstRect, localRect); return copy.release(); }
sk_sp<GrTexture> GrClipMaskManager::CreateAlphaClipMask(GrContext* context, int32_t elementsGenID, GrReducedClip::InitialState initialState, const GrReducedClip::ElementList& elements, const SkVector& clipToMaskOffset, const SkIRect& clipSpaceIBounds) { GrResourceProvider* resourceProvider = context->resourceProvider(); GrUniqueKey key; GetClipMaskKey(elementsGenID, clipSpaceIBounds, &key); if (GrTexture* texture = resourceProvider->findAndRefTextureByUniqueKey(key)) { return sk_sp<GrTexture>(texture); } // There's no texture in the cache. Let's try to allocate it then. GrPixelConfig config = kRGBA_8888_GrPixelConfig; if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { config = kAlpha_8_GrPixelConfig; } sk_sp<GrDrawContext> dc(context->newDrawContext(SkBackingFit::kApprox, clipSpaceIBounds.width(), clipSpaceIBounds.height(), config)); if (!dc) { return nullptr; } // 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. dc->clear(&maskSpaceIBounds, GrReducedClip::kAllIn_InitialState == initialState ? 0xffffffff : 0x00000000, true); // Set the matrix so that rendered clip elements are transformed to mask space from clip // space. const SkMatrix translate = SkMatrix::MakeTrans(clipToMaskOffset.fX, clipToMaskOffset.fY); // It is important that we use maskSpaceIBounds as the stencil rect in the below loop. // 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. // 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) { GrFixedClip clip(maskSpaceIBounds); // draw directly into the result with the stencil set to make the pixels affected // by the clip shape be non-zero. static constexpr GrUserStencilSettings kStencilInElement( GrUserStencilSettings::StaticInit< 0xffff, GrUserStencilTest::kAlways, 0xffff, GrUserStencilOp::kReplace, GrUserStencilOp::kReplace, 0xffff>() ); if (!stencil_element(dc.get(), clip, &kStencilInElement, translate, element)) { return nullptr; } // Draw to the exterior pixels (those with a zero stencil value). static constexpr GrUserStencilSettings kDrawOutsideElement( GrUserStencilSettings::StaticInit< 0x0000, GrUserStencilTest::kEqual, 0xffff, GrUserStencilOp::kZero, GrUserStencilOp::kZero, 0xffff>() ); if (!dc->drawContextPriv().drawAndStencilRect(clip, &kDrawOutsideElement, op, !invert, false, translate, SkRect::Make(clipSpaceIBounds))) { return nullptr; } } else { // all the remaining ops can just be directly draw into the accumulation buffer GrPaint paint; paint.setAntiAlias(element->isAA()); paint.setCoverageSetOpXPFactory(op, false); draw_element(dc.get(), GrNoClip(), paint, translate, element); } } sk_sp<GrTexture> texture(dc->asTexture()); SkASSERT(texture); texture->resourcePriv().setUniqueKey(key); return texture; }
void GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context, PMConversion* pmToUPMRule, PMConversion* upmToPMRule) { *pmToUPMRule = kNone_PMConversion; *upmToPMRule = kNone_PMConversion; static constexpr int kSize = 256; static constexpr GrPixelConfig kConfig = kRGBA_8888_GrPixelConfig; SkAutoTMalloc<uint32_t> data(kSize * kSize * 3); uint32_t* srcData = data.get(); uint32_t* firstRead = data.get() + kSize * kSize; uint32_t* secondRead = data.get() + 2 * kSize * kSize; // Fill with every possible premultiplied A, color channel value. There will be 256-y duplicate // values in row y. We set r,g, and b to the same value since they are handled identically. for (int y = 0; y < kSize; ++y) { for (int x = 0; x < kSize; ++x) { uint8_t* color = reinterpret_cast<uint8_t*>(&srcData[kSize*y + x]); color[3] = y; color[2] = SkTMin(x, y); color[1] = SkTMin(x, y); color[0] = SkTMin(x, y); } } sk_sp<GrDrawContext> readDC(context->makeDrawContext(SkBackingFit::kExact, kSize, kSize, kConfig, nullptr)); sk_sp<GrDrawContext> tempDC(context->makeDrawContext(SkBackingFit::kExact, kSize, kSize, kConfig, nullptr)); if (!readDC || !tempDC) { return; } GrSurfaceDesc desc; desc.fWidth = kSize; desc.fHeight = kSize; desc.fConfig = kConfig; SkAutoTUnref<GrTexture> dataTex(context->textureProvider()->createTexture( desc, SkBudgeted::kYes, data, 0)); if (!dataTex.get()) { return; } static const PMConversion kConversionRules[][2] = { {kDivByAlpha_RoundDown_PMConversion, kMulByAlpha_RoundUp_PMConversion}, {kDivByAlpha_RoundUp_PMConversion, kMulByAlpha_RoundDown_PMConversion}, }; bool failed = true; for (size_t i = 0; i < SK_ARRAY_COUNT(kConversionRules) && failed; ++i) { *pmToUPMRule = kConversionRules[i][0]; *upmToPMRule = kConversionRules[i][1]; static const SkRect kDstRect = SkRect::MakeIWH(kSize, kSize); static const SkRect kSrcRect = SkRect::MakeIWH(1, 1); // We do a PM->UPM draw from dataTex to readTex and read the data. Then we do a UPM->PM draw // from readTex to tempTex followed by a PM->UPM draw to readTex and finally read the data. // We then verify that two reads produced the same values. GrPaint paint1; GrPaint paint2; GrPaint paint3; sk_sp<GrFragmentProcessor> pmToUPM1(new GrConfigConversionEffect( dataTex, GrSwizzle::RGBA(), *pmToUPMRule, SkMatrix::I())); sk_sp<GrFragmentProcessor> upmToPM(new GrConfigConversionEffect( readDC->asTexture().get(), GrSwizzle::RGBA(), *upmToPMRule, SkMatrix::I())); sk_sp<GrFragmentProcessor> pmToUPM2(new GrConfigConversionEffect( tempDC->asTexture().get(), GrSwizzle::RGBA(), *pmToUPMRule, SkMatrix::I())); paint1.addColorFragmentProcessor(std::move(pmToUPM1)); paint1.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); readDC->fillRectToRect(GrNoClip(), paint1, SkMatrix::I(), kDstRect, kSrcRect); readDC->asTexture()->readPixels(0, 0, kSize, kSize, kConfig, firstRead); paint2.addColorFragmentProcessor(std::move(upmToPM)); paint2.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); tempDC->fillRectToRect(GrNoClip(), paint2, SkMatrix::I(), kDstRect, kSrcRect); paint3.addColorFragmentProcessor(std::move(pmToUPM2)); paint3.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); readDC->fillRectToRect(GrNoClip(), paint3, SkMatrix::I(), kDstRect, kSrcRect); readDC->asTexture()->readPixels(0, 0, kSize, kSize, kConfig, secondRead); failed = false; for (int y = 0; y < kSize && !failed; ++y) { for (int x = 0; x <= y; ++x) { if (firstRead[kSize * y + x] != secondRead[kSize * y + x]) { failed = true; break; } } } } if (failed) { *pmToUPMRule = kNone_PMConversion; *upmToPMRule = kNone_PMConversion; } }
sk_sp<GrTextureProxy> GrYUVProvider::refAsTextureProxy(GrContext* ctx, const GrSurfaceDesc& desc, const SkColorSpace* srcColorSpace, const SkColorSpace* dstColorSpace) { SkYUVPlanesCache::Info yuvInfo; void* planes[3]; sk_sp<SkCachedData> dataStorage = init_provider(this, &yuvInfo, planes); if (!dataStorage) { return nullptr; } sk_sp<GrTextureProxy> yuvTextureProxies[3]; for (int i = 0; i < 3; i++) { int componentWidth = yuvInfo.fSizeInfo.fSizes[i].fWidth; int componentHeight = yuvInfo.fSizeInfo.fSizes[i].fHeight; // If the sizes of the components are not all the same we choose to create exact-match // textures for the smaller onces rather than add a texture domain to the draw. // TODO: revisit this decision to imporve texture reuse? SkBackingFit fit = (componentWidth != yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fWidth) || (componentHeight != yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight) ? SkBackingFit::kExact : SkBackingFit::kApprox; SkImageInfo imageInfo = SkImageInfo::MakeA8(componentWidth, componentHeight); SkPixmap pixmap(imageInfo, planes[i], yuvInfo.fSizeInfo.fWidthBytes[i]); SkCachedData* dataStoragePtr = dataStorage.get(); // We grab a ref to cached yuv data. When the SkImage we create below goes away it will call // the YUVGen_DataReleaseProc which will release this ref. // DDL TODO: Currently we end up creating a lazy proxy that will hold onto a ref to the // SkImage in its lambda. This means that we'll keep the ref on the YUV data around for the // life time of the proxy and not just upload. For non-DDL draws we should look into // releasing this SkImage after uploads (by deleting the lambda after instantiation). dataStoragePtr->ref(); sk_sp<SkImage> yuvImage = SkImage::MakeFromRaster(pixmap, YUVGen_DataReleaseProc, dataStoragePtr); auto proxyProvider = ctx->contextPriv().proxyProvider(); yuvTextureProxies[i] = proxyProvider->createTextureProxy(yuvImage, kNone_GrSurfaceFlags, kTopLeft_GrSurfaceOrigin, 1, SkBudgeted::kYes, fit); } // We never want to perform color-space conversion during the decode. However, if the proxy // config is sRGB then we must use a sRGB color space. sk_sp<SkColorSpace> colorSpace; if (GrPixelConfigIsSRGB(desc.fConfig)) { colorSpace = SkColorSpace::MakeSRGB(); } // TODO: investigate preallocating mip maps here sk_sp<GrRenderTargetContext> renderTargetContext(ctx->makeDeferredRenderTargetContext( SkBackingFit::kExact, desc.fWidth, desc.fHeight, desc.fConfig, std::move(colorSpace), desc.fSampleCnt, GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin)); if (!renderTargetContext) { return nullptr; } GrPaint paint; auto yuvToRgbProcessor = GrYUVtoRGBEffect::Make(std::move(yuvTextureProxies[0]), std::move(yuvTextureProxies[1]), std::move(yuvTextureProxies[2]), yuvInfo.fSizeInfo.fSizes, yuvInfo.fColorSpace, false); paint.addColorFragmentProcessor(std::move(yuvToRgbProcessor)); // If we're decoding an sRGB image, the result of our linear math on the YUV planes is already // in sRGB. (The encoding is just math on bytes, with no concept of color spaces.) So, we need // to output the results of that math directly to the buffer that we will then consider sRGB. // If we have sRGB write control, we can just tell the HW not to do the Linear -> sRGB step. // Otherwise, we do our shader math to go from YUV -> sRGB, manually convert sRGB -> Linear, // then let the HW convert Linear -> sRGB. if (GrPixelConfigIsSRGB(desc.fConfig)) { if (ctx->caps()->srgbWriteControl()) { paint.setDisableOutputConversionToSRGB(true); } else { paint.addColorFragmentProcessor(GrSRGBEffect::Make(GrSRGBEffect::Mode::kSRGBToLinear, GrSRGBEffect::Alpha::kOpaque)); } } // If the caller expects the pixels in a different color space than the one from the image, // apply a color conversion to do this. std::unique_ptr<GrFragmentProcessor> colorConversionProcessor = GrNonlinearColorSpaceXformEffect::Make(srcColorSpace, dstColorSpace); if (colorConversionProcessor) { paint.addColorFragmentProcessor(std::move(colorConversionProcessor)); } paint.setPorterDuffXPFactory(SkBlendMode::kSrc); const SkRect r = SkRect::MakeIWH(yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fWidth, yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight); renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), r); return renderTargetContext->asTextureProxyRef(); }
void GrTextBlob::flush(GrTextTarget* target, const SkSurfaceProps& props, const GrDistanceFieldAdjustTable* distanceAdjustTable, const SkPaint& paint, GrColor filteredColor, const GrClip& clip, const SkMatrix& viewMatrix, SkScalar x, SkScalar y) { // GrTextBlob::makeOp only takes uint16_t values for run and subRun indices. // Encountering something larger than this is highly unlikely, so we'll just not draw it. int lastRun = SkTMin(fRunCount, (1 << 16)) - 1; // For each run in the GrTextBlob we're going to churn through all the glyphs. // Each run is broken into a path part and a Mask / DFT / ARGB part. for (int runIndex = 0; runIndex <= lastRun; runIndex++) { Run& run = fRuns[runIndex]; // first flush any path glyphs if (run.fPathGlyphs.count()) { SkPaint runPaint{paint}; runPaint.setFlags((runPaint.getFlags() & ~Run::kPaintFlagsMask) | run.fPaintFlags); for (int i = 0; i < run.fPathGlyphs.count(); i++) { GrTextBlob::Run::PathGlyph& pathGlyph = run.fPathGlyphs[i]; SkMatrix ctm; const SkPath* path = &pathGlyph.fPath; // TmpPath must be in the same scope as GrShape shape below. SkTLazy<SkPath> tmpPath; // The glyph positions and glyph outlines are either in device space or in source // space based on fPreTransformed. if (!pathGlyph.fPreTransformed) { // Positions and outlines are in source space. ctm = viewMatrix; SkMatrix pathMatrix = SkMatrix::MakeScale(pathGlyph.fScale, pathGlyph.fScale); // The origin for the blob may have changed, so figure out the delta. SkVector originShift = SkPoint{x, y} - SkPoint{fInitialX, fInitialY}; // Shift the original glyph location in source space to the position of the new // blob. pathMatrix.postTranslate(originShift.x() + pathGlyph.fX, originShift.y() + pathGlyph.fY); // If there are shaders, blurs or styles, the path must be scaled into source // space independently of the CTM. This allows the CTM to be correct for the // different effects. GrStyle style(runPaint); bool scalePath = runPaint.getShader() || style.applies() || runPaint.getMaskFilter(); if (!scalePath) { // Scale can be applied to CTM -- no effects. ctm.preConcat(pathMatrix); } else { // Scale the outline into source space. // Transform the path form the normalized outline to source space. This // way the CTM will remain the same so it can be used by the effects. SkPath* sourceOutline = tmpPath.init(); path->transform(pathMatrix, sourceOutline); sourceOutline->setIsVolatile(true); path = sourceOutline; } } else { // Positions and outlines are in device space. SkPoint originalOrigin = {fInitialX, fInitialY}; fInitialViewMatrix.mapPoints(&originalOrigin, 1); SkPoint newOrigin = {x, y}; viewMatrix.mapPoints(&newOrigin, 1); // The origin shift in device space. SkPoint originShift = newOrigin - originalOrigin; // Shift the original glyph location in device space to the position of the // new blob. ctm = SkMatrix::MakeTrans(originShift.x() + pathGlyph.fX, originShift.y() + pathGlyph.fY); } // TODO: we are losing the mutability of the path here GrShape shape(*path, paint); target->drawShape(clip, runPaint, ctm, shape); } } // then flush each subrun, if any if (!run.fInitialized) { continue; } int lastSubRun = SkTMin(run.fSubRunInfo.count(), 1 << 16) - 1; for (int subRun = 0; subRun <= lastSubRun; subRun++) { const Run::SubRunInfo& info = run.fSubRunInfo[subRun]; int glyphCount = info.glyphCount(); if (0 == glyphCount) { continue; } bool skipClip = false; bool submitOp = true; SkIRect clipRect = SkIRect::MakeEmpty(); SkRect rtBounds = SkRect::MakeWH(target->width(), target->height()); SkRRect clipRRect; GrAA aa; // We can clip geometrically if we're not using SDFs or transformed glyphs, // and we have an axis-aligned rectangular non-AA clip if (!info.drawAsDistanceFields() && !info.needsTransform() && clip.isRRect(rtBounds, &clipRRect, &aa) && clipRRect.isRect() && GrAA::kNo == aa) { skipClip = true; // We only need to do clipping work if the subrun isn't contained by the clip SkRect subRunBounds; this->computeSubRunBounds(&subRunBounds, runIndex, subRun, viewMatrix, x, y, false); if (!clipRRect.getBounds().contains(subRunBounds)) { // If the subrun is completely outside, don't add an op for it if (!clipRRect.getBounds().intersects(subRunBounds)) { submitOp = false; } else { clipRRect.getBounds().round(&clipRect); } } } if (submitOp) { auto op = this->makeOp(info, glyphCount, runIndex, subRun, viewMatrix, x, y, clipRect, paint, filteredColor, props, distanceAdjustTable, target); if (op) { if (skipClip) { target->addDrawOp(GrNoClip(), std::move(op)); } else { target->addDrawOp(clip, std::move(op)); } } } } } }
sk_sp<SkSpecialImage> SkXfermodeImageFilter::filterImageGPU(SkSpecialImage* source, sk_sp<SkSpecialImage> background, const SkIPoint& backgroundOffset, sk_sp<SkSpecialImage> foreground, const SkIPoint& foregroundOffset, const SkIRect& bounds) const { SkASSERT(source->isTextureBacked()); GrContext* context = source->getContext(); sk_sp<GrTexture> backgroundTex, foregroundTex; if (background) { backgroundTex = background->asTextureRef(context); } if (foreground) { foregroundTex = foreground->asTextureRef(context); } GrPaint paint; // SRGBTODO: AllowSRGBInputs? sk_sp<GrFragmentProcessor> bgFP; if (backgroundTex) { SkMatrix backgroundMatrix; backgroundMatrix.setIDiv(backgroundTex->width(), backgroundTex->height()); backgroundMatrix.preTranslate(SkIntToScalar(-backgroundOffset.fX), SkIntToScalar(-backgroundOffset.fY)); bgFP = GrTextureDomainEffect::Make( backgroundTex.get(), nullptr, backgroundMatrix, GrTextureDomain::MakeTexelDomain(backgroundTex.get(), background->subset()), GrTextureDomain::kDecal_Mode, GrTextureParams::kNone_FilterMode); } else { bgFP = GrConstColorProcessor::Make(GrColor_TRANSPARENT_BLACK, GrConstColorProcessor::kIgnore_InputMode); } if (foregroundTex) { SkMatrix foregroundMatrix; foregroundMatrix.setIDiv(foregroundTex->width(), foregroundTex->height()); foregroundMatrix.preTranslate(SkIntToScalar(-foregroundOffset.fX), SkIntToScalar(-foregroundOffset.fY)); sk_sp<GrFragmentProcessor> foregroundFP; foregroundFP = GrTextureDomainEffect::Make( foregroundTex.get(), nullptr, foregroundMatrix, GrTextureDomain::MakeTexelDomain(foregroundTex.get(), foreground->subset()), GrTextureDomain::kDecal_Mode, GrTextureParams::kNone_FilterMode); paint.addColorFragmentProcessor(std::move(foregroundFP)); // A null fMode is interpreted to mean kSrcOver_Mode (to match raster). SkAutoTUnref<SkXfermode> mode(SkSafeRef(fMode.get())); if (!mode) { // It would be awesome to use SkXfermode::Create here but it knows better // than us and won't return a kSrcOver_Mode SkXfermode. That means we // have to get one the hard way. struct ProcCoeff rec; rec.fProc = SkXfermode::GetProc(SkXfermode::kSrcOver_Mode); SkXfermode::ModeAsCoeff(SkXfermode::kSrcOver_Mode, &rec.fSC, &rec.fDC); mode.reset(new SkProcCoeffXfermode(rec, SkXfermode::kSrcOver_Mode)); } sk_sp<GrFragmentProcessor> xferFP( mode->makeFragmentProcessorForImageFilter(std::move(bgFP))); // A null 'xferFP' here means kSrc_Mode was used in which case we can just proceed if (xferFP) { paint.addColorFragmentProcessor(std::move(xferFP)); } } else { paint.addColorFragmentProcessor(std::move(bgFP)); } paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); sk_sp<GrDrawContext> drawContext(context->makeDrawContext(SkBackingFit::kApprox, bounds.width(), bounds.height(), kSkia8888_GrPixelConfig, sk_ref_sp(source->getColorSpace()))); if (!drawContext) { return nullptr; } SkMatrix matrix; matrix.setTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top())); drawContext->drawRect(GrNoClip(), paint, matrix, SkRect::Make(bounds)); return SkSpecialImage::MakeFromGpu(SkIRect::MakeWH(bounds.width(), bounds.height()), kNeedNewImageUniqueID_SpecialImage, drawContext->asTexture(), sk_ref_sp(drawContext->getColorSpace())); }
static GrTexture* copy_on_gpu(GrTexture* inputTexture, const SkIRect* subset, const CopyParams& copyParams) { SkASSERT(!subset || !subset->isEmpty()); GrContext* context = inputTexture->getContext(); SkASSERT(context); GrPixelConfig config = GrMakePixelConfigUncompressed(inputTexture->config()); sk_sp<GrDrawContext> copyDC = context->makeDrawContextWithFallback(SkBackingFit::kExact, copyParams.fWidth, copyParams.fHeight, config, nullptr); if (!copyDC) { return nullptr; } GrPaint paint; paint.setGammaCorrect(true); SkScalar sx SK_INIT_TO_AVOID_WARNING; SkScalar sy SK_INIT_TO_AVOID_WARNING; if (subset) { sx = 1.f / inputTexture->width(); sy = 1.f / inputTexture->height(); } if (copyParams.fFilter != GrTextureParams::kNone_FilterMode && subset && (subset->width() != copyParams.fWidth || subset->height() != copyParams.fHeight)) { SkRect domain; domain.fLeft = (subset->fLeft + 0.5f) * sx; domain.fTop = (subset->fTop + 0.5f)* sy; domain.fRight = (subset->fRight - 0.5f) * sx; domain.fBottom = (subset->fBottom - 0.5f) * sy; // This would cause us to read values from outside the subset. Surely, the caller knows // better! SkASSERT(copyParams.fFilter != GrTextureParams::kMipMap_FilterMode); paint.addColorFragmentProcessor( GrTextureDomainEffect::Make(inputTexture, nullptr, SkMatrix::I(), domain, GrTextureDomain::kClamp_Mode, copyParams.fFilter)); } else { GrTextureParams params(SkShader::kClamp_TileMode, copyParams.fFilter); paint.addColorTextureProcessor(inputTexture, nullptr, SkMatrix::I(), params); } paint.setPorterDuffXPFactory(SkBlendMode::kSrc); SkRect localRect; if (subset) { localRect = SkRect::Make(*subset); localRect.fLeft *= sx; localRect.fTop *= sy; localRect.fRight *= sx; localRect.fBottom *= sy; } else { localRect = SkRect::MakeWH(1.f, 1.f); } SkRect dstRect = SkRect::MakeIWH(copyParams.fWidth, copyParams.fHeight); copyDC->fillRectToRect(GrNoClip(), paint, SkMatrix::I(), dstRect, localRect); return copyDC->asTexture().release(); }