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