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