diff options
author | reed <reed@google.com> | 2015-05-22 08:06:21 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-05-22 08:06:22 -0700 |
commit | 92fc2ae58331662ec411a048686cb4801e0a909a (patch) | |
tree | 8b49c125bd366e77fcdff77788e6965912758dc9 /src | |
parent | eb1cb5c5b50febad115d859faca91d2d6af3fff2 (diff) |
add SkPixmap and external locking to bitmaps
BUG=skia:
Review URL: https://codereview.chromium.org/1074983003
Diffstat (limited to 'src')
-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 |
6 files changed, 346 insertions, 193 deletions
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 |