Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
ImageFactories.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2023 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
41
42namespace SkImages {
43
44using namespace skgpu::graphite;
45
48 const SkColorInfo& info) {
49 if (!texture.isValid() || texture.dimensions().width() <= 0 ||
50 texture.dimensions().height() <= 0) {
51 return false;
52 }
53
55 return false;
56 }
57
58 if (!caps->isTexturable(texture.info())) {
59 return false;
60 }
61
62 return caps->areColorTypeAndTextureInfoCompatible(info.colorType(), texture.info());
63}
64
66 const BackendTexture& backendTex,
67 SkColorType ct,
68 SkAlphaType at,
70 skgpu::Origin origin,
71 GenerateMipmapsFromBase genMipmaps,
72 TextureReleaseProc releaseP,
73 ReleaseContext releaseC) {
74 auto releaseHelper = skgpu::RefCntedCallback::Make(releaseP, releaseC);
75
76 if (!recorder) {
77 return nullptr;
78 }
79
80 const Caps* caps = recorder->priv().caps();
81
82 SkColorInfo info(ct, at, std::move(cs));
83
84 if (!validate_backend_texture(caps, backendTex, info)) {
85 return nullptr;
86 }
87
89 if (!texture) {
90 SKGPU_LOG_W("Texture creation failed");
91 return nullptr;
92 }
93 texture->setReleaseCallback(std::move(releaseHelper));
94
96 SkASSERT(proxy);
97
98 skgpu::Swizzle swizzle = caps->getReadSwizzle(ct, backendTex.info());
99 TextureProxyView view(std::move(proxy), swizzle, origin);
100
101 if (genMipmaps == GenerateMipmapsFromBase::kYes) {
102 if (view.proxy()->mipmapped() == skgpu::Mipmapped::kNo) {
103 SKGPU_LOG_W("Failed SkImage:::WrapTexture because asked to generate mipmaps for "
104 "nonmipmapped texture");
105 return nullptr;
106 }
107 if (!GenerateMipmaps(recorder, view.refProxy(), info)) {
108 SKGPU_LOG_W("Failed SkImage::WrapTexture. Could not generate mipmaps.");
109 return nullptr;
110 }
111 }
112
113 return sk_make_sp<skgpu::graphite::Image>(view, info);
114}
115
117 const BackendTexture& backendTex,
118 SkColorType ct,
119 SkAlphaType at,
121 skgpu::Origin origin,
122 TextureReleaseProc releaseP,
123 ReleaseContext releaseC) {
124 return WrapTexture(recorder,
125 backendTex,
126 ct,
127 at,
128 std::move(cs),
129 origin,
131 releaseP,
132 releaseC);
133}
134
136 const BackendTexture& backendTex,
137 SkColorType ct,
138 SkAlphaType at,
140 TextureReleaseProc releaseP,
141 ReleaseContext releaseC) {
142 return WrapTexture(recorder,
143 backendTex,
144 ct,
145 at,
146 std::move(cs),
149 releaseP,
150 releaseC);
151}
152
154 SkISize dimensions,
155 const TextureInfo& textureInfo,
156 const SkColorInfo& colorInfo,
157 skgpu::Origin origin,
158 Volatile isVolatile,
160 GraphitePromiseImageReleaseProc imageReleaseProc,
161 GraphitePromiseTextureReleaseProc textureReleaseProc,
162 GraphitePromiseImageContext imageContext) {
163 // Our contract is that we will always call the _image_ release proc even on failure.
164 // We use the helper to convey the imageContext, so we need to ensure Make doesn't fail.
165 imageReleaseProc = imageReleaseProc ? imageReleaseProc : [](void*) {};
166 auto releaseHelper = skgpu::RefCntedCallback::Make(imageReleaseProc, imageContext);
167
168 if (!recorder) {
169 SKGPU_LOG_W("Null Recorder");
170 return nullptr;
171 }
172
173 const Caps* caps = recorder->priv().caps();
174
175 SkImageInfo info = SkImageInfo::Make(dimensions, colorInfo);
176 if (!SkImageInfoIsValid(info)) {
177 SKGPU_LOG_W("Invalid SkImageInfo");
178 return nullptr;
179 }
180
181 if (!caps->areColorTypeAndTextureInfoCompatible(colorInfo.colorType(), textureInfo)) {
182 SKGPU_LOG_W("Incompatible SkColorType and TextureInfo");
183 return nullptr;
184 }
185
186 // Non-YUVA promise images use the 'imageContext' for both the release proc and fulfill proc.
188 dimensions,
189 textureInfo,
190 isVolatile,
191 std::move(releaseHelper),
192 fulfillProc,
193 imageContext,
194 textureReleaseProc);
195 if (!proxy) {
196 return nullptr;
197 }
198
199 skgpu::Swizzle swizzle = caps->getReadSwizzle(colorInfo.colorType(), textureInfo);
200 TextureProxyView view(std::move(proxy), swizzle, origin);
201 return sk_make_sp<Image>(view, colorInfo);
202}
203
205 SkISize dimensions,
206 const TextureInfo& textureInfo,
207 const SkColorInfo& colorInfo,
208 Volatile isVolatile,
210 GraphitePromiseImageReleaseProc imageReleaseProc,
211 GraphitePromiseTextureReleaseProc textureReleaseProc,
212 GraphitePromiseImageContext imageContext) {
213 return PromiseTextureFrom(recorder,
214 dimensions,
215 textureInfo,
216 colorInfo,
218 isVolatile,
219 fulfillProc,
220 imageReleaseProc,
221 textureReleaseProc,
222 imageContext);
223}
224
226 const YUVABackendTextureInfo& backendTextureInfo,
227 sk_sp<SkColorSpace> imageColorSpace,
228 skgpu::graphite::Volatile isVolatile,
230 GraphitePromiseImageReleaseProc imageReleaseProc,
231 GraphitePromiseTextureReleaseProc textureReleaseProc,
232 GraphitePromiseImageContext imageContext,
233 GraphitePromiseTextureFulfillContext planeContexts[]) {
234 // Our contract is that we will always call the _image_ release proc even on failure.
235 // We use the helper to convey the imageContext, so we need to ensure Make doesn't fail.
236 auto releaseHelper = skgpu::RefCntedCallback::Make(imageReleaseProc, imageContext);
237 if (!recorder) {
238 return nullptr;
239 }
240 // Precompute the dimensions for all promise texture planes
241 SkISize planeDimensions[SkYUVAInfo::kMaxPlanes];
242 if (!backendTextureInfo.yuvaInfo().planeDimensions(planeDimensions)) {
243 return nullptr;
244 }
245
247 for (int i = 0; i < backendTextureInfo.numPlanes(); ++i) {
249 recorder->priv().caps(),
250 planeDimensions[i],
251 backendTextureInfo.planeTextureInfo(i),
252 isVolatile,
253 releaseHelper,
254 fulfillProc,
255 planeContexts[i],
256 textureReleaseProc);
257 // Promise YUVA images assume the default rgba swizzle.
258 planes[i] = TextureProxyView(std::move(lazyProxy));
259 }
260 return Image_YUVA::Make(recorder->priv().caps(), backendTextureInfo.yuvaInfo(),
261 SkSpan(planes), std::move(imageColorSpace));
262}
263
265 const SkImage* img,
266 const SkIRect& subset,
268 if (!recorder || !img) {
269 return nullptr;
270 }
271 auto subsetImg = img->makeSubset(recorder, subset, props);
272 return SkImages::TextureFromImage(recorder, subsetImg, props);
273}
274
276 sk_sp<SkImage> src,
277 const SkImageFilter* filter,
278 const SkIRect& subset,
279 const SkIRect& clipBounds,
280 SkIRect* outSubset,
281 SkIPoint* offset) {
282 if (!recorder || !src || !filter) {
283 return nullptr;
284 }
285
286 sk_sp<skif::Backend> backend = skif::MakeGraphiteBackend(recorder, {}, src->colorType());
287 return as_IFB(filter)->makeImageWithFilter(std::move(backend),
288 std::move(src),
289 subset,
290 clipBounds,
291 outSubset,
292 offset);
293}
294
296 const SkImage_Picture* img,
297 const SkImageInfo& info,
298 SkImage::RequiredProperties requiredProps) {
299 auto mm = requiredProps.fMipmapped ? skgpu::Mipmapped::kYes : skgpu::Mipmapped::kNo;
301 if (!surface) {
302 SKGPU_LOG_E("Failed to create Surface");
303 return nullptr;
304 }
305
306 img->replay(surface->getCanvas());
307
308 if (requiredProps.fMipmapped) {
311 static_cast<Surface*>(surface.get())->readSurfaceView().refProxy();
312 if (!GenerateMipmaps(recorder, std::move(texture), info.colorInfo())) {
313 SKGPU_LOG_W("Failed to create mipmaps for texture from SkPicture");
314 }
315 }
316
318}
319
320/*
321 * We only have 2 ways to create a Graphite-backed image.
322 *
323 * 1. Ask the generator to natively create one
324 * 2. Ask the generator to return RGB(A) data, which the GPU can convert
325 */
327 const SkImage_Lazy* img,
328 SkImage::RequiredProperties requiredProps) {
329 // 1. Ask the generator to natively create one.
330 {
332 sk_sp<SkImage> newImage =
334 static_cast<const SkImage_Picture*>(img),
335 img->imageInfo(),
336 requiredProps);
337 if (newImage) {
338 SkASSERT(as_IB(newImage)->isGraphiteBacked());
339 return newImage;
340 }
341 }
342 // There is not an analog to GrTextureGenerator for Graphite yet, but if there was,
343 // we would want to call it here.
344 }
345
346 // 2. Ask the generator to return a bitmap, which the GPU can convert.
347 {
350 return skgpu::graphite::MakeFromBitmap(recorder,
351 img->imageInfo().colorInfo(),
352 bitmap,
353 nullptr,
355 requiredProps);
356 }
357 }
358
359 return nullptr;
360}
361
363 const SkImage* image,
364 SkImage::RequiredProperties requiredProps) {
365 if (!recorder || !image) {
366 return nullptr;
367 }
368 if (image->dimensions().area() <= 1) {
369 requiredProps.fMipmapped = false;
370 }
371
372 auto ib = as_IB(image);
373 SkASSERT(!ib->isGaneshBacked());
374
375 if (ib->isRasterBacked()) {
376 auto raster = static_cast<const SkImage_Raster*>(ib);
377 return skgpu::graphite::MakeFromBitmap(recorder,
378 raster->imageInfo().colorInfo(),
379 raster->bitmap(),
380 raster->refMips(),
382 requiredProps);
383 }
384 if (ib->isLazyGenerated()) {
386 recorder, static_cast<const SkImage_Lazy*>(ib), requiredProps);
387 }
388 SkASSERT(ib->isGraphiteBacked());
389 return ib->makeSubset(recorder, ib->bounds(), requiredProps);
390}
391
393 const SkYUVAPixmaps& pixmaps,
394 SkImage::RequiredProperties requiredProps,
395 bool limitToMaxTextureSize,
396 sk_sp<SkColorSpace> imageColorSpace) {
397 if (!recorder) {
398 return nullptr;
399 }
400
401 // Determine if we have to resize the pixmaps
402 const int maxTextureSize = recorder->priv().caps()->maxTextureSize();
403 const int maxDim = std::max(pixmaps.yuvaInfo().width(), pixmaps.yuvaInfo().height());
404
405 SkYUVAPixmapInfo finalInfo = pixmaps.pixmapsInfo();
406 if (maxDim > maxTextureSize) {
407 if (!limitToMaxTextureSize) {
408 return nullptr;
409 }
410 float scale = static_cast<float>(maxTextureSize) / maxDim;
411 SkISize newDimensions = {
412 std::min(static_cast<int>(pixmaps.yuvaInfo().width() * scale), maxTextureSize),
413 std::min(static_cast<int>(pixmaps.yuvaInfo().height() * scale), maxTextureSize)};
414 finalInfo = SkYUVAPixmapInfo(pixmaps.yuvaInfo().makeDimensions(newDimensions),
415 pixmaps.dataType(),
416 /*rowBytes=*/nullptr);
417 }
418
419 auto mipmapped = requiredProps.fMipmapped ? skgpu::Mipmapped::kYes : skgpu::Mipmapped::kNo;
421 for (int i = 0; i < finalInfo.yuvaInfo().numPlanes(); ++i) {
422 SkBitmap bmp;
423 if (maxDim > maxTextureSize) {
424 // Rescale the data before uploading
425 if (!bmp.tryAllocPixels(finalInfo.planeInfo(i)) ||
426 !pixmaps.plane(i).scalePixels(bmp.pixmap(), SkFilterMode::kLinear)) {
427 return nullptr;
428 }
429 } else {
430 // Use original data to upload
431 if (!bmp.installPixels(pixmaps.plane(i))) {
432 return nullptr;
433 }
434 }
435
436 auto [view, _] = MakeBitmapProxyView(recorder, bmp, /*mipmapsIn=*/nullptr,
437 mipmapped, skgpu::Budgeted::kNo);
438 planes[i] = std::move(view);
439 }
440 return Image_YUVA::Make(recorder->priv().caps(), finalInfo.yuvaInfo(),
441 SkSpan(planes), std::move(imageColorSpace));
442}
443
445 const YUVABackendTextures& yuvaTextures,
446 sk_sp<SkColorSpace> imageColorSpace,
447 TextureReleaseProc releaseP,
448 ReleaseContext releaseC) {
449 auto releaseHelper = skgpu::RefCntedCallback::Make(releaseP, releaseC);
450 if (!recorder) {
451 return nullptr;
452 }
453
455 for (int i = 0; i < yuvaTextures.yuvaInfo().numPlanes(); ++i) {
457 yuvaTextures.planeTexture(i));
458 if (!texture) {
459 SKGPU_LOG_W("Failed to wrap backend texture for YUVA plane %d", i);
460 return nullptr;
461 }
462 texture->setReleaseCallback(releaseHelper);
463 planes[i] = TextureProxyView(TextureProxy::Wrap(std::move(texture)));
464 }
465
466 return Image_YUVA::Make(recorder->priv().caps(), yuvaTextures.yuvaInfo(),
467 SkSpan(planes), std::move(imageColorSpace));
468}
469
471 const SkYUVAInfo& yuvaInfo,
472 SkSpan<const sk_sp<SkImage>> images,
473 sk_sp<SkColorSpace> imageColorSpace) {
474 // This factory is just a view of the images, so does not actually trigger any work on the
475 // recorder. It is just used to provide the Caps.
476 return Image_YUVA::WrapImages(recorder->priv().caps(), yuvaInfo, images, imageColorSpace);
477}
478
479} // namespace SkImages
const char * backend
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
#define SKGPU_LOG_E(fmt,...)
Definition Log.h:38
#define SKGPU_LOG_W(fmt,...)
Definition Log.h:40
SkAlphaType
Definition SkAlphaType.h:26
#define SkASSERT(cond)
Definition SkAssert.h:116
SkColorType
Definition SkColorType.h:19
static SkImageFilter_Base * as_IFB(SkImageFilter *filter)
static bool SkColorInfoIsValid(const SkColorInfo &info)
static bool SkImageInfoIsValid(const SkImageInfo &info)
static SkImage_Base * as_IB(SkImage *image)
bool installPixels(const SkImageInfo &info, void *pixels, size_t rowBytes, void(*releaseProc)(void *addr, void *context), void *context)
Definition SkBitmap.cpp:323
const SkPixmap & pixmap() const
Definition SkBitmap.h:133
bool tryAllocPixels(const SkImageInfo &info, size_t rowBytes)
Definition SkBitmap.cpp:271
SkColorType colorType() const
sk_sp< SkImage > makeImageWithFilter(sk_sp< skif::Backend > backend, sk_sp< SkImage > src, const SkIRect &subset, const SkIRect &clipBounds, SkIRect *outSubset, SkIPoint *offset) const
bool getROPixels(GrDirectContext *, SkBitmap *, CachingHint) const override
SkImage_Base::Type type() const override
const SkSurfaceProps * props() const
void replay(SkCanvas *) const
const SkImageInfo & imageInfo() const
Definition SkImage.h:279
SkISize dimensions() const
Definition SkImage.h:297
@ kDisallow_CachingHint
disallows internally caching decoded and copied pixels
Definition SkImage.h:465
virtual sk_sp< SkImage > makeSubset(GrDirectContext *direct, const SkIRect &subset) const =0
bool scalePixels(const SkPixmap &dst, const SkSamplingOptions &) const
int width() const
Definition SkYUVAInfo.h:172
static constexpr int kMaxPlanes
Definition SkYUVAInfo.h:98
SkYUVAInfo makeDimensions(SkISize) const
int height() const
Definition SkYUVAInfo.h:173
int planeDimensions(SkISize planeDimensions[kMaxPlanes]) const
Definition SkYUVAInfo.h:192
const SkYUVAInfo & yuvaInfo() const
const SkImageInfo & planeInfo(int i) const
DataType dataType() const
const SkYUVAInfo & yuvaInfo() const
SkYUVAPixmapInfo pixmapsInfo() const
const SkPixmap & plane(int i) const
static sk_sp< RefCntedCallback > Make(Callback proc, Context ctx)
const TextureInfo & info() const
skgpu::Swizzle getReadSwizzle(SkColorType, const TextureInfo &) const
Definition Caps.cpp:128
bool isTexturable(const TextureInfo &) const
Definition Caps.cpp:65
bool areColorTypeAndTextureInfoCompatible(SkColorType, const TextureInfo &) const
Definition Caps.cpp:84
int maxTextureSize() const
Definition Caps.h:134
static sk_sp< Image_YUVA > Make(const Caps *caps, const SkYUVAInfo &yuvaInfo, SkSpan< TextureProxyView > planes, sk_sp< SkColorSpace > imageColorSpace)
static sk_sp< Image_YUVA > WrapImages(const Caps *caps, const SkYUVAInfo &yuvaInfo, SkSpan< const sk_sp< SkImage > > images, sk_sp< SkColorSpace > imageColorSpace)
const Caps * caps() const
ResourceProvider * resourceProvider()
virtual sk_sp< Texture > createWrappedTexture(const BackendTexture &)=0
sk_sp< TextureProxy > refProxy() const
static sk_sp< TextureProxy > Wrap(sk_sp< Texture >)
Mipmapped mipmapped() const
const TextureInfo & planeTextureInfo(int i) const
BackendTexture planeTexture(int i) const
VkSurfaceKHR surface
Definition main.cc:49
sk_sp< SkImage > image
Definition examples.cpp:29
FlTexture * texture
std::array< MockImage, 3 > images
void * GraphitePromiseTextureFulfillContext
Definition Image.h:39
SK_API sk_sp< SkImage > TextureFromYUVAPixmaps(GrRecordingContext *context, const SkYUVAPixmaps &pixmaps, skgpu::Mipmapped buildMips, bool limitToMaxTextureSize, sk_sp< SkColorSpace > imageColorSpace)
void(*)(GraphitePromiseImageContext) GraphitePromiseImageReleaseProc
Definition Image.h:46
void * GraphitePromiseImageContext
Definition Image.h:36
static bool validate_backend_texture(const skgpu::graphite::Caps *caps, const skgpu::graphite::BackendTexture &texture, const SkColorInfo &info)
SK_API sk_sp< SkImage > TextureFromImage(GrDirectContext *, const SkImage *, skgpu::Mipmapped=skgpu::Mipmapped::kNo, skgpu::Budgeted=skgpu::Budgeted::kYes)
SK_API sk_sp< SkImage > SubsetTextureFrom(GrDirectContext *context, const SkImage *img, const SkIRect &subset)
std::tuple< skgpu::graphite::BackendTexture, GraphitePromiseTextureReleaseContext >(*)(GraphitePromiseTextureFulfillContext) GraphitePromiseTextureFulfillProc
Definition Image.h:45
SK_API sk_sp< SkImage > PromiseTextureFromYUVA(skgpu::graphite::Recorder *, const skgpu::graphite::YUVABackendTextureInfo &, sk_sp< SkColorSpace > imageColorSpace, skgpu::graphite::Volatile, GraphitePromiseTextureFulfillProc, GraphitePromiseImageReleaseProc, GraphitePromiseTextureReleaseProc, GraphitePromiseImageContext imageContext, GraphitePromiseTextureFulfillContext planeContexts[])
SK_API sk_sp< SkImage > TextureFromYUVATextures(GrRecordingContext *context, const GrYUVABackendTextures &yuvaTextures, sk_sp< SkColorSpace > imageColorSpace, TextureReleaseProc textureReleaseProc=nullptr, ReleaseContext releaseContext=nullptr)
void * ReleaseContext
Definition SkImage.h:50
static sk_sp< SkImage > generate_picture_texture(skgpu::graphite::Recorder *recorder, const SkImage_Picture *img, const SkImageInfo &info, SkImage::RequiredProperties requiredProps)
SK_API sk_sp< SkImage > MakeWithFilter(sk_sp< SkImage > src, const SkImageFilter *filter, const SkIRect &subset, const SkIRect &clipBounds, SkIRect *outSubset, SkIPoint *offset)
SK_API sk_sp< SkImage > PromiseTextureFrom(skgpu::graphite::Recorder *, SkISize dimensions, const skgpu::graphite::TextureInfo &, const SkColorInfo &, skgpu::Origin origin, skgpu::graphite::Volatile, GraphitePromiseTextureFulfillProc, GraphitePromiseImageReleaseProc, GraphitePromiseTextureReleaseProc, GraphitePromiseImageContext)
SK_API sk_sp< SkImage > TextureFromYUVAImages(skgpu::graphite::Recorder *recorder, const SkYUVAInfo &yuvaInfo, SkSpan< const sk_sp< SkImage > > images, sk_sp< SkColorSpace > imageColorSpace)
void(*)(GraphitePromiseTextureReleaseContext) GraphitePromiseTextureReleaseProc
Definition Image.h:47
GenerateMipmapsFromBase
Definition Image.h:31
static sk_sp< SkImage > make_texture_image_from_lazy(skgpu::graphite::Recorder *recorder, const SkImage_Lazy *img, SkImage::RequiredProperties requiredProps)
void(*)(ReleaseContext) TextureReleaseProc
SK_API sk_sp< SkImage > WrapTexture(skgpu::graphite::Recorder *, const skgpu::graphite::BackendTexture &, SkColorType colorType, SkAlphaType alphaType, sk_sp< SkColorSpace > colorSpace, skgpu::Origin origin, GenerateMipmapsFromBase generateMipmapsFromBase, TextureReleaseProc=nullptr, ReleaseContext=nullptr)
SK_API sk_sp< SkImage > AsImage(sk_sp< const SkSurface >)
SK_API sk_sp< SkSurface > RenderTarget(GrRecordingContext *context, skgpu::Budgeted budgeted, const SkImageInfo &imageInfo, int sampleCount, GrSurfaceOrigin surfaceOrigin, const SkSurfaceProps *surfaceProps, bool shouldCreateWithMips=false, bool isProtected=false)
bool GenerateMipmaps(Recorder *recorder, sk_sp< TextureProxy > texture, const SkColorInfo &colorInfo)
std::tuple< TextureProxyView, SkColorType > MakeBitmapProxyView(Recorder *recorder, const SkBitmap &bitmap, sk_sp< SkMipmap > mipmapsIn, Mipmapped mipmapped, Budgeted budgeted)
void Flush(sk_sp< SkSurface > surface)
sk_sp< SkImage > MakeFromBitmap(Recorder *recorder, const SkColorInfo &colorInfo, const SkBitmap &bitmap, sk_sp< SkMipmap > mipmaps, Budgeted budgeted, SkImage::RequiredProperties requiredProps)
sk_sp< TextureProxy > MakePromiseImageLazyProxy(const Caps *caps, SkISize dimensions, TextureInfo textureInfo, Volatile isVolatile, sk_sp< RefCntedCallback > releaseHelper, GraphitePromiseTextureFulfillProc fulfillProc, GraphitePromiseTextureFulfillContext fulfillContext, GraphitePromiseTextureReleaseProc textureReleaseProc)
const Scalar scale
Point offset
constexpr int64_t area() const
Definition SkSize.h:39
const SkColorInfo & colorInfo() const
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)