diff options
author | reed <reed@google.com> | 2015-06-22 12:48:26 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-06-22 12:48:26 -0700 |
commit | 871872f3f247f6b699617f6d9ef50ef5da6fbe74 (patch) | |
tree | 50358d6f699dd433607d9bc3c29da63e44c5d00f | |
parent | c1f56b518218d1caa65d6b7101bebf0d28c02a92 (diff) |
change old picture serialization to really handle images
BUG=skia:3965
Review URL: https://codereview.chromium.org/1199473002
32 files changed, 458 insertions, 102 deletions
@@ -64,7 +64,7 @@ void sk_test_c_api(sk_canvas_t* canvas) { sk_data_t* data = sk_image_encode(img0); sk_image_unref(img0); - sk_image_t* img1 = sk_image_new_from_data(data); + sk_image_t* img1 = sk_image_new_from_encoded(data, NULL); sk_data_unref(data); if (img1) { diff --git a/gm/factory.cpp b/gm/factory.cpp index 519b8eac79..4af37636f9 100644 --- a/gm/factory.cpp +++ b/gm/factory.cpp @@ -37,19 +37,19 @@ protected: SkAutoTUnref<SkDiscardableMemoryPool> pool( SkDiscardableMemoryPool::Create(1)); SkAssertResult(SkInstallDiscardablePixelRef(SkImageGenerator::NewFromData(data), - &fBitmap, pool)); + NULL, &fBitmap, pool)); } } - virtual SkString onShortName() override { + SkString onShortName() override { return SkString("factory"); } - virtual SkISize onISize() override { + SkISize onISize() override { return SkISize::Make(640, 480); } - virtual void onDraw(SkCanvas* canvas) override { + void onDraw(SkCanvas* canvas) override { canvas->drawBitmap(fBitmap, 0, 0); } diff --git a/gm/image.cpp b/gm/image.cpp index c420c95eef..5e840fbb2f 100644 --- a/gm/image.cpp +++ b/gm/image.cpp @@ -24,7 +24,7 @@ static void drawJpeg(SkCanvas* canvas, const SkISize& size) { if (NULL == data.get()) { return; } - SkImage* image = SkImage::NewFromData(data); + SkImage* image = SkImage::NewFromEncoded(data); if (image) { SkAutoCanvasRestore acr(canvas, true); canvas->scale(size.width() * 1.0f / image->width(), diff --git a/include/c/sk_image.h b/include/c/sk_image.h index 34682c72d3..c9973ac125 100644 --- a/include/c/sk_image.h +++ b/include/c/sk_image.h @@ -28,7 +28,7 @@ sk_image_t* sk_image_new_raster_copy(const sk_imageinfo_t*, const void* pixels, * On success, the encoded data may be processed immediately, or it may be ref()'d for later * use. */ -sk_image_t* sk_image_new_from_data(const sk_data_t* encoded); +sk_image_t* sk_image_new_from_encoded(const sk_data_t* encoded, const sk_irect_t* subset); sk_data_t* sk_image_encode(const sk_image_t*); diff --git a/include/c/sk_types.h b/include/c/sk_types.h index dc530f374b..00632e0af3 100644 --- a/include/c/sk_types.h +++ b/include/c/sk_types.h @@ -68,6 +68,13 @@ typedef struct { } sk_point_t; typedef struct { + int32_t left; + int32_t top; + int32_t right; + int32_t bottom; +} sk_irect_t; + +typedef struct { float left; float top; float right; diff --git a/include/core/SkImage.h b/include/core/SkImage.h index 9d1c546ef5..062d13afe7 100644 --- a/include/core/SkImage.h +++ b/include/core/SkImage.h @@ -65,17 +65,27 @@ public: * Construct a new SkImage based on the given ImageGenerator. * This function will always take ownership of the passed * ImageGenerator. Returns NULL on error. + * + * If a subset is specified, it must be contained within the generator's bounds. */ - static SkImage* NewFromGenerator(SkImageGenerator*); + static SkImage* NewFromGenerator(SkImageGenerator*, const SkIRect* subset = NULL); /** * Construct a new SkImage based on the specified encoded data. Returns NULL on failure, * which can mean that the format of the encoded data was not recognized/supported. * + * If a subset is specified, it must be contained within the encoded data's bounds. + * * Regardless of success or failure, the caller is responsible for managing their ownership * of the data. */ - static SkImage* NewFromData(SkData* data); + static SkImage* NewFromEncoded(SkData* encoded, const SkIRect* subset = NULL); + +#ifdef SK_SUPPORT_LEGACY_IMAGE_NEWFROMDATA + static SkImage* NewFromData(SkData* data) { + return NewFromEncoded(data, NULL); + } +#endif /** * Create a new image from the specified descriptor. Note - the caller is responsible for @@ -151,6 +161,15 @@ public: */ const void* peekPixels(SkImageInfo* info, size_t* rowBytes) const; + /** + * If the image has direct access to its pixels (i.e. they are in local + * RAM) return the (const) address of those pixels, and if not null, return + * true, and if pixmap is not NULL, set it to point into the image. + * + * On failure, return false and ignore the pixmap parameter. + */ + bool peekPixels(SkPixmap* pixmap) const; + // DEPRECATED GrTexture* getTexture() const; @@ -187,6 +206,8 @@ public: bool readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes, int srcX, int srcY) const; + bool readPixels(const SkPixmap& dst, int srcX, int srcY) const; + /** * Encode the image's pixels and return the result as a new SkData, which * the caller must manage (i.e. call unref() when they are done). @@ -194,8 +215,22 @@ public: * If the image type cannot be encoded, or the requested encoder type is * not supported, this will return NULL. */ - SkData* encode(SkImageEncoder::Type t = SkImageEncoder::kPNG_Type, - int quality = 80) const; + SkData* encode(SkImageEncoder::Type, int quality) const; + + SkData* encode() const { + return this->encode(SkImageEncoder::kPNG_Type, 100); + } + + /** + * If the image already has its contents in encoded form (e.g. PNG or JPEG), return a ref + * to that data (which the caller must call unref() on). The caller is responsible for calling + * unref on the data when they are done. + * + * If the image does not already has its contents in encoded form, return NULL. + * + * Note: to force the image to return its contents as encoded data, try calling encode(...). + */ + SkData* refEncoded() const; /** * Return a new surface that is compatible with this image's internal representation diff --git a/include/core/SkPicture.h b/include/core/SkPicture.h index fe99760f1d..358faf9524 100644 --- a/include/core/SkPicture.h +++ b/include/core/SkPicture.h @@ -175,10 +175,11 @@ private: // V40: Remove UniqueID serialization from SkImageFilter. // V41: Added serialization of SkBitmapSource's filterQuality parameter // V42: Added a bool to SkPictureShader serialization to indicate did-we-serialize-a-picture? + // V43: Added DRAW_IMAGE and DRAW_IMAGE_RECT opt codes to serialized data // Only SKPs within the min/current picture version range (inclusive) can be read. static const uint32_t MIN_PICTURE_VERSION = 35; // Produced by Chrome M39. - static const uint32_t CURRENT_PICTURE_VERSION = 42; + static const uint32_t CURRENT_PICTURE_VERSION = 43; static_assert(MIN_PICTURE_VERSION <= 41, "Remove kFontFileName and related code from SkFontDescriptor.cpp."); diff --git a/include/core/SkWriteBuffer.h b/include/core/SkWriteBuffer.h index 39739f2c24..8e4607887d 100644 --- a/include/core/SkWriteBuffer.h +++ b/include/core/SkWriteBuffer.h @@ -73,6 +73,7 @@ public: void writePath(const SkPath& path); size_t writeStream(SkStream* stream, size_t length); void writeBitmap(const SkBitmap& bitmap); + void writeImage(const SkImage*); void writeTypeface(SkTypeface* typeface); void writePaint(const SkPaint& paint) { paint.flatten(*this); } @@ -106,6 +107,7 @@ public: * be set to NULL in release and crash in debug. */ void setPixelSerializer(SkPixelSerializer*); + SkPixelSerializer* getPixelSerializer() const { return fPixelSerializer; } private: bool isValidating() const { return SkToBool(fFlags & kValidation_Flag); } diff --git a/src/c/sk_surface.cpp b/src/c/sk_surface.cpp index 15f68dba11..280897b354 100644 --- a/src/c/sk_surface.cpp +++ b/src/c/sk_surface.cpp @@ -207,8 +207,9 @@ sk_image_t* sk_image_new_raster_copy(const sk_imageinfo_t* cinfo, const void* pi return (sk_image_t*)SkImage::NewRasterCopy(info, pixels, rowBytes); } -sk_image_t* sk_image_new_from_data(const sk_data_t* cdata) { - return ToImage(SkImage::NewFromData(AsData(cdata))); +sk_image_t* sk_image_new_from_encoded(const sk_data_t* cdata, const sk_irect_t* subset) { + return ToImage(SkImage::NewFromEncoded(AsData(cdata), + reinterpret_cast<const SkIRect*>(subset))); } sk_data_t* sk_image_encode(const sk_image_t* cimage) { diff --git a/src/core/SkBitmap.cpp b/src/core/SkBitmap.cpp index 49e2c04bfb..8f799125e4 100644 --- a/src/core/SkBitmap.cpp +++ b/src/core/SkBitmap.cpp @@ -1270,7 +1270,9 @@ bool SkBitmap::requestLock(SkAutoPixmapUnlock* result) const { return false; } - SkPixelRef::LockRequest req = { fInfo.dimensions(), kNone_SkFilterQuality }; + // We have to lock the whole thing (using the pixelref's dimensions) until the api supports + // a partial lock (with offset/origin). Hence we can't use our fInfo. + SkPixelRef::LockRequest req = { pr->info().dimensions(), kNone_SkFilterQuality }; SkPixelRef::LockResult res; if (pr->requestLock(req, &res)) { SkASSERT(res.fPixels); diff --git a/src/core/SkImageGeneratorPriv.h b/src/core/SkImageGeneratorPriv.h index e03294d3fa..e55f43df8b 100644 --- a/src/core/SkImageGeneratorPriv.h +++ b/src/core/SkImageGeneratorPriv.h @@ -33,7 +33,7 @@ * * @return true iff successful. */ -bool SkInstallDiscardablePixelRef(SkImageGenerator*, SkBitmap* destination, +bool SkInstallDiscardablePixelRef(SkImageGenerator*, const SkIRect* subset, SkBitmap* destination, SkDiscardableMemory::Factory* factory); #endif diff --git a/src/core/SkPictureData.cpp b/src/core/SkPictureData.cpp index ddd074e9a4..87517e0d3d 100644 --- a/src/core/SkPictureData.cpp +++ b/src/core/SkPictureData.cpp @@ -68,6 +68,15 @@ SkPictureData::SkPictureData(const SkPictureRecord& record, fTextBlobRefs[i] = SkRef(blobs[i]); } } + + const SkTDArray<const SkImage*>& imgs = record.getImageRefs(); + fImageCount = imgs.count(); + if (fImageCount > 0) { + fImageRefs = SkNEW_ARRAY(const SkImage*, fImageCount); + for (int i = 0; i < fImageCount; ++i) { + fImageRefs[i] = SkRef(imgs[i]); + } + } } void SkPictureData::init() { @@ -75,6 +84,8 @@ void SkPictureData::init() { fPictureCount = 0; fTextBlobRefs = NULL; fTextBlobCount = 0; + fImageRefs = NULL; + fImageCount = 0; fOpData = NULL; fFactoryPlayback = NULL; } @@ -91,12 +102,17 @@ SkPictureData::~SkPictureData() { fTextBlobRefs[i]->unref(); } SkDELETE_ARRAY(fTextBlobRefs); - + + for (int i = 0; i < fImageCount; i++) { + fImageRefs[i]->unref(); + } + SkDELETE_ARRAY(fImageRefs); + SkDELETE(fFactoryPlayback); } bool SkPictureData::containsBitmaps() const { - if (fBitmaps.count() > 0) { + if (fBitmaps.count() > 0 || fImageCount > 0) { return true; } for (int i = 0; i < fPictureCount; ++i) { @@ -217,6 +233,13 @@ void SkPictureData::flattenToBuffer(SkWriteBuffer& buffer) const { fTextBlobRefs[i]->flatten(buffer); } } + + if (fImageCount > 0) { + write_tag_size(buffer, SK_PICT_IMAGE_BUFFER_TAG, fImageCount); + for (i = 0; i < fImageCount; ++i) { + buffer.writeImage(fImageRefs[i]); + } + } } void SkPictureData::serialize(SkWStream* stream, @@ -403,8 +426,67 @@ bool SkPictureData::parseStreamTag(SkStream* stream, return true; // success } -bool SkPictureData::parseBufferTag(SkReadBuffer& buffer, - uint32_t tag, uint32_t size) { +static const SkImage* create_image_from_buffer(SkReadBuffer& buffer) { + int width = buffer.read32(); + int height = buffer.read32(); + if (width <= 0 || height <= 0) { // SkImage never has a zero dimension + buffer.validate(false); + return NULL; + } + + SkAutoTUnref<SkData> encoded(buffer.readByteArrayAsData()); + int originX = buffer.read32(); + int originY = buffer.read32(); + if (0 == encoded->size() || originX < 0 || originY < 0) { + buffer.validate(false); + return NULL; + } + + const SkIRect subset = SkIRect::MakeXYWH(originX, originY, width, height); + return SkImage::NewFromEncoded(encoded, &subset); +} + +// Need a shallow wrapper to return const SkPicture* to match the other factories, +// as SkPicture::CreateFromBuffer() returns SkPicture* +static const SkPicture* create_picture_from_buffer(SkReadBuffer& buffer) { + return SkPicture::CreateFromBuffer(buffer); +} + +template <typename T> +bool new_array_from_buffer(SkReadBuffer& buffer, uint32_t inCount, + const T*** array, int* outCount, const T* (*factory)(SkReadBuffer&)) { + if (!buffer.validate((0 == *outCount) && (NULL == *array))) { + return false; + } + if (0 == inCount) { + return true; + } + *outCount = inCount; + *array = SkNEW_ARRAY(const T*, *outCount); + bool success = true; + int i = 0; + for (; i < *outCount; i++) { + (*array)[i] = factory(buffer); + if (NULL == (*array)[i]) { + success = false; + break; + } + } + if (!success) { + // Delete all of the blobs that were already created (up to but excluding i): + for (int j = 0; j < i; j++) { + (*array)[j]->unref(); + } + // Delete the array + SkDELETE_ARRAY(*array); + *array = NULL; + *outCount = 0; + return false; + } + return true; +} + +bool SkPictureData::parseBufferTag(SkReadBuffer& buffer, uint32_t tag, uint32_t size) { switch (tag) { case SK_PICT_BITMAP_BUFFER_TAG: { const int count = SkToInt(size); @@ -433,33 +515,18 @@ bool SkPictureData::parseBufferTag(SkReadBuffer& buffer, buffer.readPath(&fPaths[i]); } } break; - case SK_PICT_TEXTBLOB_BUFFER_TAG: { - if (!buffer.validate((0 == fTextBlobCount) && (NULL == fTextBlobRefs))) { + case SK_PICT_TEXTBLOB_BUFFER_TAG: + if (!new_array_from_buffer(buffer, size, &fTextBlobRefs, &fTextBlobCount, + SkTextBlob::CreateFromBuffer)) { return false; } - fTextBlobCount = size; - fTextBlobRefs = SkNEW_ARRAY(const SkTextBlob*, fTextBlobCount); - bool success = true; - int i = 0; - for ( ; i < fTextBlobCount; i++) { - fTextBlobRefs[i] = SkTextBlob::CreateFromBuffer(buffer); - if (NULL == fTextBlobRefs[i]) { - success = false; - break; - } - } - if (!success) { - // Delete all of the blobs that were already created (up to but excluding i): - for (int j = 0; j < i; j++) { - fTextBlobRefs[j]->unref(); - } - // Delete the array - SkDELETE_ARRAY(fTextBlobRefs); - fTextBlobRefs = NULL; - fTextBlobCount = 0; + break; + case SK_PICT_IMAGE_BUFFER_TAG: + if (!new_array_from_buffer(buffer, size, &fImageRefs, &fImageCount, + create_image_from_buffer)) { return false; } - } break; + break; case SK_PICT_READER_TAG: { SkAutoDataUnref data(SkData::NewUninitialized(size)); if (!buffer.readByteArray(data->writable_data(), size) || @@ -469,32 +536,11 @@ bool SkPictureData::parseBufferTag(SkReadBuffer& buffer, SkASSERT(NULL == fOpData); fOpData = data.detach(); } break; - case SK_PICT_PICTURE_TAG: { - if (!buffer.validate((0 == fPictureCount) && (NULL == fPictureRefs))) { + case SK_PICT_PICTURE_TAG: + if (!new_array_from_buffer(buffer, size, &fPictureRefs, &fPictureCount, + create_picture_from_buffer)) { return false; } - fPictureCount = size; - fPictureRefs = SkNEW_ARRAY(const SkPicture*, fPictureCount); - bool success = true; - int i = 0; - for ( ; i < fPictureCount; i++) { - fPictureRefs[i] = SkPicture::CreateFromBuffer(buffer); - if (NULL == fPictureRefs[i]) { - success = false; - break; - } - } - if (!success) { - // Delete all of the pictures that were already created (up to but excluding i): - for (int j = 0; j < i; j++) { - fPictureRefs[j]->unref(); - } - // Delete the array - SkDELETE_ARRAY(fPictureRefs); - fPictureCount = 0; - return false; - } - } break; default: // The tag was invalid. return false; diff --git a/src/core/SkPictureData.h b/src/core/SkPictureData.h index cada8d1f66..1a490ce6e4 100644 --- a/src/core/SkPictureData.h +++ b/src/core/SkPictureData.h @@ -51,6 +51,7 @@ struct SkPictInfo { #define SK_PICT_PAINT_BUFFER_TAG SkSetFourByteTag('p', 'n', 't', ' ') #define SK_PICT_PATH_BUFFER_TAG SkSetFourByteTag('p', 't', 'h', ' ') #define SK_PICT_TEXTBLOB_BUFFER_TAG SkSetFourByteTag('b', 'l', 'o', 'b') +#define SK_PICT_IMAGE_BUFFER_TAG SkSetFourByteTag('i', 'm', 'a', 'g') // Always write this guy last (with no length field afterwards) #define SK_PICT_EOF_TAG SkSetFourByteTag('e', 'o', 'f', ' ') @@ -90,6 +91,11 @@ public: return fBitmaps[index]; } + const SkImage* getImage(SkReader32* reader) const { + const int index = reader->readInt(); + return fImageRefs[index]; + } + const SkPath& getPath(SkReader32* reader) const { int index = reader->readInt() - 1; return fPaths[index]; @@ -156,6 +162,8 @@ private: int fPictureCount; const SkTextBlob** fTextBlobRefs; int fTextBlobCount; + const SkImage** fImageRefs; + int fImageCount; SkPictureContentInfo fContentInfo; diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h index 2511615994..8720e19751 100644 --- a/src/core/SkPictureFlat.h +++ b/src/core/SkPictureFlat.h @@ -69,8 +69,10 @@ enum DrawType { DRAW_PATCH, // could not add in aphabetical order DRAW_PICTURE_MATRIX_PAINT, DRAW_TEXT_BLOB, + DRAW_IMAGE, + DRAW_IMAGE_RECT, - LAST_DRAWTYPE_ENUM = DRAW_TEXT_BLOB + LAST_DRAWTYPE_ENUM = DRAW_IMAGE_RECT }; // In the 'match' method, this constant will match any flavor of DRAW_BITMAP* diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp index f4fdc8b83a..ac71c27d22 100644 --- a/src/core/SkPicturePlayback.cpp +++ b/src/core/SkPicturePlayback.cpp @@ -216,6 +216,19 @@ void SkPicturePlayback::handleOp(SkReader32* reader, case END_COMMENT_GROUP: // deprecated (M44) break; + case DRAW_IMAGE: { + const SkPaint* paint = fPictureData->getPaint(reader); + const SkImage* image = fPictureData->getImage(reader); + const SkPoint& loc = reader->skipT<SkPoint>(); + canvas->drawImage(image, loc.fX, loc.fY, paint); + } break; + case DRAW_IMAGE_RECT: { + const SkPaint* paint = fPictureData->getPaint(reader); + const SkImage* image = fPictureData->getImage(reader); + const SkRect* src = get_rect_ptr(reader); // may be null + const SkRect& dst = reader->skipT<SkRect>(); // required + canvas->drawImageRect(image, src, dst, paint); + } break; case DRAW_OVAL: { const SkPaint& paint = *fPictureData->getPaint(reader); canvas->drawOval(reader->skipT<SkRect>(), paint); diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp index c85af901c6..018da0bb48 100644 --- a/src/core/SkPictureRecord.cpp +++ b/src/core/SkPictureRecord.cpp @@ -96,6 +96,8 @@ static inline size_t get_paint_offset(DrawType op, size_t opSize) { 1, // DRAW_PATCH - right after op code 1, // DRAW_PICTURE_MATRIX_PAINT - right after op code 1, // DRAW_TEXT_BLOB- right after op code + 1, // DRAW_IMAGE - right after op code + 1, // DRAW_IMAGE_RECT - right after op code }; SK_COMPILE_ASSERT(sizeof(gPaintOffsets) == LAST_DRAWTYPE_ENUM + 1, @@ -566,18 +568,34 @@ void SkPictureRecord::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src void SkPictureRecord::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) { - SkBitmap bm; - if (as_IB(image)->getROPixels(&bm)) { - this->SkPictureRecord::onDrawBitmap(bm, x, y, paint); - } + // op + paint_index + image_index + x + y + size_t size = 3 * kUInt32Size + 2 * sizeof(SkScalar); + size_t initialOffset = this->addDraw(DRAW_IMAGE, &size); + SkASSERT(initialOffset+get_paint_offset(DRAW_IMAGE, size) == fWriter.bytesWritten()); + this->addPaintPtr(paint); + this->addImage(image); + this->addScalar(x); + this->addScalar(y); + this->validate(initialOffset, size); } void SkPictureRecord::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, const SkPaint* paint) { - SkBitmap bm; - if (as_IB(image)->getROPixels(&bm)) { - this->SkPictureRecord::onDrawBitmapRect(bm, src, dst, paint, kNone_DrawBitmapRectFlag); + // id + paint_index + bitmap_index + bool_for_src + size_t size = 4 * kUInt32Size; + if (src) { + size += sizeof(*src); // + rect } + size += sizeof(dst); // + rect + + size_t initialOffset = this->addDraw(DRAW_IMAGE_RECT, &size); + SkASSERT(initialOffset+get_paint_offset(DRAW_IMAGE_RECT, size) + == fWriter.bytesWritten()); + this->addPaintPtr(paint); + this->addImage(image); + this->addRectPtr(src); // may be null + this->addRect(dst); + this->validate(initialOffset, size); } void SkPictureRecord::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, @@ -892,6 +910,16 @@ void SkPictureRecord::addBitmap(const SkBitmap& bitmap) { this->addInt(fBitmaps.count()-1); // Remember, 0-based. } +void SkPictureRecord::addImage(const SkImage* image) { + int index = fImageRefs.find(image); + if (index >= 0) { + this->addInt(index); + } else { + *fImageRefs.append() = SkRef(image); + this->addInt(fImageRefs.count()-1); + } +} + void SkPictureRecord::addMatrix(const SkMatrix& matrix) { fWriter.writeMatrix(matrix); } diff --git a/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h index 2aeb69dcd4..0b6ef8b931 100644 --- a/src/core/SkPictureRecord.h +++ b/src/core/SkPictureRecord.h @@ -37,6 +37,10 @@ public: return fTextBlobRefs; } + const SkTDArray<const SkImage* >& getImageRefs() const { + return fImageRefs; + } + SkData* opData(bool deepCopy) const { this->validate(fWriter.bytesWritten(), 0); @@ -118,6 +122,7 @@ private: } void addBitmap(const SkBitmap& bitmap); + void addImage(const SkImage*); void addMatrix(const SkMatrix& matrix); void addPaint(const SkPaint& paint) { this->addPaintPtr(&paint); } void addPaintPtr(const SkPaint* paint); @@ -223,6 +228,7 @@ private: SkWriter32 fWriter; // we ref each item in these arrays + SkTDArray<const SkImage*> fImageRefs; SkTDArray<const SkPicture*> fPictureRefs; SkTDArray<const SkTextBlob*> fTextBlobRefs; diff --git a/src/core/SkPixelRef.cpp b/src/core/SkPixelRef.cpp index cfe93fda58..6e1f5c6417 100644 --- a/src/core/SkPixelRef.cpp +++ b/src/core/SkPixelRef.cpp @@ -259,6 +259,10 @@ bool SkPixelRef::requestLock(const LockRequest& request, LockResult* result) { if (request.fSize.isEmpty()) { return false; } + // until we support subsets, we have to check this... + if (request.fSize.width() != fInfo.width() || request.fSize.height() != fInfo.height()) { + return false; + } if (fPreLocked) { result->fUnlockProc = NULL; diff --git a/src/core/SkReadBuffer.h b/src/core/SkReadBuffer.h index ba47835daf..25640519a3 100644 --- a/src/core/SkReadBuffer.h +++ b/src/core/SkReadBuffer.h @@ -58,6 +58,7 @@ public: kImageFilterNoUniqueID_Version = 40, kBitmapSourceFilterQuality_Version = 41, kPictureShaderHasPictureBool_Version = 42, + kHasDrawImageOpCodes_Version = 43, }; /** diff --git a/src/core/SkWriteBuffer.cpp b/src/core/SkWriteBuffer.cpp index d2eb8c5d8d..faa7f00b08 100644 --- a/src/core/SkWriteBuffer.cpp +++ b/src/core/SkWriteBuffer.cpp @@ -218,6 +218,34 @@ void SkWriteBuffer::writeBitmap(const SkBitmap& bitmap) { SkBitmap::WriteRawPixels(this, bitmap); } +static bool try_write_encoded(SkWriteBuffer* buffer, SkData* encoded) { + SkPixelSerializer* ps = buffer->getPixelSerializer(); + // Assumes that if the client did not set a serializer, they are + // happy to get the encoded data. + if (!ps || ps->useEncodedData(encoded->data(), encoded->size())) { + write_encoded_bitmap(buffer, encoded, SkIPoint::Make(0, 0)); + return true; + } + return false; +} + +void SkWriteBuffer::writeImage(const SkImage* image) { + this->writeInt(image->width()); + this->writeInt(image->height()); + + SkAutoTUnref<SkData> encoded(image->refEncoded()); + if (encoded && try_write_encoded(this, encoded)) { + return; + } + + encoded.reset(image->encode(SkImageEncoder::kPNG_Type, 100)); + if (encoded && try_write_encoded(this, encoded)) { + return; + } + + this->writeUInt(0); // signal no pixels (in place of the size of the encoded data) +} + void SkWriteBuffer::writeTypeface(SkTypeface* obj) { if (NULL == obj || NULL == fTFSet) { fWriter.write32(0); diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp index 66756cb512..5e7d1b9150 100644 --- a/src/image/SkImage.cpp +++ b/src/image/SkImage.cpp @@ -7,6 +7,7 @@ #include "SkBitmap.h" #include "SkCanvas.h" +#include "SkData.h" #include "SkImageGenerator.h" #include "SkImagePriv.h" #include "SkImage_Base.h" @@ -64,12 +65,16 @@ SkData* SkImage::encode(SkImageEncoder::Type type, int quality) const { return NULL; } -SkImage* SkImage::NewFromData(SkData* data) { - if (NULL == data) { +SkData* SkImage::refEncoded() const { + return as_IB(this)->onRefEncoded(); +} + +SkImage* SkImage::NewFromEncoded(SkData* encoded, const SkIRect* subset) { + if (NULL == encoded || 0 == encoded->size()) { return NULL; } - SkImageGenerator* generator = SkImageGenerator::NewFromData(data); - return generator ? SkImage::NewFromGenerator(generator) : NULL; + SkImageGenerator* generator = SkImageGenerator::NewFromData(encoded); + return generator ? SkImage::NewFromGenerator(generator, subset) : NULL; } SkSurface* SkImage::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) const { @@ -201,7 +206,26 @@ SkImage* SkImage_Base::onNewImage(int newWidth, int newHeight, const SkIRect* su return surface->newImageSnapshot(); } -////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +bool SkImage::peekPixels(SkPixmap* pmap) const { + SkImageInfo info; + size_t rowBytes; + const void* pixels = this->peekPixels(&info, &rowBytes); + if (pixels) { + if (pmap) { + pmap->reset(info, pixels, rowBytes); + } + return true; + } + return false; +} + +bool SkImage::readPixels(const SkPixmap& pmap, int srcX, int srcY) const { + return this->readPixels(pmap.info(), pmap.writable_addr(), pmap.rowBytes(), srcX, srcY); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// #if !SK_SUPPORT_GPU diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h index 3bbff58a18..0793abb7bc 100644 --- a/src/image/SkImage_Base.h +++ b/src/image/SkImage_Base.h @@ -59,6 +59,7 @@ public: // newWidth > 0, newHeight > 0, subset either NULL or a proper subset of this bounds virtual SkImage* onNewImage(int newWidth, int newHeight, const SkIRect* subset, SkFilterQuality) const; + virtual SkData* onRefEncoded() const { return NULL; } private: const SkSurfaceProps fProps; diff --git a/src/image/SkImage_Raster.cpp b/src/image/SkImage_Raster.cpp index 2b64839bc5..ab37659f2f 100644 --- a/src/image/SkImage_Raster.cpp +++ b/src/image/SkImage_Raster.cpp @@ -9,7 +9,7 @@ #include "SkBitmap.h" #include "SkCanvas.h" #include "SkData.h" -#include "SkImageGenerator.h" +#include "SkImageGeneratorPriv.h" #include "SkImagePriv.h" #include "SkPixelRef.h" #include "SkSurface.h" @@ -59,6 +59,7 @@ public: SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) const override; bool onReadPixels(const SkImageInfo&, void*, size_t, int srcX, int srcY) const override; const void* onPeekPixels(SkImageInfo*, size_t* /*rowBytes*/) const override; + SkData* onRefEncoded() const override; bool getROPixels(SkBitmap*) const override; // exposed for SkSurface_Raster via SkNewImageFromPixelRef @@ -141,6 +142,18 @@ const void* SkImage_Raster::onPeekPixels(SkImageInfo* infoPtr, size_t* rowBytesP return fBitmap.getPixels(); } +SkData* SkImage_Raster::onRefEncoded() const { + SkPixelRef* pr = fBitmap.pixelRef(); + const SkImageInfo prInfo = pr->info(); + const SkImageInfo bmInfo = fBitmap.info(); + + // we only try if we (the image) cover the entire area of the pixelRef + if (prInfo.width() == bmInfo.width() && prInfo.height() == bmInfo.height()) { + return pr->refEncodedData(); + } + return NULL; +} + bool SkImage_Raster::getROPixels(SkBitmap* dst) const { *dst = fBitmap; return true; @@ -185,9 +198,9 @@ SkImage* SkImage::NewFromRaster(const SkImageInfo& info, const void* pixels, siz return SkNEW_ARGS(SkImage_Raster, (info, data, rowBytes, NULL)); } -SkImage* SkImage::NewFromGenerator(SkImageGenerator* generator) { +SkImage* SkImage::NewFromGenerator(SkImageGenerator* generator, const SkIRect* subset) { SkBitmap bitmap; - if (!SkInstallDiscardablePixelRef(generator, &bitmap)) { + if (!SkInstallDiscardablePixelRef(generator, subset, &bitmap, NULL)) { return NULL; } if (0 == bitmap.width() || 0 == bitmap.height()) { diff --git a/src/lazy/SkDiscardablePixelRef.cpp b/src/lazy/SkDiscardablePixelRef.cpp index 1e42042fb6..73fa4ce84c 100644 --- a/src/lazy/SkDiscardablePixelRef.cpp +++ b/src/lazy/SkDiscardablePixelRef.cpp @@ -107,37 +107,55 @@ void SkDiscardablePixelRef::onUnlockPixels() { fDiscardableMemoryIsLocked = false; } -bool SkInstallDiscardablePixelRef(SkImageGenerator* generator, SkBitmap* dst, +bool SkInstallDiscardablePixelRef(SkImageGenerator* generator, const SkIRect* subset, SkBitmap* dst, SkDiscardableMemory::Factory* factory) { SkAutoTDelete<SkImageGenerator> autoGenerator(generator); if (NULL == autoGenerator.get()) { return false; } - SkImageInfo info = autoGenerator->getInfo(); - if (info.isEmpty() || !dst->setInfo(info)) { + + SkImageInfo prInfo = autoGenerator->getInfo(); + if (prInfo.isEmpty()) { return false; } - // Since dst->setInfo() may have changed/fixed-up info, we copy it back from that bitmap - info = dst->info(); - SkASSERT(info.colorType() != kUnknown_SkColorType); + SkIPoint origin = SkIPoint::Make(0, 0); + SkImageInfo bmInfo = prInfo; + if (subset) { + const SkIRect prBounds = SkIRect::MakeWH(prInfo.width(), prInfo.height()); + if (subset->isEmpty() || !prBounds.contains(*subset)) { + return false; + } + bmInfo = prInfo.makeWH(subset->width(), subset->height()); + origin.set(subset->x(), subset->y()); + } + + // must compute our desired rowBytes w.r.t. the pixelRef's dimensions, not ours, which may be + // smaller. + if (!dst->setInfo(bmInfo, prInfo.minRowBytes())) { + return false; + } + + // Since dst->setInfo() may have changed/fixed-up info, we check from the bitmap + SkASSERT(dst->info().colorType() != kUnknown_SkColorType); + if (dst->empty()) { // Use a normal pixelref. return dst->tryAllocPixels(); } SkAutoTUnref<SkDiscardablePixelRef> ref( SkNEW_ARGS(SkDiscardablePixelRef, - (info, autoGenerator.detach(), dst->rowBytes(), factory))); - dst->setPixelRef(ref); + (prInfo, autoGenerator.detach(), dst->rowBytes(), factory))); + dst->setPixelRef(ref, origin.x(), origin.y()); return true; } // These are the public API bool SkInstallDiscardablePixelRef(SkImageGenerator* generator, SkBitmap* dst) { - return SkInstallDiscardablePixelRef(generator, dst, NULL); + return SkInstallDiscardablePixelRef(generator, NULL, dst, NULL); } bool SkInstallDiscardablePixelRef(SkData* encoded, SkBitmap* dst) { SkImageGenerator* generator = SkImageGenerator::NewFromData(encoded); - return generator ? SkInstallDiscardablePixelRef(generator, dst, NULL) : false; + return generator ? SkInstallDiscardablePixelRef(generator, NULL, dst, NULL) : false; } diff --git a/src/lazy/SkDiscardablePixelRef.h b/src/lazy/SkDiscardablePixelRef.h index 4515fd2277..5d248a2975 100644 --- a/src/lazy/SkDiscardablePixelRef.h +++ b/src/lazy/SkDiscardablePixelRef.h @@ -9,7 +9,7 @@ #define SkDiscardablePixelRef_DEFINED #include "SkDiscardableMemory.h" -#include "SkImageGenerator.h" +#include "SkImageGeneratorPriv.h" #include "SkImageInfo.h" #include "SkPixelRef.h" @@ -61,7 +61,7 @@ private: return fGenerator->getYUV8Planes(sizes, planes, rowBytes, colorSpace); } - friend bool SkInstallDiscardablePixelRef(SkImageGenerator*, SkBitmap*, + friend bool SkInstallDiscardablePixelRef(SkImageGenerator*, const SkIRect*, SkBitmap*, SkDiscardableMemory::Factory*); typedef SkPixelRef INHERITED; diff --git a/src/ports/SkImageDecoder_CG.cpp b/src/ports/SkImageDecoder_CG.cpp index eb2b46a080..ba16e4b45c 100644 --- a/src/ports/SkImageDecoder_CG.cpp +++ b/src/ports/SkImageDecoder_CG.cpp @@ -157,7 +157,18 @@ SkImageDecoder::Result SkImageDecoder_CG::onDecode(SkStream* stream, SkBitmap* b } } - bm->setInfo(SkImageInfo::MakeN32Premul(width, height, cpType)); + SkAlphaType at = kPremul_SkAlphaType; + switch (CGImageGetAlphaInfo(image)) { + case kCGImageAlphaNone: + case kCGImageAlphaNoneSkipLast: + case kCGImageAlphaNoneSkipFirst: + at = kOpaque_SkAlphaType; + break; + default: + break; + } + + bm->setInfo(SkImageInfo::Make(width, height, kN32_SkColorType, at, cpType)); if (SkImageDecoder::kDecodeBounds_Mode == mode) { return kSuccess; } diff --git a/src/utils/SkLua.cpp b/src/utils/SkLua.cpp index 11d9b2938c..cee4d4ded8 100644 --- a/src/utils/SkLua.cpp +++ b/src/utils/SkLua.cpp @@ -2007,7 +2007,7 @@ static int lsk_loadImage(lua_State* L) { const char* name = lua_tolstring(L, 1, NULL); SkAutoDataUnref data(SkData::NewFromFileName(name)); if (data.get()) { - SkImage* image = SkImage::NewFromData(data); + SkImage* image = SkImage::NewFromEncoded(data); if (image) { push_ref(L, image)->unref(); return 1; diff --git a/tests/CachedDecodingPixelRefTest.cpp b/tests/CachedDecodingPixelRefTest.cpp index 66e5273625..465b69b856 100644 --- a/tests/CachedDecodingPixelRefTest.cpp +++ b/tests/CachedDecodingPixelRefTest.cpp @@ -249,7 +249,7 @@ static void check_pixelref(TestImageGenerator::TestType type, // Ignore factory; use global cache. success = SkCachingPixelRef::Install(gen.detach(), &lazy); } else { - success = SkInstallDiscardablePixelRef(gen.detach(), &lazy, factory); + success = SkInstallDiscardablePixelRef(gen.detach(), NULL, &lazy, factory); } REPORTER_ASSERT(reporter, success); if (TestImageGenerator::kSucceedGetPixels_TestType == type) { diff --git a/tests/DrawBitmapRectTest.cpp b/tests/DrawBitmapRectTest.cpp index 80167b7083..e6d46a751d 100644 --- a/tests/DrawBitmapRectTest.cpp +++ b/tests/DrawBitmapRectTest.cpp @@ -33,8 +33,8 @@ static void test_faulty_pixelref(skiatest::Reporter* reporter) { SkAutoTUnref<SkDiscardableMemoryPool> pool( SkDiscardableMemoryPool::Create(10 * 1000, NULL)); SkBitmap bm; - bool installSuccess = SkInstallDiscardablePixelRef(SkNEW(FailureImageGenerator), &bm, pool); - REPORTER_ASSERT(reporter, installSuccess); + bool success = SkInstallDiscardablePixelRef(SkNEW(FailureImageGenerator), NULL, &bm, pool); + REPORTER_ASSERT(reporter, success); // now our bitmap has a pixelref, but we know it will fail to lock SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(200, 200)); diff --git a/tests/ImageTest.cpp b/tests/ImageTest.cpp new file mode 100644 index 0000000000..b196ca8f34 --- /dev/null +++ b/tests/ImageTest.cpp @@ -0,0 +1,105 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkCanvas.h" +#include "SkData.h" +#include "SkDevice.h" +#include "SkImageEncoder.h" +#include "SkImage_Base.h" +#include "SkRRect.h" +#include "SkSurface.h" +#include "SkUtils.h" +#include "Test.h" + +#if SK_SUPPORT_GPU +#include "GrContextFactory.h" +#include "GrTest.h" +#include "gl/GrGLInterface.h" +#include "gl/GrGLUtil.h" +#else +class GrContextFactory; +class GrContext; +#endif + +static void assert_equal(skiatest::Reporter* reporter, SkImage* a, const SkIRect* subsetA, + SkImage* b) { + const int widthA = subsetA ? subsetA->width() : a->width(); + const int heightA = subsetA ? subsetA->height() : a->height(); + + REPORTER_ASSERT(reporter, widthA == b->width()); + REPORTER_ASSERT(reporter, heightA == b->height()); +#if 0 + // see skbug.com/3965 + bool AO = a->isOpaque(); + bool BO = b->isOpaque(); + REPORTER_ASSERT(reporter, AO == BO); +#endif + + SkImageInfo info = SkImageInfo::MakeN32(widthA, heightA, + a->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType); + SkAutoPixmapStorage pmapA, pmapB; + pmapA.alloc(info); + pmapB.alloc(info); + + const int srcX = subsetA ? subsetA->x() : 0; + const int srcY = subsetA ? subsetA->y() : 0; + + REPORTER_ASSERT(reporter, a->readPixels(pmapA, srcX, srcY)); + REPORTER_ASSERT(reporter, b->readPixels(pmapB, 0, 0)); + + const size_t widthBytes = widthA * info.bytesPerPixel(); + for (int y = 0; y < heightA; ++y) { + REPORTER_ASSERT(reporter, !memcmp(pmapA.addr32(0, y), pmapB.addr32(0, y), widthBytes)); + } +} + +static SkImage* make_image(GrContext* ctx, int w, int h, const SkIRect& ir) { + const SkImageInfo info = SkImageInfo::MakeN32(w, h, kOpaque_SkAlphaType); + SkAutoTUnref<SkSurface> surface(ctx ? + SkSurface::NewRenderTarget(ctx, SkSurface::kNo_Budgeted, info) : + SkSurface::NewRaster(info)); + SkCanvas* canvas = surface->getCanvas(); + canvas->clear(SK_ColorWHITE); + + SkPaint paint; + paint.setColor(SK_ColorBLACK); + canvas->drawRect(SkRect::Make(ir), paint); + return surface->newImageSnapshot(); +} + +static void test_encode(skiatest::Reporter* reporter, GrContext* ctx) { + const SkIRect ir = SkIRect::MakeXYWH(5, 5, 10, 10); + SkAutoTUnref<SkImage> orig(make_image(ctx, 20, 20, ir)); + SkAutoTUnref<SkData> origEncoded(orig->encode()); + REPORTER_ASSERT(reporter, origEncoded); + REPORTER_ASSERT(reporter, origEncoded->size() > 0); + + SkAutoTUnref<SkImage> decoded(SkImage::NewFromEncoded(origEncoded)); + REPORTER_ASSERT(reporter, decoded); + assert_equal(reporter, orig, NULL, decoded); + + // Now see if we can instantiate an image from a subset of the surface/origEncoded + + decoded.reset(SkImage::NewFromEncoded(origEncoded, &ir)); + REPORTER_ASSERT(reporter, decoded); + assert_equal(reporter, orig, &ir, decoded); +} + +DEF_TEST(Image_Encode_Cpu, reporter) { + test_encode(reporter, NULL); +} + +#if SK_SUPPORT_GPU +DEF_GPUTEST(Image_Encode_Gpu, reporter, factory) { + GrContext* ctx = factory->get(GrContextFactory::kNative_GLContextType); + if (!ctx) { + REPORTER_ASSERT(reporter, false); + return; + } + test_encode(reporter, ctx); +} +#endif diff --git a/tests/SurfaceTest.cpp b/tests/SurfaceTest.cpp index 0eebfeb4ef..0114fb66fc 100644 --- a/tests/SurfaceTest.cpp +++ b/tests/SurfaceTest.cpp @@ -251,7 +251,7 @@ static SkImage* createImage(ImageType imageType, GrContext* context, SkColor col bitmap.installPixels(info, addr, rowBytes); SkAutoTUnref<SkData> src( SkImageEncoder::EncodeData(bitmap, SkImageEncoder::kPNG_Type, 100)); - return SkImage::NewFromData(src); + return SkImage::NewFromEncoded(src); } } SkASSERT(false); diff --git a/tools/LazyDecodeBitmap.cpp b/tools/LazyDecodeBitmap.cpp index 3b6f17e741..6c655da9e9 100644 --- a/tools/LazyDecodeBitmap.cpp +++ b/tools/LazyDecodeBitmap.cpp @@ -40,5 +40,5 @@ bool sk_tools::LazyDecodeBitmap(const void* src, size_t length, SkBitmap* dst) { // Only meaningful if platform has a default discardable // memory implementation that differs from the global DM pool. } - return SkInstallDiscardablePixelRef(gen.detach(), dst, pool); + return SkInstallDiscardablePixelRef(gen.detach(), NULL, dst, pool); } |