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