static sk_sp<SkSpecialImage> apply_morphology( GrContext* context, SkSpecialImage* input, const SkIRect& rect, GrMorphologyEffect::Type morphType, SkISize radius, const SkImageFilter::OutputProperties& outputProperties) { sk_sp<GrTextureProxy> srcTexture(input->asTextureProxyRef(context)); SkASSERT(srcTexture); sk_sp<SkColorSpace> colorSpace = sk_ref_sp(outputProperties.colorSpace()); GrPixelConfig config = SkColorType2GrPixelConfig(outputProperties.colorType()); // setup new clip const GrFixedClip clip(SkIRect::MakeWH(srcTexture->width(), srcTexture->height())); const SkIRect dstRect = SkIRect::MakeWH(rect.width(), rect.height()); SkIRect srcRect = rect; SkASSERT(radius.width() > 0 || radius.height() > 0); if (radius.fWidth > 0) { sk_sp<GrRenderTargetContext> dstRTContext( context->contextPriv().makeDeferredRenderTargetContext( SkBackingFit::kApprox, rect.width(), rect.height(), config, colorSpace)); if (!dstRTContext) { return nullptr; } apply_morphology_pass(dstRTContext.get(), clip, std::move(srcTexture), srcRect, dstRect, radius.fWidth, morphType, GrMorphologyEffect::Direction::kX); SkIRect clearRect = SkIRect::MakeXYWH(dstRect.fLeft, dstRect.fBottom, dstRect.width(), radius.fHeight); GrColor clearColor = GrMorphologyEffect::Type::kErode == morphType ? SK_ColorWHITE : SK_ColorTRANSPARENT; dstRTContext->clear(&clearRect, clearColor, GrRenderTargetContext::CanClearFullscreen::kNo); srcTexture = dstRTContext->asTextureProxyRef(); srcRect = dstRect; } if (radius.fHeight > 0) { sk_sp<GrRenderTargetContext> dstRTContext( context->contextPriv().makeDeferredRenderTargetContext( SkBackingFit::kApprox, rect.width(), rect.height(), config, colorSpace)); if (!dstRTContext) { return nullptr; } apply_morphology_pass(dstRTContext.get(), clip, std::move(srcTexture), srcRect, dstRect, radius.fHeight, morphType, GrMorphologyEffect::Direction::kY); srcTexture = dstRTContext->asTextureProxyRef(); } return SkSpecialImage::MakeDeferredFromGpu(context, SkIRect::MakeWH(rect.width(), rect.height()), kNeedNewImageUniqueID_SpecialImage, std::move(srcTexture), std::move(colorSpace), &input->props()); }
sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps, const SkISize& size, SkAlphaType at, const SkSurfaceProps* props) const override { if (!fContext) { return nullptr; } return SkSpecialSurface::MakeRenderTarget( fContext, size.width(), size.height(), SkColorType2GrPixelConfig(outProps.colorType()), sk_ref_sp(outProps.colorSpace()), props); }
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(CopySurface, reporter, ctxInfo) { GrContext* context = ctxInfo.grContext(); static const int kW = 10; static const int kH = 10; static const size_t kRowBytes = sizeof(uint32_t) * kW; SkAutoTMalloc<uint32_t> srcPixels(kW * kH); for (int i = 0; i < kW * kH; ++i) { srcPixels.get()[i] = i; } SkAutoTMalloc<uint32_t> dstPixels(kW * kH); for (int i = 0; i < kW * kH; ++i) { dstPixels.get()[i] = ~i; } static const SkIRect kSrcRects[] { { 0, 0, kW , kH }, {-1, -1, kW+1, kH+1}, { 1, 1, kW-1, kH-1}, { 5, 5, 6 , 6 }, }; static const SkIPoint kDstPoints[] { { 0 , 0 }, { 1 , 1 }, { kW/2, kH/4}, { kW-1, kH-1}, { kW , kH }, { kW+1, kH+2}, {-1 , -1 }, }; static const SkImageInfo kImageInfos[] { SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType, kPremul_SkAlphaType), SkImageInfo::Make(kW, kH, kBGRA_8888_SkColorType, kPremul_SkAlphaType) }; SkAutoTMalloc<uint32_t> read(kW * kH); for (auto sOrigin : {kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin}) { for (auto dOrigin : {kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin}) { for (auto sRenderable : {GrRenderable::kYes, GrRenderable::kNo}) { for (auto dRenderable : {GrRenderable::kYes, GrRenderable::kNo}) { for (auto srcRect : kSrcRects) { for (auto dstPoint : kDstPoints) { for (auto ii: kImageInfos) { auto src = sk_gpu_test::MakeTextureProxyFromData( context, sRenderable, kW, kH, ii.colorType(), sOrigin, srcPixels.get(), kRowBytes); auto dst = sk_gpu_test::MakeTextureProxyFromData( context, dRenderable, kW, kH, ii.colorType(), dOrigin, dstPixels.get(), kRowBytes); // Should always work if the color type is RGBA, but may not work // for BGRA if (ii.colorType() == kRGBA_8888_SkColorType) { if (!src || !dst) { ERRORF(reporter, "Could not create surfaces for copy surface test."); continue; } } else { GrPixelConfig config = SkColorType2GrPixelConfig(kBGRA_8888_SkColorType); if (!context->priv().caps()->isConfigTexturable(config)) { continue; } if (!src || !dst) { ERRORF(reporter, "Could not create surfaces for copy surface test."); continue; } } sk_sp<GrSurfaceContext> dstContext = context->priv().makeWrappedSurfaceContext(std::move(dst)); bool result = dstContext->copy(src.get(), srcRect, dstPoint); bool expectedResult = true; SkIPoint dstOffset = { dstPoint.fX - srcRect.fLeft, dstPoint.fY - srcRect.fTop }; SkIRect copiedDstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY, srcRect.width(), srcRect.height()); SkIRect copiedSrcRect; if (!copiedSrcRect.intersect(srcRect, SkIRect::MakeWH(kW, kH))) { expectedResult = false; } else { // If the src rect was clipped, apply same clipping to each side // of copied dst rect. copiedDstRect.fLeft += copiedSrcRect.fLeft - srcRect.fLeft; copiedDstRect.fTop += copiedSrcRect.fTop - srcRect.fTop; copiedDstRect.fRight -= copiedSrcRect.fRight - srcRect.fRight; copiedDstRect.fBottom -= copiedSrcRect.fBottom - srcRect.fBottom; } if (copiedDstRect.isEmpty() || !copiedDstRect.intersect(SkIRect::MakeWH(kW, kH))) { expectedResult = false; } // To make the copied src rect correct we would apply any dst // clipping back to the src rect, but we don't use it again so // don't bother. if (expectedResult != result) { ERRORF(reporter, "Expected return value %d from copySurface, " "got %d.", expectedResult, result); continue; } if (!expectedResult || !result) { continue; } sk_memset32(read.get(), 0, kW * kH); if (!dstContext->readPixels(ii, read.get(), kRowBytes, 0, 0)) { ERRORF(reporter, "Error calling readPixels"); continue; } bool abort = false; // Validate that pixels inside copiedDstRect received the correct // value from src and that those outside were not modified. for (int y = 0; y < kH && !abort; ++y) { for (int x = 0; x < kW; ++x) { uint32_t r = read.get()[y * kW + x]; if (copiedDstRect.contains(x, y)) { int sx = x - dstOffset.fX; int sy = y - dstOffset.fY; uint32_t s = srcPixels.get()[sy * kW + sx]; if (s != r) { ERRORF(reporter, "Expected dst %d,%d to contain " "0x%08x copied from src location %d,%d. Got " "0x%08x", x, y, s, sx, sy, r); abort = true; break; } } else { uint32_t d = dstPixels.get()[y * kW + x]; if (d != r) { ERRORF(reporter, "Expected dst %d,%d to be " "unmodified (0x%08x). Got 0x%08x", x, y, d, r); abort = true; break; } } } } } } } } } } } }
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WrappedProxyTest, reporter, ctxInfo) { GrProxyProvider* proxyProvider = ctxInfo.grContext()->priv().proxyProvider(); GrContext* context = ctxInfo.grContext(); GrResourceProvider* resourceProvider = context->priv().resourceProvider(); GrGpu* gpu = context->priv().getGpu(); const GrCaps& caps = *context->priv().caps(); static const int kWidthHeight = 100; for (auto origin : { kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin }) { for (auto colorType : { kAlpha_8_SkColorType, kRGBA_8888_SkColorType, kRGBA_1010102_SkColorType }) { // External on-screen render target. // Tests wrapBackendRenderTarget with a GrBackendRenderTarget // Our test-only function that creates a backend render target doesn't currently support // sample counts :(. if (ctxInfo.grContext()->colorTypeSupportedAsSurface(colorType)) { GrBackendRenderTarget backendRT = gpu->createTestingOnlyBackendRenderTarget( kWidthHeight, kWidthHeight, SkColorTypeToGrColorType(colorType)); sk_sp<GrSurfaceProxy> sProxy( proxyProvider->wrapBackendRenderTarget(backendRT, origin, nullptr, nullptr)); check_surface(reporter, sProxy.get(), origin, kWidthHeight, kWidthHeight, backendRT.pixelConfig(), SkBudgeted::kNo); static constexpr int kExpectedNumSamples = 1; check_rendertarget(reporter, caps, resourceProvider, sProxy->asRenderTargetProxy(), kExpectedNumSamples, SkBackingFit::kExact, caps.maxWindowRectangles()); gpu->deleteTestingOnlyBackendRenderTarget(backendRT); } for (auto numSamples : {1, 4}) { GrPixelConfig config = SkColorType2GrPixelConfig(colorType); SkASSERT(kUnknown_GrPixelConfig != config); int supportedNumSamples = caps.getRenderTargetSampleCount(numSamples, config); if (!supportedNumSamples) { continue; } // Test wrapping FBO 0 (with made up properties). This tests sample count and the // special case where FBO 0 doesn't support window rectangles. if (GrBackendApi::kOpenGL == ctxInfo.backend()) { GrGLFramebufferInfo fboInfo; fboInfo.fFBOID = 0; fboInfo.fFormat = GR_GL_RGBA8; static constexpr int kStencilBits = 8; GrBackendRenderTarget backendRT(kWidthHeight, kWidthHeight, numSamples, kStencilBits, fboInfo); backendRT.setPixelConfig(config); sk_sp<GrSurfaceProxy> sProxy( proxyProvider->wrapBackendRenderTarget(backendRT, origin, nullptr, nullptr)); check_surface(reporter, sProxy.get(), origin, kWidthHeight, kWidthHeight, backendRT.pixelConfig(), SkBudgeted::kNo); check_rendertarget(reporter, caps, resourceProvider, sProxy->asRenderTargetProxy(), supportedNumSamples, SkBackingFit::kExact, 0); } // Tests wrapBackendRenderTarget with a GrBackendTexture { GrBackendTexture backendTex = context->createBackendTexture(kWidthHeight, kWidthHeight, colorType, GrMipMapped::kNo, GrRenderable::kYes); sk_sp<GrSurfaceProxy> sProxy = proxyProvider->wrapBackendTextureAsRenderTarget( backendTex, origin, supportedNumSamples); if (!sProxy) { context->deleteBackendTexture(backendTex); continue; // This can fail on Mesa } check_surface(reporter, sProxy.get(), origin, kWidthHeight, kWidthHeight, backendTex.pixelConfig(), SkBudgeted::kNo); check_rendertarget(reporter, caps, resourceProvider, sProxy->asRenderTargetProxy(), supportedNumSamples, SkBackingFit::kExact, caps.maxWindowRectangles()); context->deleteBackendTexture(backendTex); } // Tests wrapBackendTexture that is only renderable { GrBackendTexture backendTex = context->createBackendTexture(kWidthHeight, kWidthHeight, colorType, GrMipMapped::kNo, GrRenderable::kYes); sk_sp<GrSurfaceProxy> sProxy = proxyProvider->wrapRenderableBackendTexture( backendTex, origin, supportedNumSamples, kBorrow_GrWrapOwnership, GrWrapCacheable::kNo, nullptr, nullptr); if (!sProxy) { context->deleteBackendTexture(backendTex); continue; // This can fail on Mesa } check_surface(reporter, sProxy.get(), origin, kWidthHeight, kWidthHeight, backendTex.pixelConfig(), SkBudgeted::kNo); check_rendertarget(reporter, caps, resourceProvider, sProxy->asRenderTargetProxy(), supportedNumSamples, SkBackingFit::kExact, caps.maxWindowRectangles()); context->deleteBackendTexture(backendTex); } // Tests wrapBackendTexture that is only textureable { // Internal offscreen texture GrBackendTexture backendTex = context->createBackendTexture(kWidthHeight, kWidthHeight, colorType, GrMipMapped::kNo, GrRenderable::kNo); sk_sp<GrSurfaceProxy> sProxy = proxyProvider->wrapBackendTexture( backendTex, origin, kBorrow_GrWrapOwnership, GrWrapCacheable::kNo, kRead_GrIOType); if (!sProxy) { context->deleteBackendTexture(backendTex); continue; } check_surface(reporter, sProxy.get(), origin, kWidthHeight, kWidthHeight, backendTex.pixelConfig(), SkBudgeted::kNo); check_texture(reporter, resourceProvider, sProxy->asTextureProxy(), SkBackingFit::kExact); context->deleteBackendTexture(backendTex); } } } } }