/* * Copyright 2023 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "src/image/SkImage_Base.h" #include "include/core/SkBitmap.h" #include "include/core/SkColorSpace.h" #include "include/core/SkColorType.h" #include "include/core/SkImage.h" #include "include/core/SkImageInfo.h" #include "include/core/SkPixmap.h" #include "include/core/SkRect.h" #include "include/core/SkSize.h" #include "include/core/SkTypes.h" #include "include/private/base/SkDebug.h" #include "src/core/SkBitmapCache.h" #include "src/core/SkColorSpacePriv.h" #include "src/image/SkRescaleAndReadPixels.h" #include #include SkImage_Base::SkImage_Base(const SkImageInfo& info, uint32_t uniqueID) : SkImage(info, uniqueID), fAddedToRasterCache(false) {} SkImage_Base::~SkImage_Base() { if (fAddedToRasterCache.load()) { SkNotifyBitmapGenIDIsStale(this->uniqueID()); } } void SkImage_Base::onAsyncRescaleAndReadPixels(const SkImageInfo& info, SkIRect origSrcRect, RescaleGamma rescaleGamma, RescaleMode rescaleMode, ReadPixelsCallback callback, ReadPixelsContext context) const { SkBitmap src; SkPixmap peek; SkIRect srcRect; if (this->peekPixels(&peek)) { src.installPixels(peek); srcRect = origSrcRect; } else { // Context TODO: Elevate GrDirectContext requirement to public API. auto dContext = as_IB(this)->directContext(); src.setInfo(this->imageInfo().makeDimensions(origSrcRect.size())); src.allocPixels(); if (!this->readPixels(dContext, src.pixmap(), origSrcRect.x(), origSrcRect.y())) { callback(context, nullptr); return; } srcRect = SkIRect::MakeSize(src.dimensions()); } return SkRescaleAndReadPixels(src, info, srcRect, rescaleGamma, rescaleMode, callback, context); } bool SkImage_Base::onAsLegacyBitmap(GrDirectContext* dContext, SkBitmap* bitmap) const { // As the base-class, all we can do is make a copy (regardless of mode). // Subclasses that want to be more optimal should override. SkImageInfo info = fInfo.makeColorType(kN32_SkColorType).makeColorSpace(nullptr); if (!bitmap->tryAllocPixels(info)) { return false; } if (!this->readPixels( dContext, bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) { bitmap->reset(); return false; } bitmap->setImmutable(); return true; } sk_sp SkImage_Base::makeSubset(GrDirectContext* direct, const SkIRect& subset) const { if (subset.isEmpty()) { return nullptr; } const SkIRect bounds = SkIRect::MakeWH(this->width(), this->height()); if (!bounds.contains(subset)) { return nullptr; } // optimization : return self if the subset == our bounds if (bounds == subset) { return sk_ref_sp(const_cast(this)); } return this->onMakeSubset(direct, subset); } sk_sp SkImage_Base::makeSubset(skgpu::graphite::Recorder* recorder, const SkIRect& subset, RequiredProperties requiredProps) const { if (subset.isEmpty()) { return nullptr; } const SkIRect bounds = SkIRect::MakeWH(this->width(), this->height()); if (!bounds.contains(subset)) { return nullptr; } return this->onMakeSubset(recorder, subset, requiredProps); } void SkImage_Base::onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace, bool readAlpha, sk_sp dstColorSpace, SkIRect srcRect, SkISize dstSize, RescaleGamma, RescaleMode, ReadPixelsCallback callback, ReadPixelsContext context) const { // TODO: Call non-YUV asyncRescaleAndReadPixels and then make our callback convert to YUV and // call client's callback. callback(context, nullptr); } sk_sp SkImage_Base::makeColorSpace(GrDirectContext* direct, sk_sp target) const { return this->makeColorTypeAndColorSpace(direct, this->colorType(), std::move(target)); } sk_sp SkImage_Base::makeColorSpace(skgpu::graphite::Recorder* recorder, sk_sp target, RequiredProperties props) const { return this->makeColorTypeAndColorSpace(recorder, this->colorType(), std::move(target), props); } sk_sp SkImage_Base::makeColorTypeAndColorSpace(GrDirectContext* dContext, SkColorType targetColorType, sk_sp targetCS) const { if (kUnknown_SkColorType == targetColorType || !targetCS) { return nullptr; } SkColorType colorType = this->colorType(); SkColorSpace* colorSpace = this->colorSpace(); if (!colorSpace) { colorSpace = sk_srgb_singleton(); } if (colorType == targetColorType && (SkColorSpace::Equals(colorSpace, targetCS.get()) || this->isAlphaOnly())) { return sk_ref_sp(const_cast(this)); } return this->onMakeColorTypeAndColorSpace(targetColorType, std::move(targetCS), dContext); } sk_sp SkImage_Base::makeColorTypeAndColorSpace(skgpu::graphite::Recorder*, SkColorType ct, sk_sp cs, RequiredProperties) const { // Default to the ganesh version which should be backend agnostic if this // image is, for example, a raster backed image. The graphite subclass overrides // this method and things work correctly. return this->makeColorTypeAndColorSpace(nullptr, ct, std::move(cs)); }