aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gyp/core.gypi1
-rw-r--r--include/core/SkBitmap.h3
-rw-r--r--include/core/SkImageInfo.h17
-rw-r--r--include/core/SkPixelRef.h32
-rw-r--r--include/core/SkPixmap.h152
-rw-r--r--src/core/SkBitmap.cpp291
-rw-r--r--src/core/SkBitmapScaler.cpp117
-rw-r--r--src/core/SkDraw.cpp13
-rw-r--r--src/core/SkPixelRef.cpp89
-rw-r--r--src/core/SkPixmap.cpp18
-rw-r--r--src/core/SkWriteBuffer.cpp11
-rw-r--r--tests/BitmapCopyTest.cpp17
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;
+ }
}
}