2 * Copyright 2012 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
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"
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"
46 SkImage::SkImage(const SkImageInfo& info, uint32_t uniqueID)
48 , fUniqueID(kNeedNewImageUniqueID == uniqueID ? SkNextID::ImageID() : uniqueID) {
49 SkASSERT(info.width() > 0);
50 SkASSERT(info.height() > 0);
53 bool SkImage::peekPixels(SkPixmap* pm) const {
58 return as_IB(this)->onPeekPixels(pm);
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);
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);
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);
85 as_IB(this)->onAsyncRescaleAndReadPixels(
86 info, srcRect, rescaleGamma, rescaleMode, callback, context);
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);
102 as_IB(this)->onAsyncRescaleAndReadPixelsYUV420(yuvColorSpace,
103 std::move(dstColorSpace),
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);
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.
124 if (as_IB(this)->getROPixels(dContext, &bm, chint)) {
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.
130 return bm.peekPixels(&pmap) && pmap.scalePixels(dst, sampling);
135 ///////////////////////////////////////////////////////////////////////////////////////////////////
137 SkColorType SkImage::colorType() const { return fInfo.colorType(); }
139 SkAlphaType SkImage::alphaType() const { return fInfo.alphaType(); }
141 SkColorSpace* SkImage::colorSpace() const { return fInfo.colorSpace(); }
143 sk_sp<SkColorSpace> SkImage::refColorSpace() const { return fInfo.refColorSpace(); }
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);
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);
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();
163 if (as_IB(this)->getROPixels(dContext, &bm)) {
164 return SkEncodeBitmap(bm, type, quality);
169 sk_sp<SkData> SkImage::encodeToData() const {
170 if (auto encoded = this->refEncodedData()) {
174 return this->encodeToData(SkEncodedImageFormat::kPNG, 100);
177 sk_sp<SkData> SkImage::refEncodedData() const {
178 return sk_sp<SkData>(as_IB(this)->onRefEncoded());
181 sk_sp<SkImage> SkImage::MakeFromEncoded(sk_sp<SkData> encoded,
182 std::optional<SkAlphaType> alphaType) {
183 if (nullptr == encoded || 0 == encoded->size()) {
186 return SkImage::MakeFromGenerator(
187 SkImageGenerator::MakeFromEncoded(std::move(encoded), alphaType));
190 ///////////////////////////////////////////////////////////////////////////////////////////////////
192 sk_sp<SkImage> SkImage::makeSubset(const SkIRect& subset, GrDirectContext* direct) const {
193 if (subset.isEmpty()) {
197 const SkIRect bounds = SkIRect::MakeWH(this->width(), this->height());
198 if (!bounds.contains(subset)) {
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)) {
210 // optimization : return self if the subset == our bounds
211 if (bounds == subset) {
212 return sk_ref_sp(const_cast<SkImage*>(this));
215 return as_IB(this)->onMakeSubset(subset, direct);
220 bool SkImage::isTextureBacked() const {
221 return as_IB(this)->isGaneshBacked() || as_IB(this)->isGraphiteBacked();
224 size_t SkImage::textureSize() const { return as_IB(this)->onTextureSize(); }
226 GrBackendTexture SkImage::getBackendTexture(bool flushPendingGrContextIO,
227 GrSurfaceOrigin* origin) const {
228 return as_IB(this)->onGetBackendTexture(flushPendingGrContextIO, origin);
231 bool SkImage::isValid(GrRecordingContext* rContext) const {
232 if (rContext && rContext->abandoned()) {
235 return as_IB(this)->onIsValid(rContext);
238 GrSemaphoresSubmitted SkImage::flush(GrDirectContext* dContext,
239 const GrFlushInfo& flushInfo) const {
240 return as_IB(this)->onFlush(dContext, flushInfo);
243 void SkImage::flushAndSubmit(GrDirectContext* dContext) const {
244 this->flush(dContext, {});
250 bool SkImage::isTextureBacked() const { return false; }
252 bool SkImage::isValid(GrRecordingContext* rContext) const {
256 return as_IB(this)->onIsValid(nullptr);
261 ///////////////////////////////////////////////////////////////////////////////
263 SkImage_Base::SkImage_Base(const SkImageInfo& info, uint32_t uniqueID)
264 : INHERITED(info, uniqueID), fAddedToRasterCache(false) {}
266 SkImage_Base::~SkImage_Base() {
267 if (fAddedToRasterCache.load()) {
268 SkNotifyBitmapGenIDIsStale(this->uniqueID());
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 {
281 if (this->peekPixels(&peek)) {
282 src.installPixels(peek);
283 srcRect = origSrcRect;
285 // Context TODO: Elevate GrDirectContext requirement to public API.
286 auto dContext = as_IB(this)->directContext();
287 src.setInfo(this->imageInfo().makeDimensions(origSrcRect.size()));
289 if (!this->readPixels(dContext, src.pixmap(), origSrcRect.x(), origSrcRect.y())) {
290 callback(context, nullptr);
293 srcRect = SkIRect::MakeSize(src.dimensions());
295 return SkRescaleAndReadPixels(src, info, srcRect, rescaleGamma, rescaleMode, callback, context);
298 void SkImage_Base::onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace,
299 sk_sp<SkColorSpace> dstColorSpace,
300 const SkIRect& srcRect,
301 const SkISize& dstSize,
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);
312 std::tuple<GrSurfaceProxyView, GrColorType> SkImage_Base::asView(GrRecordingContext* context,
313 GrMipmapped mipmapped,
314 GrImageTexGenPolicy policy) const {
318 if (!context->priv().caps()->mipmapSupport() || this->dimensions().area() <= 1) {
319 mipmapped = GrMipmapped::kNo;
321 return this->onAsView(context, mipmapped, policy);
324 std::unique_ptr<GrFragmentProcessor> SkImage_Base::asFragmentProcessor(
325 GrRecordingContext* rContext,
326 SkSamplingOptions sampling,
327 const SkTileMode tileModes[2],
329 const SkRect* subset,
330 const SkRect* domain) const {
334 if (sampling.useCubic && !GrValidCubicResampler(sampling.cubic)) {
337 if (sampling.mipmap != SkMipmapMode::kNone &&
338 (!rContext->priv().caps()->mipmapSupport() || this->dimensions().area() <= 1)) {
339 sampling = SkSamplingOptions(sampling.filter);
341 return this->onAsFragmentProcessor(rContext, sampling, tileModes, m, subset, domain);
344 std::unique_ptr<GrFragmentProcessor> SkImage_Base::MakeFragmentProcessorFromView(
345 GrRecordingContext* rContext,
346 GrSurfaceProxyView view,
348 SkSamplingOptions sampling,
349 const SkTileMode tileModes[2],
351 const SkRect* subset,
352 const SkRect* domain) {
356 const GrCaps& caps = *rContext->priv().caps();
357 auto wmx = SkTileModeToWrapMode(tileModes[0]);
358 auto wmy = SkTileModeToWrapMode(tileModes[1]);
359 if (sampling.useCubic) {
362 return GrBicubicEffect::MakeSubset(std::move(view),
370 GrBicubicEffect::Direction::kXY,
371 *rContext->priv().caps());
373 return GrBicubicEffect::MakeSubset(std::move(view),
380 GrBicubicEffect::Direction::kXY,
381 *rContext->priv().caps());
383 return GrBicubicEffect::Make(std::move(view),
389 GrBicubicEffect::Direction::kXY,
390 *rContext->priv().caps());
392 if (sampling.isAniso()) {
393 if (!rContext->priv().caps()->anisoSupport()) {
394 // Fallback to linear
395 sampling = SkSamplingPriv::AnisoFallback(view.mipmapped() == GrMipmapped::kYes);
397 } else if (view.mipmapped() == GrMipmapped::kNo) {
398 sampling = SkSamplingOptions(sampling.filter);
400 GrSamplerState sampler;
401 if (sampling.isAniso()) {
402 sampler = GrSamplerState::Aniso(wmx, wmy, sampling.maxAniso, view.mipmapped());
404 sampler = GrSamplerState(wmx, wmy, sampling.filter, sampling.mipmap);
408 return GrTextureEffect::MakeSubset(std::move(view),
416 return GrTextureEffect::MakeSubset(std::move(view),
423 return GrTextureEffect::Make(std::move(view), at, m, sampler, caps);
427 GrSurfaceProxyView SkImage_Base::FindOrMakeCachedMipmappedView(GrRecordingContext* rContext,
428 GrSurfaceProxyView view,
429 uint32_t imageUniqueID) {
431 SkASSERT(imageUniqueID != SK_InvalidUniqueID);
433 if (!view || view.proxy()->asTextureProxy()->mipmapped() == GrMipmapped::kYes) {
436 GrProxyProvider* proxyProvider = rContext->priv().proxyProvider();
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
445 skgpu::UniqueKey::Builder b(&mipmappedKey, baseKey, kMipmappedDomain, 0);
447 SkASSERT(mipmappedKey.isValid());
448 if (sk_sp<GrTextureProxy> cachedMippedView =
449 proxyProvider->findOrCreateProxyByUniqueKey(mipmappedKey)) {
450 return {std::move(cachedMippedView), view.origin(), view.swizzle()};
453 auto copy = GrCopyBaseMipMapToView(rContext, view);
457 // TODO: If we move listeners up from SkImage_Lazy to SkImage_Base then add one here.
458 proxyProvider->assignUniqueKeyToProxy(mipmappedKey, copy.asTextureProxy());
462 GrBackendTexture SkImage_Base::onGetBackendTexture(bool flushPendingGrContextIO,
463 GrSurfaceOrigin* origin) const {
464 return GrBackendTexture(); // invalid
467 #endif // SK_SUPPORT_GPU
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 {
475 if (this->dimensions().area() <= 1) {
476 mipmapped = skgpu::graphite::Mipmapped::kNo;
478 return this->onAsView(recorder, mipmapped);
480 #endif // SK_GRAPHITE_ENABLED
482 GrDirectContext* SkImage_Base::directContext() const {
484 return GrAsDirectContext(this->context());
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,
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);
503 ///////////////////////////////////////////////////////////////////////////////////////////////////
505 sk_sp<SkImage> SkImage::MakeFromBitmap(const SkBitmap& bm) {
506 if (!bm.pixelRef()) {
510 return SkMakeImageFromRasterBitmap(bm, kIfMutable_SkCopyPixelsMode);
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);
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)) {
527 if (!this->readPixels(dContext, bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(),
533 bitmap->setImmutable();
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)));
545 sk_sp<SkImage> SkImage::makeWithFilter(GrRecordingContext* rContext, const SkImageFilter* filter,
546 const SkIRect& subset, const SkIRect& clipBounds,
547 SkIRect* outSubset, SkIPoint* offset) const {
549 if (!filter || !outSubset || !offset || !this->bounds().contains(subset)) {
552 sk_sp<SkSpecialImage> srcSpecialImage;
554 auto myContext = as_IB(this)->context();
555 if (myContext && !myContext->priv().matches(rContext)) {
558 srcSpecialImage = SkSpecialImage::MakeFromImage(rContext, subset,
559 sk_ref_sp(const_cast<SkImage*>(this)),
562 srcSpecialImage = SkSpecialImage::MakeFromImage(nullptr, subset,
563 sk_ref_sp(const_cast<SkImage*>(this)),
566 if (!srcSpecialImage) {
570 sk_sp<SkImageFilterCache> cache(
571 SkImageFilterCache::Create(SkImageFilterCache::kDefaultTransientSize));
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());
582 sk_sp<SkSpecialImage> result = as_IFB(filter)->filterImage(context).imageAndOffset(offset);
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();
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))) {
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();
609 bool SkImage::isLazyGenerated() const {
610 return as_IB(this)->onIsLazyGenerated();
613 bool SkImage::isAlphaOnly() const { return SkColorTypeIsAlphaOnly(fInfo.colorType()); }
615 sk_sp<SkImage> SkImage::makeColorSpace(sk_sp<SkColorSpace> target, GrDirectContext* direct) const {
616 return this->makeColorTypeAndColorSpace(this->colorType(), std::move(target), direct);
619 sk_sp<SkImage> SkImage::makeColorTypeAndColorSpace(SkColorType targetColorType,
620 sk_sp<SkColorSpace> targetColorSpace,
621 GrDirectContext* dContext) const {
622 if (kUnknown_SkColorType == targetColorType || !targetColorSpace) {
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)) {
634 SkColorType colorType = this->colorType();
635 SkColorSpace* colorSpace = this->colorSpace();
637 colorSpace = sk_srgb_singleton();
639 if (colorType == targetColorType &&
640 (SkColorSpace::Equals(colorSpace, targetColorSpace.get()) || this->isAlphaOnly())) {
641 return sk_ref_sp(const_cast<SkImage*>(this));
644 return as_IB(this)->onMakeColorTypeAndColorSpace(targetColorType,
645 std::move(targetColorSpace), dContext);
648 sk_sp<SkImage> SkImage::reinterpretColorSpace(sk_sp<SkColorSpace> target) const {
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();
658 colorSpace = sk_srgb_singleton();
660 if (SkColorSpace::Equals(colorSpace, target.get()) || this->isAlphaOnly()) {
661 return sk_ref_sp(const_cast<SkImage*>(this));
664 return as_IB(this)->onReinterpretColorSpace(std::move(target));
667 sk_sp<SkImage> SkImage::makeNonTextureImage() const {
668 if (!this->isTextureBacked()) {
669 return sk_ref_sp(const_cast<SkImage*>(this));
671 return this->makeRasterImage();
674 sk_sp<SkImage> SkImage::makeRasterImage(CachingHint chint) const {
676 if (this->peekPixels(&pm)) {
677 return sk_ref_sp(const_cast<SkImage*>(this));
680 const size_t rowBytes = fInfo.minRowBytes();
681 size_t size = fInfo.computeByteSize(rowBytes);
682 if (SkImageInfo::ByteSizeOverflowed(size)) {
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)) {
694 return SkImage::MakeRasterData(fInfo, std::move(data), rowBytes);
697 bool SkImage_pinAsTexture(const SkImage* image, GrRecordingContext* rContext) {
700 return as_IB(image)->onPinAsTexture(rContext);
703 void SkImage_unpinAsTexture(const SkImage* image, GrRecordingContext* rContext) {
706 as_IB(image)->onUnpinAsTexture(rContext);
709 ///////////////////////////////////////////////////////////////////////////////////////////////////
711 SkMipmapBuilder::SkMipmapBuilder(const SkImageInfo& info) {
712 fMM = sk_sp<SkMipmap>(SkMipmap::Build({info, nullptr, 0}, nullptr, false));
715 SkMipmapBuilder::~SkMipmapBuilder() {}
717 int SkMipmapBuilder::countLevels() const {
718 return fMM ? fMM->countLevels() : 0;
721 SkPixmap SkMipmapBuilder::level(int index) const {
724 SkMipmap::Level level;
725 if (fMM && fMM->getLevel(index, &level)) {
731 bool SkImage::hasMipmaps() const { return as_IB(this)->onHasMipmaps(); }
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))) {
739 return sk_ref_sp((const_cast<SkImage*>(this)));
742 sk_sp<SkImage> SkImage::withDefaultMipmaps() const {
743 return this->withMipmaps(nullptr);
746 sk_sp<SkImage> SkMipmapBuilder::attachTo(const SkImage* src) {
747 return src->withMipmaps(fMM);
750 //////////////////////////////////////////////////////////////////////////////////////////////////
752 SkSamplingOptions SkSamplingPriv::FromFQ(SkLegacyFQ fq, SkMediumAs behavior) {
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:
765 return SkSamplingOptions(SkFilterMode::kNearest, SkMipmapMode::kNone);