| /* |
| * Copyright 2012 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include <cstddef> |
| #include <cstring> |
| #include <type_traits> |
| |
| #include "include/core/SkCanvas.h" |
| #include "include/gpu/GrBackendSurface.h" |
| #include "include/gpu/GrContext.h" |
| #include "include/gpu/GrTexture.h" |
| #include "include/private/GrRecordingContext.h" |
| #include "include/private/SkImageInfoPriv.h" |
| #include "src/core/SkAutoPixmapStorage.h" |
| #include "src/core/SkBitmapCache.h" |
| #include "src/core/SkMipMap.h" |
| #include "src/core/SkScopeExit.h" |
| #include "src/core/SkTraceEvent.h" |
| #include "src/gpu/GrAHardwareBufferImageGenerator.h" |
| #include "src/gpu/GrAHardwareBufferUtils.h" |
| #include "src/gpu/GrBackendTextureImageGenerator.h" |
| #include "src/gpu/GrBitmapTextureMaker.h" |
| #include "src/gpu/GrCaps.h" |
| #include "src/gpu/GrClip.h" |
| #include "src/gpu/GrColorSpaceXform.h" |
| #include "src/gpu/GrContextPriv.h" |
| #include "src/gpu/GrDrawingManager.h" |
| #include "src/gpu/GrGpu.h" |
| #include "src/gpu/GrImageInfo.h" |
| #include "src/gpu/GrImageTextureMaker.h" |
| #include "src/gpu/GrProxyProvider.h" |
| #include "src/gpu/GrRecordingContextPriv.h" |
| #include "src/gpu/GrRenderTargetContext.h" |
| #include "src/gpu/GrSemaphore.h" |
| #include "src/gpu/GrSurfacePriv.h" |
| #include "src/gpu/GrTextureAdjuster.h" |
| #include "src/gpu/GrTexturePriv.h" |
| #include "src/gpu/GrTextureProxy.h" |
| #include "src/gpu/GrTextureProxyPriv.h" |
| #include "src/gpu/SkGr.h" |
| #include "src/gpu/gl/GrGLTexture.h" |
| #include "src/image/SkImage_Gpu.h" |
| |
| static SkColorType proxy_color_type(GrSurfaceProxy* proxy) { |
| SkColorType colorType; |
| if (!GrPixelConfigToColorType(proxy->config(), &colorType)) { |
| colorType = kUnknown_SkColorType; |
| } |
| return colorType; |
| } |
| |
| SkImage_Gpu::SkImage_Gpu(sk_sp<GrContext> context, uint32_t uniqueID, SkAlphaType at, |
| GrSurfaceProxyView view, sk_sp<SkColorSpace> colorSpace) |
| : INHERITED(std::move(context), view.proxy()->backingStoreDimensions(), uniqueID, |
| proxy_color_type(view.proxy()), at, colorSpace) |
| , fView(std::move(view)) {} |
| |
| SkImage_Gpu::~SkImage_Gpu() {} |
| |
| GrSemaphoresSubmitted SkImage_Gpu::onFlush(GrContext* context, const GrFlushInfo& info) { |
| if (!context || !fContext->priv().matches(context) || fContext->abandoned()) { |
| return GrSemaphoresSubmitted::kNo; |
| } |
| |
| GrSurfaceProxy* p[1] = {fView.proxy()}; |
| return context->priv().flushSurfaces(p, 1, info); |
| } |
| |
| sk_sp<SkImage> SkImage_Gpu::onMakeColorTypeAndColorSpace(GrRecordingContext* context, |
| SkColorType targetCT, |
| sk_sp<SkColorSpace> targetCS) const { |
| if (!context || !fContext->priv().matches(context)) { |
| return nullptr; |
| } |
| |
| auto xform = GrColorSpaceXformEffect::Make(this->colorSpace(), this->alphaType(), |
| targetCS.get(), this->alphaType()); |
| SkASSERT(xform || targetCT != this->colorType()); |
| |
| auto renderTargetContext = GrRenderTargetContext::MakeWithFallback( |
| context, SkColorTypeToGrColorType(targetCT), nullptr, SkBackingFit::kExact, |
| this->dimensions()); |
| if (!renderTargetContext) { |
| return nullptr; |
| } |
| |
| GrPaint paint; |
| paint.setPorterDuffXPFactory(SkBlendMode::kSrc); |
| sk_sp<GrTextureProxy> proxy = this->asTextureProxyRef(context); |
| paint.addColorTextureProcessor(std::move(proxy), this->alphaType(), SkMatrix::I()); |
| if (xform) { |
| paint.addColorFragmentProcessor(std::move(xform)); |
| } |
| |
| renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), |
| SkRect::MakeIWH(this->width(), this->height())); |
| if (!renderTargetContext->asTextureProxy()) { |
| return nullptr; |
| } |
| |
| // MDB: this call is okay bc we know 'renderTargetContext' was exact |
| return sk_make_sp<SkImage_Gpu>(fContext, kNeedNewImageUniqueID, this->alphaType(), |
| renderTargetContext->readSurfaceView(), std::move(targetCS)); |
| } |
| |
| sk_sp<SkImage> SkImage_Gpu::onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const { |
| return sk_make_sp<SkImage_Gpu>(fContext, kNeedNewImageUniqueID, this->alphaType(), fView, |
| std::move(newCS)); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| static sk_sp<SkImage> new_wrapped_texture_common(GrContext* ctx, |
| const GrBackendTexture& backendTex, |
| GrColorType colorType, GrSurfaceOrigin origin, |
| SkAlphaType at, sk_sp<SkColorSpace> colorSpace, |
| GrWrapOwnership ownership, |
| SkImage::TextureReleaseProc releaseProc, |
| SkImage::ReleaseContext releaseCtx) { |
| if (!backendTex.isValid() || backendTex.width() <= 0 || backendTex.height() <= 0) { |
| return nullptr; |
| } |
| |
| GrProxyProvider* proxyProvider = ctx->priv().proxyProvider(); |
| sk_sp<GrTextureProxy> proxy = |
| proxyProvider->wrapBackendTexture(backendTex, colorType, origin, ownership, |
| GrWrapCacheable::kNo, kRead_GrIOType, |
| releaseProc, releaseCtx); |
| if (!proxy) { |
| return nullptr; |
| } |
| |
| GrSwizzle swizzle = ctx->priv().caps()->getReadSwizzle(proxy->backendFormat(), colorType); |
| GrSurfaceProxyView view(std::move(proxy), origin, swizzle); |
| return sk_make_sp<SkImage_Gpu>(sk_ref_sp(ctx), kNeedNewImageUniqueID, at, std::move(view), |
| std::move(colorSpace)); |
| } |
| |
| sk_sp<SkImage> SkImage::MakeFromCompressedTexture(GrContext* ctx, |
| const GrBackendTexture& tex, |
| GrSurfaceOrigin origin, |
| SkAlphaType at, |
| sk_sp<SkColorSpace> cs, |
| TextureReleaseProc releaseP, |
| ReleaseContext releaseC) { |
| if (!ctx) { |
| return nullptr; |
| } |
| |
| const GrCaps* caps = ctx->priv().caps(); |
| |
| if (!SkImage_GpuBase::ValidateCompressedBackendTexture(caps, tex, at)) { |
| return nullptr; |
| } |
| |
| GrProxyProvider* proxyProvider = ctx->priv().proxyProvider(); |
| sk_sp<GrTextureProxy> proxy = |
| proxyProvider->wrapCompressedBackendTexture(tex, origin, kBorrow_GrWrapOwnership, |
| GrWrapCacheable::kNo, releaseP, releaseC); |
| if (!proxy) { |
| return nullptr; |
| } |
| |
| GrSurfaceProxyView view(std::move(proxy), origin, GrSwizzle::RGBA()); |
| return sk_make_sp<SkImage_Gpu>(sk_ref_sp(ctx), kNeedNewImageUniqueID, at, std::move(view), |
| std::move(cs)); |
| } |
| |
| sk_sp<SkImage> SkImage::MakeFromTexture(GrContext* ctx, |
| const GrBackendTexture& tex, GrSurfaceOrigin origin, |
| SkColorType ct, SkAlphaType at, sk_sp<SkColorSpace> cs, |
| TextureReleaseProc releaseP, ReleaseContext releaseC) { |
| if (!ctx) { |
| return nullptr; |
| } |
| |
| const GrCaps* caps = ctx->priv().caps(); |
| |
| GrColorType grColorType = SkColorTypeAndFormatToGrColorType(caps, ct, tex.getBackendFormat()); |
| if (GrColorType::kUnknown == grColorType) { |
| return nullptr; |
| } |
| |
| if (!SkImage_GpuBase::ValidateBackendTexture(caps, tex, grColorType, ct, at, cs)) { |
| return nullptr; |
| } |
| |
| return new_wrapped_texture_common(ctx, tex, grColorType, origin, at, std::move(cs), |
| kBorrow_GrWrapOwnership, releaseP, releaseC); |
| } |
| |
| sk_sp<SkImage> SkImage::MakeFromAdoptedTexture(GrContext* ctx, |
| const GrBackendTexture& tex, GrSurfaceOrigin origin, |
| SkColorType ct, SkAlphaType at, |
| sk_sp<SkColorSpace> cs) { |
| if (!ctx || !ctx->priv().resourceProvider()) { |
| // We have a DDL context and we don't support adopted textures for them. |
| return nullptr; |
| } |
| |
| const GrCaps* caps = ctx->priv().caps(); |
| |
| GrColorType grColorType = SkColorTypeAndFormatToGrColorType(caps, ct, tex.getBackendFormat()); |
| if (GrColorType::kUnknown == grColorType) { |
| return nullptr; |
| } |
| |
| if (!SkImage_GpuBase::ValidateBackendTexture(caps, tex, grColorType, ct, at, cs)) { |
| return nullptr; |
| } |
| |
| return new_wrapped_texture_common(ctx, tex, grColorType, origin, at, std::move(cs), |
| kAdopt_GrWrapOwnership, nullptr, nullptr); |
| } |
| |
| sk_sp<SkImage> SkImage::MakeFromCompressed(GrContext* context, sk_sp<SkData> data, |
| int width, int height, CompressionType type) { |
| GrProxyProvider* proxyProvider = context->priv().proxyProvider(); |
| sk_sp<GrTextureProxy> proxy = proxyProvider->createCompressedTextureProxy( |
| {width, height}, SkBudgeted::kYes, type, std::move(data)); |
| if (!proxy) { |
| return nullptr; |
| } |
| // TODO: remove asserts when proxy doesn't hold origin or swizzle |
| SkASSERT(proxy->origin() == kTopLeft_GrSurfaceOrigin); |
| SkASSERT(proxy->textureSwizzle() == GrSwizzle()); |
| GrSurfaceProxyView view(std::move(proxy)); |
| |
| return sk_make_sp<SkImage_Gpu>(sk_ref_sp(context), kNeedNewImageUniqueID, kOpaque_SkAlphaType, |
| std::move(view), nullptr); |
| } |
| |
| sk_sp<SkImage> SkImage_Gpu::ConvertYUVATexturesToRGB(GrContext* ctx, SkYUVColorSpace yuvColorSpace, |
| const GrBackendTexture yuvaTextures[], |
| const SkYUVAIndex yuvaIndices[4], SkISize size, |
| GrSurfaceOrigin origin, |
| GrRenderTargetContext* renderTargetContext) { |
| SkASSERT(renderTargetContext); |
| |
| int numTextures; |
| if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures)) { |
| return nullptr; |
| } |
| |
| sk_sp<GrTextureProxy> tempTextureProxies[4]; |
| if (!SkImage_GpuBase::MakeTempTextureProxies(ctx, yuvaTextures, numTextures, yuvaIndices, |
| origin, tempTextureProxies)) { |
| return nullptr; |
| } |
| |
| const SkRect rect = SkRect::MakeIWH(size.width(), size.height()); |
| if (!RenderYUVAToRGBA(ctx, renderTargetContext, rect, yuvColorSpace, nullptr, |
| tempTextureProxies, yuvaIndices)) { |
| return nullptr; |
| } |
| |
| SkAlphaType at = GetAlphaTypeFromYUVAIndices(yuvaIndices); |
| // MDB: this call is okay bc we know 'renderTargetContext' was exact |
| return sk_make_sp<SkImage_Gpu>(sk_ref_sp(ctx), kNeedNewImageUniqueID, at, |
| renderTargetContext->readSurfaceView(), |
| renderTargetContext->colorInfo().refColorSpace()); |
| } |
| |
| sk_sp<SkImage> SkImage::MakeFromYUVATexturesCopy(GrContext* ctx, |
| SkYUVColorSpace yuvColorSpace, |
| const GrBackendTexture yuvaTextures[], |
| const SkYUVAIndex yuvaIndices[4], |
| SkISize imageSize, |
| GrSurfaceOrigin imageOrigin, |
| sk_sp<SkColorSpace> imageColorSpace) { |
| auto renderTargetContext = GrRenderTargetContext::Make( |
| ctx, GrColorType::kRGBA_8888, std::move(imageColorSpace), SkBackingFit::kExact, |
| imageSize, 1, GrMipMapped::kNo, GrProtected::kNo, imageOrigin); |
| if (!renderTargetContext) { |
| return nullptr; |
| } |
| |
| return SkImage_Gpu::ConvertYUVATexturesToRGB(ctx, yuvColorSpace, yuvaTextures, yuvaIndices, |
| imageSize, imageOrigin, renderTargetContext.get()); |
| } |
| |
| sk_sp<SkImage> SkImage::MakeFromYUVATexturesCopyWithExternalBackend( |
| GrContext* ctx, |
| SkYUVColorSpace yuvColorSpace, |
| const GrBackendTexture yuvaTextures[], |
| const SkYUVAIndex yuvaIndices[4], |
| SkISize imageSize, |
| GrSurfaceOrigin imageOrigin, |
| const GrBackendTexture& backendTexture, |
| sk_sp<SkColorSpace> imageColorSpace, |
| TextureReleaseProc textureReleaseProc, |
| ReleaseContext releaseContext) { |
| const GrCaps* caps = ctx->priv().caps(); |
| |
| GrColorType grColorType = SkColorTypeAndFormatToGrColorType(caps, kRGBA_8888_SkColorType, |
| backendTexture.getBackendFormat()); |
| if (GrColorType::kUnknown == grColorType) { |
| return nullptr; |
| } |
| |
| SkAlphaType at = SkImage_GpuBase::GetAlphaTypeFromYUVAIndices(yuvaIndices); |
| if (!SkImage_Gpu::ValidateBackendTexture(caps, backendTexture, grColorType, |
| kRGBA_8888_SkColorType, at, nullptr)) { |
| return nullptr; |
| } |
| |
| // Needs to create a render target with external texture |
| // in order to draw to it for the yuv->rgb conversion. |
| auto renderTargetContext = ctx->priv().makeBackendTextureRenderTargetContext( |
| backendTexture, imageOrigin, 1, grColorType, std::move(imageColorSpace), nullptr, |
| textureReleaseProc, releaseContext); |
| if (!renderTargetContext) { |
| return nullptr; |
| } |
| |
| return SkImage_Gpu::ConvertYUVATexturesToRGB(ctx, yuvColorSpace, yuvaTextures, yuvaIndices, |
| imageSize, imageOrigin, renderTargetContext.get()); |
| } |
| |
| sk_sp<SkImage> SkImage::MakeFromYUVTexturesCopy(GrContext* ctx, SkYUVColorSpace yuvColorSpace, |
| const GrBackendTexture yuvTextures[3], |
| GrSurfaceOrigin imageOrigin, |
| sk_sp<SkColorSpace> imageColorSpace) { |
| // TODO: SkImageSourceChannel input is being ingored right now. Setup correctly in the future. |
| SkYUVAIndex yuvaIndices[4] = { |
| SkYUVAIndex{0, SkColorChannel::kR}, |
| SkYUVAIndex{1, SkColorChannel::kR}, |
| SkYUVAIndex{2, SkColorChannel::kR}, |
| SkYUVAIndex{-1, SkColorChannel::kA}}; |
| SkISize size{yuvTextures[0].width(), yuvTextures[0].height()}; |
| return SkImage_Gpu::MakeFromYUVATexturesCopy(ctx, yuvColorSpace, yuvTextures, yuvaIndices, |
| size, imageOrigin, std::move(imageColorSpace)); |
| } |
| |
| sk_sp<SkImage> SkImage::MakeFromYUVTexturesCopyWithExternalBackend( |
| GrContext* ctx, SkYUVColorSpace yuvColorSpace, const GrBackendTexture yuvTextures[3], |
| GrSurfaceOrigin imageOrigin, const GrBackendTexture& backendTexture, |
| sk_sp<SkColorSpace> imageColorSpace) { |
| SkYUVAIndex yuvaIndices[4] = { |
| SkYUVAIndex{0, SkColorChannel::kR}, |
| SkYUVAIndex{1, SkColorChannel::kR}, |
| SkYUVAIndex{2, SkColorChannel::kR}, |
| SkYUVAIndex{-1, SkColorChannel::kA}}; |
| SkISize size{yuvTextures[0].width(), yuvTextures[0].height()}; |
| return SkImage_Gpu::MakeFromYUVATexturesCopyWithExternalBackend( |
| ctx, yuvColorSpace, yuvTextures, yuvaIndices, size, imageOrigin, backendTexture, |
| std::move(imageColorSpace), nullptr, nullptr); |
| } |
| |
| sk_sp<SkImage> SkImage::MakeFromNV12TexturesCopy(GrContext* ctx, SkYUVColorSpace yuvColorSpace, |
| const GrBackendTexture nv12Textures[2], |
| GrSurfaceOrigin imageOrigin, |
| sk_sp<SkColorSpace> imageColorSpace) { |
| // TODO: SkImageSourceChannel input is being ingored right now. Setup correctly in the future. |
| SkYUVAIndex yuvaIndices[4] = { |
| SkYUVAIndex{0, SkColorChannel::kR}, |
| SkYUVAIndex{1, SkColorChannel::kR}, |
| SkYUVAIndex{1, SkColorChannel::kG}, |
| SkYUVAIndex{-1, SkColorChannel::kA}}; |
| SkISize size{nv12Textures[0].width(), nv12Textures[0].height()}; |
| return SkImage_Gpu::MakeFromYUVATexturesCopy(ctx, yuvColorSpace, nv12Textures, yuvaIndices, |
| size, imageOrigin, std::move(imageColorSpace)); |
| } |
| |
| sk_sp<SkImage> SkImage::MakeFromNV12TexturesCopyWithExternalBackend( |
| GrContext* ctx, |
| SkYUVColorSpace yuvColorSpace, |
| const GrBackendTexture nv12Textures[2], |
| GrSurfaceOrigin imageOrigin, |
| const GrBackendTexture& backendTexture, |
| sk_sp<SkColorSpace> imageColorSpace, |
| TextureReleaseProc textureReleaseProc, |
| ReleaseContext releaseContext) { |
| SkYUVAIndex yuvaIndices[4] = { |
| SkYUVAIndex{0, SkColorChannel::kR}, |
| SkYUVAIndex{1, SkColorChannel::kR}, |
| SkYUVAIndex{1, SkColorChannel::kG}, |
| SkYUVAIndex{-1, SkColorChannel::kA}}; |
| SkISize size{nv12Textures[0].width(), nv12Textures[0].height()}; |
| return SkImage_Gpu::MakeFromYUVATexturesCopyWithExternalBackend( |
| ctx, yuvColorSpace, nv12Textures, yuvaIndices, size, imageOrigin, backendTexture, |
| std::move(imageColorSpace), textureReleaseProc, releaseContext); |
| } |
| |
| static sk_sp<SkImage> create_image_from_producer(GrContext* context, GrTextureProducer* producer, |
| SkAlphaType at, uint32_t id, |
| GrMipMapped mipMapped) { |
| // TODO: have texture producer return a GrSurfaceProxyView |
| sk_sp<GrTextureProxy> proxy(producer->refTextureProxy(mipMapped)); |
| if (!proxy) { |
| return nullptr; |
| } |
| GrSurfaceOrigin origin = proxy->origin(); |
| const GrSwizzle& swizzle = proxy->textureSwizzle(); |
| GrSurfaceProxyView view(std::move(proxy), origin, swizzle); |
| return sk_make_sp<SkImage_Gpu>(sk_ref_sp(context), id, at, std::move(view), |
| sk_ref_sp(producer->colorSpace())); |
| } |
| |
| sk_sp<SkImage> SkImage::makeTextureImage(GrContext* context, GrMipMapped mipMapped) const { |
| if (!context) { |
| return nullptr; |
| } |
| |
| if (this->isTextureBacked()) { |
| if (!as_IB(this)->context()->priv().matches(context)) { |
| return nullptr; |
| } |
| |
| sk_sp<GrTextureProxy> proxy = as_IB(this)->asTextureProxyRef(context); |
| SkASSERT(proxy); |
| if (GrMipMapped::kNo == mipMapped || proxy->mipMapped() == mipMapped) { |
| return sk_ref_sp(const_cast<SkImage*>(this)); |
| } |
| GrTextureAdjuster adjuster(context, std::move(proxy), this->imageInfo().colorInfo(), |
| this->uniqueID()); |
| return create_image_from_producer(context, &adjuster, this->alphaType(), |
| this->uniqueID(), mipMapped); |
| } |
| |
| if (this->isLazyGenerated()) { |
| GrImageTextureMaker maker(context, this, kDisallow_CachingHint); |
| return create_image_from_producer(context, &maker, this->alphaType(), |
| this->uniqueID(), mipMapped); |
| } |
| |
| if (const SkBitmap* bmp = as_IB(this)->onPeekBitmap()) { |
| GrBitmapTextureMaker maker(context, *bmp); |
| return create_image_from_producer(context, &maker, this->alphaType(), |
| this->uniqueID(), mipMapped); |
| } |
| return nullptr; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| sk_sp<SkImage> SkImage_Gpu::MakePromiseTexture(GrContext* context, |
| const GrBackendFormat& backendFormat, |
| int width, |
| int height, |
| GrMipMapped mipMapped, |
| GrSurfaceOrigin origin, |
| SkColorType colorType, |
| SkAlphaType alphaType, |
| sk_sp<SkColorSpace> colorSpace, |
| PromiseImageTextureFulfillProc textureFulfillProc, |
| PromiseImageTextureReleaseProc textureReleaseProc, |
| PromiseImageTextureDoneProc textureDoneProc, |
| PromiseImageTextureContext textureContext, |
| PromiseImageApiVersion version) { |
| // The contract here is that if 'promiseDoneProc' is passed in it should always be called, |
| // even if creation of the SkImage fails. Once we call MakePromiseImageLazyProxy it takes |
| // responsibility for calling the done proc. |
| if (!textureDoneProc) { |
| return nullptr; |
| } |
| SkScopeExit callDone([textureDoneProc, textureContext]() { textureDoneProc(textureContext); }); |
| |
| SkImageInfo info = SkImageInfo::Make(width, height, colorType, alphaType, colorSpace); |
| if (!SkImageInfoIsValid(info)) { |
| return nullptr; |
| } |
| |
| if (!context) { |
| return nullptr; |
| } |
| |
| if (width <= 0 || height <= 0) { |
| return nullptr; |
| } |
| |
| GrColorType grColorType = SkColorTypeAndFormatToGrColorType(context->priv().caps(), |
| colorType, |
| backendFormat); |
| if (GrColorType::kUnknown == grColorType) { |
| return nullptr; |
| } |
| |
| callDone.clear(); |
| auto proxy = MakePromiseImageLazyProxy(context, width, height, origin, |
| grColorType, backendFormat, |
| mipMapped, textureFulfillProc, textureReleaseProc, |
| textureDoneProc, textureContext, version); |
| if (!proxy) { |
| return nullptr; |
| } |
| GrSwizzle swizzle = context->priv().caps()->getReadSwizzle(backendFormat, grColorType); |
| GrSurfaceProxyView view(std::move(proxy), origin, swizzle); |
| return sk_make_sp<SkImage_Gpu>(sk_ref_sp(context), kNeedNewImageUniqueID, alphaType, |
| std::move(view), std::move(colorSpace)); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| sk_sp<SkImage> SkImage::MakeCrossContextFromPixmap(GrContext* context, |
| const SkPixmap& originalPixmap, bool buildMips, |
| bool limitToMaxTextureSize) { |
| // Some backends or drivers don't support (safely) moving resources between contexts |
| if (!context || !context->priv().caps()->crossContextTextureSupport()) { |
| return SkImage::MakeRasterCopy(originalPixmap); |
| } |
| |
| // If we don't have access to the resource provider and gpu (i.e. in a DDL context) we will not |
| // be able to make everything needed for a GPU CrossContext image. Thus return a raster copy |
| // instead. |
| if (!context->priv().resourceProvider()) { |
| return SkImage::MakeRasterCopy(originalPixmap); |
| } |
| |
| // If non-power-of-two mipmapping isn't supported, ignore the client's request |
| if (!context->priv().caps()->mipMapSupport()) { |
| buildMips = false; |
| } |
| |
| const SkPixmap* pixmap = &originalPixmap; |
| SkAutoPixmapStorage resized; |
| int maxTextureSize = context->priv().caps()->maxTextureSize(); |
| int maxDim = SkTMax(originalPixmap.width(), originalPixmap.height()); |
| if (limitToMaxTextureSize && maxDim > maxTextureSize) { |
| float scale = static_cast<float>(maxTextureSize) / maxDim; |
| int newWidth = SkTMin(static_cast<int>(originalPixmap.width() * scale), maxTextureSize); |
| int newHeight = SkTMin(static_cast<int>(originalPixmap.height() * scale), maxTextureSize); |
| SkImageInfo info = originalPixmap.info().makeWH(newWidth, newHeight); |
| if (!resized.tryAlloc(info) || !originalPixmap.scalePixels(resized, kLow_SkFilterQuality)) { |
| return nullptr; |
| } |
| pixmap = &resized; |
| } |
| GrProxyProvider* proxyProvider = context->priv().proxyProvider(); |
| // Turn the pixmap into a GrTextureProxy |
| SkBitmap bmp; |
| bmp.installPixels(*pixmap); |
| GrMipMapped mipMapped = buildMips ? GrMipMapped::kYes : GrMipMapped::kNo; |
| sk_sp<GrTextureProxy> proxy = proxyProvider->createProxyFromBitmap(bmp, mipMapped); |
| if (!proxy) { |
| return SkImage::MakeRasterCopy(*pixmap); |
| } |
| |
| sk_sp<GrTexture> texture = sk_ref_sp(proxy->peekTexture()); |
| |
| // Flush any writes or uploads |
| context->priv().flushSurface(proxy.get()); |
| GrGpu* gpu = context->priv().getGpu(); |
| |
| std::unique_ptr<GrSemaphore> sema = gpu->prepareTextureForCrossContextUsage(texture.get()); |
| |
| auto gen = GrBackendTextureImageGenerator::Make(std::move(texture), proxy->origin(), |
| std::move(sema), pixmap->colorType(), |
| pixmap->alphaType(), |
| pixmap->info().refColorSpace()); |
| return SkImage::MakeFromGenerator(std::move(gen)); |
| } |
| |
| #if defined(SK_BUILD_FOR_ANDROID) && __ANDROID_API__ >= 26 |
| sk_sp<SkImage> SkImage::MakeFromAHardwareBuffer(AHardwareBuffer* graphicBuffer, SkAlphaType at, |
| sk_sp<SkColorSpace> cs, |
| GrSurfaceOrigin surfaceOrigin) { |
| auto gen = GrAHardwareBufferImageGenerator::Make(graphicBuffer, at, cs, surfaceOrigin); |
| return SkImage::MakeFromGenerator(std::move(gen)); |
| } |
| |
| sk_sp<SkImage> SkImage::MakeFromAHardwareBufferWithData(GrContext* context, |
| const SkPixmap& pixmap, |
| AHardwareBuffer* hardwareBuffer, |
| GrSurfaceOrigin surfaceOrigin) { |
| AHardwareBuffer_Desc bufferDesc; |
| AHardwareBuffer_describe(hardwareBuffer, &bufferDesc); |
| |
| if (!SkToBool(bufferDesc.usage & AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE)) { |
| return nullptr; |
| } |
| |
| GrBackendFormat backendFormat = GrAHardwareBufferUtils::GetBackendFormat(context, |
| hardwareBuffer, |
| bufferDesc.format, |
| true); |
| |
| if (!backendFormat.isValid()) { |
| return nullptr; |
| } |
| |
| GrAHardwareBufferUtils::DeleteImageProc deleteImageProc = nullptr; |
| GrAHardwareBufferUtils::UpdateImageProc updateImageProc = nullptr; |
| GrAHardwareBufferUtils::TexImageCtx deleteImageCtx = nullptr; |
| |
| GrBackendTexture backendTexture = |
| GrAHardwareBufferUtils::MakeBackendTexture(context, hardwareBuffer, |
| bufferDesc.width, bufferDesc.height, |
| &deleteImageProc, &updateImageProc, |
| &deleteImageCtx, false, backendFormat, true); |
| if (!backendTexture.isValid()) { |
| return nullptr; |
| } |
| SkASSERT(deleteImageProc); |
| |
| SkColorType colorType = |
| GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(bufferDesc.format); |
| |
| GrColorType grColorType = SkColorTypeToGrColorType(colorType); |
| |
| GrProxyProvider* proxyProvider = context->priv().proxyProvider(); |
| if (!proxyProvider) { |
| deleteImageProc(deleteImageCtx); |
| return nullptr; |
| } |
| |
| sk_sp<GrTextureProxy> proxy = |
| proxyProvider->wrapBackendTexture(backendTexture, grColorType, surfaceOrigin, |
| kBorrow_GrWrapOwnership, GrWrapCacheable::kNo, |
| kRW_GrIOType, deleteImageProc, deleteImageCtx); |
| if (!proxy) { |
| deleteImageProc(deleteImageCtx); |
| return nullptr; |
| } |
| |
| sk_sp<SkColorSpace> cs = pixmap.refColorSpace(); |
| SkAlphaType at = pixmap.alphaType(); |
| |
| GrSwizzle swizzle = context->priv().caps()->getReadSwizzle(backendFormat, grColorType); |
| GrSurfaceProxyView view(proxy, surfaceOrigin, swizzle); |
| sk_sp<SkImage> image = sk_make_sp<SkImage_Gpu>(sk_ref_sp(context), kNeedNewImageUniqueID, at, |
| std::move(view), cs); |
| if (!image) { |
| return nullptr; |
| } |
| |
| GrDrawingManager* drawingManager = context->priv().drawingManager(); |
| if (!drawingManager) { |
| return nullptr; |
| } |
| |
| GrSurfaceContext surfaceContext(context, std::move(proxy), |
| SkColorTypeToGrColorType(pixmap.colorType()), |
| pixmap.alphaType(), cs, surfaceOrigin, swizzle); |
| |
| SkImageInfo srcInfo = SkImageInfo::Make(bufferDesc.width, bufferDesc.height, colorType, at, |
| std::move(cs)); |
| surfaceContext.writePixels(srcInfo, pixmap.addr(0, 0), pixmap.rowBytes(), {0, 0}); |
| |
| GrFlushInfo info; |
| info.fFlags = kSyncCpu_GrFlushFlag; |
| GrSurfaceProxy* p[1] = {surfaceContext.asSurfaceProxy()}; |
| drawingManager->flush(p, 1, SkSurface::BackendSurfaceAccess::kNoAccess, info, |
| GrPrepareForExternalIORequests()); |
| |
| return image; |
| } |
| #endif |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| bool SkImage::MakeBackendTextureFromSkImage(GrContext* ctx, |
| sk_sp<SkImage> image, |
| GrBackendTexture* backendTexture, |
| BackendTextureReleaseProc* releaseProc) { |
| if (!image || !ctx || !backendTexture || !releaseProc) { |
| return false; |
| } |
| |
| // Ensure we have a texture backed image. |
| if (!image->isTextureBacked()) { |
| image = image->makeTextureImage(ctx); |
| if (!image) { |
| return false; |
| } |
| } |
| GrTexture* texture = image->getTexture(); |
| if (!texture) { |
| // In context-loss cases, we may not have a texture. |
| return false; |
| } |
| |
| // If the image's context doesn't match the provided context, fail. |
| if (texture->getContext() != ctx) { |
| return false; |
| } |
| |
| // Flush any pending IO on the texture. |
| ctx->priv().flushSurface(as_IB(image)->peekProxy()); |
| |
| // We must make a copy of the image if the image is not unique, if the GrTexture owned by the |
| // image is not unique, or if the texture wraps an external object. |
| if (!image->unique() || !texture->unique() || |
| texture->resourcePriv().refsWrappedObjects()) { |
| // onMakeSubset will always copy the image. |
| image = as_IB(image)->onMakeSubset(ctx, image->bounds()); |
| if (!image) { |
| return false; |
| } |
| |
| texture = image->getTexture(); |
| if (!texture) { |
| return false; |
| } |
| |
| // Flush to ensure that the copy is completed before we return the texture. |
| ctx->priv().flushSurface(as_IB(image)->peekProxy()); |
| } |
| |
| SkASSERT(!texture->resourcePriv().refsWrappedObjects()); |
| SkASSERT(texture->unique()); |
| SkASSERT(image->unique()); |
| |
| // Take a reference to the GrTexture and release the image. |
| sk_sp<GrTexture> textureRef(SkSafeRef(texture)); |
| image = nullptr; |
| |
| // Steal the backend texture from the GrTexture, releasing the GrTexture in the process. |
| return GrTexture::StealBackendTexture(std::move(textureRef), backendTexture, releaseProc); |
| } |