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 mode) const { 219 return as_IB(this)->onAsLegacyBitmap(bitmap, mode); 220} 221 222bool SkImage_Base::onAsLegacyBitmap(SkBitmap* bitmap, LegacyBitmapMode mode) 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 if (kRO_LegacyBitmapMode == mode) { 235 bitmap->setImmutable(); 236 } 237 return true; 238} 239 240sk_sp<SkImage> SkImage::MakeFromPicture(sk_sp<SkPicture> picture, const SkISize& dimensions, 241 const SkMatrix* matrix, const SkPaint* paint, 242 BitDepth bitDepth, sk_sp<SkColorSpace> colorSpace) { 243 return MakeFromGenerator(SkImageGenerator::MakeFromPicture(dimensions, std::move(picture), 244 matrix, paint, bitDepth, 245 std::move(colorSpace))); 246} 247 248sk_sp<SkImage> SkImage::makeWithFilter(const SkImageFilter* filter, const SkIRect& subset, 249 const SkIRect& clipBounds, SkIRect* outSubset, 250 SkIPoint* offset) const { 251 if (!filter || !outSubset || !offset || !this->bounds().contains(subset)) { 252 return nullptr; 253 } 254 SkColorSpace* colorSpace = as_IB(this)->onImageInfo().colorSpace(); 255 sk_sp<SkSpecialImage> srcSpecialImage = SkSpecialImage::MakeFromImage( 256 subset, sk_ref_sp(const_cast<SkImage*>(this)), colorSpace); 257 if (!srcSpecialImage) { 258 return nullptr; 259 } 260 261 sk_sp<SkImageFilterCache> cache( 262 SkImageFilterCache::Create(SkImageFilterCache::kDefaultTransientSize)); 263 SkImageFilter::OutputProperties outputProperties(colorSpace); 264 SkImageFilter::Context context(SkMatrix::I(), clipBounds, cache.get(), outputProperties); 265 266 sk_sp<SkSpecialImage> result = filter->filterImage(srcSpecialImage.get(), context, offset); 267 if (!result) { 268 return nullptr; 269 } 270 271 *outSubset = SkIRect::MakeWH(result->width(), result->height()); 272 if (!outSubset->intersect(clipBounds.makeOffset(-offset->x(), -offset->y()))) { 273 return nullptr; 274 } 275 offset->fX += outSubset->x(); 276 offset->fY += outSubset->y(); 277 278 // Note that here we're returning the special image's entire backing store, loose padding 279 // and all! 280 return result->asImage(); 281} 282 283bool SkImage::isLazyGenerated() const { 284 return as_IB(this)->onIsLazyGenerated(); 285} 286 287bool SkImage::isAlphaOnly() const { 288 return as_IB(this)->onImageInfo().colorType() == kAlpha_8_SkColorType; 289} 290 291sk_sp<SkImage> SkImage::makeColorSpace(sk_sp<SkColorSpace> target, 292 SkTransferFunctionBehavior premulBehavior) const { 293 SkColorSpaceTransferFn fn; 294 if (!target || !target->isNumericalTransferFn(&fn)) { 295 return nullptr; 296 } 297 298 // No need to create a new image if: 299 // (1) The color spaces are equal. 300 // (2) The color type is kAlpha8. 301 if (SkColorSpace::Equals(this->colorSpace(), target.get()) || 302 kAlpha_8_SkColorType == as_IB(this)->onImageInfo().colorType()) { 303 return sk_ref_sp(const_cast<SkImage*>(this)); 304 } 305 306 SkColorType targetColorType = kN32_SkColorType; 307 if (SkTransferFunctionBehavior::kRespect == premulBehavior && target->gammaIsLinear()) { 308 targetColorType = kRGBA_F16_SkColorType; 309 } 310 311 // TODO: We might consider making this a deferred conversion? 312 return as_IB(this)->onMakeColorSpace(std::move(target), targetColorType, premulBehavior); 313} 314 315sk_sp<SkImage> SkImage::makeNonTextureImage() const { 316 if (!this->isTextureBacked()) { 317 return sk_ref_sp(const_cast<SkImage*>(this)); 318 } 319 return this->makeRasterImage(); 320} 321 322sk_sp<SkImage> SkImage::makeRasterImage() const { 323 SkPixmap pm; 324 if (this->peekPixels(&pm)) { 325 return sk_ref_sp(const_cast<SkImage*>(this)); 326 } 327 328 const SkImageInfo info = as_IB(this)->onImageInfo(); 329 const size_t rowBytes = info.minRowBytes(); 330 size_t size = info.computeByteSize(rowBytes); 331 if (SkImageInfo::ByteSizeOverflowed(size)) { 332 return nullptr; 333 } 334 335 sk_sp<SkData> data = SkData::MakeUninitialized(size); 336 pm = { info.makeColorSpace(nullptr), data->writable_data(), info.minRowBytes() }; 337 if (!this->readPixels(pm, 0, 0)) { 338 return nullptr; 339 } 340 341 return SkImage::MakeRasterData(info, std::move(data), rowBytes); 342} 343 344////////////////////////////////////////////////////////////////////////////////////// 345 346#if !SK_SUPPORT_GPU 347 348sk_sp<SkImage> MakeTextureFromMipMap(GrContext*, const SkImageInfo&, const GrMipLevel texels[], 349 int mipLevelCount, SkBudgeted, SkDestinationSurfaceColorMode) { 350 return nullptr; 351} 352 353sk_sp<SkImage> SkImage::MakeFromTexture(GrContext* ctx, 354 const GrBackendTexture& tex, GrSurfaceOrigin origin, 355 SkAlphaType at, sk_sp<SkColorSpace> cs, 356 TextureReleaseProc releaseP, ReleaseContext releaseC) { 357 return nullptr; 358} 359 360size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy&, 361 const DeferredTextureImageUsageParams[], 362 int paramCnt, void* buffer, 363 SkColorSpace* dstColorSpace, 364 SkColorType dstColorType) const { 365 return 0; 366} 367 368sk_sp<SkImage> SkImage::MakeFromDeferredTextureImageData(GrContext* context, const void*, 369 SkBudgeted) { 370 return nullptr; 371} 372 373sk_sp<SkImage> SkImage::MakeFromTexture(GrContext* ctx, 374 const GrBackendTexture& tex, GrSurfaceOrigin origin, 375 SkColorType ct, SkAlphaType at, sk_sp<SkColorSpace> cs, 376 TextureReleaseProc releaseP, ReleaseContext releaseC) { 377 return nullptr; 378} 379 380bool SkImage::MakeBackendTextureFromSkImage(GrContext*, 381 sk_sp<SkImage>, 382 GrBackendTexture*, 383 BackendTextureReleaseProc*) { 384 return false; 385} 386 387sk_sp<SkImage> SkImage::MakeFromAdoptedTexture(GrContext* ctx, 388 const GrBackendTexture& tex, GrSurfaceOrigin origin, 389 SkAlphaType at, sk_sp<SkColorSpace> cs) { 390 return nullptr; 391} 392 393sk_sp<SkImage> SkImage::MakeFromAdoptedTexture(GrContext* ctx, 394 const GrBackendTexture& tex, GrSurfaceOrigin origin, 395 SkColorType ct, SkAlphaType at, 396 sk_sp<SkColorSpace> cs) { 397 return nullptr; 398} 399 400sk_sp<SkImage> SkImage::MakeFromYUVTexturesCopy(GrContext* ctx, SkYUVColorSpace space, 401 const GrBackendObject yuvTextureHandles[3], 402 const SkISize yuvSizes[3], 403 GrSurfaceOrigin origin, 404 sk_sp<SkColorSpace> imageColorSpace) { 405 return nullptr; 406} 407 408sk_sp<SkImage> SkImage::MakeFromYUVTexturesCopy(GrContext* ctx, SkYUVColorSpace space, 409 const GrBackendTexture yuvTextureHandles[3], 410 const SkISize yuvSizes[3], 411 GrSurfaceOrigin origin, 412 sk_sp<SkColorSpace> imageColorSpace) { 413 return nullptr; 414} 415 416sk_sp<SkImage> SkImage::makeTextureImage(GrContext*, SkColorSpace* dstColorSpace) const { 417 return nullptr; 418} 419 420#endif 421 422/////////////////////////////////////////////////////////////////////////////////////////////////// 423 424sk_sp<SkImage> MakeTextureFromMipMap(GrContext*, const SkImageInfo&, const GrMipLevel texels[], 425 int mipLevelCount, SkBudgeted) { 426 return nullptr; 427} 428 429/////////////////////////////////////////////////////////////////////////////////////////////////// 430 431bool SkImage_pinAsTexture(const SkImage* image, GrContext* ctx) { 432 SkASSERT(image); 433 SkASSERT(ctx); 434 return as_IB(image)->onPinAsTexture(ctx); 435} 436 437void SkImage_unpinAsTexture(const SkImage* image, GrContext* ctx) { 438 SkASSERT(image); 439 SkASSERT(ctx); 440 as_IB(image)->onUnpinAsTexture(ctx); 441} 442 443/////////////////////////////////////////////////////////////////////////////////////////////////// 444 445sk_sp<SkImage> SkImageMakeRasterCopyAndAssignColorSpace(const SkImage* src, 446 SkColorSpace* colorSpace) { 447 // Read the pixels out of the source image, with no conversion 448 SkImageInfo info = as_IB(src)->onImageInfo(); 449 if (kUnknown_SkColorType == info.colorType()) { 450 SkDEBUGFAIL("Unexpected color type"); 451 return nullptr; 452 } 453 454 size_t rowBytes = info.minRowBytes(); 455 size_t size = info.computeByteSize(rowBytes); 456 if (SkImageInfo::ByteSizeOverflowed(size)) { 457 return nullptr; 458 } 459 auto data = SkData::MakeUninitialized(size); 460 if (!data) { 461 return nullptr; 462 } 463 464 SkPixmap pm(info, data->writable_data(), rowBytes); 465 if (!src->readPixels(pm, 0, 0, SkImage::kDisallow_CachingHint)) { 466 return nullptr; 467 } 468 469 // Wrap them in a new image with a different color space 470 return SkImage::MakeRasterData(info.makeColorSpace(sk_ref_sp(colorSpace)), data, rowBytes); 471} 472