diff options
-rw-r--r-- | gyp/core.gypi | 1 | ||||
-rw-r--r-- | include/core/SkBitmap.h | 3 | ||||
-rw-r--r-- | include/core/SkImageInfo.h | 17 | ||||
-rw-r--r-- | include/core/SkPixelRef.h | 32 | ||||
-rw-r--r-- | include/core/SkPixmap.h | 152 | ||||
-rw-r--r-- | src/core/SkBitmap.cpp | 291 | ||||
-rw-r--r-- | src/core/SkBitmapScaler.cpp | 117 | ||||
-rw-r--r-- | src/core/SkDraw.cpp | 13 | ||||
-rw-r--r-- | src/core/SkPixelRef.cpp | 89 | ||||
-rw-r--r-- | src/core/SkPixmap.cpp | 18 | ||||
-rw-r--r-- | src/core/SkWriteBuffer.cpp | 11 | ||||
-rw-r--r-- | tests/BitmapCopyTest.cpp | 17 |
12 files changed, 556 insertions, 205 deletions
diff --git a/gyp/core.gypi b/gyp/core.gypi index 3a61933356..82c0c43cd2 100644 --- a/gyp/core.gypi +++ b/gyp/core.gypi @@ -157,6 +157,7 @@ '<(skia_src_path)/core/SkPictureShader.cpp', '<(skia_src_path)/core/SkPictureShader.h', '<(skia_src_path)/core/SkPixelRef.cpp', + '<(skia_src_path)/core/SkPixmap.cpp', '<(skia_src_path)/core/SkPoint.cpp', '<(skia_src_path)/core/SkPtrRecorder.cpp', '<(skia_src_path)/core/SkQuadClipper.cpp', diff --git a/include/core/SkBitmap.h b/include/core/SkBitmap.h index a39c8688ae..144e93ba88 100644 --- a/include/core/SkBitmap.h +++ b/include/core/SkBitmap.h @@ -11,6 +11,7 @@ #include "SkColor.h" #include "SkColorTable.h" #include "SkImageInfo.h" +#include "SkPixmap.h" #include "SkPoint.h" #include "SkRefCnt.h" @@ -428,6 +429,8 @@ public: */ bool lockPixelsAreWritable() const; + bool requestLock(SkAutoPixmapUnlock* result) const; + /** Call this to be sure that the bitmap is valid enough to be drawn (i.e. it has non-null pixels, and if required by its colortype, it has a non-null colortable. Returns true if all of the above are met. diff --git a/include/core/SkImageInfo.h b/include/core/SkImageInfo.h index 01318fd1aa..daa50dcdb2 100644 --- a/include/core/SkImageInfo.h +++ b/include/core/SkImageInfo.h @@ -111,6 +111,17 @@ static inline bool SkColorTypeIsValid(unsigned value) { return value <= kLastEnum_SkColorType; } +static inline size_t SkColorTypeComputeOffset(SkColorType ct, int x, int y, size_t rowBytes) { + int shift = 0; + switch (SkColorTypeBytesPerPixel(ct)) { + case 4: shift = 2; break; + case 2: shift = 1; break; + case 1: shift = 0; break; + default: return 0; + } + return y * rowBytes + (x << shift); +} + /////////////////////////////////////////////////////////////////////////////// /** @@ -247,6 +258,12 @@ public: return (size_t)this->minRowBytes64(); } + size_t computeOffset(int x, int y, size_t rowBytes) const { + SkASSERT((unsigned)x < (unsigned)fWidth); + SkASSERT((unsigned)y < (unsigned)fHeight); + return SkColorTypeComputeOffset(fColorType, x, y, rowBytes); + } + bool operator==(const SkImageInfo& other) const { return 0 == memcmp(this, &other, sizeof(other)); } diff --git a/include/core/SkPixelRef.h b/include/core/SkPixelRef.h index 8a1e3db2b6..7c3156ee74 100644 --- a/include/core/SkPixelRef.h +++ b/include/core/SkPixelRef.h @@ -10,15 +10,15 @@ #include "SkAtomics.h" #include "SkBitmap.h" +#include "SkFilterQuality.h" #include "SkImageInfo.h" #include "SkMutex.h" +#include "SkPixmap.h" #include "SkRefCnt.h" #include "SkSize.h" #include "SkString.h" #include "SkTDArray.h" -//#define xed - #ifdef SK_DEBUG /** * Defining SK_IGNORE_PIXELREF_SETPRELOCKED will force all pixelref @@ -193,6 +193,30 @@ public: return this->onRefEncodedData(); } + struct LockRequest { + SkISize fSize; + SkFilterQuality fQuality; + }; + + struct LockResult { + void (*fUnlockProc)(void* ctx); + void* fUnlockContext; + + SkColorTable* fCTable; // should be NULL unless colortype is kIndex8 + const void* fPixels; + size_t fRowBytes; + SkISize fSize; + + void unlock() { + if (fUnlockProc) { + fUnlockProc(fUnlockContext); + fUnlockProc = NULL; // can't unlock twice! + } + } + }; + + bool requestLock(const LockRequest&, LockResult*); + /** Are we really wrapping a texture instead of a bitmap? */ virtual GrTexture* getTexture() { return NULL; } @@ -299,6 +323,8 @@ protected: */ virtual size_t getAllocatedSizeInBytes() const; + virtual bool onRequestLock(const LockRequest&, LockResult*); + /** Return the mutex associated with this pixelref. This value is assigned in the constructor, and cannot change during the lifetime of the object. */ @@ -319,6 +345,8 @@ private: LockRec fRec; int fLockCount; + bool lockPixelsInsideMutex(LockRec* rec); + // Bottom bit indicates the Gen ID is unique. bool genIDIsUnique() const { return SkToBool(fTaggedGenID.load() & 1); } mutable SkAtomic<uint32_t> fTaggedGenID; diff --git a/include/core/SkPixmap.h b/include/core/SkPixmap.h new file mode 100644 index 0000000000..a2ce6cd788 --- /dev/null +++ b/include/core/SkPixmap.h @@ -0,0 +1,152 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkPixmap_DEFINED +#define SkPixmap_DEFINED + +#include "SkImageInfo.h" + +class SkColorTable; + +/** + * Pairs SkImageInfo with actual pixels and rowbytes. This class does not try to manage the + * lifetime of the pixel memory (nor the colortable if provided). + */ +class SkPixmap { +public: + SkPixmap() + : fPixels(NULL), fCTable(NULL), fRowBytes(0), fInfo(SkImageInfo::MakeUnknown(0, 0)) + {} + + SkPixmap(const SkImageInfo& info, const void* addr, size_t rowBytes, + SkColorTable* ctable = NULL) + : fPixels(addr), fCTable(ctable), fRowBytes(rowBytes), fInfo(info) + { + if (kIndex_8_SkColorType == info.colorType()) { + SkASSERT(ctable); + } else { + SkASSERT(NULL == ctable); + } + } + + const SkImageInfo& info() const { return fInfo; } + size_t rowBytes() const { return fRowBytes; } + const void* addr() const { return fPixels; } + SkColorTable* ctable() const { return fCTable; } + + int width() const { return fInfo.width(); } + int height() const { return fInfo.height(); } + SkColorType colorType() const { return fInfo.colorType(); } + SkAlphaType alphaType() const { return fInfo.alphaType(); } + bool isOpaque() const { return fInfo.isOpaque(); } + + int64_t getSafeSize64() const { return fInfo.getSafeSize64(fRowBytes); } + size_t getSafeSize() const { return fInfo.getSafeSize(fRowBytes); } + + const uint32_t* addr32() const { + SkASSERT(4 == SkColorTypeBytesPerPixel(fInfo.colorType())); + return reinterpret_cast<const uint32_t*>(fPixels); + } + + const uint16_t* addr16() const { + SkASSERT(2 == SkColorTypeBytesPerPixel(fInfo.colorType())); + return reinterpret_cast<const uint16_t*>(fPixels); + } + + const uint8_t* addr8() const { + SkASSERT(1 == SkColorTypeBytesPerPixel(fInfo.colorType())); + return reinterpret_cast<const uint8_t*>(fPixels); + } + + const uint32_t* addr32(int x, int y) const { + SkASSERT((unsigned)x < (unsigned)fInfo.width()); + SkASSERT((unsigned)y < (unsigned)fInfo.height()); + return (const uint32_t*)((const char*)this->addr32() + y * fRowBytes + (x << 2)); + } + const uint16_t* addr16(int x, int y) const { + SkASSERT((unsigned)x < (unsigned)fInfo.width()); + SkASSERT((unsigned)y < (unsigned)fInfo.height()); + return (const uint16_t*)((const char*)this->addr16() + y * fRowBytes + (x << 1)); + } + const uint8_t* addr8(int x, int y) const { + SkASSERT((unsigned)x < (unsigned)fInfo.width()); + SkASSERT((unsigned)y < (unsigned)fInfo.height()); + return (const uint8_t*)((const char*)this->addr8() + y * fRowBytes + (x << 0)); + } + const void* addr(int x, int y) const { + return (const char*)fPixels + fInfo.computeOffset(x, y, fRowBytes); + } + + // Writable versions + + void* writable_addr() const { return const_cast<void*>(fPixels); } + uint32_t* writable_addr32(int x, int y) const { + return const_cast<uint32_t*>(this->addr32(x, y)); + } + uint16_t* writable_addr16(int x, int y) const { + return const_cast<uint16_t*>(this->addr16(x, y)); + } + uint8_t* writable_addr8(int x, int y) const { + return const_cast<uint8_t*>(this->addr8(x, y)); + } + +private: + const void* fPixels; + SkColorTable* fCTable; + size_t fRowBytes; + SkImageInfo fInfo; +}; + +///////////////////////////////////////////////////////////////////////////////////////////// + +class SkAutoPixmapUnlock : ::SkNoncopyable { +public: + SkAutoPixmapUnlock() : fUnlockProc(NULL), fIsLocked(false) {} + SkAutoPixmapUnlock(const SkPixmap& pm, void (*unlock)(void*), void* ctx) + : fUnlockProc(unlock), fUnlockContext(ctx), fPixmap(pm), fIsLocked(true) + {} + ~SkAutoPixmapUnlock() { this->unlock(); } + + /** + * Return the currently locked pixmap. Undefined if it has been unlocked. + */ + const SkPixmap& pixmap() const { + SkASSERT(this->isLocked()); + return fPixmap; + } + + bool isLocked() const { return fIsLocked; } + + /** + * Unlocks the pixmap. Can safely be called more than once as it will only call the underlying + * unlock-proc once. + */ + void unlock() { + if (fUnlockProc) { + SkASSERT(fIsLocked); + fUnlockProc(fUnlockContext); + fUnlockProc = NULL; + fIsLocked = false; + } + } + + /** + * If there is a currently locked pixmap, unlock it, then copy the specified pixmap + * and (optional) unlock proc/context. + */ + void reset(const SkPixmap& pm, void (*unlock)(void*), void* ctx); + +private: + void (*fUnlockProc)(void*); + void* fUnlockContext; + SkPixmap fPixmap; + bool fIsLocked; + + friend class SkBitmap; +}; + +#endif diff --git a/src/core/SkBitmap.cpp b/src/core/SkBitmap.cpp index 6f90b71054..01f79f2707 100644 --- a/src/core/SkBitmap.cpp +++ b/src/core/SkBitmap.cpp @@ -430,43 +430,41 @@ bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst, /////////////////////////////////////////////////////////////////////////////// -bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize, - size_t dstRowBytes, bool preserveDstPad) const { +static bool copy_pixels_to(const SkPixmap& src, void* const dst, size_t dstSize, + size_t dstRowBytes, bool preserveDstPad) { + const SkImageInfo& info = src.info(); if (0 == dstRowBytes) { - dstRowBytes = fRowBytes; + dstRowBytes = src.rowBytes(); } - - if (dstRowBytes < fInfo.minRowBytes() || - dst == NULL || (getPixels() == NULL && pixelRef() == NULL)) { + if (dstRowBytes < info.minRowBytes()) { return false; } - if (!preserveDstPad && static_cast<uint32_t>(dstRowBytes) == fRowBytes) { - size_t safeSize = this->getSafeSize(); + if (!preserveDstPad && static_cast<uint32_t>(dstRowBytes) == src.rowBytes()) { + size_t safeSize = src.getSafeSize(); if (safeSize > dstSize || safeSize == 0) return false; else { - SkAutoLockPixels lock(*this); // This implementation will write bytes beyond the end of each row, // excluding the last row, if the bitmap's stride is greater than // strictly required by the current config. - memcpy(dst, getPixels(), safeSize); - + memcpy(dst, src.addr(), safeSize); return true; } } else { // If destination has different stride than us, then copy line by line. - if (fInfo.getSafeSize(dstRowBytes) > dstSize) { + if (info.getSafeSize(dstRowBytes) > dstSize) { return false; } else { // Just copy what we need on each line. - size_t rowBytes = fInfo.minRowBytes(); - SkAutoLockPixels lock(*this); - const uint8_t* srcP = reinterpret_cast<const uint8_t*>(getPixels()); + size_t rowBytes = info.minRowBytes(); + const uint8_t* srcP = reinterpret_cast<const uint8_t*>(src.addr()); uint8_t* dstP = reinterpret_cast<uint8_t*>(dst); - for (int row = 0; row < fInfo.height(); row++, srcP += fRowBytes, dstP += dstRowBytes) { + for (int row = 0; row < info.height(); ++row) { memcpy(dstP, srcP, rowBytes); + srcP += src.rowBytes(); + dstP += dstRowBytes; } return true; @@ -474,6 +472,17 @@ bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize, } } +bool SkBitmap::copyPixelsTo(void* dst, size_t dstSize, size_t dstRB, bool preserveDstPad) const { + if (NULL == dst) { + return false; + } + SkAutoPixmapUnlock result; + if (!this->requestLock(&result)) { + return false; + } + return copy_pixels_to(result.pixmap(), dst, dstSize, dstRB, preserveDstPad); +} + /////////////////////////////////////////////////////////////////////////////// bool SkBitmap::isImmutable() const { @@ -567,20 +576,15 @@ SkColor SkBitmap::getColor(int x, int y) const { return 0; } -bool SkBitmap::ComputeIsOpaque(const SkBitmap& bm) { - SkAutoLockPixels alp(bm); - if (!bm.getPixels()) { - return false; - } +static bool compute_is_opaque(const SkPixmap& pmap) { + const int height = pmap.height(); + const int width = pmap.width(); - const int height = bm.height(); - const int width = bm.width(); - - switch (bm.colorType()) { + switch (pmap.colorType()) { case kAlpha_8_SkColorType: { unsigned a = 0xFF; for (int y = 0; y < height; ++y) { - const uint8_t* row = bm.getAddr8(0, y); + const uint8_t* row = pmap.addr8(0, y); for (int x = 0; x < width; ++x) { a &= row[x]; } @@ -591,12 +595,13 @@ bool SkBitmap::ComputeIsOpaque(const SkBitmap& bm) { return true; } break; case kIndex_8_SkColorType: { - if (!bm.getColorTable()) { + const SkColorTable* ctable = pmap.ctable(); + if (NULL == ctable) { return false; } - const SkPMColor* table = bm.getColorTable()->readColors(); + const SkPMColor* table = ctable->readColors(); SkPMColor c = (SkPMColor)~0; - for (int i = bm.getColorTable()->count() - 1; i >= 0; --i) { + for (int i = ctable->count() - 1; i >= 0; --i) { c &= table[i]; } return 0xFF == SkGetPackedA32(c); @@ -608,7 +613,7 @@ bool SkBitmap::ComputeIsOpaque(const SkBitmap& bm) { case kARGB_4444_SkColorType: { unsigned c = 0xFFFF; for (int y = 0; y < height; ++y) { - const SkPMColor16* row = bm.getAddr16(0, y); + const SkPMColor16* row = pmap.addr16(0, y); for (int x = 0; x < width; ++x) { c &= row[x]; } @@ -622,7 +627,7 @@ bool SkBitmap::ComputeIsOpaque(const SkBitmap& bm) { case kRGBA_8888_SkColorType: { SkPMColor c = (SkPMColor)~0; for (int y = 0; y < height; ++y) { - const SkPMColor* row = bm.getAddr32(0, y); + const SkPMColor* row = pmap.addr32(0, y); for (int x = 0; x < width; ++x) { c &= row[x]; } @@ -638,6 +643,14 @@ bool SkBitmap::ComputeIsOpaque(const SkBitmap& bm) { return false; } +bool SkBitmap::ComputeIsOpaque(const SkBitmap& bm) { + SkAutoPixmapUnlock result; + if (!bm.requestLock(&result)) { + return false; + } + return compute_is_opaque(result.pixmap()); +} + /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// @@ -650,36 +663,13 @@ static uint16_t pack_8888_to_4444(unsigned a, unsigned r, unsigned g, unsigned b return SkToU16(pixel); } -void SkBitmap::internalErase(const SkIRect& area, - U8CPU a, U8CPU r, U8CPU g, U8CPU b) const { -#ifdef SK_DEBUG - SkDEBUGCODE(this->validate();) - SkASSERT(!area.isEmpty()); - { - SkIRect total = { 0, 0, this->width(), this->height() }; - SkASSERT(total.contains(area)); - } -#endif - - switch (fInfo.colorType()) { - case kUnknown_SkColorType: - case kIndex_8_SkColorType: - return; // can't erase. Should we bzero so the memory is not uninitialized? - default: - break; - } - - SkAutoLockPixels alp(*this); - // perform this check after the lock call - if (!this->readyToDraw()) { - return; - } - +static bool internal_erase(const SkPixmap& pmap, const SkIRect& area, + U8CPU a, U8CPU r, U8CPU g, U8CPU b) { int height = area.height(); const int width = area.width(); - const int rowBytes = fRowBytes; + const int rowBytes = pmap.rowBytes(); - switch (this->colorType()) { + switch (pmap.colorType()) { case kGray_8_SkColorType: { if (255 != a) { r = SkMulDiv255Round(r, a); @@ -687,7 +677,7 @@ void SkBitmap::internalErase(const SkIRect& area, b = SkMulDiv255Round(b, a); } int gray = SkComputeLuminance(r, g, b); - uint8_t* p = this->getAddr8(area.fLeft, area.fTop); + uint8_t* p = pmap.writable_addr8(area.fLeft, area.fTop); while (--height >= 0) { memset(p, gray, width); p += rowBytes; @@ -695,7 +685,7 @@ void SkBitmap::internalErase(const SkIRect& area, break; } case kAlpha_8_SkColorType: { - uint8_t* p = this->getAddr8(area.fLeft, area.fTop); + uint8_t* p = pmap.writable_addr8(area.fLeft, area.fTop); while (--height >= 0) { memset(p, a, width); p += rowBytes; @@ -704,7 +694,7 @@ void SkBitmap::internalErase(const SkIRect& area, } case kARGB_4444_SkColorType: case kRGB_565_SkColorType: { - uint16_t* p = this->getAddr16(area.fLeft, area.fTop); + uint16_t* p = pmap.writable_addr16(area.fLeft, area.fTop); uint16_t v; // make rgb premultiplied @@ -714,7 +704,7 @@ void SkBitmap::internalErase(const SkIRect& area, b = SkAlphaMul(b, a); } - if (kARGB_4444_SkColorType == this->colorType()) { + if (kARGB_4444_SkColorType == pmap.colorType()) { v = pack_8888_to_4444(a, r, g, b); } else { v = SkPackRGB16(r >> (8 - SK_R16_BITS), @@ -729,15 +719,15 @@ void SkBitmap::internalErase(const SkIRect& area, } case kBGRA_8888_SkColorType: case kRGBA_8888_SkColorType: { - uint32_t* p = this->getAddr32(area.fLeft, area.fTop); + uint32_t* p = pmap.writable_addr32(area.fLeft, area.fTop); - if (255 != a && kPremul_SkAlphaType == this->alphaType()) { + if (255 != a && kPremul_SkAlphaType == pmap.alphaType()) { r = SkAlphaMul(r, a); g = SkAlphaMul(g, a); b = SkAlphaMul(b, a); } - uint32_t v = kRGBA_8888_SkColorType == this->colorType() ? - SkPackARGB_as_RGBA(a, r, g, b) : SkPackARGB_as_BGRA(a, r, g, b); + uint32_t v = kRGBA_8888_SkColorType == pmap.colorType() ? + SkPackARGB_as_RGBA(a, r, g, b) : SkPackARGB_as_BGRA(a, r, g, b); while (--height >= 0) { sk_memset32(p, v, width); @@ -746,10 +736,38 @@ void SkBitmap::internalErase(const SkIRect& area, break; } default: - return; // no change, so don't call notifyPixelsChanged() + return false; // no change, so don't call notifyPixelsChanged() + } + return true; +} + +void SkBitmap::internalErase(const SkIRect& area, U8CPU a, U8CPU r, U8CPU g, U8CPU b) const { +#ifdef SK_DEBUG + SkDEBUGCODE(this->validate();) + SkASSERT(!area.isEmpty()); + { + SkIRect total = { 0, 0, this->width(), this->height() }; + SkASSERT(total.contains(area)); + } +#endif + + switch (fInfo.colorType()) { + case kUnknown_SkColorType: + case kIndex_8_SkColorType: + // TODO: can we ASSERT that we never get here? + return; // can't erase. Should we bzero so the memory is not uninitialized? + default: + break; + } + + SkAutoPixmapUnlock result; + if (!this->requestLock(&result)) { + return; } - this->notifyPixelsChanged(); + if (internal_erase(result.pixmap(), area, a, r, g, b)) { + this->notifyPixelsChanged(); + } } void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const { @@ -898,18 +916,16 @@ bool SkBitmap::readPixels(const SkImageInfo& requestedDstInfo, void* dstPixels, ////////////// - SkAutoLockPixels alp(*this); - - // since we don't stop creating un-pixeled devices yet, check for no pixels here - if (NULL == this->getPixels()) { + SkAutoPixmapUnlock result; + if (!this->requestLock(&result)) { return false; } + const SkPixmap& pmap = result.pixmap(); + const SkImageInfo srcInfo = pmap.info().makeWH(dstInfo.width(), dstInfo.height()); - const SkImageInfo srcInfo = this->info().makeWH(dstInfo.width(), dstInfo.height()); - - const void* srcPixels = this->getAddr(srcR.x(), srcR.y()); - return SkPixelInfo::CopyPixels(dstInfo, dstPixels, dstRB, srcInfo, srcPixels, this->rowBytes(), - this->getColorTable()); + const void* srcPixels = pmap.addr(srcR.x(), srcR.y()); + return SkPixelInfo::CopyPixels(dstInfo, dstPixels, dstRB, srcInfo, srcPixels, pmap.rowBytes(), + pmap.ctable()); } bool SkBitmap::copyTo(SkBitmap* dst, SkColorType dstColorType, Allocator* alloc) const { @@ -1048,35 +1064,28 @@ bool SkBitmap::deepCopyTo(SkBitmap* dst) const { /////////////////////////////////////////////////////////////////////////////// -static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha, - int alphaRowBytes) { - SkASSERT(alpha != NULL); - SkASSERT(alphaRowBytes >= src.width()); - - SkColorType colorType = src.colorType(); - int w = src.width(); - int h = src.height(); - size_t rb = src.rowBytes(); - - SkAutoLockPixels alp(src); - if (!src.readyToDraw()) { - // zero out the alpha buffer and return - while (--h >= 0) { - memset(alpha, 0, w); - alpha += alphaRowBytes; - } - return false; +static void rect_memset(uint8_t* array, U8CPU value, SkISize size, size_t rowBytes) { + for (int y = 0; y < size.height(); ++y) { + memset(array, value, size.width()); + array += rowBytes; } +} + +static void get_bitmap_alpha(const SkPixmap& pmap, uint8_t* SK_RESTRICT alpha, int alphaRowBytes) { + SkColorType colorType = pmap.colorType(); + int w = pmap.width(); + int h = pmap.height(); + size_t rb = pmap.rowBytes(); - if (kAlpha_8_SkColorType == colorType && !src.isOpaque()) { - const uint8_t* s = src.getAddr8(0, 0); + if (kAlpha_8_SkColorType == colorType && !pmap.isOpaque()) { + const uint8_t* s = pmap.addr8(0, 0); while (--h >= 0) { memcpy(alpha, s, w); s += rb; alpha += alphaRowBytes; } - } else if (kN32_SkColorType == colorType && !src.isOpaque()) { - const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0); + } else if (kN32_SkColorType == colorType && !pmap.isOpaque()) { + const SkPMColor* SK_RESTRICT s = pmap.addr32(0, 0); while (--h >= 0) { for (int x = 0; x < w; x++) { alpha[x] = SkGetPackedA32(s[x]); @@ -1084,8 +1093,8 @@ static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha, s = (const SkPMColor*)((const char*)s + rb); alpha += alphaRowBytes; } - } else if (kARGB_4444_SkColorType == colorType && !src.isOpaque()) { - const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0); + } else if (kARGB_4444_SkColorType == colorType && !pmap.isOpaque()) { + const SkPMColor16* SK_RESTRICT s = pmap.addr16(0, 0); while (--h >= 0) { for (int x = 0; x < w; x++) { alpha[x] = SkPacked4444ToA32(s[x]); @@ -1093,11 +1102,11 @@ static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha, s = (const SkPMColor16*)((const char*)s + rb); alpha += alphaRowBytes; } - } else if (kIndex_8_SkColorType == colorType && !src.isOpaque()) { - SkColorTable* ct = src.getColorTable(); + } else if (kIndex_8_SkColorType == colorType && !pmap.isOpaque()) { + const SkColorTable* ct = pmap.ctable(); if (ct) { const SkPMColor* SK_RESTRICT table = ct->readColors(); - const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0); + const uint8_t* SK_RESTRICT s = pmap.addr8(0, 0); while (--h >= 0) { for (int x = 0; x < w; x++) { alpha[x] = SkGetPackedA32(table[s[x]]); @@ -1107,8 +1116,20 @@ static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha, } } } else { // src is opaque, so just fill alpha[] with 0xFF - memset(alpha, 0xFF, h * alphaRowBytes); + rect_memset(alpha, 0xFF, pmap.info().dimensions(), alphaRowBytes); + } +} + +static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha, int alphaRowBytes) { + SkASSERT(alpha != NULL); + SkASSERT(alphaRowBytes >= src.width()); + + SkAutoPixmapUnlock apl; + if (!src.requestLock(&apl)) { + rect_memset(alpha, 0, src.info().dimensions(), alphaRowBytes); + return false; } + get_bitmap_alpha(apl.pixmap(), alpha, alphaRowBytes); return true; } @@ -1183,17 +1204,11 @@ bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint, /////////////////////////////////////////////////////////////////////////////// -void SkBitmap::WriteRawPixels(SkWriteBuffer* buffer, const SkBitmap& bitmap) { - const SkImageInfo info = bitmap.info(); - SkAutoLockPixels alp(bitmap); - if (0 == info.width() || 0 == info.height() || NULL == bitmap.getPixels()) { - buffer->writeUInt(0); // instead of snugRB, signaling no pixels - return; - } - +static void write_raw_pixels(SkWriteBuffer* buffer, const SkPixmap& pmap) { + const SkImageInfo& info = pmap.info(); const size_t snugRB = info.width() * info.bytesPerPixel(); - const char* src = (const char*)bitmap.getPixels(); - const size_t ramRB = bitmap.rowBytes(); + const char* src = (const char*)pmap.addr(); + const size_t ramRB = pmap.rowBytes(); buffer->write32(SkToU32(snugRB)); info.flatten(*buffer); @@ -1208,7 +1223,7 @@ void SkBitmap::WriteRawPixels(SkWriteBuffer* buffer, const SkBitmap& bitmap) { } buffer->writeByteArray(storage.get(), size); - SkColorTable* ct = bitmap.getColorTable(); + const SkColorTable* ct = pmap.ctable(); if (kIndex_8_SkColorType == info.colorType() && ct) { buffer->writeBool(true); ct->writeToBuffer(*buffer); @@ -1217,6 +1232,22 @@ void SkBitmap::WriteRawPixels(SkWriteBuffer* buffer, const SkBitmap& bitmap) { } } +void SkBitmap::WriteRawPixels(SkWriteBuffer* buffer, const SkBitmap& bitmap) { + const SkImageInfo info = bitmap.info(); + if (0 == info.width() || 0 == info.height() || NULL == bitmap.pixelRef()) { + buffer->writeUInt(0); // instead of snugRB, signaling no pixels + return; + } + + SkAutoPixmapUnlock result; + if (!bitmap.requestLock(&result)) { + buffer->writeUInt(0); // instead of snugRB, signaling no pixels + return; + } + + write_raw_pixels(buffer, result.pixmap()); +} + bool SkBitmap::ReadRawPixels(SkReadBuffer* buffer, SkBitmap* bitmap) { const size_t snugRB = buffer->readUInt(); if (0 == snugRB) { // no pixels @@ -1364,6 +1395,34 @@ void SkBitmap::toString(SkString* str) const { /////////////////////////////////////////////////////////////////////////////// +bool SkBitmap::requestLock(SkAutoPixmapUnlock* result) const { + SkASSERT(result); + + SkPixelRef* pr = fPixelRef; + if (NULL == pr) { + return false; + } + + SkPixelRef::LockRequest req = { fInfo.dimensions(), kNone_SkFilterQuality }; + SkPixelRef::LockResult res; + if (pr->requestLock(req, &res)) { + // The bitmap may be a subset of the pixelref's dimensions + SkASSERT(fPixelRefOrigin.x() + fInfo.width() <= res.fSize.width()); + SkASSERT(fPixelRefOrigin.y() + fInfo.height() <= res.fSize.height()); + const void* addr = (const char*)res.fPixels + SkColorTypeComputeOffset(fInfo.colorType(), + fPixelRefOrigin.x(), + fPixelRefOrigin.y(), + res.fRowBytes); + + result->reset(SkPixmap(this->info(), addr, res.fRowBytes, res.fCTable), + res.fUnlockProc, res.fUnlockContext); + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + #ifdef SK_DEBUG void SkImageInfo::validate() const { SkASSERT(fWidth >= 0); diff --git a/src/core/SkBitmapScaler.cpp b/src/core/SkBitmapScaler.cpp index 4bd3cc732d..9ce4171879 100644 --- a/src/core/SkBitmapScaler.cpp +++ b/src/core/SkBitmapScaler.cpp @@ -1,3 +1,10 @@ +/* + * 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 "SkBitmapScaler.h" #include "SkBitmapFilter.h" #include "SkRect.h" @@ -241,74 +248,78 @@ static SkBitmapScaler::ResizeMethod ResizeMethodToAlgorithmMethod( } } -// static +static bool resize(const SkPixmap& pmap, SkBitmap* resultPtr, SkBitmapScaler::ResizeMethod method, + float destWidth, float destHeight, SkBitmap::Allocator* allocator, + const SkConvolutionProcs& convolveProcs) { + const SkRect destSubset = SkRect::MakeWH(destWidth, destHeight); + SkResizeFilter filter(method, pmap.width(), pmap.height(), + destWidth, destHeight, destSubset, convolveProcs); + + // Get a source bitmap encompassing this touched area. We construct the + // offsets and row strides such that it looks like a new bitmap, while + // referring to the old data. + const uint8_t* sourceSubset = reinterpret_cast<const uint8_t*>(pmap.addr()); + + // Convolve into the result. + SkBitmap result; + result.setInfo(SkImageInfo::MakeN32(SkScalarCeilToInt(destSubset.width()), + SkScalarCeilToInt(destSubset.height()), + pmap.alphaType())); + result.allocPixels(allocator, NULL); + if (!result.readyToDraw()) { + return false; + } + + BGRAConvolve2D(sourceSubset, static_cast<int>(pmap.rowBytes()), + !pmap.isOpaque(), filter.xFilter(), filter.yFilter(), + static_cast<int>(result.rowBytes()), + static_cast<unsigned char*>(result.getPixels()), + convolveProcs, true); + + *resultPtr = result; + resultPtr->lockPixels(); + SkASSERT(resultPtr->getPixels()); + return true; +} + bool SkBitmapScaler::Resize(SkBitmap* resultPtr, const SkBitmap& source, ResizeMethod method, float destWidth, float destHeight, SkBitmap::Allocator* allocator) { + if (source.colorType() != kN32_SkColorType) { + return false; + } - SkConvolutionProcs convolveProcs= { 0, NULL, NULL, NULL, NULL }; - PlatformConvolutionProcs(&convolveProcs); - - SkRect destSubset = { 0, 0, destWidth, destHeight }; + SkConvolutionProcs convolveProcs= { 0, NULL, NULL, NULL, NULL }; + PlatformConvolutionProcs(&convolveProcs); - // Ensure that the ResizeMethod enumeration is sound. - SkASSERT(((RESIZE_FIRST_QUALITY_METHOD <= method) && + // Ensure that the ResizeMethod enumeration is sound. + SkASSERT(((RESIZE_FIRST_QUALITY_METHOD <= method) && (method <= RESIZE_LAST_QUALITY_METHOD)) || ((RESIZE_FIRST_ALGORITHM_METHOD <= method) && (method <= RESIZE_LAST_ALGORITHM_METHOD))); - // If the size of source or destination is 0, i.e. 0x0, 0xN or Nx0, just - // return empty. - if (source.width() < 1 || source.height() < 1 || - destWidth < 1 || destHeight < 1) { - // todo: seems like we could handle negative dstWidth/Height, since that - // is just a negative scale (flip) - return false; - } + // If the size of source or destination is 0, i.e. 0x0, 0xN or Nx0, just + // return empty. + if (source.width() < 1 || source.height() < 1 || destWidth < 1 || destHeight < 1) { + // todo: seems like we could handle negative dstWidth/Height, since that + // is just a negative scale (flip) + return false; + } - method = ResizeMethodToAlgorithmMethod(method); + method = ResizeMethodToAlgorithmMethod(method); - // Check that we deal with an "algorithm methods" from this point onward. - SkASSERT((SkBitmapScaler::RESIZE_FIRST_ALGORITHM_METHOD <= method) && + // Check that we deal with an "algorithm methods" from this point onward. + SkASSERT((SkBitmapScaler::RESIZE_FIRST_ALGORITHM_METHOD <= method) && (method <= SkBitmapScaler::RESIZE_LAST_ALGORITHM_METHOD)); - SkAutoLockPixels locker(source); - if (!source.readyToDraw() || - source.colorType() != kN32_SkColorType) { - return false; - } - - SkResizeFilter filter(method, source.width(), source.height(), - destWidth, destHeight, destSubset, convolveProcs); - - // Get a source bitmap encompassing this touched area. We construct the - // offsets and row strides such that it looks like a new bitmap, while - // referring to the old data. - const unsigned char* sourceSubset = - reinterpret_cast<const unsigned char*>(source.getPixels()); - - // Convolve into the result. - SkBitmap result; - result.setInfo(SkImageInfo::MakeN32(SkScalarCeilToInt(destSubset.width()), - SkScalarCeilToInt(destSubset.height()), - source.alphaType())); - result.allocPixels(allocator, NULL); - if (!result.readyToDraw()) { - return false; - } - - BGRAConvolve2D(sourceSubset, static_cast<int>(source.rowBytes()), - !source.isOpaque(), filter.xFilter(), filter.yFilter(), - static_cast<int>(result.rowBytes()), - static_cast<unsigned char*>(result.getPixels()), - convolveProcs, true); - - *resultPtr = result; - resultPtr->lockPixels(); - SkASSERT(resultPtr->getPixels()); - return true; + SkAutoPixmapUnlock result; + if (!source.requestLock(&result)) { + return false; + } + return resize(result.pixmap(), resultPtr, method, destWidth, destHeight, allocator, + convolveProcs); } // static -- simpler interface to the resizer; returns a default bitmap if scaling diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp index ab2d5cccb3..918ef765eb 100644 --- a/src/core/SkDraw.cpp +++ b/src/core/SkDraw.cpp @@ -1156,16 +1156,17 @@ void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap, int ix = SkScalarRoundToInt(fMatrix->getTranslateX()); int iy = SkScalarRoundToInt(fMatrix->getTranslateY()); - SkAutoLockPixels alp(bitmap); - if (!bitmap.readyToDraw()) { + SkAutoPixmapUnlock result; + if (!bitmap.requestLock(&result)) { return; } - + const SkPixmap& pmap = result.pixmap(); SkMask mask; - mask.fBounds.set(ix, iy, ix + bitmap.width(), iy + bitmap.height()); + mask.fBounds.set(ix, iy, ix + pmap.width(), iy + pmap.height()); mask.fFormat = SkMask::kA8_Format; - mask.fRowBytes = SkToU32(bitmap.rowBytes()); - mask.fImage = bitmap.getAddr8(0, 0); + mask.fRowBytes = SkToU32(pmap.rowBytes()); + // fImage is typed as writable, but in this case it is used read-only + mask.fImage = (uint8_t*)pmap.addr8(0, 0); this->drawDevMask(mask, paint); } else { // need to xform the bitmap first diff --git a/src/core/SkPixelRef.cpp b/src/core/SkPixelRef.cpp index e7f5c8d947..90e2a40ea4 100644 --- a/src/core/SkPixelRef.cpp +++ b/src/core/SkPixelRef.cpp @@ -172,27 +172,48 @@ void SkPixelRef::setPreLocked(void* pixels, size_t rowBytes, SkColorTable* ctabl #endif } +bool SkPixelRef::lockPixelsInsideMutex(LockRec* rec) { + fMutex->assertHeld(); + + // For historical reasons, we always inc fLockCount, even if we return false. + // It would be nice to change this (it seems), and only inc if we actually succeed... + if (1 == ++fLockCount) { + SkASSERT(fRec.isZero()); + + LockRec rec; + if (!this->onNewLockPixels(&rec)) { + fLockCount -= 1; // we return fLockCount unchanged if we fail. + return false; + } + SkASSERT(!rec.isZero()); // else why did onNewLock return true? + fRec = rec; + } + *rec = fRec; + return true; +} + bool SkPixelRef::lockPixels(LockRec* rec) { SkASSERT(!fPreLocked || SKPIXELREF_PRELOCKED_LOCKCOUNT == fLockCount); - if (!fPreLocked) { + if (fPreLocked) { + *rec = fRec; + return true; + } else { TRACE_EVENT_BEGIN0("skia", "SkPixelRef::lockPixelsMutex"); SkAutoMutexAcquire ac(*fMutex); TRACE_EVENT_END0("skia", "SkPixelRef::lockPixelsMutex"); - - if (1 == ++fLockCount) { - SkASSERT(fRec.isZero()); - - LockRec rec; - if (!this->onNewLockPixels(&rec)) { - return false; - } - SkASSERT(!rec.isZero()); // else why did onNewLock return true? - fRec = rec; + SkDEBUGCODE(int oldCount = fLockCount;) + bool success = this->lockPixelsInsideMutex(rec); + // lockPixelsInsideMutex only increments the count if it succeeds. + SkASSERT(oldCount + (int)success == fLockCount); + + if (!success) { + // For compatibility with SkBitmap calling lockPixels, we still want to increment + // fLockCount even if we failed. If we updated SkBitmap we could remove this oddity. + fLockCount += 1; } + return success; } - *rec = fRec; - return true; } bool SkPixelRef::lockPixels() { @@ -219,6 +240,26 @@ void SkPixelRef::unlockPixels() { } } +bool SkPixelRef::requestLock(const LockRequest& request, LockResult* result) { + SkASSERT(result); + if (request.fSize.isEmpty()) { + return false; + } + + if (fPreLocked) { + result->fUnlockProc = NULL; + result->fUnlockContext = NULL; + result->fCTable = fRec.fColorTable; + result->fPixels = fRec.fPixels; + result->fRowBytes = fRec.fRowBytes; + result->fSize.set(fInfo.width(), fInfo.height()); + return true; + } else { + SkAutoMutexAcquire ac(*fMutex); + return this->onRequestLock(request, result); + } +} + bool SkPixelRef::lockPixelsAreWritable() const { return this->onLockPixelsAreWritable(); } @@ -291,6 +332,8 @@ bool SkPixelRef::readPixels(SkBitmap* dst, const SkIRect* subset) { return this->onReadPixels(dst, subset); } +/////////////////////////////////////////////////////////////////////////////////////////////////// + bool SkPixelRef::onReadPixels(SkBitmap* dst, const SkIRect* subset) { return false; } @@ -308,3 +351,23 @@ size_t SkPixelRef::getAllocatedSizeInBytes() const { return 0; } +static void unlock_legacy_result(void* ctx) { + SkPixelRef* pr = (SkPixelRef*)ctx; + pr->unlockPixels(); + pr->unref(); // balancing the Ref in onRequestLoc +} + +bool SkPixelRef::onRequestLock(const LockRequest& request, LockResult* result) { + LockRec rec; + if (!this->lockPixelsInsideMutex(&rec)) { + return false; + } + + result->fUnlockProc = unlock_legacy_result; + result->fUnlockContext = SkRef(this); // this is balanced in our fUnlockProc + result->fCTable = rec.fColorTable; + result->fPixels = rec.fPixels; + result->fRowBytes = rec.fRowBytes; + result->fSize.set(fInfo.width(), fInfo.height()); + return true; +} diff --git a/src/core/SkPixmap.cpp b/src/core/SkPixmap.cpp new file mode 100644 index 0000000000..2d15e8a185 --- /dev/null +++ b/src/core/SkPixmap.cpp @@ -0,0 +1,18 @@ +/* + * 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 "SkPixmap.h" + +void SkAutoPixmapUnlock::reset(const SkPixmap& pm, void (*unlock)(void*), void* ctx) { + SkASSERT(pm.addr() != NULL); + + this->unlock(); + fPixmap = pm; + fUnlockProc = unlock; + fUnlockContext = ctx; + fIsLocked = true; +} diff --git a/src/core/SkWriteBuffer.cpp b/src/core/SkWriteBuffer.cpp index 6048c9f5c2..d2eb8c5d8d 100644 --- a/src/core/SkWriteBuffer.cpp +++ b/src/core/SkWriteBuffer.cpp @@ -198,12 +198,13 @@ void SkWriteBuffer::writeBitmap(const SkBitmap& bitmap) { } // see if the caller wants to manually encode - if (fPixelSerializer) { + SkAutoPixmapUnlock result; + if (fPixelSerializer && bitmap.requestLock(&result)) { + const SkPixmap& pmap = result.pixmap(); SkASSERT(NULL == fBitmapHeap); - SkAutoLockPixels alp(bitmap); - SkAutoDataUnref data(fPixelSerializer->encodePixels(bitmap.info(), - bitmap.getPixels(), - bitmap.rowBytes())); + SkAutoDataUnref data(fPixelSerializer->encodePixels(pmap.info(), + pmap.addr(), + pmap.rowBytes())); if (data.get() != NULL) { // if we have to "encode" the bitmap, then we assume there is no // offset to share, since we are effectively creating a new pixelref diff --git a/tests/BitmapCopyTest.cpp b/tests/BitmapCopyTest.cpp index eb7c663254..1cb2944e99 100644 --- a/tests/BitmapCopyTest.cpp +++ b/tests/BitmapCopyTest.cpp @@ -161,18 +161,15 @@ static void reportCopyVerification(const SkBitmap& bm1, const SkBitmap& bm2, Coordinates& coords, const char* msg, skiatest::Reporter* reporter){ - bool success = true; - // Confirm all pixels in the list match. for (int i = 0; i < coords.length; ++i) { - success = success && - (getPixel(coords[i]->fX, coords[i]->fY, bm1) == - getPixel(coords[i]->fX, coords[i]->fY, bm2)); - } - - if (!success) { - ERRORF(reporter, "%s [colortype = %s]", msg, - gColorTypeName[bm1.colorType()]); + uint32_t p1 = getPixel(coords[i]->fX, coords[i]->fY, bm1); + uint32_t p2 = getPixel(coords[i]->fX, coords[i]->fY, bm2); +// SkDebugf("[%d] (%d %d) p1=%x p2=%x\n", i, coords[i]->fX, coords[i]->fY, p1, p2); + if (p1 != p2) { + ERRORF(reporter, "%s [colortype = %s]", msg, gColorTypeName[bm1.colorType()]); + break; + } } } |