diff options
-rw-r--r-- | include/core/SkImage.h | 44 | ||||
-rw-r--r-- | include/core/SkSurface.h | 14 | ||||
-rw-r--r-- | src/core/SkCanvas.cpp | 38 | ||||
-rw-r--r-- | src/core/SkImageInfo.cpp | 44 | ||||
-rw-r--r-- | src/image/SkImage.cpp | 58 | ||||
-rw-r--r-- | src/image/SkImage_Base.h | 7 | ||||
-rw-r--r-- | src/image/SkImage_Raster.cpp | 15 | ||||
-rw-r--r-- | src/image/SkReadPixelsRec.h | 41 | ||||
-rw-r--r-- | src/image/SkSurface.cpp | 9 | ||||
-rw-r--r-- | tests/SurfaceTest.cpp | 59 |
10 files changed, 197 insertions, 132 deletions
diff --git a/include/core/SkImage.h b/include/core/SkImage.h index 63fa41b4e7..6070b6bf6b 100644 --- a/include/core/SkImage.h +++ b/include/core/SkImage.h @@ -85,6 +85,27 @@ public: const void* peekPixels(SkImageInfo* info, size_t* rowBytes) const; /** + * Copy the pixels from the image into the specified buffer (pixels + rowBytes), + * converting them into the requested format (dstInfo). The image pixels are read + * starting at the specified (srcX,srcY) location. + * + * The specified ImageInfo and (srcX,srcY) offset specifies a source rectangle + * + * srcR.setXYWH(srcX, srcY, dstInfo.width(), dstInfo.height()); + * + * srcR is intersected with the bounds of the image. If this intersection is not empty, + * then we have two sets of pixels (of equal size). Replace the dst pixels with the + * corresponding src pixels, performing any colortype/alphatype transformations needed + * (in the case where the src and dst have different colortypes or alphatypes). + * + * This call can fail, returning false, for several reasons: + * - If srcR does not intersect the image bounds. + * - If the requested colortype/alphatype cannot be converted from the image's types. + */ + bool readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes, + 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). * @@ -134,29 +155,6 @@ private: * See SkCanvas::drawBitmapRectToRect for similar behavior. */ void drawRect(SkCanvas*, const SkRect* src, const SkRect& dst, const SkPaint*) const; - - /** - * Return a copy of the image's pixels, limiting them to the subset - * rectangle's intersection wit the image bounds. If subset is NULL, then - * the entire image will be considered. - * - * If the bitmap's pixels have already been allocated, then readPixels() - * will succeed only if it can support converting the image's pixels into - * the bitmap's ColorType/AlphaType. Any pixels in the bitmap that do not - * intersect with the image's bounds and the subset (if not null) will be - * left untouched. - * - * If the bitmap is initially empty/unallocated, then it will be allocated - * using the default allocator, and the ColorType/AlphaType will be chosen - * to most closely fit the image's configuration. - * - * On failure, false will be returned, and bitmap will unmodified. - */ - // On ice for now: - // - should it respect the particular colortype/alphatype of the src - // - should it have separate entrypoints for preallocated and not bitmaps? - // - isn't it enough to allow the caller to draw() the image into a canvas? - bool readPixels(SkBitmap* bitmap, const SkIRect* subset = NULL) const; }; #endif diff --git a/include/core/SkSurface.h b/include/core/SkSurface.h index 8b36314360..74c54e8d68 100644 --- a/include/core/SkSurface.h +++ b/include/core/SkSurface.h @@ -219,8 +219,8 @@ public: /** * Copy the pixels from the surface into the specified buffer (pixels + rowBytes), - * converting them into the requested format (dstInfo). The base-layer pixels are read - * starting at the specified (srcX,srcY) location in the coordinate system of the base-layer. + * converting them into the requested format (dstInfo). The surface pixels are read + * starting at the specified (srcX,srcY) location. * * The specified ImageInfo and (srcX,srcY) offset specifies a source rectangle * @@ -233,19 +233,11 @@ public: * * This call can fail, returning false, for several reasons: * - If srcR does not intersect the surface bounds. - * - If the requested colortype/alphatype cannot be converted from the base-layer's types. + * - If the requested colortype/alphatype cannot be converted from the surface's types. */ bool readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes, int srcX, int srcY); - /** - * Helper for allocating pixels and then calling readPixels(info, ...). The bitmap is resized - * to the intersection of srcRect and the surface bounds (if srcRect is non-null). - * On success, pixels will be allocated in bitmap and true returned. On failure, - * false is returned and bitmap will be set to empty. - */ - bool readPixels(SkBitmap* dst, const SkIRect* srcRect = NULL); - const SkSurfaceProps& props() const { return fProps; } protected: diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index bf86005315..083a8ed7e8 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -19,6 +19,7 @@ #include "SkPatchUtils.h" #include "SkPicture.h" #include "SkRasterClip.h" +#include "SkReadPixelsRec.h" #include "SkRRect.h" #include "SkSmallAllocator.h" #include "SkSurface_Base.h" @@ -686,47 +687,20 @@ bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) { return true; } -bool SkCanvas::readPixels(const SkImageInfo& origInfo, void* dstP, size_t rowBytes, int x, int y) { - switch (origInfo.colorType()) { - case kUnknown_SkColorType: - case kIndex_8_SkColorType: - return false; - default: - break; - } - if (NULL == dstP || rowBytes < origInfo.minRowBytes()) { - return false; - } - if (0 == origInfo.width() || 0 == origInfo.height()) { - return false; - } - +bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) { SkBaseDevice* device = this->getDevice(); if (!device) { return false; } - const SkISize size = this->getBaseLayerSize(); - SkIRect srcR = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height()); - if (!srcR.intersect(0, 0, size.width(), size.height())) { + + SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y); + if (!rec.trim(size.width(), size.height())) { return false; } - // the intersect may have shrunk info's logical size - const SkImageInfo info = origInfo.makeWH(srcR.width(), srcR.height()); - - // if x or y are negative, then we have to adjust pixels - if (x > 0) { - x = 0; - } - if (y > 0) { - y = 0; - } - // here x,y are either 0 or negative - dstP = ((char*)dstP - y * rowBytes - x * info.bytesPerPixel()); - // The device can assert that the requested area is always contained in its bounds - return device->readPixels(info, dstP, rowBytes, srcR.x(), srcR.y()); + return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY); } bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) { diff --git a/src/core/SkImageInfo.cpp b/src/core/SkImageInfo.cpp index 7931358d82..146a3aeed8 100644 --- a/src/core/SkImageInfo.cpp +++ b/src/core/SkImageInfo.cpp @@ -76,3 +76,47 @@ bool SkColorTypeValidateAlphaType(SkColorType colorType, SkAlphaType alphaType, } return true; } + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkReadPixelsRec.h" + +bool SkReadPixelsRec::trim(int srcWidth, int srcHeight) { + switch (fInfo.colorType()) { + case kUnknown_SkColorType: + case kIndex_8_SkColorType: + return false; + default: + break; + } + if (NULL == fPixels || fRowBytes < fInfo.minRowBytes()) { + return false; + } + if (0 == fInfo.width() || 0 == fInfo.height()) { + return false; + } + + int x = fX; + int y = fY; + SkIRect srcR = SkIRect::MakeXYWH(x, y, fInfo.width(), fInfo.height()); + if (!srcR.intersect(0, 0, srcWidth, srcHeight)) { + return false; + } + + // if x or y are negative, then we have to adjust pixels + if (x > 0) { + x = 0; + } + if (y > 0) { + y = 0; + } + // here x,y are either 0 or negative + fPixels = ((char*)fPixels - y * fRowBytes - x * fInfo.bytesPerPixel()); + // the intersect may have shrunk info's logical size + fInfo = fInfo.makeWH(srcR.width(), srcR.height()); + fX = srcR.x(); + fY = srcR.y(); + + return true; +} + diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp index cbde961b78..1acff3e4ed 100644 --- a/src/image/SkImage.cpp +++ b/src/image/SkImage.cpp @@ -9,6 +9,7 @@ #include "SkCanvas.h" #include "SkImagePriv.h" #include "SkImage_Base.h" +#include "SkReadPixelsRec.h" #include "SkSurface.h" uint32_t SkImage::NextUniqueID() { @@ -43,27 +44,13 @@ const void* SkImage::peekPixels(SkImageInfo* info, size_t* rowBytes) const { return as_IB(this)->onPeekPixels(info, rowBytes); } -bool SkImage::readPixels(SkBitmap* bitmap, const SkIRect* subset) const { - if (NULL == bitmap) { +bool SkImage::readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes, + int srcX, int srcY) const { + SkReadPixelsRec rec(dstInfo, dstPixels, dstRowBytes, srcX, srcY); + if (!rec.trim(this->width(), this->height())) { return false; } - - SkIRect bounds = SkIRect::MakeWH(this->width(), this->height()); - - // trim against the bitmap, if its already been allocated - if (bitmap->pixelRef()) { - bounds.fRight = SkMin32(bounds.fRight, bitmap->width()); - bounds.fBottom = SkMin32(bounds.fBottom, bitmap->height()); - if (bounds.isEmpty()) { - return false; - } - } - - if (subset && !bounds.intersect(*subset)) { - // perhaps we could return true + empty-bitmap? - return false; - } - return as_IB(this)->onReadPixels(bitmap, bounds); + return as_IB(this)->onReadPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY); } GrTexture* SkImage::getTexture() { @@ -107,34 +94,19 @@ static bool raster_canvas_supports(const SkImageInfo& info) { return false; } -bool SkImage_Base::onReadPixels(SkBitmap* bitmap, const SkIRect& subset) const { - if (bitmap->pixelRef()) { - const SkImageInfo info = bitmap->info(); - if (kUnknown_SkColorType == info.colorType()) { - return false; - } - if (!raster_canvas_supports(info)) { - return false; - } - } else { - SkBitmap tmp; - if (!tmp.tryAllocN32Pixels(subset.width(), subset.height())) { - return false; - } - *bitmap = tmp; +bool SkImage_Base::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes, + int srcX, int srcY) const { + if (!raster_canvas_supports(dstInfo)) { + return false; } - SkRect srcR, dstR; - srcR.set(subset); - dstR = srcR; - dstR.offset(-dstR.left(), -dstR.top()); - - SkCanvas canvas(*bitmap); + SkBitmap bm; + bm.installPixels(dstInfo, dstPixels, dstRowBytes); + SkCanvas canvas(bm); SkPaint paint; - paint.setXfermodeMode(SkXfermode::kClear_Mode); - canvas.drawRect(dstR, paint); + paint.setXfermodeMode(SkXfermode::kSrc_Mode); + canvas.drawImage(this, -SkIntToScalar(srcX), -SkIntToScalar(srcY), &paint); - canvas.drawImageRect(this, &srcR, dstR); return true; } diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h index 8f0fefe4f8..8c1dfada63 100644 --- a/src/image/SkImage_Base.h +++ b/src/image/SkImage_Base.h @@ -41,13 +41,14 @@ public: const SkRect& dst, const SkPaint*) const = 0; virtual SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) const = 0; - // Default impl calls onDraw - virtual bool onReadPixels(SkBitmap*, const SkIRect& subset) const; - virtual const void* onPeekPixels(SkImageInfo*, size_t* /*rowBytes*/) const { return NULL; } + // Default impl calls onDraw + virtual bool onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes, + int srcX, int srcY) const; + virtual GrTexture* onGetTexture() const { return NULL; } // return a read-only copy of the pixels. We promise to not modify them, diff --git a/src/image/SkImage_Raster.cpp b/src/image/SkImage_Raster.cpp index 9ae3d36426..24b971ad84 100644 --- a/src/image/SkImage_Raster.cpp +++ b/src/image/SkImage_Raster.cpp @@ -57,7 +57,7 @@ public: void onDraw(SkCanvas*, SkScalar, SkScalar, const SkPaint*) const SK_OVERRIDE; void onDrawRect(SkCanvas*, const SkRect*, const SkRect&, const SkPaint*) const SK_OVERRIDE; SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) const SK_OVERRIDE; - bool onReadPixels(SkBitmap*, const SkIRect&) const SK_OVERRIDE; + bool onReadPixels(const SkImageInfo&, void*, size_t, int srcX, int srcY) const SK_OVERRIDE; const void* onPeekPixels(SkImageInfo*, size_t* /*rowBytes*/) const SK_OVERRIDE; bool getROPixels(SkBitmap*) const SK_OVERRIDE; @@ -143,16 +143,9 @@ SkSurface* SkImage_Raster::onNewSurface(const SkImageInfo& info, const SkSurface return SkSurface::NewRaster(info, &props); } -bool SkImage_Raster::onReadPixels(SkBitmap* dst, const SkIRect& subset) const { - if (dst->pixelRef()) { - return this->INHERITED::onReadPixels(dst, subset); - } else { - SkBitmap src; - if (!fBitmap.extractSubset(&src, subset)) { - return false; - } - return src.copyTo(dst, src.colorType()); - } +bool SkImage_Raster::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes, + int srcX, int srcY) const { + return fBitmap.readPixels(dstInfo, dstPixels, dstRowBytes, srcX, srcY); } const void* SkImage_Raster::onPeekPixels(SkImageInfo* infoPtr, size_t* rowBytesPtr) const { diff --git a/src/image/SkReadPixelsRec.h b/src/image/SkReadPixelsRec.h new file mode 100644 index 0000000000..ed93b74b0f --- /dev/null +++ b/src/image/SkReadPixelsRec.h @@ -0,0 +1,41 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkReadPixelsRec_DEFINED +#define SkReadPixelsRec_DEFINED + +#include "SkImageInfo.h" + +/** + * Helper class to package and trim the parameters passed to readPixels() + */ +struct SkReadPixelsRec { + SkReadPixelsRec(const SkImageInfo& info, void* pixels, size_t rowBytes, int x, int y) + : fPixels(pixels) + , fRowBytes(rowBytes) + , fInfo(info) + , fX(x) + , fY(y) + {} + + void* fPixels; + size_t fRowBytes; + SkImageInfo fInfo; + int fX; + int fY; + + /* + * On true, may have modified its fields (except fRowBytes) to make it a legal subset + * of the specified src width/height. + * + * On false, leaves self unchanged, but indicates that it does not overlap src, or + * is not valid (e.g. bad fInfo) for readPixels(). + */ + bool trim(int srcWidth, int srcHeight); +}; + +#endif diff --git a/src/image/SkSurface.cpp b/src/image/SkSurface.cpp index 13b7d65546..7587f25284 100644 --- a/src/image/SkSurface.cpp +++ b/src/image/SkSurface.cpp @@ -175,15 +175,6 @@ bool SkSurface::readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t d return this->getCanvas()->readPixels(dstInfo, dstPixels, dstRowBytes, srcX, srcY); } -bool SkSurface::readPixels(SkBitmap* dst, const SkIRect* srcRect) { - SkIRect storage; - if (NULL == srcRect) { - storage.set(0, 0, this->width(), this->height()); - srcRect = &storage; - } - return this->getCanvas()->readPixels(*srcRect, dst); -} - ////////////////////////////////////////////////////////////////////////////////////// #ifdef SK_SUPPORT_LEGACY_TEXTRENDERMODE diff --git a/tests/SurfaceTest.cpp b/tests/SurfaceTest.cpp index 1f0db67c5c..28abf9804c 100644 --- a/tests/SurfaceTest.cpp +++ b/tests/SurfaceTest.cpp @@ -118,6 +118,63 @@ static SkImage* createImage(ImageType imageType, GrContext* context, SkColor col return NULL; } +static void set_pixels(SkPMColor pixels[], int count, SkPMColor color) { + sk_memset32(pixels, color, count); +} +static bool has_pixels(const SkPMColor pixels[], int count, SkPMColor expected) { + for (int i = 0; i < count; ++i) { + if (pixels[i] != expected) { + return false; + } + } + return true; +} + +static void test_image_readpixels(skiatest::Reporter* reporter, SkImage* image, + SkPMColor expected) { + const SkPMColor notExpected = ~expected; + + const int w = 2, h = 2; + const size_t rowBytes = w * sizeof(SkPMColor); + SkPMColor pixels[w*h]; + + SkImageInfo info; + + info = SkImageInfo::MakeUnknown(w, h); + REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, 0, 0)); + + // out-of-bounds should fail + info = SkImageInfo::MakeN32Premul(w, h); + REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, -w, 0)); + REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, 0, -h)); + REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, image->width(), 0)); + REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, 0, image->height())); + + // top-left should succeed + set_pixels(pixels, w*h, notExpected); + REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes, 0, 0)); + REPORTER_ASSERT(reporter, has_pixels(pixels, w*h, expected)); + + // bottom-right should succeed + set_pixels(pixels, w*h, notExpected); + REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes, + image->width() - w, image->height() - h)); + REPORTER_ASSERT(reporter, has_pixels(pixels, w*h, expected)); + + // partial top-left should succeed + set_pixels(pixels, w*h, notExpected); + REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes, -1, -1)); + REPORTER_ASSERT(reporter, pixels[3] == expected); + REPORTER_ASSERT(reporter, has_pixels(pixels, w*h - 1, notExpected)); + + // partial bottom-right should succeed + set_pixels(pixels, w*h, notExpected); + REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes, + image->width() - 1, image->height() - 1)); + REPORTER_ASSERT(reporter, pixels[0] == expected); + REPORTER_ASSERT(reporter, has_pixels(&pixels[1], w*h - 1, notExpected)); +} + static void test_imagepeek(skiatest::Reporter* reporter, GrContextFactory* factory) { static const struct { ImageType fType; @@ -159,6 +216,8 @@ static void test_imagepeek(skiatest::Reporter* reporter, GrContextFactory* facto REPORTER_ASSERT(reporter, info.minRowBytes() <= rowBytes); REPORTER_ASSERT(reporter, pmcolor == *(const SkPMColor*)addr); } + + test_image_readpixels(reporter, image, pmcolor); } } |