• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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