Home | History | Annotate | Download | only in image
      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 
     33 SkImage::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 
     42 bool SkImage::peekPixels(SkPixmap* pm) const {
     43     SkPixmap tmp;
     44     if (!pm) {
     45         pm = &tmp;
     46     }
     47     return as_IB(this)->onPeekPixels(pm);
     48 }
     49 
     50 bool 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 
     55 bool 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 
     77 SkAlphaType SkImage::alphaType() const {
     78     return as_IB(this)->onAlphaType();
     79 }
     80 
     81 SkColorSpace* SkImage::colorSpace() const {
     82     return as_IB(this)->onImageInfo().colorSpace();
     83 }
     84 
     85 sk_sp<SkColorSpace> SkImage::refColorSpace() const {
     86     return as_IB(this)->onImageInfo().refColorSpace();
     87 }
     88 
     89 sk_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 
     94 sk_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 
    103 sk_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 
    117 sk_sp<SkData> SkImage::refEncodedData() const {
    118     return sk_sp<SkData>(as_IB(this)->onRefEncoded());
    119 }
    120 
    121 sk_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 
    130 const 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 
    136 sk_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 
    155 GrTexture* SkImage::getTexture() const {
    156     return as_IB(this)->onGetTexture();
    157 }
    158 
    159 bool SkImage::isTextureBacked() const { return SkToBool(as_IB(this)->peekProxy()); }
    160 
    161 GrBackendObject SkImage::getTextureHandle(bool flushPendingGrContextIO,
    162                                           GrSurfaceOrigin* origin) const {
    163     return as_IB(this)->onGetTextureHandle(flushPendingGrContextIO, origin);
    164 }
    165 
    166 bool 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 
    175 GrTexture* SkImage::getTexture() const { return nullptr; }
    176 
    177 bool SkImage::isTextureBacked() const { return false; }
    178 
    179 GrBackendObject SkImage::getTextureHandle(bool, GrSurfaceOrigin*) const { return 0; }
    180 
    181 bool 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 
    192 SkImage_Base::SkImage_Base(int width, int height, uint32_t uniqueID)
    193     : INHERITED(width, height, uniqueID)
    194     , fAddedToCache(false)
    195 {}
    196 
    197 SkImage_Base::~SkImage_Base() {
    198     if (fAddedToCache.load()) {
    199         SkNotifyBitmapGenIDIsStale(this->uniqueID());
    200     }
    201 }
    202 
    203 bool 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 
    209 sk_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 
    218 bool SkImage::asLegacyBitmap(SkBitmap* bitmap, LegacyBitmapMode mode) const {
    219     return as_IB(this)->onAsLegacyBitmap(bitmap, mode);
    220 }
    221 
    222 bool 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 
    240 sk_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 
    248 sk_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 
    283 bool SkImage::isLazyGenerated() const {
    284     return as_IB(this)->onIsLazyGenerated();
    285 }
    286 
    287 bool SkImage::isAlphaOnly() const {
    288     return as_IB(this)->onImageInfo().colorType() == kAlpha_8_SkColorType;
    289 }
    290 
    291 sk_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 
    315 sk_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 
    322 sk_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 
    348 sk_sp<SkImage> MakeTextureFromMipMap(GrContext*, const SkImageInfo&, const GrMipLevel texels[],
    349                                      int mipLevelCount, SkBudgeted, SkDestinationSurfaceColorMode) {
    350     return nullptr;
    351 }
    352 
    353 sk_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 
    360 size_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 
    368 sk_sp<SkImage> SkImage::MakeFromDeferredTextureImageData(GrContext* context, const void*,
    369                                                          SkBudgeted) {
    370     return nullptr;
    371 }
    372 
    373 sk_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 
    380 bool SkImage::MakeBackendTextureFromSkImage(GrContext*,
    381                                             sk_sp<SkImage>,
    382                                             GrBackendTexture*,
    383                                             BackendTextureReleaseProc*) {
    384     return false;
    385 }
    386 
    387 sk_sp<SkImage> SkImage::MakeFromAdoptedTexture(GrContext* ctx,
    388                                                const GrBackendTexture& tex, GrSurfaceOrigin origin,
    389                                                SkAlphaType at, sk_sp<SkColorSpace> cs) {
    390     return nullptr;
    391 }
    392 
    393 sk_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 
    400 sk_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 
    408 sk_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 
    416 sk_sp<SkImage> SkImage::makeTextureImage(GrContext*, SkColorSpace* dstColorSpace) const {
    417     return nullptr;
    418 }
    419 
    420 #endif
    421 
    422 ///////////////////////////////////////////////////////////////////////////////////////////////////
    423 
    424 sk_sp<SkImage> MakeTextureFromMipMap(GrContext*, const SkImageInfo&, const GrMipLevel texels[],
    425                                      int mipLevelCount, SkBudgeted) {
    426     return nullptr;
    427 }
    428 
    429 ///////////////////////////////////////////////////////////////////////////////////////////////////
    430 
    431 bool SkImage_pinAsTexture(const SkImage* image, GrContext* ctx) {
    432     SkASSERT(image);
    433     SkASSERT(ctx);
    434     return as_IB(image)->onPinAsTexture(ctx);
    435 }
    436 
    437 void SkImage_unpinAsTexture(const SkImage* image, GrContext* ctx) {
    438     SkASSERT(image);
    439     SkASSERT(ctx);
    440     as_IB(image)->onUnpinAsTexture(ctx);
    441 }
    442 
    443 ///////////////////////////////////////////////////////////////////////////////////////////////////
    444 
    445 sk_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