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 "SkBitmap.h" 9#include "SkBitmapCache.h" 10#include "SkCanvas.h" 11#include "SkData.h" 12#include "SkImageEncoder.h" 13#include "SkImageFilter.h" 14#include "SkImageFilterCache.h" 15#include "SkImageGenerator.h" 16#include "SkImagePriv.h" 17#include "SkImageShader.h" 18#include "SkImage_Base.h" 19#include "SkNextID.h" 20#include "SkPicture.h" 21#include "SkPixelRef.h" 22#include "SkReadPixelsRec.h" 23#include "SkSpecialImage.h" 24#include "SkString.h" 25#include "SkSurface.h" 26 27#if SK_SUPPORT_GPU 28#include "GrTexture.h" 29#include "GrContext.h" 30#include "SkImage_Gpu.h" 31#endif 32 33SkImage::SkImage(int width, int height, uint32_t uniqueID) 34 : fWidth(width) 35 , fHeight(height) 36 , fUniqueID(kNeedNewImageUniqueID == uniqueID ? SkNextID::ImageID() : uniqueID) 37{ 38 SkASSERT(width > 0); 39 SkASSERT(height > 0); 40} 41 42bool SkImage::peekPixels(SkPixmap* pm) const { 43 SkPixmap tmp; 44 if (!pm) { 45 pm = &tmp; 46 } 47 return as_IB(this)->onPeekPixels(pm); 48} 49 50bool SkImage::readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes, 51 int srcX, int srcY, CachingHint chint) const { 52 return as_IB(this)->onReadPixels(dstInfo, dstPixels, dstRowBytes, srcX, srcY, chint); 53} 54 55bool SkImage::scalePixels(const SkPixmap& dst, SkFilterQuality quality, CachingHint chint) const { 56 if (this->width() == dst.width() && this->height() == dst.height()) { 57 return this->readPixels(dst, 0, 0, chint); 58 } 59 60 // Idea: If/when SkImageGenerator supports a native-scaling API (where the generator itself 61 // can scale more efficiently) we should take advantage of it here. 62 // 63 SkBitmap bm; 64 if (as_IB(this)->getROPixels(&bm, dst.info().colorSpace(), chint)) { 65 SkPixmap pmap; 66 // Note: By calling the pixmap scaler, we never cache the final result, so the chint 67 // is (currently) only being applied to the getROPixels. If we get a request to 68 // also attempt to cache the final (scaled) result, we would add that logic here. 69 // 70 return bm.peekPixels(&pmap) && pmap.scalePixels(dst, quality); 71 } 72 return false; 73} 74 75/////////////////////////////////////////////////////////////////////////////////////////////////// 76 77SkAlphaType SkImage::alphaType() const { 78 return as_IB(this)->onAlphaType(); 79} 80 81SkColorSpace* SkImage::colorSpace() const { 82 return as_IB(this)->onImageInfo().colorSpace(); 83} 84 85sk_sp<SkColorSpace> SkImage::refColorSpace() const { 86 return as_IB(this)->onImageInfo().refColorSpace(); 87} 88 89sk_sp<SkShader> SkImage::makeShader(SkShader::TileMode tileX, SkShader::TileMode tileY, 90 const SkMatrix* localMatrix) const { 91 return SkImageShader::Make(sk_ref_sp(const_cast<SkImage*>(this)), tileX, tileY, localMatrix); 92} 93 94sk_sp<SkData> SkImage::encodeToData(SkEncodedImageFormat type, int quality) const { 95 SkBitmap bm; 96 SkColorSpace* legacyColorSpace = nullptr; 97 if (as_IB(this)->getROPixels(&bm, legacyColorSpace)) { 98 return SkEncodeBitmap(bm, type, quality); 99 } 100 return nullptr; 101} 102 103sk_sp<SkData> SkImage::encodeToData() const { 104 if (auto encoded = this->refEncodedData()) { 105 return encoded; 106 } 107 108 SkBitmap bm; 109 SkPixmap pmap; 110 SkColorSpace* legacyColorSpace = nullptr; 111 if (as_IB(this)->getROPixels(&bm, legacyColorSpace) && bm.peekPixels(&pmap)) { 112 return SkEncodePixmap(pmap, SkEncodedImageFormat::kPNG, 100); 113 } 114 return nullptr; 115} 116 117sk_sp<SkData> SkImage::refEncodedData() const { 118 return sk_sp<SkData>(as_IB(this)->onRefEncoded()); 119} 120 121sk_sp<SkImage> SkImage::MakeFromEncoded(sk_sp<SkData> encoded, const SkIRect* subset) { 122 if (nullptr == encoded || 0 == encoded->size()) { 123 return nullptr; 124 } 125 return SkImage::MakeFromGenerator(SkImageGenerator::MakeFromEncoded(encoded), subset); 126} 127 128/////////////////////////////////////////////////////////////////////////////////////////////////// 129 130const char* SkImage::toString(SkString* str) const { 131 str->appendf("image: (id:%d (%d, %d) %s)", this->uniqueID(), this->width(), this->height(), 132 this->isOpaque() ? "opaque" : ""); 133 return str->c_str(); 134} 135 136sk_sp<SkImage> SkImage::makeSubset(const SkIRect& subset) const { 137 if (subset.isEmpty()) { 138 return nullptr; 139 } 140 141 const SkIRect bounds = SkIRect::MakeWH(this->width(), this->height()); 142 if (!bounds.contains(subset)) { 143 return nullptr; 144 } 145 146 // optimization : return self if the subset == our bounds 147 if (bounds == subset) { 148 return sk_ref_sp(const_cast<SkImage*>(this)); 149 } 150 return as_IB(this)->onMakeSubset(subset); 151} 152 153#if SK_SUPPORT_GPU 154 155GrTexture* SkImage::getTexture() const { 156 return as_IB(this)->onGetTexture(); 157} 158 159bool SkImage::isTextureBacked() const { return SkToBool(as_IB(this)->peekProxy()); } 160 161GrBackendObject SkImage::getTextureHandle(bool flushPendingGrContextIO, 162 GrSurfaceOrigin* origin) const { 163 return as_IB(this)->onGetTextureHandle(flushPendingGrContextIO, origin); 164} 165 166bool SkImage::isValid(GrContext* context) const { 167 if (context && context->abandoned()) { 168 return false; 169 } 170 return as_IB(this)->onIsValid(context); 171} 172 173#else 174 175GrTexture* SkImage::getTexture() const { return nullptr; } 176 177bool SkImage::isTextureBacked() const { return false; } 178 179GrBackendObject SkImage::getTextureHandle(bool, GrSurfaceOrigin*) const { return 0; } 180 181bool SkImage::isValid(GrContext* context) const { 182 if (context) { 183 return false; 184 } 185 return as_IB(this)->onIsValid(context); 186} 187 188#endif 189 190/////////////////////////////////////////////////////////////////////////////// 191 192SkImage_Base::SkImage_Base(int width, int height, uint32_t uniqueID) 193 : INHERITED(width, height, uniqueID) 194 , fAddedToCache(false) 195{} 196 197SkImage_Base::~SkImage_Base() { 198 if (fAddedToCache.load()) { 199 SkNotifyBitmapGenIDIsStale(this->uniqueID()); 200 } 201} 202 203bool SkImage::readPixels(const SkPixmap& pmap, int srcX, int srcY, CachingHint chint) const { 204 return this->readPixels(pmap.info(), pmap.writable_addr(), pmap.rowBytes(), srcX, srcY, chint); 205} 206 207/////////////////////////////////////////////////////////////////////////////////////////////////// 208 209sk_sp<SkImage> SkImage::MakeFromBitmap(const SkBitmap& bm) { 210 SkPixelRef* pr = bm.pixelRef(); 211 if (nullptr == pr) { 212 return nullptr; 213 } 214 215 return SkMakeImageFromRasterBitmap(bm, kIfMutable_SkCopyPixelsMode); 216} 217 218bool SkImage::asLegacyBitmap(SkBitmap* bitmap, LegacyBitmapMode ) const { 219 return as_IB(this)->onAsLegacyBitmap(bitmap); 220} 221 222bool SkImage_Base::onAsLegacyBitmap(SkBitmap* bitmap) const { 223 // As the base-class, all we can do is make a copy (regardless of mode). 224 // Subclasses that want to be more optimal should override. 225 SkImageInfo info = this->onImageInfo().makeColorType(kN32_SkColorType).makeColorSpace(nullptr); 226 if (!bitmap->tryAllocPixels(info)) { 227 return false; 228 } 229 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) { 230 bitmap->reset(); 231 return false; 232 } 233 234 bitmap->setImmutable(); 235 return true; 236} 237 238sk_sp<SkImage> SkImage::MakeFromPicture(sk_sp<SkPicture> picture, const SkISize& dimensions, 239 const SkMatrix* matrix, const SkPaint* paint, 240 BitDepth bitDepth, sk_sp<SkColorSpace> colorSpace) { 241 return MakeFromGenerator(SkImageGenerator::MakeFromPicture(dimensions, std::move(picture), 242 matrix, paint, bitDepth, 243 std::move(colorSpace))); 244} 245 246sk_sp<SkImage> SkImage::makeWithFilter(const SkImageFilter* filter, const SkIRect& subset, 247 const SkIRect& clipBounds, SkIRect* outSubset, 248 SkIPoint* offset) const { 249 if (!filter || !outSubset || !offset || !this->bounds().contains(subset)) { 250 return nullptr; 251 } 252 SkColorSpace* colorSpace = as_IB(this)->onImageInfo().colorSpace(); 253 sk_sp<SkSpecialImage> srcSpecialImage = SkSpecialImage::MakeFromImage( 254 subset, sk_ref_sp(const_cast<SkImage*>(this)), colorSpace); 255 if (!srcSpecialImage) { 256 return nullptr; 257 } 258 259 sk_sp<SkImageFilterCache> cache( 260 SkImageFilterCache::Create(SkImageFilterCache::kDefaultTransientSize)); 261 SkImageFilter::OutputProperties outputProperties(colorSpace); 262 SkImageFilter::Context context(SkMatrix::I(), clipBounds, cache.get(), outputProperties); 263 264 sk_sp<SkSpecialImage> result = filter->filterImage(srcSpecialImage.get(), context, offset); 265 if (!result) { 266 return nullptr; 267 } 268 269 *outSubset = SkIRect::MakeWH(result->width(), result->height()); 270 if (!outSubset->intersect(clipBounds.makeOffset(-offset->x(), -offset->y()))) { 271 return nullptr; 272 } 273 offset->fX += outSubset->x(); 274 offset->fY += outSubset->y(); 275 276 // Note that here we're returning the special image's entire backing store, loose padding 277 // and all! 278 return result->asImage(); 279} 280 281bool SkImage::isLazyGenerated() const { 282 return as_IB(this)->onIsLazyGenerated(); 283} 284 285bool SkImage::isAlphaOnly() const { 286 return as_IB(this)->onImageInfo().colorType() == kAlpha_8_SkColorType; 287} 288 289sk_sp<SkImage> SkImage::makeColorSpace(sk_sp<SkColorSpace> target, 290 SkTransferFunctionBehavior premulBehavior) const { 291 SkColorSpaceTransferFn fn; 292 if (!target || !target->isNumericalTransferFn(&fn)) { 293 return nullptr; 294 } 295 296 // No need to create a new image if: 297 // (1) The color spaces are equal. 298 // (2) The color type is kAlpha8. 299 if (SkColorSpace::Equals(this->colorSpace(), target.get()) || 300 kAlpha_8_SkColorType == as_IB(this)->onImageInfo().colorType()) { 301 return sk_ref_sp(const_cast<SkImage*>(this)); 302 } 303 304 SkColorType targetColorType = kN32_SkColorType; 305 if (SkTransferFunctionBehavior::kRespect == premulBehavior && target->gammaIsLinear()) { 306 targetColorType = kRGBA_F16_SkColorType; 307 } 308 309 // TODO: We might consider making this a deferred conversion? 310 return as_IB(this)->onMakeColorSpace(std::move(target), targetColorType, premulBehavior); 311} 312 313sk_sp<SkImage> SkImage::makeNonTextureImage() const { 314 if (!this->isTextureBacked()) { 315 return sk_ref_sp(const_cast<SkImage*>(this)); 316 } 317 return this->makeRasterImage(); 318} 319 320sk_sp<SkImage> SkImage::makeRasterImage() const { 321 SkPixmap pm; 322 if (this->peekPixels(&pm)) { 323 return sk_ref_sp(const_cast<SkImage*>(this)); 324 } 325 326 const SkImageInfo info = as_IB(this)->onImageInfo(); 327 const size_t rowBytes = info.minRowBytes(); 328 size_t size = info.computeByteSize(rowBytes); 329 if (SkImageInfo::ByteSizeOverflowed(size)) { 330 return nullptr; 331 } 332 333 sk_sp<SkData> data = SkData::MakeUninitialized(size); 334 pm = { info.makeColorSpace(nullptr), data->writable_data(), info.minRowBytes() }; 335 if (!this->readPixels(pm, 0, 0)) { 336 return nullptr; 337 } 338 339 return SkImage::MakeRasterData(info, std::move(data), rowBytes); 340} 341 342////////////////////////////////////////////////////////////////////////////////////// 343 344#if !SK_SUPPORT_GPU 345 346sk_sp<SkImage> SkImage::MakeFromTexture(GrContext* ctx, 347 const GrBackendTexture& tex, GrSurfaceOrigin origin, 348 SkAlphaType at, sk_sp<SkColorSpace> cs, 349 TextureReleaseProc releaseP, ReleaseContext releaseC) { 350 return nullptr; 351} 352 353bool SkImage::MakeBackendTextureFromSkImage(GrContext*, 354 sk_sp<SkImage>, 355 GrBackendTexture*, 356 BackendTextureReleaseProc*) { 357 return false; 358} 359 360sk_sp<SkImage> SkImage::MakeFromAdoptedTexture(GrContext* ctx, 361 const GrBackendTexture& tex, GrSurfaceOrigin origin, 362 SkAlphaType at, sk_sp<SkColorSpace> cs) { 363 return nullptr; 364} 365 366sk_sp<SkImage> SkImage::MakeFromAdoptedTexture(GrContext* ctx, 367 const GrBackendTexture& tex, GrSurfaceOrigin origin, 368 SkColorType ct, SkAlphaType at, 369 sk_sp<SkColorSpace> cs) { 370 return nullptr; 371} 372 373sk_sp<SkImage> SkImage::MakeFromYUVTexturesCopy(GrContext* ctx, SkYUVColorSpace space, 374 const GrBackendObject yuvTextureHandles[3], 375 const SkISize yuvSizes[3], 376 GrSurfaceOrigin origin, 377 sk_sp<SkColorSpace> imageColorSpace) { 378 return nullptr; 379} 380 381sk_sp<SkImage> SkImage::MakeFromYUVTexturesCopy(GrContext* ctx, SkYUVColorSpace space, 382 const GrBackendTexture yuvTextureHandles[3], 383 const SkISize yuvSizes[3], 384 GrSurfaceOrigin origin, 385 sk_sp<SkColorSpace> imageColorSpace) { 386 return nullptr; 387} 388 389sk_sp<SkImage> SkImage::makeTextureImage(GrContext*, SkColorSpace* dstColorSpace) const { 390 return nullptr; 391} 392 393#endif 394 395/////////////////////////////////////////////////////////////////////////////////////////////////// 396 397bool SkImage_pinAsTexture(const SkImage* image, GrContext* ctx) { 398 SkASSERT(image); 399 SkASSERT(ctx); 400 return as_IB(image)->onPinAsTexture(ctx); 401} 402 403void SkImage_unpinAsTexture(const SkImage* image, GrContext* ctx) { 404 SkASSERT(image); 405 SkASSERT(ctx); 406 as_IB(image)->onUnpinAsTexture(ctx); 407} 408 409/////////////////////////////////////////////////////////////////////////////////////////////////// 410 411sk_sp<SkImage> SkImageMakeRasterCopyAndAssignColorSpace(const SkImage* src, 412 SkColorSpace* colorSpace) { 413 // Read the pixels out of the source image, with no conversion 414 SkImageInfo info = as_IB(src)->onImageInfo(); 415 if (kUnknown_SkColorType == info.colorType()) { 416 SkDEBUGFAIL("Unexpected color type"); 417 return nullptr; 418 } 419 420 size_t rowBytes = info.minRowBytes(); 421 size_t size = info.computeByteSize(rowBytes); 422 if (SkImageInfo::ByteSizeOverflowed(size)) { 423 return nullptr; 424 } 425 auto data = SkData::MakeUninitialized(size); 426 if (!data) { 427 return nullptr; 428 } 429 430 SkPixmap pm(info, data->writable_data(), rowBytes); 431 if (!src->readPixels(pm, 0, 0, SkImage::kDisallow_CachingHint)) { 432 return nullptr; 433 } 434 435 // Wrap them in a new image with a different color space 436 return SkImage::MakeRasterData(info.makeColorSpace(sk_ref_sp(colorSpace)), data, rowBytes); 437} 438