1 /*
2 * Copyright 2012 Google Inc.
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
8 #include "include/core/SkImage.h"
9
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkColorSpace.h"
12 #include "include/core/SkColorType.h"
13 #include "include/core/SkData.h"
14 #include "include/core/SkEncodedImageFormat.h"
15 #include "include/core/SkImageEncoder.h"
16 #include "include/core/SkImageGenerator.h"
17 #include "include/core/SkMatrix.h"
18 #include "include/core/SkPicture.h"
19 #include "include/core/SkPixmap.h"
20 #include "include/core/SkPoint.h"
21 #include "include/core/SkSamplingOptions.h"
22 #include "include/core/SkSurfaceProps.h"
23 #include "include/core/SkTileMode.h"
24 #include "include/core/SkTypes.h"
25 #include "src/core/SkBitmapCache.h"
26 #include "src/core/SkColorSpacePriv.h"
27 #include "src/core/SkImageFilterCache.h"
28 #include "src/core/SkImageFilterTypes.h"
29 #include "src/core/SkImageFilter_Base.h"
30 #include "src/core/SkImageInfoPriv.h"
31 #include "src/core/SkImagePriv.h"
32 #include "src/core/SkMipmap.h"
33 #include "src/core/SkMipmapBuilder.h"
34 #include "src/core/SkNextID.h"
35 #include "src/core/SkSamplingPriv.h"
36 #include "src/core/SkSpecialImage.h"
37 #include "src/image/SkImage_Base.h"
38 #include "src/image/SkRescaleAndReadPixels.h"
39 #include "src/shaders/SkImageShader.h"
40
41 #if defined(SK_GANESH)
42 #include "include/gpu/GrBackendSurface.h"
43 #include "include/gpu/GrDirectContext.h"
44 #include "include/gpu/GrRecordingContext.h"
45 #include "include/private/gpu/ganesh/GrImageContext.h"
46 #include "src/gpu/ResourceKey.h"
47 #include "src/gpu/SkBackingFit.h"
48 #include "src/gpu/ganesh/GrCaps.h"
49 #include "src/gpu/ganesh/GrFragmentProcessor.h"
50 #include "src/gpu/ganesh/GrImageContextPriv.h"
51 #include "src/gpu/ganesh/GrProxyProvider.h"
52 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
53 #include "src/gpu/ganesh/GrSamplerState.h"
54 #include "src/gpu/ganesh/GrSurfaceProxy.h"
55 #include "src/gpu/ganesh/GrSurfaceProxyView.h"
56 #include "src/gpu/ganesh/GrTextureProxy.h"
57 #include "src/gpu/ganesh/SkGr.h"
58 #include "src/gpu/ganesh/effects/GrBicubicEffect.h"
59 #include "src/gpu/ganesh/effects/GrTextureEffect.h"
60 enum class GrColorType;
61 #endif
62
63 #if defined(SK_GRAPHITE)
64 #include "src/gpu/graphite/Image_Graphite.h"
65 #include "src/gpu/graphite/Log.h"
66 #endif
67
68 #include <atomic>
69 #include <string_view>
70 #include <tuple>
71 #include <utility>
72
73 class SkShader;
74
SkImage(const SkImageInfo & info,uint32_t uniqueID)75 SkImage::SkImage(const SkImageInfo& info, uint32_t uniqueID)
76 : fInfo(info)
77 , fUniqueID(kNeedNewImageUniqueID == uniqueID ? SkNextID::ImageID() : uniqueID) {
78 SkASSERT(info.width() > 0);
79 SkASSERT(info.height() > 0);
80 }
81
peekPixels(SkPixmap * pm) const82 bool SkImage::peekPixels(SkPixmap* pm) const {
83 SkPixmap tmp;
84 if (!pm) {
85 pm = &tmp;
86 }
87 return as_IB(this)->onPeekPixels(pm);
88 }
89
readPixels(GrDirectContext * dContext,const SkImageInfo & dstInfo,void * dstPixels,size_t dstRowBytes,int srcX,int srcY,CachingHint chint) const90 bool SkImage::readPixels(GrDirectContext* dContext, const SkImageInfo& dstInfo, void* dstPixels,
91 size_t dstRowBytes, int srcX, int srcY, CachingHint chint) const {
92 return as_IB(this)->onReadPixels(dContext, dstInfo, dstPixels, dstRowBytes, srcX, srcY, chint);
93 }
94
95 #ifndef SK_IMAGE_READ_PIXELS_DISABLE_LEGACY_API
readPixels(const SkImageInfo & dstInfo,void * dstPixels,size_t dstRowBytes,int srcX,int srcY,CachingHint chint) const96 bool SkImage::readPixels(const SkImageInfo& dstInfo, void* dstPixels,
97 size_t dstRowBytes, int srcX, int srcY, CachingHint chint) const {
98 auto dContext = as_IB(this)->directContext();
99 return this->readPixels(dContext, dstInfo, dstPixels, dstRowBytes, srcX, srcY, chint);
100 }
101 #endif
102
asyncRescaleAndReadPixels(const SkImageInfo & info,const SkIRect & srcRect,RescaleGamma rescaleGamma,RescaleMode rescaleMode,ReadPixelsCallback callback,ReadPixelsContext context) const103 void SkImage::asyncRescaleAndReadPixels(const SkImageInfo& info,
104 const SkIRect& srcRect,
105 RescaleGamma rescaleGamma,
106 RescaleMode rescaleMode,
107 ReadPixelsCallback callback,
108 ReadPixelsContext context) const {
109 if (!SkIRect::MakeWH(this->width(), this->height()).contains(srcRect) ||
110 !SkImageInfoIsValid(info)) {
111 callback(context, nullptr);
112 return;
113 }
114 as_IB(this)->onAsyncRescaleAndReadPixels(
115 info, srcRect, rescaleGamma, rescaleMode, callback, context);
116 }
117
asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,sk_sp<SkColorSpace> dstColorSpace,const SkIRect & srcRect,const SkISize & dstSize,RescaleGamma rescaleGamma,RescaleMode rescaleMode,ReadPixelsCallback callback,ReadPixelsContext context) const118 void SkImage::asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
119 sk_sp<SkColorSpace> dstColorSpace,
120 const SkIRect& srcRect,
121 const SkISize& dstSize,
122 RescaleGamma rescaleGamma,
123 RescaleMode rescaleMode,
124 ReadPixelsCallback callback,
125 ReadPixelsContext context) const {
126 if (!SkIRect::MakeWH(this->width(), this->height()).contains(srcRect) || dstSize.isZero() ||
127 (dstSize.width() & 0b1) || (dstSize.height() & 0b1)) {
128 callback(context, nullptr);
129 return;
130 }
131 as_IB(this)->onAsyncRescaleAndReadPixelsYUV420(yuvColorSpace,
132 std::move(dstColorSpace),
133 srcRect,
134 dstSize,
135 rescaleGamma,
136 rescaleMode,
137 callback,
138 context);
139 }
140
scalePixels(const SkPixmap & dst,const SkSamplingOptions & sampling,CachingHint chint) const141 bool SkImage::scalePixels(const SkPixmap& dst, const SkSamplingOptions& sampling,
142 CachingHint chint) const {
143 // Context TODO: Elevate GrDirectContext requirement to public API.
144 auto dContext = as_IB(this)->directContext();
145 if (this->width() == dst.width() && this->height() == dst.height()) {
146 return this->readPixels(dContext, dst, 0, 0, chint);
147 }
148
149 // Idea: If/when SkImageGenerator supports a native-scaling API (where the generator itself
150 // can scale more efficiently) we should take advantage of it here.
151 //
152 SkBitmap bm;
153 if (as_IB(this)->getROPixels(dContext, &bm, chint)) {
154 SkPixmap pmap;
155 // Note: By calling the pixmap scaler, we never cache the final result, so the chint
156 // is (currently) only being applied to the getROPixels. If we get a request to
157 // also attempt to cache the final (scaled) result, we would add that logic here.
158 //
159 return bm.peekPixels(&pmap) && pmap.scalePixels(dst, sampling);
160 }
161 return false;
162 }
163
164 ///////////////////////////////////////////////////////////////////////////////////////////////////
165
colorType() const166 SkColorType SkImage::colorType() const { return fInfo.colorType(); }
167
alphaType() const168 SkAlphaType SkImage::alphaType() const { return fInfo.alphaType(); }
169
colorSpace() const170 SkColorSpace* SkImage::colorSpace() const { return fInfo.colorSpace(); }
171
refColorSpace() const172 sk_sp<SkColorSpace> SkImage::refColorSpace() const { return fInfo.refColorSpace(); }
173
makeShader(const SkSamplingOptions & sampling,const SkMatrix & lm) const174 sk_sp<SkShader> SkImage::makeShader(const SkSamplingOptions& sampling, const SkMatrix& lm) const {
175 return SkImageShader::Make(sk_ref_sp(const_cast<SkImage*>(this)),
176 SkTileMode::kClamp, SkTileMode::kClamp,
177 sampling, &lm);
178 }
179
makeShader(const SkSamplingOptions & sampling,const SkMatrix * lm) const180 sk_sp<SkShader> SkImage::makeShader(const SkSamplingOptions& sampling, const SkMatrix* lm) const {
181 return SkImageShader::Make(sk_ref_sp(const_cast<SkImage*>(this)),
182 SkTileMode::kClamp, SkTileMode::kClamp,
183 sampling, lm);
184 }
185
makeShader(SkTileMode tmx,SkTileMode tmy,const SkSamplingOptions & sampling,const SkMatrix & lm) const186 sk_sp<SkShader> SkImage::makeShader(SkTileMode tmx, SkTileMode tmy,
187 const SkSamplingOptions& sampling,
188 const SkMatrix& lm) const {
189 return SkImageShader::Make(sk_ref_sp(const_cast<SkImage*>(this)), tmx, tmy,
190 sampling, &lm);
191 }
192
makeShader(SkTileMode tmx,SkTileMode tmy,const SkSamplingOptions & sampling,const SkMatrix * localMatrix) const193 sk_sp<SkShader> SkImage::makeShader(SkTileMode tmx, SkTileMode tmy,
194 const SkSamplingOptions& sampling,
195 const SkMatrix* localMatrix) const {
196 return SkImageShader::Make(sk_ref_sp(const_cast<SkImage*>(this)), tmx, tmy,
197 sampling, localMatrix);
198 }
199
makeRawShader(SkTileMode tmx,SkTileMode tmy,const SkSamplingOptions & sampling,const SkMatrix & lm) const200 sk_sp<SkShader> SkImage::makeRawShader(SkTileMode tmx, SkTileMode tmy,
201 const SkSamplingOptions& sampling,
202 const SkMatrix& lm) const {
203 return SkImageShader::MakeRaw(sk_ref_sp(const_cast<SkImage*>(this)), tmx, tmy,
204 sampling, &lm);
205 }
206
makeRawShader(const SkSamplingOptions & sampling,const SkMatrix & lm) const207 sk_sp<SkShader> SkImage::makeRawShader(const SkSamplingOptions& sampling,
208 const SkMatrix& lm) const {
209 return SkImageShader::MakeRaw(sk_ref_sp(const_cast<SkImage*>(this)),
210 SkTileMode::kClamp, SkTileMode::kClamp,
211 sampling, &lm);
212 }
213
makeRawShader(const SkSamplingOptions & sampling,const SkMatrix * localMatrix) const214 sk_sp<SkShader> SkImage::makeRawShader(const SkSamplingOptions& sampling,
215 const SkMatrix* localMatrix) const {
216 return SkImageShader::MakeRaw(sk_ref_sp(const_cast<SkImage*>(this)),
217 SkTileMode::kClamp, SkTileMode::kClamp,
218 sampling, localMatrix);
219 }
220
makeRawShader(SkTileMode tmx,SkTileMode tmy,const SkSamplingOptions & sampling,const SkMatrix * localMatrix) const221 sk_sp<SkShader> SkImage::makeRawShader(SkTileMode tmx, SkTileMode tmy,
222 const SkSamplingOptions& sampling,
223 const SkMatrix* localMatrix) const {
224 return SkImageShader::MakeRaw(sk_ref_sp(const_cast<SkImage*>(this)), tmx, tmy,
225 sampling, localMatrix);
226 }
227
encodeToData(SkEncodedImageFormat type,int quality) const228 sk_sp<SkData> SkImage::encodeToData(SkEncodedImageFormat type, int quality) const {
229 // Context TODO: Elevate GrDirectContext requirement to public API.
230 auto dContext = as_IB(this)->directContext();
231 SkBitmap bm;
232 if (as_IB(this)->getROPixels(dContext, &bm)) {
233 return SkEncodeBitmap(bm, type, quality);
234 }
235 return nullptr;
236 }
237
encodeToData() const238 sk_sp<SkData> SkImage::encodeToData() const {
239 if (auto encoded = this->refEncodedData()) {
240 return encoded;
241 }
242
243 return this->encodeToData(SkEncodedImageFormat::kPNG, 100);
244 }
245
refEncodedData() const246 sk_sp<SkData> SkImage::refEncodedData() const {
247 return sk_sp<SkData>(as_IB(this)->onRefEncoded());
248 }
249
MakeFromEncoded(sk_sp<SkData> encoded,std::optional<SkAlphaType> alphaType)250 sk_sp<SkImage> SkImage::MakeFromEncoded(sk_sp<SkData> encoded,
251 std::optional<SkAlphaType> alphaType) {
252 if (nullptr == encoded || 0 == encoded->size()) {
253 return nullptr;
254 }
255 return SkImage::MakeFromGenerator(
256 SkImageGenerator::MakeFromEncoded(std::move(encoded), alphaType));
257 }
258
259 ///////////////////////////////////////////////////////////////////////////////////////////////////
260
makeSubset(const SkIRect & subset,GrDirectContext * direct) const261 sk_sp<SkImage> SkImage::makeSubset(const SkIRect& subset, GrDirectContext* direct) const {
262 if (subset.isEmpty()) {
263 return nullptr;
264 }
265
266 const SkIRect bounds = SkIRect::MakeWH(this->width(), this->height());
267 if (!bounds.contains(subset)) {
268 return nullptr;
269 }
270
271 #if defined(SK_GANESH)
272 auto myContext = as_IB(this)->context();
273 // This check is also performed in the subclass, but we do it here for the short-circuit below.
274 if (myContext && !myContext->priv().matches(direct)) {
275 return nullptr;
276 }
277 #endif
278
279 // optimization : return self if the subset == our bounds
280 if (bounds == subset) {
281 return sk_ref_sp(const_cast<SkImage*>(this));
282 }
283
284 return as_IB(this)->onMakeSubset(subset, direct);
285 }
286
287 #if defined(SK_GANESH)
288
isTextureBacked() const289 bool SkImage::isTextureBacked() const {
290 return as_IB(this)->isGaneshBacked() || as_IB(this)->isGraphiteBacked();
291 }
292
textureSize() const293 size_t SkImage::textureSize() const { return as_IB(this)->onTextureSize(); }
294
getBackendTexture(bool flushPendingGrContextIO,GrSurfaceOrigin * origin) const295 GrBackendTexture SkImage::getBackendTexture(bool flushPendingGrContextIO,
296 GrSurfaceOrigin* origin) const {
297 return as_IB(this)->onGetBackendTexture(flushPendingGrContextIO, origin);
298 }
299
isValid(GrRecordingContext * rContext) const300 bool SkImage::isValid(GrRecordingContext* rContext) const {
301 if (rContext && rContext->abandoned()) {
302 return false;
303 }
304 return as_IB(this)->onIsValid(rContext);
305 }
306
flush(GrDirectContext * dContext,const GrFlushInfo & flushInfo) const307 GrSemaphoresSubmitted SkImage::flush(GrDirectContext* dContext,
308 const GrFlushInfo& flushInfo) const {
309 return as_IB(this)->onFlush(dContext, flushInfo);
310 }
311
flushAndSubmit(GrDirectContext * dContext) const312 void SkImage::flushAndSubmit(GrDirectContext* dContext) const {
313 this->flush(dContext, {});
314 dContext->submit();
315 }
316
317 #else
318
isTextureBacked() const319 bool SkImage::isTextureBacked() const { return false; }
320
isValid(GrRecordingContext * rContext) const321 bool SkImage::isValid(GrRecordingContext* rContext) const {
322 if (rContext) {
323 return false;
324 }
325 return as_IB(this)->onIsValid(nullptr);
326 }
327
328 #endif
329
330 ///////////////////////////////////////////////////////////////////////////////
331
SkImage_Base(const SkImageInfo & info,uint32_t uniqueID)332 SkImage_Base::SkImage_Base(const SkImageInfo& info, uint32_t uniqueID)
333 : SkImage(info, uniqueID), fAddedToRasterCache(false) {}
334
~SkImage_Base()335 SkImage_Base::~SkImage_Base() {
336 if (fAddedToRasterCache.load()) {
337 SkNotifyBitmapGenIDIsStale(this->uniqueID());
338 }
339 }
340
onAsyncRescaleAndReadPixels(const SkImageInfo & info,SkIRect origSrcRect,RescaleGamma rescaleGamma,RescaleMode rescaleMode,ReadPixelsCallback callback,ReadPixelsContext context) const341 void SkImage_Base::onAsyncRescaleAndReadPixels(const SkImageInfo& info,
342 SkIRect origSrcRect,
343 RescaleGamma rescaleGamma,
344 RescaleMode rescaleMode,
345 ReadPixelsCallback callback,
346 ReadPixelsContext context) const {
347 SkBitmap src;
348 SkPixmap peek;
349 SkIRect srcRect;
350 if (this->peekPixels(&peek)) {
351 src.installPixels(peek);
352 srcRect = origSrcRect;
353 } else {
354 // Context TODO: Elevate GrDirectContext requirement to public API.
355 auto dContext = as_IB(this)->directContext();
356 src.setInfo(this->imageInfo().makeDimensions(origSrcRect.size()));
357 src.allocPixels();
358 if (!this->readPixels(dContext, src.pixmap(), origSrcRect.x(), origSrcRect.y())) {
359 callback(context, nullptr);
360 return;
361 }
362 srcRect = SkIRect::MakeSize(src.dimensions());
363 }
364 return SkRescaleAndReadPixels(src, info, srcRect, rescaleGamma, rescaleMode, callback, context);
365 }
366
onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace,sk_sp<SkColorSpace> dstColorSpace,SkIRect srcRect,SkISize dstSize,RescaleGamma,RescaleMode,ReadPixelsCallback callback,ReadPixelsContext context) const367 void SkImage_Base::onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace,
368 sk_sp<SkColorSpace> dstColorSpace,
369 SkIRect srcRect,
370 SkISize dstSize,
371 RescaleGamma,
372 RescaleMode,
373 ReadPixelsCallback callback,
374 ReadPixelsContext context) const {
375 // TODO: Call non-YUV asyncRescaleAndReadPixels and then make our callback convert to YUV and
376 // call client's callback.
377 callback(context, nullptr);
378 }
379
380 #if defined(SK_GANESH)
asView(GrRecordingContext * context,GrMipmapped mipmapped,GrImageTexGenPolicy policy) const381 std::tuple<GrSurfaceProxyView, GrColorType> SkImage_Base::asView(GrRecordingContext* context,
382 GrMipmapped mipmapped,
383 GrImageTexGenPolicy policy) const {
384 if (!context) {
385 return {};
386 }
387 if (!context->priv().caps()->mipmapSupport() || this->dimensions().area() <= 1) {
388 mipmapped = GrMipmapped::kNo;
389 }
390 return this->onAsView(context, mipmapped, policy);
391 }
392
asFragmentProcessor(GrRecordingContext * rContext,SkSamplingOptions sampling,const SkTileMode tileModes[2],const SkMatrix & m,const SkRect * subset,const SkRect * domain) const393 std::unique_ptr<GrFragmentProcessor> SkImage_Base::asFragmentProcessor(
394 GrRecordingContext* rContext,
395 SkSamplingOptions sampling,
396 const SkTileMode tileModes[2],
397 const SkMatrix& m,
398 const SkRect* subset,
399 const SkRect* domain) const {
400 if (!rContext) {
401 return {};
402 }
403 if (sampling.useCubic && !GrValidCubicResampler(sampling.cubic)) {
404 return {};
405 }
406 if (sampling.mipmap != SkMipmapMode::kNone &&
407 (!rContext->priv().caps()->mipmapSupport() || this->dimensions().area() <= 1)) {
408 sampling = SkSamplingOptions(sampling.filter);
409 }
410 return this->onAsFragmentProcessor(rContext, sampling, tileModes, m, subset, domain);
411 }
412
MakeFragmentProcessorFromView(GrRecordingContext * rContext,GrSurfaceProxyView view,SkAlphaType at,SkSamplingOptions sampling,const SkTileMode tileModes[2],const SkMatrix & m,const SkRect * subset,const SkRect * domain)413 std::unique_ptr<GrFragmentProcessor> SkImage_Base::MakeFragmentProcessorFromView(
414 GrRecordingContext* rContext,
415 GrSurfaceProxyView view,
416 SkAlphaType at,
417 SkSamplingOptions sampling,
418 const SkTileMode tileModes[2],
419 const SkMatrix& m,
420 const SkRect* subset,
421 const SkRect* domain) {
422 if (!view) {
423 return nullptr;
424 }
425 const GrCaps& caps = *rContext->priv().caps();
426 auto wmx = SkTileModeToWrapMode(tileModes[0]);
427 auto wmy = SkTileModeToWrapMode(tileModes[1]);
428 if (sampling.useCubic) {
429 if (subset) {
430 if (domain) {
431 return GrBicubicEffect::MakeSubset(std::move(view),
432 at,
433 m,
434 wmx,
435 wmy,
436 *subset,
437 *domain,
438 sampling.cubic,
439 GrBicubicEffect::Direction::kXY,
440 *rContext->priv().caps());
441 }
442 return GrBicubicEffect::MakeSubset(std::move(view),
443 at,
444 m,
445 wmx,
446 wmy,
447 *subset,
448 sampling.cubic,
449 GrBicubicEffect::Direction::kXY,
450 *rContext->priv().caps());
451 }
452 return GrBicubicEffect::Make(std::move(view),
453 at,
454 m,
455 wmx,
456 wmy,
457 sampling.cubic,
458 GrBicubicEffect::Direction::kXY,
459 *rContext->priv().caps());
460 }
461 if (sampling.isAniso()) {
462 if (!rContext->priv().caps()->anisoSupport()) {
463 // Fallback to linear
464 sampling = SkSamplingPriv::AnisoFallback(view.mipmapped() == GrMipmapped::kYes);
465 }
466 } else if (view.mipmapped() == GrMipmapped::kNo) {
467 sampling = SkSamplingOptions(sampling.filter);
468 }
469 GrSamplerState sampler;
470 if (sampling.isAniso()) {
471 sampler = GrSamplerState::Aniso(wmx, wmy, sampling.maxAniso, view.mipmapped());
472 } else {
473 sampler = GrSamplerState(wmx, wmy, sampling.filter, sampling.mipmap);
474 }
475 if (subset) {
476 if (domain) {
477 return GrTextureEffect::MakeSubset(std::move(view),
478 at,
479 m,
480 sampler,
481 *subset,
482 *domain,
483 caps);
484 }
485 return GrTextureEffect::MakeSubset(std::move(view),
486 at,
487 m,
488 sampler,
489 *subset,
490 caps);
491 } else {
492 return GrTextureEffect::Make(std::move(view), at, m, sampler, caps);
493 }
494 }
495
FindOrMakeCachedMipmappedView(GrRecordingContext * rContext,GrSurfaceProxyView view,uint32_t imageUniqueID)496 GrSurfaceProxyView SkImage_Base::FindOrMakeCachedMipmappedView(GrRecordingContext* rContext,
497 GrSurfaceProxyView view,
498 uint32_t imageUniqueID) {
499 SkASSERT(rContext);
500 SkASSERT(imageUniqueID != SK_InvalidUniqueID);
501
502 if (!view || view.proxy()->asTextureProxy()->mipmapped() == GrMipmapped::kYes) {
503 return view;
504 }
505 GrProxyProvider* proxyProvider = rContext->priv().proxyProvider();
506
507 skgpu::UniqueKey baseKey;
508 GrMakeKeyFromImageID(&baseKey, imageUniqueID, SkIRect::MakeSize(view.dimensions()));
509 SkASSERT(baseKey.isValid());
510 skgpu::UniqueKey mipmappedKey;
511 static const skgpu::UniqueKey::Domain kMipmappedDomain = skgpu::UniqueKey::GenerateDomain();
512 { // No extra values beyond the domain are required. Must name the var to please
513 // clang-tidy.
514 skgpu::UniqueKey::Builder b(&mipmappedKey, baseKey, kMipmappedDomain, 0);
515 }
516 SkASSERT(mipmappedKey.isValid());
517 if (sk_sp<GrTextureProxy> cachedMippedView =
518 proxyProvider->findOrCreateProxyByUniqueKey(mipmappedKey)) {
519 return {std::move(cachedMippedView), view.origin(), view.swizzle()};
520 }
521
522 auto copy = GrCopyBaseMipMapToView(rContext, view);
523 if (!copy) {
524 return view;
525 }
526 // TODO: If we move listeners up from SkImage_Lazy to SkImage_Base then add one here.
527 proxyProvider->assignUniqueKeyToProxy(mipmappedKey, copy.asTextureProxy());
528 return copy;
529 }
530
onGetBackendTexture(bool flushPendingGrContextIO,GrSurfaceOrigin * origin) const531 GrBackendTexture SkImage_Base::onGetBackendTexture(bool flushPendingGrContextIO,
532 GrSurfaceOrigin* origin) const {
533 return GrBackendTexture(); // invalid
534 }
535
CopyView(GrRecordingContext * context,GrSurfaceProxyView src,GrMipmapped mipmapped,GrImageTexGenPolicy policy,std::string_view label)536 GrSurfaceProxyView SkImage_Base::CopyView(GrRecordingContext* context,
537 GrSurfaceProxyView src,
538 GrMipmapped mipmapped,
539 GrImageTexGenPolicy policy,
540 std::string_view label) {
541 skgpu::Budgeted budgeted = policy == GrImageTexGenPolicy::kNew_Uncached_Budgeted
542 ? skgpu::Budgeted::kYes
543 : skgpu::Budgeted::kNo;
544 return GrSurfaceProxyView::Copy(context,
545 std::move(src),
546 mipmapped,
547 SkBackingFit::kExact,
548 budgeted,
549 /*label=*/label);
550 }
551
552 #endif // defined(SK_GANESH)
553
554 #if defined(SK_GRAPHITE)
asView(skgpu::graphite::Recorder * recorder,skgpu::Mipmapped mipmapped) const555 std::tuple<skgpu::graphite::TextureProxyView, SkColorType> SkImage_Base::asView(
556 skgpu::graphite::Recorder* recorder,
557 skgpu::Mipmapped mipmapped) const {
558 if (!recorder) {
559 return {};
560 }
561
562 if (!as_IB(this)->isGraphiteBacked()) {
563 return {};
564 }
565
566 auto image = reinterpret_cast<const skgpu::graphite::Image*>(this);
567
568 if (this->dimensions().area() <= 1) {
569 mipmapped = skgpu::Mipmapped::kNo;
570 }
571
572 if (mipmapped == skgpu::Mipmapped::kYes &&
573 image->textureProxyView().proxy()->mipmapped() != skgpu::Mipmapped::kYes) {
574 SKGPU_LOG_W("Graphite does not auto-generate mipmap levels");
575 return {};
576 }
577
578 SkColorType ct = this->colorType();
579 return { image->textureProxyView(), ct };
580 }
581
makeColorSpace(sk_sp<SkColorSpace> targetColorSpace,skgpu::graphite::Recorder * recorder,RequiredImageProperties requiredProps) const582 sk_sp<SkImage> SkImage::makeColorSpace(sk_sp<SkColorSpace> targetColorSpace,
583 skgpu::graphite::Recorder* recorder,
584 RequiredImageProperties requiredProps) const {
585 return this->makeColorTypeAndColorSpace(this->colorType(), std::move(targetColorSpace),
586 recorder, requiredProps);
587 }
588
makeColorTypeAndColorSpace(SkColorType targetColorType,sk_sp<SkColorSpace> targetColorSpace,skgpu::graphite::Recorder * recorder,RequiredImageProperties requiredProps) const589 sk_sp<SkImage> SkImage::makeColorTypeAndColorSpace(SkColorType targetColorType,
590 sk_sp<SkColorSpace> targetColorSpace,
591 skgpu::graphite::Recorder* recorder,
592 RequiredImageProperties requiredProps) const {
593 if (kUnknown_SkColorType == targetColorType || !targetColorSpace) {
594 return nullptr;
595 }
596
597 SkColorType colorType = this->colorType();
598 SkColorSpace* colorSpace = this->colorSpace();
599 if (!colorSpace) {
600 colorSpace = sk_srgb_singleton();
601 }
602 if (colorType == targetColorType &&
603 (SkColorSpace::Equals(colorSpace, targetColorSpace.get()) || this->isAlphaOnly())) {
604 return sk_ref_sp(const_cast<SkImage*>(this));
605 }
606
607 return as_IB(this)->onMakeColorTypeAndColorSpace(targetColorType,
608 std::move(targetColorSpace),
609 recorder,
610 requiredProps);
611 }
612
613 #endif // SK_GRAPHITE
614
directContext() const615 GrDirectContext* SkImage_Base::directContext() const {
616 #if defined(SK_GANESH)
617 return GrAsDirectContext(this->context());
618 #else
619 return nullptr;
620 #endif
621 }
622
readPixels(GrDirectContext * dContext,const SkPixmap & pmap,int srcX,int srcY,CachingHint chint) const623 bool SkImage::readPixels(GrDirectContext* dContext, const SkPixmap& pmap, int srcX, int srcY,
624 CachingHint chint) const {
625 return this->readPixels(dContext, pmap.info(), pmap.writable_addr(), pmap.rowBytes(), srcX,
626 srcY, chint);
627 }
628
629 #ifndef SK_IMAGE_READ_PIXELS_DISABLE_LEGACY_API
readPixels(const SkPixmap & pmap,int srcX,int srcY,CachingHint chint) const630 bool SkImage::readPixels(const SkPixmap& pmap, int srcX, int srcY, CachingHint chint) const {
631 auto dContext = as_IB(this)->directContext();
632 return this->readPixels(dContext, pmap, srcX, srcY, chint);
633 }
634 #endif
635
636 ///////////////////////////////////////////////////////////////////////////////////////////////////
637
MakeFromBitmap(const SkBitmap & bm)638 sk_sp<SkImage> SkImage::MakeFromBitmap(const SkBitmap& bm) {
639 if (!bm.pixelRef()) {
640 return nullptr;
641 }
642
643 return SkMakeImageFromRasterBitmap(bm, kIfMutable_SkCopyPixelsMode);
644 }
645
asLegacyBitmap(SkBitmap * bitmap,LegacyBitmapMode) const646 bool SkImage::asLegacyBitmap(SkBitmap* bitmap, LegacyBitmapMode ) const {
647 // Context TODO: Elevate GrDirectContext requirement to public API.
648 auto dContext = as_IB(this)->directContext();
649 return as_IB(this)->onAsLegacyBitmap(dContext, bitmap);
650 }
651
onAsLegacyBitmap(GrDirectContext * dContext,SkBitmap * bitmap) const652 bool SkImage_Base::onAsLegacyBitmap(GrDirectContext* dContext, SkBitmap* bitmap) const {
653 // As the base-class, all we can do is make a copy (regardless of mode).
654 // Subclasses that want to be more optimal should override.
655 SkImageInfo info = fInfo.makeColorType(kN32_SkColorType).makeColorSpace(nullptr);
656 if (!bitmap->tryAllocPixels(info)) {
657 return false;
658 }
659
660 if (!this->readPixels(dContext, bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(),
661 0, 0)) {
662 bitmap->reset();
663 return false;
664 }
665
666 bitmap->setImmutable();
667 return true;
668 }
669
MakeFromPicture(sk_sp<SkPicture> picture,const SkISize & dimensions,const SkMatrix * matrix,const SkPaint * paint,BitDepth bitDepth,sk_sp<SkColorSpace> colorSpace)670 sk_sp<SkImage> SkImage::MakeFromPicture(sk_sp<SkPicture> picture, const SkISize& dimensions,
671 const SkMatrix* matrix, const SkPaint* paint,
672 BitDepth bitDepth, sk_sp<SkColorSpace> colorSpace) {
673 return SkImage::MakeFromPicture(picture, dimensions, matrix, paint, bitDepth, colorSpace, {});
674 }
675
MakeFromPicture(sk_sp<SkPicture> picture,const SkISize & dimensions,const SkMatrix * matrix,const SkPaint * paint,BitDepth bitDepth,sk_sp<SkColorSpace> colorSpace,SkSurfaceProps props)676 sk_sp<SkImage> SkImage::MakeFromPicture(sk_sp<SkPicture> picture, const SkISize& dimensions,
677 const SkMatrix* matrix, const SkPaint* paint,
678 BitDepth bitDepth, sk_sp<SkColorSpace> colorSpace,
679 SkSurfaceProps props) {
680 return MakeFromGenerator(SkImageGenerator::MakeFromPicture(dimensions, std::move(picture),
681 matrix, paint, bitDepth,
682 std::move(colorSpace), props));
683 }
684
makeWithFilter(GrRecordingContext * rContext,const SkImageFilter * filter,const SkIRect & subset,const SkIRect & clipBounds,SkIRect * outSubset,SkIPoint * offset) const685 sk_sp<SkImage> SkImage::makeWithFilter(GrRecordingContext* rContext, const SkImageFilter* filter,
686 const SkIRect& subset, const SkIRect& clipBounds,
687 SkIRect* outSubset, SkIPoint* offset) const {
688
689 if (!filter || !outSubset || !offset || !this->bounds().contains(subset)) {
690 return nullptr;
691 }
692 sk_sp<SkSpecialImage> srcSpecialImage;
693 #if defined(SK_GANESH)
694 auto myContext = as_IB(this)->context();
695 if (myContext && !myContext->priv().matches(rContext)) {
696 return nullptr;
697 }
698 srcSpecialImage = SkSpecialImage::MakeFromImage(rContext, subset,
699 sk_ref_sp(const_cast<SkImage*>(this)),
700 SkSurfaceProps());
701 #else
702 srcSpecialImage = SkSpecialImage::MakeFromImage(nullptr, subset,
703 sk_ref_sp(const_cast<SkImage*>(this)),
704 SkSurfaceProps());
705 #endif
706 if (!srcSpecialImage) {
707 return nullptr;
708 }
709
710 sk_sp<SkImageFilterCache> cache(
711 SkImageFilterCache::Create(SkImageFilterCache::kDefaultTransientSize));
712
713 // The filters operate in the local space of the src image, where (0,0) corresponds to the
714 // subset's top left corner. But the clip bounds and any crop rects on the filters are in the
715 // original coordinate system, so configure the CTM to correct crop rects and explicitly adjust
716 // the clip bounds (since it is assumed to already be in image space).
717 SkImageFilter_Base::Context context(SkMatrix::Translate(-subset.x(), -subset.y()),
718 clipBounds.makeOffset(-subset.topLeft()),
719 cache.get(), fInfo.colorType(), fInfo.colorSpace(),
720 srcSpecialImage.get());
721
722 sk_sp<SkSpecialImage> result = as_IFB(filter)->filterImage(context).imageAndOffset(offset);
723 if (!result) {
724 return nullptr;
725 }
726
727 // The output image and offset are relative to the subset rectangle, so the offset needs to
728 // be shifted to put it in the correct spot with respect to the original coordinate system
729 offset->fX += subset.x();
730 offset->fY += subset.y();
731
732 // Final clip against the exact clipBounds (the clip provided in the context gets adjusted
733 // to account for pixel-moving filters so doesn't always exactly match when finished). The
734 // clipBounds are translated into the clippedDstRect coordinate space, including the
735 // result->subset() ensures that the result's image pixel origin does not affect results.
736 SkIRect dstRect = result->subset();
737 SkIRect clippedDstRect = dstRect;
738 if (!clippedDstRect.intersect(clipBounds.makeOffset(result->subset().topLeft() - *offset))) {
739 return nullptr;
740 }
741
742 // Adjust the geometric offset if the top-left corner moved as well
743 offset->fX += (clippedDstRect.x() - dstRect.x());
744 offset->fY += (clippedDstRect.y() - dstRect.y());
745 *outSubset = clippedDstRect;
746 return result->asImage();
747 }
748
isLazyGenerated() const749 bool SkImage::isLazyGenerated() const {
750 return as_IB(this)->onIsLazyGenerated();
751 }
752
isAlphaOnly() const753 bool SkImage::isAlphaOnly() const { return SkColorTypeIsAlphaOnly(fInfo.colorType()); }
754
makeColorSpace(sk_sp<SkColorSpace> target,GrDirectContext * direct) const755 sk_sp<SkImage> SkImage::makeColorSpace(sk_sp<SkColorSpace> target, GrDirectContext* direct) const {
756 return this->makeColorTypeAndColorSpace(this->colorType(), std::move(target), direct);
757 }
758
makeColorTypeAndColorSpace(SkColorType targetColorType,sk_sp<SkColorSpace> targetColorSpace,GrDirectContext * dContext) const759 sk_sp<SkImage> SkImage::makeColorTypeAndColorSpace(SkColorType targetColorType,
760 sk_sp<SkColorSpace> targetColorSpace,
761 GrDirectContext* dContext) const {
762 if (kUnknown_SkColorType == targetColorType || !targetColorSpace) {
763 return nullptr;
764 }
765
766 #if defined(SK_GANESH)
767 auto myContext = as_IB(this)->context();
768 // This check is also performed in the subclass, but we do it here for the short-circuit below.
769 if (myContext && !myContext->priv().matches(dContext)) {
770 return nullptr;
771 }
772 #endif
773
774 SkColorType colorType = this->colorType();
775 SkColorSpace* colorSpace = this->colorSpace();
776 if (!colorSpace) {
777 colorSpace = sk_srgb_singleton();
778 }
779 if (colorType == targetColorType &&
780 (SkColorSpace::Equals(colorSpace, targetColorSpace.get()) || this->isAlphaOnly())) {
781 return sk_ref_sp(const_cast<SkImage*>(this));
782 }
783
784 return as_IB(this)->onMakeColorTypeAndColorSpace(targetColorType,
785 std::move(targetColorSpace), dContext);
786 }
787
reinterpretColorSpace(sk_sp<SkColorSpace> target) const788 sk_sp<SkImage> SkImage::reinterpretColorSpace(sk_sp<SkColorSpace> target) const {
789 if (!target) {
790 return nullptr;
791 }
792
793 // No need to create a new image if:
794 // (1) The color spaces are equal.
795 // (2) The color type is kAlpha8.
796 SkColorSpace* colorSpace = this->colorSpace();
797 if (!colorSpace) {
798 colorSpace = sk_srgb_singleton();
799 }
800 if (SkColorSpace::Equals(colorSpace, target.get()) || this->isAlphaOnly()) {
801 return sk_ref_sp(const_cast<SkImage*>(this));
802 }
803
804 return as_IB(this)->onReinterpretColorSpace(std::move(target));
805 }
806
makeNonTextureImage() const807 sk_sp<SkImage> SkImage::makeNonTextureImage() const {
808 if (!this->isTextureBacked()) {
809 return sk_ref_sp(const_cast<SkImage*>(this));
810 }
811 return this->makeRasterImage();
812 }
813
makeRasterImage(CachingHint chint) const814 sk_sp<SkImage> SkImage::makeRasterImage(CachingHint chint) const {
815 SkPixmap pm;
816 if (this->peekPixels(&pm)) {
817 return sk_ref_sp(const_cast<SkImage*>(this));
818 }
819
820 const size_t rowBytes = fInfo.minRowBytes();
821 size_t size = fInfo.computeByteSize(rowBytes);
822 if (SkImageInfo::ByteSizeOverflowed(size)) {
823 return nullptr;
824 }
825
826 // Context TODO: Elevate GrDirectContext requirement to public API.
827 auto dContext = as_IB(this)->directContext();
828 sk_sp<SkData> data = SkData::MakeUninitialized(size);
829 pm = {fInfo.makeColorSpace(nullptr), data->writable_data(), fInfo.minRowBytes()};
830 if (!this->readPixels(dContext, pm, 0, 0, chint)) {
831 return nullptr;
832 }
833
834 return SkImage::MakeRasterData(fInfo, std::move(data), rowBytes);
835 }
836
SkImage_pinAsTexture(const SkImage * image,GrRecordingContext * rContext)837 bool SkImage_pinAsTexture(const SkImage* image, GrRecordingContext* rContext) {
838 SkASSERT(image);
839 SkASSERT(rContext);
840 return as_IB(image)->onPinAsTexture(rContext);
841 }
842
SkImage_unpinAsTexture(const SkImage * image,GrRecordingContext * rContext)843 void SkImage_unpinAsTexture(const SkImage* image, GrRecordingContext* rContext) {
844 SkASSERT(image);
845 SkASSERT(rContext);
846 as_IB(image)->onUnpinAsTexture(rContext);
847 }
848
849 ///////////////////////////////////////////////////////////////////////////////////////////////////
850
SkMipmapBuilder(const SkImageInfo & info)851 SkMipmapBuilder::SkMipmapBuilder(const SkImageInfo& info) {
852 fMM = sk_sp<SkMipmap>(SkMipmap::Build({info, nullptr, 0}, nullptr, false));
853 }
854
~SkMipmapBuilder()855 SkMipmapBuilder::~SkMipmapBuilder() {}
856
countLevels() const857 int SkMipmapBuilder::countLevels() const {
858 return fMM ? fMM->countLevels() : 0;
859 }
860
level(int index) const861 SkPixmap SkMipmapBuilder::level(int index) const {
862 SkPixmap pm;
863
864 SkMipmap::Level level;
865 if (fMM && fMM->getLevel(index, &level)) {
866 pm = level.fPixmap;
867 }
868 return pm;
869 }
870
hasMipmaps() const871 bool SkImage::hasMipmaps() const { return as_IB(this)->onHasMipmaps(); }
872
withMipmaps(sk_sp<SkMipmap> mips) const873 sk_sp<SkImage> SkImage::withMipmaps(sk_sp<SkMipmap> mips) const {
874 if (mips == nullptr || mips->validForRootLevel(this->imageInfo())) {
875 if (auto result = as_IB(this)->onMakeWithMipmaps(std::move(mips))) {
876 return result;
877 }
878 }
879 return sk_ref_sp((const_cast<SkImage*>(this)));
880 }
881
withDefaultMipmaps() const882 sk_sp<SkImage> SkImage::withDefaultMipmaps() const {
883 return this->withMipmaps(nullptr);
884 }
885
attachTo(const SkImage * src)886 sk_sp<SkImage> SkMipmapBuilder::attachTo(const SkImage* src) {
887 return src->withMipmaps(fMM);
888 }
889
890 //////////////////////////////////////////////////////////////////////////////////////////////////
891
FromFQ(SkLegacyFQ fq,SkMediumAs behavior)892 SkSamplingOptions SkSamplingPriv::FromFQ(SkLegacyFQ fq, SkMediumAs behavior) {
893 switch (fq) {
894 case kHigh_SkLegacyFQ:
895 return SkSamplingOptions(SkCubicResampler{1/3.0f, 1/3.0f});
896 case kMedium_SkLegacyFQ:
897 return SkSamplingOptions(SkFilterMode::kLinear,
898 behavior == kNearest_SkMediumAs ? SkMipmapMode::kNearest
899 : SkMipmapMode::kLinear);
900 case kLow_SkLegacyFQ:
901 return SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone);
902 case kNone_SkLegacyFQ:
903 break;
904 }
905 return SkSamplingOptions(SkFilterMode::kNearest, SkMipmapMode::kNone);
906 }
907