diff --git a/include/core/SkImage.h b/include/core/SkImage.h index 41a1bd8527..65b9c9cb92 100644 --- a/include/core/SkImage.h +++ b/include/core/SkImage.h @@ -264,6 +264,24 @@ public: SkImage* newImage(int newWidth, int newHeight, const SkIRect* subset = NULL, SkFilterQuality = kNone_SkFilterQuality) const; + // Helper functions to convert to SkBitmap + + enum LegacyBitmapMode { + kRO_LegacyBitmapMode, + kRW_LegacyBitmapMode, + }; + + /** + * Attempt to create a bitmap with the same pixels as the image. The result will always be + * a raster-backed bitmap (texture-backed bitmaps are DEPRECATED, and not supported here). + * + * If the mode is kRO (read-only), the resulting bitmap will be marked as immutable. + * + * On succcess, returns true. On failure, returns false and the bitmap parameter will be reset + * to empty. + */ + bool asLegacyBitmap(SkBitmap*, LegacyBitmapMode) const; + protected: SkImage(int width, int height) : fWidth(width), diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp index 3817733942..e8a3ead2b1 100644 --- a/src/image/SkImage.cpp +++ b/src/image/SkImage.cpp @@ -257,6 +257,29 @@ SkImage* SkImage::NewFromBitmap(const SkBitmap& bm) { return SkNewImageFromRasterBitmap(bm, false, NULL); } +bool SkImage::asLegacyBitmap(SkBitmap* bitmap, LegacyBitmapMode mode) const { + return as_IB(this)->onAsLegacyBitmap(bitmap, mode); +} + +bool SkImage_Base::onAsLegacyBitmap(SkBitmap* bitmap, LegacyBitmapMode mode) const { + // As the base-class, all we can do is make a copy (regardless of mode). + // Subclasses that want to be more optimal should override. + SkImageInfo info = SkImageInfo::MakeN32(this->width(), this->height(), + this->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType); + if (!bitmap->tryAllocPixels(info)) { + return false; + } + if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) { + bitmap->reset(); + return false; + } + + if (kRO_LegacyBitmapMode == mode) { + bitmap->setImmutable(); + } + return true; +} + ////////////////////////////////////////////////////////////////////////////////////// #if !SK_SUPPORT_GPU diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h index 0793abb7bc..b3661d99fe 100644 --- a/src/image/SkImage_Base.h +++ b/src/image/SkImage_Base.h @@ -61,6 +61,8 @@ public: SkFilterQuality) const; virtual SkData* onRefEncoded() const { return NULL; } + virtual bool onAsLegacyBitmap(SkBitmap*, LegacyBitmapMode) const; + private: const SkSurfaceProps fProps; diff --git a/src/image/SkImage_Raster.cpp b/src/image/SkImage_Raster.cpp index 250e3624ea..489685b2fc 100644 --- a/src/image/SkImage_Raster.cpp +++ b/src/image/SkImage_Raster.cpp @@ -79,6 +79,7 @@ public: const SkMatrix* localMatrix) const override; bool isOpaque() const override; + bool onAsLegacyBitmap(SkBitmap*, LegacyBitmapMode) const override; SkImage_Raster(const SkBitmap& bm, const SkSurfaceProps* props) : INHERITED(bm.width(), bm.height(), props) @@ -262,3 +263,17 @@ bool SkImage_Raster::isOpaque() const { return fBitmap.isOpaque(); } +bool SkImage_Raster::onAsLegacyBitmap(SkBitmap* bitmap, LegacyBitmapMode mode) const { + if (kRO_LegacyBitmapMode == mode) { + // When we're a snapshot from a surface, our bitmap may not be marked immutable + // even though logically always we are, but in that case we can't physically share our + // pixelref since the caller might call setImmutable() themselves + // (thus changing our state). + if (fBitmap.isImmutable()) { + bitmap->setInfo(fBitmap.info()); + bitmap->setPixelRef(fBitmap.pixelRef(), fBitmap.pixelRefOrigin()); + return true; + } + } + return this->INHERITED::onAsLegacyBitmap(bitmap, mode); +} diff --git a/tests/SurfaceTest.cpp b/tests/SurfaceTest.cpp index d78498f6b9..1b57b778f6 100644 --- a/tests/SurfaceTest.cpp +++ b/tests/SurfaceTest.cpp @@ -407,6 +407,33 @@ static void test_image_readpixels(skiatest::Reporter* reporter, SkImage* image, REPORTER_ASSERT(reporter, has_pixels(&pixels[1], w*h - 1, notExpected)); } +static void test_legacy_bitmap(skiatest::Reporter* reporter, const SkImage* image) { + const SkImage::LegacyBitmapMode modes[] = { + SkImage::kRO_LegacyBitmapMode, + SkImage::kRW_LegacyBitmapMode, + }; + for (size_t i = 0; i < SK_ARRAY_COUNT(modes); ++i) { + SkBitmap bitmap; + REPORTER_ASSERT(reporter, image->asLegacyBitmap(&bitmap, modes[i])); + + REPORTER_ASSERT(reporter, image->width() == bitmap.width()); + REPORTER_ASSERT(reporter, image->height() == bitmap.height()); + REPORTER_ASSERT(reporter, image->isOpaque() == bitmap.isOpaque()); + + bitmap.lockPixels(); + REPORTER_ASSERT(reporter, bitmap.getPixels()); + + const SkImageInfo info = SkImageInfo::MakeN32(1, 1, bitmap.alphaType()); + SkPMColor imageColor; + REPORTER_ASSERT(reporter, image->readPixels(info, &imageColor, sizeof(SkPMColor), 0, 0)); + REPORTER_ASSERT(reporter, imageColor == *bitmap.getAddr32(0, 0)); + + if (SkImage::kRO_LegacyBitmapMode == modes[i]) { + REPORTER_ASSERT(reporter, bitmap.isImmutable()); + } + } +} + static void test_imagepeek(skiatest::Reporter* reporter, GrContextFactory* factory) { static const struct { ImageType fType; @@ -450,6 +477,8 @@ static void test_imagepeek(skiatest::Reporter* reporter, GrContextFactory* facto REPORTER_ASSERT(reporter, NULL == releaseCtx.fData); // we ignored the context } + test_legacy_bitmap(reporter, image); + const void* addr = image->peekPixels(&info, &rowBytes); bool success = SkToBool(addr); REPORTER_ASSERT(reporter, gRec[i].fPeekShouldSucceed == success);