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