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<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 void convolve_gaussian_2d(GrDrawContext* drawContext, const GrClip& clip, const SkIRect& dstRect, const SkIPoint& srcOffset, GrTexture* texture, int radiusX, int radiusY, SkScalar sigmaX, SkScalar sigmaY, const SkIRect* srcBounds) { SkMatrix localMatrix = SkMatrix::MakeTrans(-SkIntToScalar(srcOffset.x()), -SkIntToScalar(srcOffset.y())); SkISize size = SkISize::Make(2 * radiusX + 1, 2 * radiusY + 1); SkIPoint kernelOffset = SkIPoint::Make(radiusX, radiusY); GrPaint paint; paint.setGammaCorrect(drawContext->isGammaCorrect()); SkIRect bounds = srcBounds ? *srcBounds : SkIRect::EmptyIRect(); sk_sp<GrFragmentProcessor> conv(GrMatrixConvolutionEffect::MakeGaussian( texture, bounds, size, 1.0, 0.0, kernelOffset, srcBounds ? GrTextureDomain::kDecal_Mode : GrTextureDomain::kIgnore_Mode, true, sigmaX, sigmaY)); paint.addColorFragmentProcessor(std::move(conv)); paint.setPorterDuffXPFactory(SkBlendMode::kSrc); drawContext->fillRectWithLocalMatrix(clip, paint, SkMatrix::I(), SkRect::Make(dstRect), localMatrix); }
sk_sp<SkSpecialImage> SkImageFilter::DrawWithFP(GrContext* context, sk_sp<GrFragmentProcessor> fp, const SkIRect& bounds) { GrPaint paint; paint.addColorFragmentProcessor(fp.get()); paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = bounds.width(); desc.fHeight = bounds.height(); desc.fConfig = kRGBA_8888_GrPixelConfig; sk_sp<GrTexture> dst(context->textureProvider()->createApproxTexture(desc)); if (!dst) { return nullptr; } sk_sp<GrDrawContext> drawContext(context->drawContext(sk_ref_sp(dst->asRenderTarget()))); if (!drawContext) { return nullptr; } SkRect srcRect = SkRect::Make(bounds); SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); GrClip clip(dstRect); drawContext->fillRectToRect(clip, paint, SkMatrix::I(), dstRect, srcRect); return SkSpecialImage::MakeFromGpu(SkIRect::MakeWH(bounds.width(), bounds.height()), kNeedNewImageUniqueID_SpecialImage, std::move(dst)); }
static void convolve_gaussian_2d(GrDrawContext* drawContext, const GrClip& clip, const SkRect& dstRect, const SkPoint& srcOffset, GrTexture* texture, int radiusX, int radiusY, SkScalar sigmaX, SkScalar sigmaY, const SkRect* srcBounds) { SkMatrix localMatrix = SkMatrix::MakeTrans(-srcOffset.x(), -srcOffset.y()); SkISize size = SkISize::Make(2 * radiusX + 1, 2 * radiusY + 1); SkIPoint kernelOffset = SkIPoint::Make(radiusX, radiusY); GrPaint paint; SkIRect bounds; if (srcBounds) { srcBounds->roundOut(&bounds); } else { bounds.setEmpty(); } SkAutoTUnref<GrFragmentProcessor> conv(GrMatrixConvolutionEffect::CreateGaussian( texture, bounds, size, 1.0, 0.0, kernelOffset, srcBounds ? GrTextureDomain::kDecal_Mode : GrTextureDomain::kIgnore_Mode, true, sigmaX, sigmaY)); paint.addColorFragmentProcessor(conv); paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); drawContext->fillRectWithLocalMatrix(clip, paint, SkMatrix::I(), dstRect, localMatrix); }
sk_sp<SkSpecialImage> SkImageFilter::DrawWithFP(GrContext* context, sk_sp<GrFragmentProcessor> fp, const SkIRect& bounds, sk_sp<SkColorSpace> colorSpace) { GrPaint paint; paint.addColorFragmentProcessor(std::move(fp)); paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); sk_sp<GrDrawContext> drawContext(context->makeDrawContext(SkBackingFit::kApprox, bounds.width(), bounds.height(), kRGBA_8888_GrPixelConfig, std::move(colorSpace))); if (!drawContext) { return nullptr; } SkIRect dstIRect = SkIRect::MakeWH(bounds.width(), bounds.height()); SkRect srcRect = SkRect::Make(bounds); SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); GrFixedClip clip(dstIRect); drawContext->fillRectToRect(clip, paint, SkMatrix::I(), dstRect, srcRect); return SkSpecialImage::MakeFromGpu(dstIRect, kNeedNewImageUniqueID_SpecialImage, drawContext->asTexture(), sk_ref_sp(drawContext->getColorSpace())); }
bool SkImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { #if SK_SUPPORT_GPU SkBitmap input = src; SkASSERT(fInputCount == 1); SkIPoint srcOffset = SkIPoint::Make(0, 0); if (!this->filterInputGPU(0, proxy, src, ctx, &input, &srcOffset)) { return false; } GrTexture* srcTexture = input.getTexture(); SkIRect bounds; if (!this->applyCropRect(ctx, proxy, input, &srcOffset, &bounds, &input)) { return false; } SkRect srcRect = SkRect::Make(bounds); SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); GrContext* context = srcTexture->getContext(); GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag, desc.fWidth = bounds.width(); desc.fHeight = bounds.height(); desc.fConfig = kRGBA_8888_GrPixelConfig; SkAutoTUnref<GrTexture> dst(context->textureProvider()->createTexture(desc, GrTextureProvider::FromImageFilter(ctx.sizeConstraint()))); if (!dst) { return false; } // setup new clip GrClip clip(dstRect); GrFragmentProcessor* fp; offset->fX = bounds.left(); offset->fY = bounds.top(); bounds.offset(-srcOffset); SkMatrix matrix(ctx.ctm()); matrix.postTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top())); GrPaint paint; if (this->asFragmentProcessor(&fp, srcTexture, matrix, bounds)) { SkASSERT(fp); paint.addColorFragmentProcessor(fp)->unref(); paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(dst->asRenderTarget())); if (drawContext) { drawContext->fillRectToRect(clip, paint, SkMatrix::I(), dstRect, srcRect); WrapTexture(dst, bounds.width(), bounds.height(), result); return true; } } #endif return false; }
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(); }
static void apply_morphology_rect_no_bounds(GrRenderTargetContext* renderTargetContext, const GrClip& clip, sk_sp<GrTextureProxy> proxy, const SkIRect& srcRect, const SkIRect& dstRect, int radius, GrMorphologyEffect::Type morphType, GrMorphologyEffect::Direction direction) { GrPaint paint; paint.addColorFragmentProcessor(GrMorphologyEffect::Make(std::move(proxy), direction, radius, morphType)); paint.setPorterDuffXPFactory(SkBlendMode::kSrc); renderTargetContext->fillRectToRect(clip, std::move(paint), GrAA::kNo, SkMatrix::I(), SkRect::Make(dstRect), SkRect::Make(srcRect)); }
static void apply_morphology_rect_no_bounds(GrDrawContext* drawContext, const GrClip& clip, GrTexture* texture, const SkIRect& srcRect, const SkIRect& dstRect, int radius, GrMorphologyEffect::MorphologyType morphType, Gr1DKernelEffect::Direction direction) { GrPaint paint; paint.addColorFragmentProcessor(GrMorphologyEffect::Create(texture, direction, radius, morphType))->unref(); paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); drawContext->fillRectToRect(clip, paint, SkMatrix::I(), SkRect::Make(dstRect), SkRect::Make(srcRect)); }
static void convolve_gaussian_1d(GrDrawContext* drawContext, const GrClip& clip, const SkRect& dstRect, const SkPoint& srcOffset, GrTexture* texture, Gr1DKernelEffect::Direction direction, int radius, float sigma, bool useBounds, float bounds[2]) { GrPaint paint; SkAutoTUnref<GrFragmentProcessor> conv(GrConvolutionEffect::CreateGaussian( texture, direction, radius, sigma, useBounds, bounds)); paint.addColorFragmentProcessor(conv); paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); SkMatrix localMatrix = SkMatrix::MakeTrans(-srcOffset.x(), -srcOffset.y()); drawContext->fillRectWithLocalMatrix(clip, paint, SkMatrix::I(), dstRect, localMatrix); }
// 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"); } }
static void convolve_gaussian_1d(GrDrawContext* drawContext, const GrClip& clip, const SkIRect& dstRect, const SkIPoint& srcOffset, GrTexture* texture, Gr1DKernelEffect::Direction direction, int radius, float sigma, bool useBounds, float bounds[2]) { GrPaint paint; paint.setGammaCorrect(drawContext->isGammaCorrect()); sk_sp<GrFragmentProcessor> conv(GrConvolutionEffect::MakeGaussian( texture, direction, radius, sigma, useBounds, bounds)); paint.addColorFragmentProcessor(std::move(conv)); paint.setPorterDuffXPFactory(SkBlendMode::kSrc); SkMatrix localMatrix = SkMatrix::MakeTrans(-SkIntToScalar(srcOffset.x()), -SkIntToScalar(srcOffset.y())); drawContext->fillRectWithLocalMatrix(clip, paint, SkMatrix::I(), SkRect::Make(dstRect), localMatrix); }
sk_sp<SkSpecialImage> SkImageFilter::DrawWithFP(GrContext* context, sk_sp<GrFragmentProcessor> fp, const SkIRect& bounds) { GrPaint paint; paint.addColorFragmentProcessor(fp.get()); paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); sk_sp<GrDrawContext> drawContext(context->newDrawContext(GrContext::kLoose_BackingFit, bounds.width(), bounds.height(), kRGBA_8888_GrPixelConfig)); if (!drawContext) { return nullptr; } SkRect srcRect = SkRect::Make(bounds); SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); GrClip clip(dstRect); drawContext->fillRectToRect(clip, paint, SkMatrix::I(), dstRect, srcRect); return SkSpecialImage::MakeFromGpu(SkIRect::MakeWH(bounds.width(), bounds.height()), kNeedNewImageUniqueID_SpecialImage, drawContext->asTexture()); }
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(); }
bool SkDisplacementMapEffect::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { SkBitmap colorBM = src; SkIPoint colorOffset = SkIPoint::Make(0, 0); if (!this->filterInputGPU(1, proxy, src, ctx, &colorBM, &colorOffset)) { return false; } SkBitmap displacementBM = src; SkIPoint displacementOffset = SkIPoint::Make(0, 0); if (!this->filterInputGPU(0, proxy, src, ctx, &displacementBM, &displacementOffset)) { return false; } SkIRect bounds; // Since GrDisplacementMapEffect does bounds checking on color pixel access, we don't need to // pad the color bitmap to bounds here. if (!this->applyCropRect(ctx, colorBM, colorOffset, &bounds)) { return false; } SkIRect displBounds; if (!this->applyCropRect(ctx, proxy, displacementBM, &displacementOffset, &displBounds, &displacementBM)) { return false; } if (!bounds.intersect(displBounds)) { return false; } GrTexture* color = colorBM.getTexture(); GrTexture* displacement = displacementBM.getTexture(); GrContext* context = color->getContext(); GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = bounds.width(); desc.fHeight = bounds.height(); desc.fConfig = kSkia8888_GrPixelConfig; auto constraint = GrTextureProvider::FromImageFilter(ctx.sizeConstraint()); SkAutoTUnref<GrTexture> dst(context->textureProvider()->createTexture(desc, constraint)); if (!dst) { return false; } SkVector scale = SkVector::Make(fScale, fScale); ctx.ctm().mapVectors(&scale, 1); GrPaint paint; SkMatrix offsetMatrix = GrCoordTransform::MakeDivByTextureWHMatrix(displacement); offsetMatrix.preTranslate(SkIntToScalar(colorOffset.fX - displacementOffset.fX), SkIntToScalar(colorOffset.fY - displacementOffset.fY)); paint.addColorFragmentProcessor( GrDisplacementMapEffect::Create(fXChannelSelector, fYChannelSelector, scale, displacement, offsetMatrix, color, colorBM.dimensions()))->unref(); paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); SkIRect colorBounds = bounds; colorBounds.offset(-colorOffset); SkMatrix matrix; matrix.setTranslate(-SkIntToScalar(colorBounds.x()), -SkIntToScalar(colorBounds.y())); SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(dst->asRenderTarget())); if (!drawContext) { return false; } drawContext->drawRect(GrClip::WideOpen(), paint, matrix, SkRect::Make(colorBounds)); offset->fX = bounds.left(); offset->fY = bounds.top(); WrapTexture(dst, bounds.width(), bounds.height(), result); return true; }
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; } GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = bounds.width(); desc.fHeight = bounds.height(); desc.fConfig = kSkia8888_GrPixelConfig; SkAutoTUnref<GrTexture> dst(context->textureProvider()->createApproxTexture(desc)); if (!dst) { 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::Create(fXChannelSelector, fYChannelSelector, scale, displTexture.get(), offsetMatrix, colorTexture.get(), SkISize::Make(color->width(), color->height())))->unref(); paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); SkMatrix matrix; matrix.setTranslate(-SkIntToScalar(colorBounds.x()), -SkIntToScalar(colorBounds.y())); SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(dst->asRenderTarget())); if (!drawContext) { return nullptr; } drawContext->drawRect(GrClip::WideOpen(), paint, matrix, SkRect::Make(colorBounds)); offset->fX = bounds.left(); offset->fY = bounds.top(); return SkSpecialImage::MakeFromGpu(SkIRect::MakeWH(bounds.width(), bounds.height()), kNeedNewImageUniqueID_SpecialImage, dst); } #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); }
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); }
bool SkXfermodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { SkBitmap background = src; SkIPoint backgroundOffset = SkIPoint::Make(0, 0); if (!this->filterInputGPU(0, proxy, src, ctx, &background, &backgroundOffset)) { return false; } GrTexture* backgroundTex = background.getTexture(); if (nullptr == backgroundTex) { SkASSERT(false); return false; } SkBitmap foreground = src; SkIPoint foregroundOffset = SkIPoint::Make(0, 0); if (!this->filterInputGPU(1, proxy, src, ctx, &foreground, &foregroundOffset)) { return false; } GrTexture* foregroundTex = foreground.getTexture(); GrContext* context = foregroundTex->getContext(); SkIRect bounds = background.bounds().makeOffset(backgroundOffset.x(), backgroundOffset.y()); bounds.join(foreground.bounds().makeOffset(foregroundOffset.x(), foregroundOffset.y())); if (bounds.isEmpty()) { return false; } const GrFragmentProcessor* xferFP = nullptr; GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = bounds.width(); desc.fHeight = bounds.height(); desc.fConfig = kSkia8888_GrPixelConfig; SkAutoTUnref<GrTexture> dst(context->textureProvider()->createApproxTexture(desc)); if (!dst) { return false; } GrPaint paint; SkMatrix backgroundMatrix; backgroundMatrix.setIDiv(backgroundTex->width(), backgroundTex->height()); backgroundMatrix.preTranslate(SkIntToScalar(-backgroundOffset.fX), SkIntToScalar(-backgroundOffset.fY)); SkAutoTUnref<const GrFragmentProcessor> bgFP(GrTextureDomainEffect::Create( backgroundTex, backgroundMatrix, GrTextureDomain::MakeTexelDomain(backgroundTex, background.bounds()), GrTextureDomain::kDecal_Mode, GrTextureParams::kNone_FilterMode) ); if (!fMode || !fMode->asFragmentProcessor(&xferFP, bgFP)) { // canFilterImageGPU() should've taken care of this SkASSERT(false); return false; } SkMatrix foregroundMatrix; foregroundMatrix.setIDiv(foregroundTex->width(), foregroundTex->height()); foregroundMatrix.preTranslate(SkIntToScalar(-foregroundOffset.fX), SkIntToScalar(-foregroundOffset.fY)); SkAutoTUnref<const GrFragmentProcessor> foregroundFP(GrTextureDomainEffect::Create( foregroundTex, foregroundMatrix, GrTextureDomain::MakeTexelDomain(foregroundTex, foreground.bounds()), GrTextureDomain::kDecal_Mode, GrTextureParams::kNone_FilterMode) ); paint.addColorFragmentProcessor(foregroundFP.get()); if (xferFP) { paint.addColorFragmentProcessor(xferFP)->unref(); } paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(dst->asRenderTarget())); if (!drawContext) { return false; } SkMatrix matrix; matrix.setTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top())); drawContext->drawRect(GrClip::WideOpen(), paint, matrix, SkRect::Make(bounds)); offset->fX = bounds.left(); offset->fY = bounds.top(); GrWrapTextureInBitmap(dst, bounds.width(), bounds.height(), false, result); return true; }
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); }
SkImage* SkImage::NewFromYUVTexturesCopy(GrContext* ctx , SkYUVColorSpace colorSpace, const GrBackendObject yuvTextureHandles[3], const SkISize yuvSizes[3], GrSurfaceOrigin origin) { const SkSurface::Budgeted budgeted = SkSurface::kYes_Budgeted; 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; } GrSurfaceDesc dstDesc; // Needs to be a render target in order to draw to it for the yuv->rgb conversion. dstDesc.fFlags = kRenderTarget_GrSurfaceFlag; dstDesc.fOrigin = origin; dstDesc.fWidth = yuvSizes[0].fWidth; dstDesc.fHeight = yuvSizes[0].fHeight; dstDesc.fConfig = kRGBA_8888_GrPixelConfig; dstDesc.fSampleCnt = 0; SkAutoTUnref<GrTexture> dst(ctx->textureProvider()->createTexture(dstDesc, true)); if (!dst) { return nullptr; } GrPaint paint; paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); paint.addColorFragmentProcessor(GrYUVtoRGBEffect::Create(paint.getProcessorDataManager(), yTex, uTex, vTex, yuvSizes, colorSpace))->unref(); const SkRect rect = SkRect::MakeWH(SkIntToScalar(dstDesc.fWidth), SkIntToScalar(dstDesc.fHeight)); SkAutoTUnref<GrDrawContext> drawContext(ctx->drawContext()); if (!drawContext) { return nullptr; } drawContext->drawRect(dst->asRenderTarget(), GrClip::WideOpen(), paint, SkMatrix::I(), rect); ctx->flushSurfaceWrites(dst); return new SkImage_Gpu(dstDesc.fWidth, dstDesc.fHeight, kNeedNewImageUniqueID, kOpaque_SkAlphaType, dst, budgeted); }
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; } } GrSurfaceDesc rtDesc = desc; rtDesc.fFlags = rtDesc.fFlags | kRenderTarget_GrSurfaceFlag; SkAutoTUnref<GrTexture> result(ctx->textureProvider()->createTexture(rtDesc, SkBudgeted::kYes, nullptr, 0)); if (!result) { return nullptr; } GrRenderTarget* renderTarget = result->asRenderTarget(); SkASSERT(renderTarget); GrPaint paint; // We may be decoding an sRGB image, but the result of our linear math on the YUV planes // is already in sRGB in that case. Don't convert (which will make the image too bright). paint.setDisableOutputConversionToSRGB(true); SkAutoTUnref<const GrFragmentProcessor> yuvToRgbProcessor( GrYUVEffect::CreateYUVToRGB(yuvTextures[0], yuvTextures[1], yuvTextures[2], yuvInfo.fSizeInfo.fSizes, yuvInfo.fColorSpace)); paint.addColorFragmentProcessor(yuvToRgbProcessor); paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); const SkRect r = SkRect::MakeIWH(yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fWidth, yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight); SkAutoTUnref<GrDrawContext> drawContext(ctx->drawContext(renderTarget)); if (!drawContext) { return nullptr; } drawContext->drawRect(GrClip::WideOpen(), paint, SkMatrix::I(), r); return result.release(); }
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<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(GrContext::kTight_BackingFit, 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(GrClip::WideOpen(), paint, SkMatrix::I(), rect); ctx->flushSurfaceWrites(drawContext->accessRenderTarget()); return sk_make_sp<SkImage_Gpu>(width, height, kNeedNewImageUniqueID, kOpaque_SkAlphaType, drawContext->asTexture().get(), budgeted); }
sk_sp<GrDrawContext> GaussianBlur(GrContext* context, GrTexture* origSrc, sk_sp<SkColorSpace> colorSpace, const SkIRect& dstBounds, const SkIRect* srcBounds, float sigmaX, float sigmaY, SkBackingFit fit) { 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); SkASSERT(sigmaX || sigmaY); SkIPoint srcOffset = SkIPoint::Make(-dstBounds.x(), -dstBounds.y()); SkIRect localDstBounds = SkIRect::MakeWH(dstBounds.width(), dstBounds.height()); SkIRect localSrcBounds; SkIRect srcRect; if (srcBounds) { srcRect = localSrcBounds = *srcBounds; srcRect.offset(srcOffset); srcBounds = &localSrcBounds; } else { srcRect = localDstBounds; } scale_irect_roundout(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY); scale_irect(&srcRect, scaleFactorX, scaleFactorY); // setup new clip GrFixedClip clip(localDstBounds); sk_sp<GrTexture> srcTexture(sk_ref_sp(origSrc)); SkASSERT(kBGRA_8888_GrPixelConfig == srcTexture->config() || kRGBA_8888_GrPixelConfig == srcTexture->config() || kSRGBA_8888_GrPixelConfig == srcTexture->config() || kSBGRA_8888_GrPixelConfig == srcTexture->config() || kRGBA_half_GrPixelConfig == srcTexture->config() || kAlpha_8_GrPixelConfig == srcTexture->config()); const int width = dstBounds.width(); const int height = dstBounds.height(); const GrPixelConfig config = srcTexture->config(); sk_sp<GrDrawContext> dstDrawContext(context->makeDrawContext(fit, width, height, config, colorSpace, 0, kDefault_GrSurfaceOrigin)); if (!dstDrawContext) { return nullptr; } // 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.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)); convolve_gaussian_2d(dstDrawContext.get(), clip, localDstBounds, srcOffset, srcTexture.get(), radiusX, radiusY, sigmaX, sigmaY, srcBounds); return dstDrawContext; } sk_sp<GrDrawContext> tmpDrawContext(context->makeDrawContext(fit, width, height, config, colorSpace, 0, kDefault_GrSurfaceOrigin)); if (!tmpDrawContext) { return nullptr; } sk_sp<GrDrawContext> srcDrawContext; SkASSERT(SkIsPow2(scaleFactorX) && SkIsPow2(scaleFactorY)); for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) { GrPaint paint; paint.setGammaCorrect(dstDrawContext->isGammaCorrect()); SkMatrix matrix; matrix.setIDiv(srcTexture->width(), srcTexture->height()); SkIRect dstRect(srcRect); if (srcBounds && i == 1) { SkRect domain; matrix.mapRect(&domain, SkRect::Make(*srcBounds)); domain.inset((i < scaleFactorX) ? SK_ScalarHalf / srcTexture->width() : 0.0f, (i < scaleFactorY) ? SK_ScalarHalf / srcTexture->height() : 0.0f); sk_sp<GrFragmentProcessor> fp(GrTextureDomainEffect::Make( srcTexture.get(), nullptr, matrix, domain, GrTextureDomain::kDecal_Mode, GrTextureParams::kBilerp_FilterMode)); paint.addColorFragmentProcessor(std::move(fp)); srcRect.offset(-srcOffset); srcOffset.set(0, 0); } else { GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode); paint.addColorTextureProcessor(srcTexture.get(), nullptr, matrix, params); } paint.setPorterDuffXPFactory(SkBlendMode::kSrc); shrink_irect_by_2(&dstRect, i < scaleFactorX, i < scaleFactorY); dstDrawContext->fillRectToRect(clip, paint, SkMatrix::I(), SkRect::Make(dstRect), SkRect::Make(srcRect)); srcDrawContext = dstDrawContext; srcRect = dstRect; srcTexture = srcDrawContext->asTexture(); dstDrawContext.swap(tmpDrawContext); localSrcBounds = srcRect; } srcRect = localDstBounds; scale_irect_roundout(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY); if (sigmaX > 0.0f) { if (scaleFactorX > 1) { SkASSERT(srcDrawContext); // Clear out a radius to the right of the srcRect to prevent the // X convolution from reading garbage. clearRect = SkIRect::MakeXYWH(srcRect.fRight, srcRect.fTop, radiusX, srcRect.height()); srcDrawContext->clear(&clearRect, 0x0, false); } convolve_gaussian(dstDrawContext.get(), clip, srcRect, srcTexture.get(), Gr1DKernelEffect::kX_Direction, radiusX, sigmaX, srcBounds, srcOffset); srcDrawContext = dstDrawContext; srcTexture = srcDrawContext->asTexture(); srcRect.offsetTo(0, 0); dstDrawContext.swap(tmpDrawContext); localSrcBounds = srcRect; srcOffset.set(0, 0); } if (sigmaY > 0.0f) { if (scaleFactorY > 1 || sigmaX > 0.0f) { SkASSERT(srcDrawContext); // Clear out a radius below the srcRect to prevent the Y // convolution from reading garbage. clearRect = SkIRect::MakeXYWH(srcRect.fLeft, srcRect.fBottom, srcRect.width(), radiusY); srcDrawContext->clear(&clearRect, 0x0, false); } convolve_gaussian(dstDrawContext.get(), clip, srcRect, srcTexture.get(), Gr1DKernelEffect::kY_Direction, radiusY, sigmaY, srcBounds, srcOffset); srcDrawContext = dstDrawContext; srcRect.offsetTo(0, 0); dstDrawContext.swap(tmpDrawContext); } SkASSERT(srcDrawContext); srcTexture = nullptr; // we don't use this from here on out if (scaleFactorX > 1 || scaleFactorY > 1) { // Clear one pixel to the right and below, to accommodate bilinear upsampling. clearRect = SkIRect::MakeXYWH(srcRect.fLeft, srcRect.fBottom, srcRect.width() + 1, 1); srcDrawContext->clear(&clearRect, 0x0, false); clearRect = SkIRect::MakeXYWH(srcRect.fRight, srcRect.fTop, 1, srcRect.height()); srcDrawContext->clear(&clearRect, 0x0, false); SkMatrix matrix; matrix.setIDiv(srcDrawContext->width(), srcDrawContext->height()); GrPaint paint; paint.setGammaCorrect(dstDrawContext->isGammaCorrect()); // FIXME: this should be mitchell, not bilinear. GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode); sk_sp<GrTexture> tex(srcDrawContext->asTexture()); paint.addColorTextureProcessor(tex.get(), nullptr, matrix, params); paint.setPorterDuffXPFactory(SkBlendMode::kSrc); SkIRect dstRect(srcRect); scale_irect(&dstRect, scaleFactorX, scaleFactorY); dstDrawContext->fillRectToRect(clip, paint, SkMatrix::I(), SkRect::Make(dstRect), SkRect::Make(srcRect)); srcDrawContext = dstDrawContext; srcRect = dstRect; dstDrawContext.swap(tmpDrawContext); } return srcDrawContext; }
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(); }
bool SkXfermodeImageFilter::filterImageGPUDeprecated(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { GrContext* context = nullptr; SkBitmap background = src; SkIPoint backgroundOffset = SkIPoint::Make(0, 0); if (!this->filterInputGPUDeprecated(0, proxy, src, ctx, &background, &backgroundOffset)) { background.reset(); } GrTexture* backgroundTex = background.getTexture(); if (backgroundTex) { context = backgroundTex->getContext(); } SkBitmap foreground = src; SkIPoint foregroundOffset = SkIPoint::Make(0, 0); if (!this->filterInputGPUDeprecated(1, proxy, src, ctx, &foreground, &foregroundOffset)) { foreground.reset(); } GrTexture* foregroundTex = foreground.getTexture(); if (foregroundTex) { context = foregroundTex->getContext(); } if (!context) { return false; } SkIRect bounds = background.bounds().makeOffset(backgroundOffset.x(), backgroundOffset.y()); bounds.join(foreground.bounds().makeOffset(foregroundOffset.x(), foregroundOffset.y())); if (bounds.isEmpty()) { return false; } GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = bounds.width(); desc.fHeight = bounds.height(); desc.fConfig = kSkia8888_GrPixelConfig; SkAutoTUnref<GrTexture> dst(context->textureProvider()->createApproxTexture(desc)); if (!dst) { return false; } GrPaint paint; SkAutoTUnref<const GrFragmentProcessor> bgFP; if (backgroundTex) { SkMatrix backgroundMatrix; backgroundMatrix.setIDiv(backgroundTex->width(), backgroundTex->height()); backgroundMatrix.preTranslate(SkIntToScalar(-backgroundOffset.fX), SkIntToScalar(-backgroundOffset.fY)); bgFP.reset(GrTextureDomainEffect::Create( backgroundTex, backgroundMatrix, GrTextureDomain::MakeTexelDomain(backgroundTex, background.bounds()), GrTextureDomain::kDecal_Mode, GrTextureParams::kNone_FilterMode)); } else { bgFP.reset(GrConstColorProcessor::Create(GrColor_TRANSPARENT_BLACK, GrConstColorProcessor::kIgnore_InputMode)); } if (foregroundTex) { SkMatrix foregroundMatrix; foregroundMatrix.setIDiv(foregroundTex->width(), foregroundTex->height()); foregroundMatrix.preTranslate(SkIntToScalar(-foregroundOffset.fX), SkIntToScalar(-foregroundOffset.fY)); SkAutoTUnref<const GrFragmentProcessor> foregroundFP; foregroundFP.reset(GrTextureDomainEffect::Create( foregroundTex, foregroundMatrix, GrTextureDomain::MakeTexelDomain(foregroundTex, foreground.bounds()), GrTextureDomain::kDecal_Mode, GrTextureParams::kNone_FilterMode)); paint.addColorFragmentProcessor(foregroundFP.get()); // 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)); } SkAutoTUnref<const GrFragmentProcessor> xferFP(mode->getFragmentProcessorForImageFilter(bgFP)); // A null 'xferFP' here means kSrc_Mode was used in which case we can just proceed if (xferFP) { paint.addColorFragmentProcessor(xferFP); } } else { paint.addColorFragmentProcessor(bgFP); } paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(dst->asRenderTarget())); if (!drawContext) { return false; } SkMatrix matrix; matrix.setTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top())); drawContext->drawRect(GrClip::WideOpen(), paint, matrix, SkRect::Make(bounds)); offset->fX = bounds.left(); offset->fY = bounds.top(); GrWrapTextureInBitmap(dst, bounds.width(), bounds.height(), false, result); return true; }
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(); }
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())); }
DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(SRGBMipMaps, reporter, ctxInfo) { GrContext* context = ctxInfo.grContext(); if (!context->caps()->srgbSupport()) { return; } const int rtS = 16; const int texS = rtS * 2; // Fill texture with a dither of black and 60% sRGB (~ 32.5% linear) gray. Although there is // only one likely failure mode (doing a direct downsample of the sRGB values), this pattern // maximizes the minimum error across all three conceivable failure modes: // 1) Likely incorrect: // (A + B) / 2 // 2) No input decode, decode output: // linear_to_srgb((A + B) / 2) // 3) Decode input, no output encode: // (srgb_to_linear(A) + srgb_to_linear(B)) / 2 const U8CPU srgb60 = sk_float_round2int(0.6f * 255.0f); static const SkPMColor colors[2] = { SkPackARGB32(0xFF, srgb60, srgb60, srgb60), SkPackARGB32(0xFF, 0x00, 0x00, 0x00) }; uint32_t texData[texS * texS]; for (int y = 0; y < texS; ++y) { for (int x = 0; x < texS; ++x) { texData[y * texS + x] = colors[(x + y) % 2]; } } // We can be pretty generous with the error detection, thanks to the choice of input. // The closest likely failure mode is off by > 0.1, so anything that encodes within // 10/255 of optimal is more than good enough for this test. const U8CPU expectedSRGB = sk_float_round2int( linear_to_srgb(srgb_to_linear(srgb60 / 255.0f) / 2.0f) * 255.0f); const U8CPU expectedLinear = srgb60 / 2; const U8CPU error = 10; // Create our test texture GrSurfaceDesc desc; desc.fFlags = kNone_GrSurfaceFlags; desc.fConfig = kSRGBA_8888_GrPixelConfig; desc.fWidth = texS; desc.fHeight = texS; GrTextureProvider* texProvider = context->textureProvider(); SkAutoTUnref<GrTexture> texture(texProvider->createTexture(desc, SkBudgeted::kNo, texData, 0)); // Create two render target contexts (L32 and S32) sk_sp<SkColorSpace> srgbColorSpace = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named); sk_sp<GrRenderTargetContext> l32RenderTargetContext = context->makeRenderTargetContext( SkBackingFit::kExact, rtS, rtS, kRGBA_8888_GrPixelConfig, nullptr); sk_sp<GrRenderTargetContext> s32RenderTargetContext = context->makeRenderTargetContext( SkBackingFit::kExact, rtS, rtS, kSRGBA_8888_GrPixelConfig, std::move(srgbColorSpace)); SkRect rect = SkRect::MakeWH(SkIntToScalar(rtS), SkIntToScalar(rtS)); GrNoClip noClip; GrPaint paint; paint.setPorterDuffXPFactory(SkBlendMode::kSrc); GrTextureParams mipMapParams(SkShader::kRepeat_TileMode, GrTextureParams::kMipMap_FilterMode); paint.addColorTextureProcessor(texture, nullptr, SkMatrix::MakeScale(0.5f), mipMapParams); // 1) Draw texture to S32 surface (should generate/use sRGB mips) paint.setGammaCorrect(true); s32RenderTargetContext->drawRect(noClip, paint, SkMatrix::I(), rect); read_and_check_pixels(reporter, s32RenderTargetContext->asTexture().get(), expectedSRGB, error, "first render of sRGB"); // 2) Draw texture to L32 surface (should generate/use linear mips) paint.setGammaCorrect(false); l32RenderTargetContext->drawRect(noClip, paint, SkMatrix::I(), rect); read_and_check_pixels(reporter, l32RenderTargetContext->asTexture().get(), expectedLinear, error, "re-render as linear"); // 3) Go back to sRGB paint.setGammaCorrect(true); s32RenderTargetContext->drawRect(noClip, paint, SkMatrix::I(), rect); read_and_check_pixels(reporter, s32RenderTargetContext->asTexture().get(), expectedSRGB, error, "re-render as sRGB"); }