void GrContext::drawFontCache(const SkRect& rect, GrMaskFormat format, const SkPaint& paint, GrRenderTarget* target) { GrBatchFontCache* cache = this->getBatchFontCache(); GrTexture* atlas = cache->getTexture(format); SkAutoTUnref<GrDrawContext> drawContext(this->drawContext(target)); // TODO: add drawContext method to encapsulate this. GrPaint grPaint; SkMatrix mat; mat.reset(); if (!SkPaintToGrPaint(this, paint, mat, &grPaint)) { return; } SkMatrix textureMat; textureMat.reset(); // TODO: use setScaleTranslate() textureMat[SkMatrix::kMScaleX] = 1.0f/rect.width(); textureMat[SkMatrix::kMScaleY] = 1.0f/rect.height(); textureMat[SkMatrix::kMTransX] = -rect.fLeft/rect.width(); textureMat[SkMatrix::kMTransY] = -rect.fTop/rect.height(); grPaint.addColorTextureProcessor(atlas, textureMat); GrClip clip; drawContext->drawRect(clip, grPaint, mat, rect); }
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); }
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 (getInput(0) && !getInput(0)->getInputResultGPU(proxy, src, ctx, &background, &backgroundOffset)) { return onFilterImage(proxy, src, ctx, result, offset); } GrTexture* backgroundTex = background.getTexture(); SkBitmap foreground = src; SkIPoint foregroundOffset = SkIPoint::Make(0, 0); if (getInput(1) && !getInput(1)->getInputResultGPU(proxy, src, ctx, &foreground, &foregroundOffset)) { return onFilterImage(proxy, src, ctx, result, offset); } GrTexture* foregroundTex = foreground.getTexture(); GrContext* context = foregroundTex->getContext(); GrFragmentProcessor* xferProcessor = NULL; GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag | kNoStencil_GrSurfaceFlag; desc.fWidth = src.width(); desc.fHeight = src.height(); desc.fConfig = kSkia8888_GrPixelConfig; SkAutoTUnref<GrTexture> dst( context->refScratchTexture(desc, GrContext::kApprox_ScratchTexMatch)); if (!dst) { return false; } GrContext::AutoRenderTarget art(context, dst->asRenderTarget()); if (!fMode || !fMode->asFragmentProcessor(&xferProcessor, backgroundTex)) { // canFilterImageGPU() should've taken care of this SkASSERT(false); return false; } SkMatrix foregroundMatrix = GrCoordTransform::MakeDivByTextureWHMatrix(foregroundTex); foregroundMatrix.preTranslate(SkIntToScalar(backgroundOffset.fX-foregroundOffset.fX), SkIntToScalar(backgroundOffset.fY-foregroundOffset.fY)); SkRect srcRect; src.getBounds(&srcRect); GrPaint paint; paint.addColorTextureProcessor(foregroundTex, foregroundMatrix); paint.addColorProcessor(xferProcessor)->unref(); context->drawRect(paint, srcRect); offset->fX = backgroundOffset.fX; offset->fY = backgroundOffset.fY; WrapTexture(dst, src.width(), src.height(), result); 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(); }
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); }
GrTexture* GaussianBlur(GrContext* context, GrTexture* srcTexture, bool canClobberSrc, const SkRect& rect, bool cropToRect, float sigmaX, float sigmaY) { SkASSERT(context); SkIRect clearRect; int scaleFactorX, radiusX; int scaleFactorY, radiusY; int maxTextureSize = context->caps()->maxTextureSize(); sigmaX = adjust_sigma(sigmaX, maxTextureSize, &scaleFactorX, &radiusX); sigmaY = adjust_sigma(sigmaY, maxTextureSize, &scaleFactorY, &radiusY); SkRect srcRect(rect); scale_rect(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY); srcRect.roundOut(&srcRect); scale_rect(&srcRect, static_cast<float>(scaleFactorX), static_cast<float>(scaleFactorY)); // setup new clip GrClip clip(SkRect::MakeWH(srcRect.width(), srcRect.height())); SkASSERT(kBGRA_8888_GrPixelConfig == srcTexture->config() || kRGBA_8888_GrPixelConfig == srcTexture->config() || kAlpha_8_GrPixelConfig == srcTexture->config()); GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = SkScalarFloorToInt(srcRect.width()); desc.fHeight = SkScalarFloorToInt(srcRect.height()); desc.fConfig = srcTexture->config(); GrTexture* dstTexture; GrTexture* tempTexture; SkAutoTUnref<GrTexture> temp1, temp2; temp1.reset(context->textureProvider()->refScratchTexture( desc, GrTextureProvider::kApprox_ScratchTexMatch)); dstTexture = temp1.get(); if (canClobberSrc) { tempTexture = srcTexture; } else { temp2.reset(context->textureProvider()->refScratchTexture( desc, GrTextureProvider::kApprox_ScratchTexMatch)); tempTexture = temp2.get(); } if (NULL == dstTexture || NULL == tempTexture) { return NULL; } GrDrawContext* drawContext = context->drawContext(); if (!drawContext) { return NULL; } for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) { GrPaint paint; SkMatrix matrix; matrix.setIDiv(srcTexture->width(), srcTexture->height()); SkRect dstRect(srcRect); if (cropToRect && i == 1) { dstRect.offset(-dstRect.fLeft, -dstRect.fTop); SkRect domain; matrix.mapRect(&domain, rect); domain.inset(i < scaleFactorX ? SK_ScalarHalf / srcTexture->width() : 0.0f, i < scaleFactorY ? SK_ScalarHalf / srcTexture->height() : 0.0f); SkAutoTUnref<GrFragmentProcessor> fp( GrTextureDomainEffect::Create( paint.getProcessorDataManager(), srcTexture, matrix, domain, GrTextureDomain::kDecal_Mode, GrTextureParams::kBilerp_FilterMode)); paint.addColorProcessor(fp); } else { GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode); paint.addColorTextureProcessor(srcTexture, matrix, params); } scale_rect(&dstRect, i < scaleFactorX ? 0.5f : 1.0f, i < scaleFactorY ? 0.5f : 1.0f); drawContext->drawNonAARectToRect(dstTexture->asRenderTarget(), clip, paint, SkMatrix::I(), dstRect, srcRect); srcRect = dstRect; srcTexture = dstTexture; SkTSwap(dstTexture, tempTexture); } const SkIRect srcIRect = srcRect.roundOut(); // For really small blurs(Certainly no wider than 5x5 on desktop gpus) it is faster to just // launch a single non separable kernel vs two launches if (sigmaX > 0.0f && sigmaY > 0 && (2 * radiusX + 1) * (2 * radiusY + 1) <= MAX_KERNEL_SIZE) { // We shouldn't be scaling because this is a small size blur SkASSERT((scaleFactorX == scaleFactorY) == 1); SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); convolve_gaussian_2d(drawContext, dstTexture->asRenderTarget(), clip, srcRect, dstRect, srcTexture, radiusX, radiusY, sigmaX, sigmaY, cropToRect, srcIRect); srcTexture = dstTexture; srcRect = dstRect; SkTSwap(dstTexture, tempTexture); } else { if (sigmaX > 0.0f) { if (scaleFactorX > 1) { // Clear out a radius to the right of the srcRect to prevent the // X convolution from reading garbage. clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop, radiusX, srcIRect.height()); drawContext->clear(srcTexture->asRenderTarget(), &clearRect, 0x0, false); } SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); convolve_gaussian(drawContext, dstTexture->asRenderTarget(), clip, srcRect, dstRect, srcTexture, Gr1DKernelEffect::kX_Direction, radiusX, sigmaX, cropToRect); srcTexture = dstTexture; srcRect = dstRect; SkTSwap(dstTexture, tempTexture); } if (sigmaY > 0.0f) { if (scaleFactorY > 1 || sigmaX > 0.0f) { // Clear out a radius below the srcRect to prevent the Y // convolution from reading garbage. clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom, srcIRect.width(), radiusY); drawContext->clear(srcTexture->asRenderTarget(), &clearRect, 0x0, false); } SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); convolve_gaussian(drawContext, dstTexture->asRenderTarget(), clip, srcRect, dstRect, srcTexture, Gr1DKernelEffect::kY_Direction, radiusY, sigmaY, cropToRect); srcTexture = dstTexture; srcRect = dstRect; SkTSwap(dstTexture, tempTexture); } } if (scaleFactorX > 1 || scaleFactorY > 1) { // Clear one pixel to the right and below, to accommodate bilinear // upsampling. clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom, srcIRect.width() + 1, 1); drawContext->clear(srcTexture->asRenderTarget(), &clearRect, 0x0, false); clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop, 1, srcIRect.height()); drawContext->clear(srcTexture->asRenderTarget(), &clearRect, 0x0, false); SkMatrix matrix; matrix.setIDiv(srcTexture->width(), srcTexture->height()); GrPaint paint; // FIXME: this should be mitchell, not bilinear. GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode); paint.addColorTextureProcessor(srcTexture, matrix, params); SkRect dstRect(srcRect); scale_rect(&dstRect, (float) scaleFactorX, (float) scaleFactorY); drawContext->drawNonAARectToRect(dstTexture->asRenderTarget(), clip, paint, SkMatrix::I(), dstRect, srcRect); srcRect = dstRect; srcTexture = dstTexture; SkTSwap(dstTexture, tempTexture); } return SkRef(srcTexture); }
static 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(); }
// creates a new texture that is the input texture scaled up. If optionalKey is valid it will be // set on the new texture. stretch controls whether the scaling is done using nearest or bilerp // filtering and the size to stretch the texture to. GrTexture* stretch_texture(GrTexture* inputTexture, const SkGrStretch& stretch, SkPixelRef* pixelRef, const GrUniqueKey& optionalKey) { SkASSERT(SkGrStretch::kNone_Type != stretch.fType); GrContext* context = inputTexture->getContext(); SkASSERT(context); const GrCaps* caps = context->caps(); // Either it's a cache miss or the original wasn't cached to begin with. GrSurfaceDesc rtDesc = inputTexture->desc(); rtDesc.fFlags = rtDesc.fFlags | kRenderTarget_GrSurfaceFlag; rtDesc.fWidth = stretch.fWidth; rtDesc.fHeight = stretch.fHeight; rtDesc.fConfig = GrMakePixelConfigUncompressed(rtDesc.fConfig); // If the config isn't renderable try converting to either A8 or an 32 bit config. Otherwise, // fail. if (!caps->isConfigRenderable(rtDesc.fConfig, false)) { if (GrPixelConfigIsAlphaOnly(rtDesc.fConfig)) { if (caps->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { rtDesc.fConfig = kAlpha_8_GrPixelConfig; } else if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) { rtDesc.fConfig = kSkia8888_GrPixelConfig; } else { return 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> stretched(GrCreateTextureForPixels(context, optionalKey, rtDesc, pixelRef, nullptr,0)); if (!stretched) { return nullptr; } GrPaint paint; // If filtering is not desired then we want to ensure all texels in the resampled image are // copies of texels from the original. GrTextureParams params(SkShader::kClamp_TileMode, SkGrStretch::kBilerp_Type == stretch.fType ? GrTextureParams::kBilerp_FilterMode : GrTextureParams::kNone_FilterMode); paint.addColorTextureProcessor(inputTexture, SkMatrix::I(), params); SkRect rect = SkRect::MakeWH(SkIntToScalar(rtDesc.fWidth), SkIntToScalar(rtDesc.fHeight)); SkRect localRect = SkRect::MakeWH(1.f, 1.f); SkAutoTUnref<GrDrawContext> drawContext(context->drawContext()); if (!drawContext) { return nullptr; } drawContext->drawNonAARectToRect(stretched->asRenderTarget(), GrClip::WideOpen(), paint, SkMatrix::I(), rect, localRect); return stretched.detach(); }
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"); }
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<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; }
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); }