blob: cc57d429e8855183f4cde20ca1200dbe3b5f317b [file] [log] [blame]
/*
* Copyright 2023 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/gpu/ganesh/SkImageGanesh.h"
#include "include/core/SkAlphaType.h"
#include "include/core/SkBitmap.h"
#include "include/core/SkColorSpace.h"
#include "include/core/SkData.h"
#include "include/core/SkImage.h"
#include "include/core/SkImageGenerator.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkPixmap.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkSamplingOptions.h"
#include "include/core/SkSize.h"
#include "include/gpu/GpuTypes.h"
#include "include/gpu/GrBackendSurface.h"
#include "include/gpu/GrContextThreadSafeProxy.h"
#include "include/gpu/GrDirectContext.h"
#include "include/gpu/GrRecordingContext.h"
#include "include/gpu/GrTypes.h"
#include "include/private/base/SkAssert.h"
#include "include/private/gpu/ganesh/GrImageContext.h"
#include "include/private/gpu/ganesh/GrTypesPriv.h"
#include "src/core/SkAutoPixmapStorage.h"
#include "src/core/SkImageInfoPriv.h"
#include "src/gpu/RefCntedCallback.h"
#include "src/gpu/Swizzle.h"
#include "src/gpu/ganesh/GrBackendTextureImageGenerator.h"
#include "src/gpu/ganesh/GrBackendUtils.h"
#include "src/gpu/ganesh/GrCaps.h"
#include "src/gpu/ganesh/GrContextThreadSafeProxyPriv.h"
#include "src/gpu/ganesh/GrDirectContextPriv.h"
#include "src/gpu/ganesh/GrGpu.h"
#include "src/gpu/ganesh/GrGpuResourcePriv.h"
#include "src/gpu/ganesh/GrImageContextPriv.h"
#include "src/gpu/ganesh/GrProxyProvider.h"
#include "src/gpu/ganesh/GrRecordingContextPriv.h"
#include "src/gpu/ganesh/GrSemaphore.h"
#include "src/gpu/ganesh/GrSurfaceProxy.h"
#include "src/gpu/ganesh/GrSurfaceProxyView.h"
#include "src/gpu/ganesh/GrTexture.h"
#include "src/gpu/ganesh/GrTextureProxy.h"
#include "src/gpu/ganesh/SkGr.h"
#include "src/gpu/ganesh/image/GrImageUtils.h"
#include "src/gpu/ganesh/image/SkImage_Ganesh.h"
#include "src/gpu/ganesh/image/SkImage_GaneshBase.h"
#include "src/image/SkImage_Base.h"
#include <algorithm>
#include <memory>
#include <utility>
enum SkColorType : int;
enum class SkTextureCompressionType;
namespace SkImages {
bool MakeBackendTextureFromImage(GrDirectContext* direct,
sk_sp<SkImage> image,
GrBackendTexture* backendTexture,
BackendTextureReleaseProc* releaseProc) {
if (!image || !backendTexture || !releaseProc) {
return false;
}
auto [view, ct] = skgpu::ganesh::AsView(direct, image, GrMipmapped::kNo);
if (!view) {
return false;
}
// Flush any pending IO on the texture.
direct->priv().flushSurface(view.proxy());
GrTexture* texture = view.asTextureProxy()->peekTexture();
if (!texture) {
return false;
}
// 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(image->bounds(), direct);
if (!image) {
return false;
}
return MakeBackendTextureFromImage(direct, std::move(image), backendTexture, releaseProc);
}
SkASSERT(!texture->resourcePriv().refsWrappedObjects());
SkASSERT(texture->unique());
SkASSERT(image->unique());
// Take a reference to the GrTexture and release the image.
sk_sp<GrTexture> textureRef = sk_ref_sp(texture);
view.reset();
image = nullptr;
SkASSERT(textureRef->unique());
// Steal the backend texture from the GrTexture, releasing the GrTexture in the process.
return GrTexture::StealBackendTexture(std::move(textureRef), backendTexture, releaseProc);
}
bool GetBackendTextureFromImage(sk_sp<const SkImage> img,
GrBackendTexture* outTexture,
bool flushPendingGrContextIO,
GrSurfaceOrigin* origin) {
if (!img) {
return false;
}
auto ib = as_IB(img);
if (ib->type() != SkImage_Base::Type::kGanesh) {
return false;
}
auto ig = static_cast<const SkImage_Ganesh*>(img.get());
return ig->getExistingBackendTexture(outTexture, flushPendingGrContextIO, origin);
}
sk_sp<SkImage> TextureFromCompressedTexture(GrRecordingContext* context,
const GrBackendTexture& backendTexture,
GrSurfaceOrigin origin,
SkAlphaType alphaType,
sk_sp<SkColorSpace> colorSpace,
TextureReleaseProc textureReleaseProc,
ReleaseContext releaseContext) {
auto releaseHelper = skgpu::RefCntedCallback::Make(textureReleaseProc, releaseContext);
if (!context) {
return nullptr;
}
const GrCaps* caps = context->priv().caps();
if (!SkImage_GaneshBase::ValidateCompressedBackendTexture(caps, backendTexture, alphaType)) {
return nullptr;
}
GrProxyProvider* proxyProvider = context->priv().proxyProvider();
sk_sp<GrTextureProxy> proxy =
proxyProvider->wrapCompressedBackendTexture(backendTexture,
kBorrow_GrWrapOwnership,
GrWrapCacheable::kNo,
std::move(releaseHelper));
if (!proxy) {
return nullptr;
}
SkTextureCompressionType type =
GrBackendFormatToCompressionType(backendTexture.getBackendFormat());
SkColorType ct = GrCompressionTypeToSkColorType(type);
GrSurfaceProxyView view(std::move(proxy), origin, skgpu::Swizzle::RGBA());
return sk_make_sp<SkImage_Ganesh>(sk_ref_sp(context),
kNeedNewImageUniqueID,
std::move(view),
SkColorInfo(ct, alphaType, std::move(colorSpace)));
}
static sk_sp<SkImage> new_wrapped_texture_common(GrRecordingContext* rContext,
const GrBackendTexture& backendTex,
GrColorType colorType,
GrSurfaceOrigin origin,
SkAlphaType at,
sk_sp<SkColorSpace> colorSpace,
GrWrapOwnership ownership,
sk_sp<skgpu::RefCntedCallback> releaseHelper) {
if (!backendTex.isValid() || backendTex.width() <= 0 || backendTex.height() <= 0) {
return nullptr;
}
GrProxyProvider* proxyProvider = rContext->priv().proxyProvider();
sk_sp<GrTextureProxy> proxy = proxyProvider->wrapBackendTexture(
backendTex, ownership, GrWrapCacheable::kNo, kRead_GrIOType, std::move(releaseHelper));
if (!proxy) {
return nullptr;
}
skgpu::Swizzle swizzle =
rContext->priv().caps()->getReadSwizzle(proxy->backendFormat(), colorType);
GrSurfaceProxyView view(std::move(proxy), origin, swizzle);
SkColorInfo info(GrColorTypeToSkColorType(colorType), at, std::move(colorSpace));
return sk_make_sp<SkImage_Ganesh>(
sk_ref_sp(rContext), kNeedNewImageUniqueID, std::move(view), std::move(info));
}
sk_sp<SkImage> BorrowTextureFrom(GrRecordingContext* context,
const GrBackendTexture& backendTexture,
GrSurfaceOrigin origin,
SkColorType colorType,
SkAlphaType alphaType,
sk_sp<SkColorSpace> colorSpace,
TextureReleaseProc textureReleaseProc,
ReleaseContext releaseContext) {
auto releaseHelper = skgpu::RefCntedCallback::Make(textureReleaseProc, releaseContext);
if (!context) {
return nullptr;
}
const GrCaps* caps = context->priv().caps();
GrColorType grColorType = SkColorTypeToGrColorType(colorType);
if (GrColorType::kUnknown == grColorType) {
return nullptr;
}
if (!SkImage_GaneshBase::ValidateBackendTexture(
caps, backendTexture, grColorType, colorType, alphaType, colorSpace)) {
return nullptr;
}
return new_wrapped_texture_common(context,
backendTexture,
grColorType,
origin,
alphaType,
std::move(colorSpace),
kBorrow_GrWrapOwnership,
std::move(releaseHelper));
}
sk_sp<SkImage> AdoptTextureFrom(GrRecordingContext* context,
const GrBackendTexture& backendTexture,
GrSurfaceOrigin textureOrigin,
SkColorType colorType) {
return AdoptTextureFrom(
context, backendTexture, textureOrigin, colorType, kPremul_SkAlphaType, nullptr);
}
sk_sp<SkImage> AdoptTextureFrom(GrRecordingContext* context,
const GrBackendTexture& backendTexture,
GrSurfaceOrigin textureOrigin,
SkColorType colorType,
SkAlphaType alphaType) {
return AdoptTextureFrom(context, backendTexture, textureOrigin, colorType, alphaType, nullptr);
}
sk_sp<SkImage> AdoptTextureFrom(GrRecordingContext* context,
const GrBackendTexture& backendTexture,
GrSurfaceOrigin origin,
SkColorType colorType,
SkAlphaType alphaType,
sk_sp<SkColorSpace> colorSpace) {
auto dContext = GrAsDirectContext(context);
if (!dContext) {
// We have a DDL context and we don't support adopted textures for them.
return nullptr;
}
const GrCaps* caps = dContext->priv().caps();
GrColorType grColorType = SkColorTypeToGrColorType(colorType);
if (GrColorType::kUnknown == grColorType) {
return nullptr;
}
if (!SkImage_GaneshBase::ValidateBackendTexture(
caps, backendTexture, grColorType, colorType, alphaType, colorSpace)) {
return nullptr;
}
return new_wrapped_texture_common(dContext,
backendTexture,
grColorType,
origin,
alphaType,
std::move(colorSpace),
kAdopt_GrWrapOwnership,
nullptr);
}
sk_sp<SkImage> TextureFromCompressedTextureData(GrDirectContext* direct,
sk_sp<SkData> data,
int width,
int height,
SkTextureCompressionType type,
GrMipmapped mipmapped,
GrProtected isProtected) {
if (!direct || !data) {
return nullptr;
}
GrBackendFormat beFormat = direct->compressedBackendFormat(type);
if (!beFormat.isValid()) {
sk_sp<SkImage> tmp = RasterFromCompressedTextureData(std::move(data), width, height, type);
if (!tmp) {
return nullptr;
}
return TextureFromImage(direct, tmp, mipmapped);
}
GrProxyProvider* proxyProvider = direct->priv().proxyProvider();
sk_sp<GrTextureProxy> proxy = proxyProvider->createCompressedTextureProxy(
{width, height}, skgpu::Budgeted::kYes, mipmapped, isProtected, type, std::move(data));
if (!proxy) {
return nullptr;
}
GrSurfaceProxyView view(std::move(proxy));
SkColorType colorType = GrCompressionTypeToSkColorType(type);
return sk_make_sp<SkImage_Ganesh>(sk_ref_sp(direct),
kNeedNewImageUniqueID,
std::move(view),
SkColorInfo(colorType, kOpaque_SkAlphaType, nullptr));
}
sk_sp<SkImage> PromiseTextureFrom(sk_sp<GrContextThreadSafeProxy> threadSafeProxy,
const GrBackendFormat& backendFormat,
SkISize dimensions,
GrMipmapped mipmapped,
GrSurfaceOrigin origin,
SkColorType colorType,
SkAlphaType alphaType,
sk_sp<SkColorSpace> colorSpace,
PromiseImageTextureFulfillProc textureFulfillProc,
PromiseImageTextureReleaseProc textureReleaseProc,
PromiseImageTextureContext textureContext) {
// Our contract is that we will always call the release proc even on failure.
// We use the helper to convey the context, so we need to ensure make doesn't fail.
textureReleaseProc = textureReleaseProc ? textureReleaseProc : [](void*) {};
auto releaseHelper = skgpu::RefCntedCallback::Make(textureReleaseProc, textureContext);
SkImageInfo info = SkImageInfo::Make(dimensions, colorType, alphaType, colorSpace);
if (!SkImageInfoIsValid(info)) {
return nullptr;
}
if (!threadSafeProxy) {
return nullptr;
}
if (dimensions.isEmpty()) {
return nullptr;
}
GrColorType grColorType = SkColorTypeToGrColorType(colorType);
if (GrColorType::kUnknown == grColorType) {
return nullptr;
}
if (!threadSafeProxy->priv().caps()->areColorTypeAndFormatCompatible(grColorType,
backendFormat)) {
return nullptr;
}
auto proxy = SkImage_GaneshBase::MakePromiseImageLazyProxy(threadSafeProxy.get(),
dimensions,
backendFormat,
mipmapped,
textureFulfillProc,
std::move(releaseHelper));
if (!proxy) {
return nullptr;
}
skgpu::Swizzle swizzle =
threadSafeProxy->priv().caps()->getReadSwizzle(backendFormat, grColorType);
GrSurfaceProxyView view(std::move(proxy), origin, swizzle);
sk_sp<GrImageContext> ctx(GrImageContextPriv::MakeForPromiseImage(std::move(threadSafeProxy)));
return sk_make_sp<SkImage_Ganesh>(std::move(ctx),
kNeedNewImageUniqueID,
std::move(view),
SkColorInfo(colorType, alphaType, std::move(colorSpace)));
}
sk_sp<SkImage> CrossContextTextureFromPixmap(GrDirectContext* dContext,
const SkPixmap& originalPixmap,
bool buildMips,
bool limitToMaxTextureSize) {
// Some backends or drivers don't support (safely) moving resources between contexts
if (!dContext || !dContext->priv().caps()->crossContextTextureSupport()) {
return RasterFromPixmapCopy(originalPixmap);
}
// If non-power-of-two mipmapping isn't supported, ignore the client's request
if (!dContext->priv().caps()->mipmapSupport()) {
buildMips = false;
}
const SkPixmap* pixmap = &originalPixmap;
SkAutoPixmapStorage resized;
int maxTextureSize = dContext->priv().caps()->maxTextureSize();
int maxDim = std::max(originalPixmap.width(), originalPixmap.height());
if (limitToMaxTextureSize && maxDim > maxTextureSize) {
float scale = static_cast<float>(maxTextureSize) / maxDim;
int newWidth = std::min(static_cast<int>(originalPixmap.width() * scale), maxTextureSize);
int newHeight = std::min(static_cast<int>(originalPixmap.height() * scale), maxTextureSize);
SkImageInfo info = originalPixmap.info().makeWH(newWidth, newHeight);
SkSamplingOptions sampling(SkFilterMode::kLinear);
if (!resized.tryAlloc(info) || !originalPixmap.scalePixels(resized, sampling)) {
return nullptr;
}
pixmap = &resized;
}
// Turn the pixmap into a GrTextureProxy
SkBitmap bmp;
bmp.installPixels(*pixmap);
GrMipmapped mipmapped = buildMips ? GrMipmapped::kYes : GrMipmapped::kNo;
auto [view, ct] = GrMakeUncachedBitmapProxyView(dContext, bmp, mipmapped);
if (!view) {
return RasterFromPixmapCopy(*pixmap);
}
sk_sp<GrTexture> texture = sk_ref_sp(view.proxy()->peekTexture());
// Flush any writes or uploads
dContext->priv().flushSurface(view.proxy());
GrGpu* gpu = dContext->priv().getGpu();
std::unique_ptr<GrSemaphore> sema = gpu->prepareTextureForCrossContextUsage(texture.get());
SkColorType skCT = GrColorTypeToSkColorType(ct);
auto gen = GrBackendTextureImageGenerator::Make(std::move(texture),
view.origin(),
std::move(sema),
skCT,
pixmap->alphaType(),
pixmap->info().refColorSpace());
return DeferredFromGenerator(std::move(gen));
}
sk_sp<SkImage> TextureFromImage(GrDirectContext* dContext,
const SkImage* img,
skgpu::Mipmapped mipmapped,
skgpu::Budgeted budgeted) {
if (!dContext || !img) {
return nullptr;
}
auto ib = as_IB(img);
if (!dContext->priv().caps()->mipmapSupport() || ib->dimensions().area() <= 1) {
mipmapped = GrMipmapped::kNo;
}
if (ib->isGaneshBacked()) {
if (!ib->context()->priv().matches(dContext)) {
return nullptr;
}
if (mipmapped == GrMipmapped::kNo || ib->hasMipmaps()) {
return sk_ref_sp(const_cast<SkImage_Base*>(ib));
}
}
GrImageTexGenPolicy policy = budgeted == skgpu::Budgeted::kYes
? GrImageTexGenPolicy::kNew_Uncached_Budgeted
: GrImageTexGenPolicy::kNew_Uncached_Unbudgeted;
// TODO: Don't flatten YUVA images here. Add mips to the planes instead.
auto [view, ct] = skgpu::ganesh::AsView(dContext, ib, mipmapped, policy);
if (!view) {
return nullptr;
}
SkASSERT(view.asTextureProxy());
SkASSERT(mipmapped == GrMipmapped::kNo ||
view.asTextureProxy()->mipmapped() == GrMipmapped::kYes);
SkColorInfo colorInfo(GrColorTypeToSkColorType(ct), ib->alphaType(), ib->refColorSpace());
return sk_make_sp<SkImage_Ganesh>(
sk_ref_sp(dContext), ib->uniqueID(), std::move(view), std::move(colorInfo));
}
} // namespace SkImages