diff options
author | Torne (Richard Coles) <torne@google.com> | 2013-12-18 16:25:41 +0000 |
---|---|---|
committer | Torne (Richard Coles) <torne@google.com> | 2013-12-18 16:25:41 +0000 |
commit | 13b628ac69a70ac0044585997f16633af83c353a (patch) | |
tree | 3c0229f3d6163ee2f43fb32a886cb15deb58227d | |
parent | ea135c1c1fb61ba5ff8063bdf74ffbd3c84f1942 (diff) | |
parent | 24df3e66d89fff6e91ba76a9fe1a4f1e7c413402 (diff) | |
download | src-13b628ac69a70ac0044585997f16633af83c353a.tar.gz |
Merge from Chromium at DEPS revision 240154
This commit was generated by merge_to_master.py.
Change-Id: I46e962d43e6d822150c4556f33ed018d9f4225e3
197 files changed, 3741 insertions, 2955 deletions
diff --git a/animator/SkDrawBitmap.cpp b/animator/SkDrawBitmap.cpp index 568401d0..327e8136 100644 --- a/animator/SkDrawBitmap.cpp +++ b/animator/SkDrawBitmap.cpp @@ -75,11 +75,10 @@ void SkDrawBitmap::dump(SkAnimateMaker* maker) { const char* formatName; switch (format) { case 0: formatName = "none"; break; - case 1: formatName = "A1"; break; - case 2: formatName = "A8"; break; - case 3: formatName = "Index8"; break; - case 4: formatName = "RGB16"; break; - case 5: formatName = "RGB32"; break; + case 1: formatName = "A8"; break; + case 2: formatName = "Index8"; break; + case 3: formatName = "RGB16"; break; + case 4: formatName = "RGB32"; break; } SkDebugf("format=\"%s\" />\n", formatName); } diff --git a/animator/SkScript.cpp b/animator/SkScript.cpp index c74b195f..934b0abe 100644 --- a/animator/SkScript.cpp +++ b/animator/SkScript.cpp @@ -1532,11 +1532,11 @@ bool SkScriptEngine::ConvertTo(SkScriptEngine* engine, SkDisplayTypes toType, Sk SkString* strPtr = new SkString(); SkASSERT(engine); engine->track(strPtr); - if (type == SkType_Int) + if (type == SkType_Int) { strPtr->appendS32(operand.fS32); - else if (type == SkType_Displayable) + } else if (type == SkType_Displayable) { SkASSERT(0); // must call through instance version instead of static version - else { + } else { if (type != SkType_Float) { success = false; break; diff --git a/core/SkAdvancedTypefaceMetrics.cpp b/core/SkAdvancedTypefaceMetrics.cpp index c7ed34df..ce64a42d 100644 --- a/core/SkAdvancedTypefaceMetrics.cpp +++ b/core/SkAdvancedTypefaceMetrics.cpp @@ -10,8 +10,6 @@ #include "SkAdvancedTypefaceMetrics.h" #include "SkTypes.h" -SK_DEFINE_INST_COUNT(SkAdvancedTypefaceMetrics) - #if defined(SK_BUILD_FOR_WIN) #include <dwrite.h> #endif diff --git a/core/SkBBoxHierarchy.cpp b/core/SkBBoxHierarchy.cpp deleted file mode 100644 index 5232fb7c..00000000 --- a/core/SkBBoxHierarchy.cpp +++ /dev/null @@ -1,11 +0,0 @@ - -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkBBoxHierarchy.h" - -SK_DEFINE_INST_COUNT(SkBBoxHierarchy) diff --git a/core/SkBitmap.cpp b/core/SkBitmap.cpp index 429d0921..25a6b1db 100644 --- a/core/SkBitmap.cpp +++ b/core/SkBitmap.cpp @@ -24,8 +24,6 @@ #include "SkPackBits.h" #include <new> -SK_DEFINE_INST_COUNT(SkBitmap::Allocator) - static bool isPos32Bits(const Sk64& value) { return !value.isNeg() && value.is32(); } @@ -161,7 +159,6 @@ int SkBitmap::ComputeBytesPerPixel(SkBitmap::Config config) { int bpp; switch (config) { case kNo_Config: - case kA1_Config: bpp = 0; // not applicable break; case kA8_Config: @@ -194,11 +191,6 @@ size_t SkBitmap::ComputeRowBytes(Config c, int width) { switch (c) { case kNo_Config: break; - case kA1_Config: - rowBytes.set(width); - rowBytes.add(7); - rowBytes.shiftRight(3); - break; case kA8_Config: case kIndex8_Config: rowBytes.set(width); @@ -275,7 +267,6 @@ static bool validate_alphaType(SkBitmap::Config config, SkAlphaType alphaType, case SkBitmap::kNo_Config: alphaType = kIgnore_SkAlphaType; break; - case SkBitmap::kA1_Config: case SkBitmap::kA8_Config: if (kUnpremul_SkAlphaType == alphaType) { alphaType = kPremul_SkAlphaType; @@ -291,6 +282,8 @@ static bool validate_alphaType(SkBitmap::Config config, SkAlphaType alphaType, case SkBitmap::kRGB_565_Config: alphaType = kOpaque_SkAlphaType; break; + default: + return false; } if (canonical) { *canonical = alphaType; @@ -368,6 +361,48 @@ void SkBitmap::updatePixelsFromRef() const { } } +static bool config_to_colorType(SkBitmap::Config config, SkColorType* ctOut) { + SkColorType ct; + switch (config) { + case SkBitmap::kA8_Config: + ct = kAlpha_8_SkColorType; + break; + case SkBitmap::kIndex8_Config: + ct = kIndex_8_SkColorType; + break; + case SkBitmap::kRGB_565_Config: + ct = kRGB_565_SkColorType; + break; + case SkBitmap::kARGB_4444_Config: + ct = kARGB_4444_SkColorType; + break; + case SkBitmap::kARGB_8888_Config: + ct = kPMColor_SkColorType; + break; + case SkBitmap::kNo_Config: + default: + return false; + } + if (ctOut) { + *ctOut = ct; + } + return true; +} + +bool SkBitmap::asImageInfo(SkImageInfo* info) const { + SkColorType ct; + if (!config_to_colorType(this->config(), &ct)) { + return false; + } + if (info) { + info->fWidth = fWidth; + info->fHeight = fHeight; + info->fAlphaType = this->alphaType(); + info->fColorType = ct; + } + return true; +} + SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, size_t offset) { // do this first, we that we never have a non-zero offset with a null ref if (NULL == pr) { @@ -606,8 +641,6 @@ void* SkBitmap::getAddr(int x, int y) const { case SkBitmap::kIndex8_Config: base += x; break; - case SkBitmap::kA1_Config: - base += x >> 3; break; default: SkDEBUGFAIL("Can't return addr for config"); @@ -623,15 +656,6 @@ SkColor SkBitmap::getColor(int x, int y) const { SkASSERT((unsigned)y < (unsigned)this->height()); switch (this->config()) { - case SkBitmap::kA1_Config: { - uint8_t* addr = this->getAddr1(x, y); - uint8_t mask = 1 << (7 - (x % 8)); - if (addr[0] & mask) { - return SK_ColorBLACK; - } else { - return 0; - } - } case SkBitmap::kA8_Config: { uint8_t* addr = this->getAddr8(x, y); return SkColorSetA(0, addr[0]); @@ -654,6 +678,7 @@ SkColor SkBitmap::getColor(int x, int y) const { return SkUnPreMultiply::PMColorToColor(addr[0]); } case kNo_Config: + default: SkASSERT(false); return 0; } @@ -671,9 +696,6 @@ bool SkBitmap::ComputeIsOpaque(const SkBitmap& bm) { const int width = bm.width(); switch (bm.config()) { - case SkBitmap::kA1_Config: { - // TODO - } break; case SkBitmap::kA8_Config: { unsigned a = 0xFF; for (int y = 0; y < height; ++y) { @@ -779,38 +801,6 @@ void SkBitmap::internalErase(const SkIRect& area, } switch (fConfig) { - case kA1_Config: { - uint8_t* p = this->getAddr1(area.fLeft, area.fTop); - const int left = area.fLeft >> 3; - const int right = area.fRight >> 3; - - int middle = right - left - 1; - - uint8_t leftMask = 0xFF >> (area.fLeft & 7); - uint8_t rightMask = ~(0xFF >> (area.fRight & 7)); - if (left == right) { - leftMask &= rightMask; - rightMask = 0; - } - - a = (a >> 7) ? 0xFF : 0; - while (--height >= 0) { - uint8_t* startP = p; - - *p = (*p & ~leftMask) | (a & leftMask); - p++; - if (middle > 0) { - memset(p, a, middle); - p += middle; - } - if (rightMask) { - *p = (*p & ~rightMask) | (a & rightMask); - } - - p = startP + rowBytes; - } - break; - } case kA8_Config: { uint8_t* p = this->getAddr8(area.fLeft, area.fTop); while (--height >= 0) { @@ -896,7 +886,6 @@ static size_t get_sub_offset(const SkBitmap& bm, int x, int y) { break; case SkBitmap::kNo_Config: - case SkBitmap::kA1_Config: default: return SUB_OFFSET_FAILURE; } @@ -939,8 +928,6 @@ bool get_upper_left_from_offset(SkBitmap::Config config, size_t offset, size_t r case SkBitmap::kNo_Config: // Fall through. - case SkBitmap::kA1_Config: - // Fall through. default: return false; } @@ -1021,7 +1008,6 @@ bool SkBitmap::canCopyTo(Config dstConfig) const { case kRGB_565_Config: case kARGB_8888_Config: break; - case kA1_Config: case kIndex8_Config: if (!sameConfigs) { return false; @@ -1032,12 +1018,6 @@ bool SkBitmap::canCopyTo(Config dstConfig) const { default: return false; } - - // do not copy src if srcConfig == kA1_Config while dstConfig != kA1_Config - if (this->config() == kA1_Config && !sameConfigs) { - return false; - } - return true; } @@ -1619,7 +1599,11 @@ void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) { buffer.validate((width >= 0) && (height >= 0) && (rowBytes >= 0) && SkIsValidConfig(config) && validate_alphaType(config, alphaType)); - this->setConfig(config, width, height, rowBytes, alphaType); + bool configIsValid = this->setConfig(config, width, height, rowBytes, alphaType); + // Note : Using (fRowBytes >= (fWidth * fBytesPerPixel)) in the following test can create false + // positives if the multiplication causes an integer overflow. Use the division instead. + buffer.validate(configIsValid && (fBytesPerPixel > 0) && + ((fRowBytes / fBytesPerPixel) >= fWidth)); int reftype = buffer.readInt(); if (buffer.validate((SERIALIZE_PIXELTYPE_REF_DATA == reftype) || @@ -1628,6 +1612,10 @@ void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) { case SERIALIZE_PIXELTYPE_REF_DATA: { size_t offset = buffer.readUInt(); SkPixelRef* pr = buffer.readPixelRef(); + if (!buffer.validate((NULL == pr) || + (pr->getAllocatedSizeInBytes() >= (offset + this->getSafeSize())))) { + offset = 0; + } SkSafeUnref(this->setPixelRef(pr, offset)); break; } @@ -1683,7 +1671,7 @@ void SkBitmap::validate() const { void SkBitmap::toString(SkString* str) const { static const char* gConfigNames[kConfigCount] = { - "NONE", "A1", "A8", "INDEX8", "565", "4444", "8888" + "NONE", "A8", "INDEX8", "565", "4444", "8888" }; str->appendf("bitmap: ((%d, %d) %s", this->width(), this->height(), diff --git a/core/SkBitmapDevice.cpp b/core/SkBitmapDevice.cpp index 369f10c1..1668618c 100644 --- a/core/SkBitmapDevice.cpp +++ b/core/SkBitmapDevice.cpp @@ -11,8 +11,6 @@ #include "SkRasterClip.h" #include "SkShader.h" -SK_DEFINE_INST_COUNT(SkBitmapDevice) - #define CHECK_FOR_ANNOTATION(paint) \ do { if (paint.getAnnotation()) { return; } } while (0) @@ -29,7 +27,10 @@ SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap, const SkDeviceProperties& SkBitmapDevice::SkBitmapDevice(SkBitmap::Config config, int width, int height, bool isOpaque) { fBitmap.setConfig(config, width, height, 0, isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType); - fBitmap.allocPixels(); + if (!fBitmap.allocPixels()) { + fBitmap.setConfig(config, 0, 0, 0, isOpaque ? + kOpaque_SkAlphaType : kPremul_SkAlphaType); + } if (!isOpaque) { fBitmap.eraseColor(SK_ColorTRANSPARENT); } @@ -41,7 +42,10 @@ SkBitmapDevice::SkBitmapDevice(SkBitmap::Config config, int width, int height, b fBitmap.setConfig(config, width, height, 0, isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType); - fBitmap.allocPixels(); + if (!fBitmap.allocPixels()) { + fBitmap.setConfig(config, 0, 0, 0, isOpaque ? + kOpaque_SkAlphaType : kPremul_SkAlphaType); + } if (!isOpaque) { fBitmap.eraseColor(SK_ColorTRANSPARENT); } @@ -61,8 +65,14 @@ SkBaseDevice* SkBitmapDevice::onCreateCompatibleDevice(SkBitmap::Config config, int width, int height, bool isOpaque, Usage usage) { - return SkNEW_ARGS(SkBitmapDevice,(config, width, height, isOpaque, - this->getDeviceProperties())); + SkBitmapDevice* device = SkNEW_ARGS(SkBitmapDevice,(config, width, height, + isOpaque, this->getDeviceProperties())); + // Check if allocation failed and delete device if it did fail + if ((device->width() != width) || (device->height() != height)) { + SkDELETE(device); + device = NULL; + } + return device; } void SkBitmapDevice::lockPixels() { diff --git a/core/SkBitmapHeap.cpp b/core/SkBitmapHeap.cpp index a1711384..1f2f3dcf 100644 --- a/core/SkBitmapHeap.cpp +++ b/core/SkBitmapHeap.cpp @@ -12,9 +12,6 @@ #include "SkFlattenableBuffers.h" #include "SkTSearch.h" -SK_DEFINE_INST_COUNT(SkBitmapHeapReader) -SK_DEFINE_INST_COUNT(SkBitmapHeap::ExternalStorage) - SkBitmapHeapEntry::SkBitmapHeapEntry() : fSlot(-1) , fRefCount(0) diff --git a/core/SkBitmapProcShader.cpp b/core/SkBitmapProcShader.cpp index ded1b720..bb161192 100644 --- a/core/SkBitmapProcShader.cpp +++ b/core/SkBitmapProcShader.cpp @@ -358,13 +358,12 @@ GrEffectRef* SkBitmapProcShader::asNewEffect(GrContext* context, const SkPaint& SkMatrix matrix; matrix.setIDiv(fRawBitmap.width(), fRawBitmap.height()); - if (this->hasLocalMatrix()) { - SkMatrix inverse; - if (!this->getLocalMatrix().invert(&inverse)) { - return NULL; - } - matrix.preConcat(inverse); + SkMatrix inverse; + if (!this->getLocalMatrix().invert(&inverse)) { + return NULL; } + matrix.preConcat(inverse); + SkShader::TileMode tm[] = { (TileMode)fState.fTileModeX, (TileMode)fState.fTileModeY, @@ -384,9 +383,21 @@ GrEffectRef* SkBitmapProcShader::asNewEffect(GrContext* context, const SkPaint& textureFilterMode = GrTextureParams::kMipMap_FilterMode; break; case SkPaint::kHigh_FilterLevel: - // fall back to no filtering here; we will install another - // shader that will do the HQ filtering. - textureFilterMode = GrTextureParams::kNone_FilterMode; + // Minification can look bad with the bicubic effect. This is an overly aggressive + // check for MIP fallbacks. It doesn't consider the fact that minification in the local + // matrix could be offset by the view matrix and vice versa. We also don't know whether + // the draw has explicit local coords (e.g. drawVertices) where the scale factor is + // unknown and varies. + if (context->getMatrix().getMinStretch() >= SK_Scalar1 && + this->getLocalMatrix().getMaxStretch() <= SK_Scalar1) { + // fall back to no filtering here; we will install another + // shader that will do the HQ filtering. + textureFilterMode = GrTextureParams::kNone_FilterMode; + } else { + // Fall back to mip-mapping. + paintFilterLevel = SkPaint::kMedium_FilterLevel; + textureFilterMode = GrTextureParams::kMipMap_FilterMode; + } break; default: SkErrorInternals::SetError( kInvalidPaint_SkError, @@ -408,7 +419,7 @@ GrEffectRef* SkBitmapProcShader::asNewEffect(GrContext* context, const SkPaint& GrEffectRef* effect = NULL; if (paintFilterLevel == SkPaint::kHigh_FilterLevel) { - effect = GrBicubicEffect::Create(texture, matrix, params); + effect = GrBicubicEffect::Create(texture, matrix, tm); } else { effect = GrSimpleTextureEffect::Create(texture, matrix, params); } diff --git a/core/SkBitmapProcState.cpp b/core/SkBitmapProcState.cpp index 59483d66..c93a2b5c 100644 --- a/core/SkBitmapProcState.cpp +++ b/core/SkBitmapProcState.cpp @@ -147,7 +147,8 @@ bool SkBitmapProcState::possiblyScaleImage() { SkBitmapScaler::RESIZE_BEST, dest_width, dest_height, - simd)) { + simd, + SkScaledImageCache::GetAllocator())) { // we failed to create fScaledBitmap, so just return and let // the scanline proc handle it. return false; diff --git a/core/SkBlitter.cpp b/core/SkBlitter.cpp index dc7946a4..9682d557 100644 --- a/core/SkBlitter.cpp +++ b/core/SkBlitter.cpp @@ -945,11 +945,6 @@ SkBlitter* SkBlitter::Choose(const SkBitmap& device, switch (device.config()) { - case SkBitmap::kA1_Config: - SK_PLACEMENT_NEW_ARGS(blitter, SkA1_Blitter, - storage, storageSize, (device, *paint)); - break; - case SkBitmap::kA8_Config: if (drawCoverage) { SkASSERT(NULL == shader); diff --git a/core/SkBlitter_A1.cpp b/core/SkBlitter_A1.cpp deleted file mode 100644 index b64afe2a..00000000 --- a/core/SkBlitter_A1.cpp +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#include "SkCoreBlitters.h" - -SkA1_Blitter::SkA1_Blitter(const SkBitmap& device, const SkPaint& paint) - : INHERITED(device) { - fSrcA = paint.getAlpha(); -} - -void SkA1_Blitter::blitH(int x, int y, int width) { - SkASSERT(x >= 0 && y >= 0 && - (unsigned)(x + width) <= (unsigned)fDevice.width()); - - if (fSrcA <= 0x7F) { - return; - } - uint8_t* dst = fDevice.getAddr1(x, y); - int right = x + width; - - int left_mask = 0xFF >> (x & 7); - int rite_mask = 0xFF << (8 - (right & 7)); - int full_runs = (right >> 3) - ((x + 7) >> 3); - - // check for empty right mask, so we don't read off the end - // (or go slower than we need to) - if (rite_mask == 0) { - SkASSERT(full_runs >= 0); - full_runs -= 1; - rite_mask = 0xFF; - } - if (left_mask == 0xFF) { - full_runs -= 1; - } - if (full_runs < 0) { - SkASSERT((left_mask & rite_mask) != 0); - *dst |= (left_mask & rite_mask); - } else { - *dst++ |= left_mask; - memset(dst, 0xFF, full_runs); - dst += full_runs; - *dst |= rite_mask; - } -} diff --git a/core/SkCanvas.cpp b/core/SkCanvas.cpp index 4eaea11b..47d3cca8 100644 --- a/core/SkCanvas.cpp +++ b/core/SkCanvas.cpp @@ -30,10 +30,6 @@ #include "GrRenderTarget.h" #endif -SK_DEFINE_INST_COUNT(SkBounder) -SK_DEFINE_INST_COUNT(SkCanvas) -SK_DEFINE_INST_COUNT(SkDrawFilter) - // experimental for faster tiled drawing... //#define SK_ENABLE_CLIP_QUICKREJECT diff --git a/core/SkColorFilter.cpp b/core/SkColorFilter.cpp index abf191e1..ef0bda74 100644 --- a/core/SkColorFilter.cpp +++ b/core/SkColorFilter.cpp @@ -12,8 +12,6 @@ #include "SkUnPreMultiply.h" #include "SkString.h" -SK_DEFINE_INST_COUNT(SkColorFilter) - bool SkColorFilter::asColorMode(SkColor* color, SkXfermode::Mode* mode) const { return false; } diff --git a/core/SkColorTable.cpp b/core/SkColorTable.cpp index 38a46c51..c719defe 100644 --- a/core/SkColorTable.cpp +++ b/core/SkColorTable.cpp @@ -12,8 +12,6 @@ #include "SkStream.h" #include "SkTemplates.h" -SK_DEFINE_INST_COUNT(SkColorTable) - // As copy constructor is hidden in the class hierarchy, we need to call // default constructor explicitly to suppress a compiler warning. SkColorTable::SkColorTable(const SkColorTable& src) : INHERITED() { diff --git a/core/SkCoreBlitters.h b/core/SkCoreBlitters.h index 673b8745..1605a527 100644 --- a/core/SkCoreBlitters.h +++ b/core/SkCoreBlitters.h @@ -162,22 +162,6 @@ private: /////////////////////////////////////////////////////////////////////////////// -class SkA1_Blitter : public SkRasterBlitter { -public: - SkA1_Blitter(const SkBitmap& device, const SkPaint& paint); - virtual void blitH(int x, int y, int width) SK_OVERRIDE; - -private: - uint8_t fSrcA; - - // illegal - SkA1_Blitter& operator=(const SkA1_Blitter&); - - typedef SkRasterBlitter INHERITED; -}; - -/////////////////////////////////////////////////////////////////////////////// - /* These return the correct subclass of blitter for their device config. Currently, they make the following assumptions about the state of the diff --git a/core/SkData.cpp b/core/SkData.cpp index 56c1256d..fd963a9f 100644 --- a/core/SkData.cpp +++ b/core/SkData.cpp @@ -10,8 +10,6 @@ #include "SkOSFile.h" #include "SkOnce.h" -SK_DEFINE_INST_COUNT(SkData) - SkData::SkData(const void* ptr, size_t size, ReleaseProc proc, void* context) { fPtr = ptr; fSize = size; diff --git a/core/SkDataTable.cpp b/core/SkDataTable.cpp index 917445c9..e2644a05 100644 --- a/core/SkDataTable.cpp +++ b/core/SkDataTable.cpp @@ -8,8 +8,6 @@ #include "SkData.h" #include "SkDataTable.h" -SK_DEFINE_INST_COUNT(SkDataTable) - static void malloc_freeproc(void* context) { sk_free(context); } diff --git a/core/SkDevice.cpp b/core/SkDevice.cpp index c0d4ad36..0c9e9d66 100644 --- a/core/SkDevice.cpp +++ b/core/SkDevice.cpp @@ -9,8 +9,6 @@ #include "SkDevice.h" #include "SkMetaData.h" -SK_DEFINE_INST_COUNT(SkBaseDevice) - #if SK_PMCOLOR_BYTE_ORDER(B,G,R,A) const SkCanvas::Config8888 SkBaseDevice::kPMColorAlias = SkCanvas::kBGRA_Premul_Config8888; #elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A) diff --git a/core/SkDeviceProfile.cpp b/core/SkDeviceProfile.cpp index a15069a1..8d60151f 100644 --- a/core/SkDeviceProfile.cpp +++ b/core/SkDeviceProfile.cpp @@ -9,8 +9,6 @@ #include "SkDeviceProfile.h" #include "SkThread.h" -SK_DEFINE_INST_COUNT(SkDeviceProfile) - #define DEFAULT_GAMMAEXP 2.2f #define DEFAULT_CONTRASTSCALE 0.5f #define DEFAULT_LCDCONFIG SkDeviceProfile::kNone_LCDConfig diff --git a/core/SkDiscardableMemory.h b/core/SkDiscardableMemory.h index e1634eef..f3159fe2 100644 --- a/core/SkDiscardableMemory.h +++ b/core/SkDiscardableMemory.h @@ -8,6 +8,7 @@ #ifndef SkDiscardableMemory_DEFINED #define SkDiscardableMemory_DEFINED +#include "SkRefCnt.h" #include "SkTypes.h" /** @@ -15,7 +16,6 @@ * embedder. */ class SK_API SkDiscardableMemory { - public: /** * Factory method that creates, initializes and locks an SkDiscardableMemory @@ -23,6 +23,17 @@ public: */ static SkDiscardableMemory* Create(size_t bytes); + /** + * Factory class that creates, initializes and locks an SkDiscardableMemory + * object. If either of these steps fails, a NULL pointer will be returned. + */ + class Factory : public SkRefCnt { + public: + virtual SkDiscardableMemory* create(size_t bytes) = 0; + private: + typedef SkRefCnt INHERITED; + }; + /** Must not be called while locked. */ virtual ~SkDiscardableMemory() {} diff --git a/core/SkDrawLooper.cpp b/core/SkDrawLooper.cpp index 0277986a..bac2d969 100644 --- a/core/SkDrawLooper.cpp +++ b/core/SkDrawLooper.cpp @@ -11,8 +11,6 @@ #include "SkPaint.h" #include "SkRect.h" -SK_DEFINE_INST_COUNT(SkDrawLooper) - bool SkDrawLooper::canComputeFastBounds(const SkPaint& paint) { SkCanvas canvas; diff --git a/core/SkFlattenable.cpp b/core/SkFlattenable.cpp index 6cebb225..877fa0a8 100644 --- a/core/SkFlattenable.cpp +++ b/core/SkFlattenable.cpp @@ -8,8 +8,6 @@ #include "SkFlattenable.h" #include "SkPtrRecorder.h" -SK_DEFINE_INST_COUNT(SkFlattenable) - /////////////////////////////////////////////////////////////////////////////// void SkFlattenable::flatten(SkFlattenableWriteBuffer&) const diff --git a/core/SkFontHost.cpp b/core/SkFontHost.cpp index f3d30e85..a209b970 100644 --- a/core/SkFontHost.cpp +++ b/core/SkFontHost.cpp @@ -69,8 +69,6 @@ SkFontStyle::SkFontStyle(int weight, int width, Slant slant) { #include "SkFontMgr.h" -SK_DEFINE_INST_COUNT(SkFontStyleSet) - class SkEmptyFontStyleSet : public SkFontStyleSet { public: virtual int count() SK_OVERRIDE { return 0; } @@ -92,8 +90,6 @@ SkFontStyleSet* SkFontStyleSet::CreateEmpty() { /////////////////////////////////////////////////////////////////////////////// -SK_DEFINE_INST_COUNT(SkFontMgr) - class SkEmptyFontMgr : public SkFontMgr { protected: virtual int onCountFamilies() SK_OVERRIDE { diff --git a/core/SkGlyphCache.cpp b/core/SkGlyphCache.cpp index faa3f890..a78b197a 100644 --- a/core/SkGlyphCache.cpp +++ b/core/SkGlyphCache.cpp @@ -87,8 +87,6 @@ SkGlyphCache::SkGlyphCache(SkTypeface* typeface, const SkDescriptor* desc, SkSca fGlyphArray.setReserve(kMinGlyphCount); - fMetricsCount = 0; - fAdvanceCount = 0; fAuxProcList = NULL; } @@ -320,11 +318,9 @@ SkGlyph* SkGlyphCache::lookupMetrics(uint32_t id, MetricsType mtype) { if (kJustAdvance_MetricsType == mtype) { fScalerContext->getAdvance(glyph); - fAdvanceCount += 1; } else { SkASSERT(kFull_MetricsType == mtype); fScalerContext->getMetrics(glyph); - fMetricsCount += 1; } return glyph; diff --git a/core/SkGlyphCache.h b/core/SkGlyphCache.h index 52a8132f..7b2ebb84 100644 --- a/core/SkGlyphCache.h +++ b/core/SkGlyphCache.h @@ -211,8 +211,6 @@ private: SkTDArray<SkGlyph*> fGlyphArray; SkChunkAlloc fGlyphAlloc; - int fMetricsCount, fAdvanceCount; - struct CharGlyphRec { uint32_t fID; // unichar + subpixel SkGlyph* fGlyph; diff --git a/core/SkImageFilter.cpp b/core/SkImageFilter.cpp index cca22bba..cda635b4 100644 --- a/core/SkImageFilter.cpp +++ b/core/SkImageFilter.cpp @@ -17,8 +17,6 @@ #include "SkImageFilterUtils.h" #endif -SK_DEFINE_INST_COUNT(SkImageFilter) - SkImageFilter::SkImageFilter(int inputCount, SkImageFilter** inputs, const CropRect* cropRect) : fInputCount(inputCount), fInputs(new SkImageFilter*[inputCount]), @@ -53,9 +51,9 @@ SkImageFilter::~SkImageFilter() { delete[] fInputs; } -SkImageFilter::SkImageFilter(int maxInputCount, SkFlattenableReadBuffer& buffer) { +SkImageFilter::SkImageFilter(int inputCount, SkFlattenableReadBuffer& buffer) { fInputCount = buffer.readInt(); - if (buffer.validate((fInputCount >= 0) && (fInputCount <= maxInputCount))) { + if (buffer.validate((fInputCount >= 0) && ((inputCount < 0) || (fInputCount == inputCount)))) { fInputs = new SkImageFilter*[fInputCount]; for (int i = 0; i < fInputCount; i++) { if (buffer.readBool()) { @@ -63,10 +61,14 @@ SkImageFilter::SkImageFilter(int maxInputCount, SkFlattenableReadBuffer& buffer) } else { fInputs[i] = NULL; } + if (!buffer.isValid()) { + fInputCount = i; // Do not use fInputs past that point in the destructor + break; + } } SkRect rect; buffer.readRect(&rect); - if (buffer.validate(SkIsValidRect(rect))) { + if (buffer.isValid() && buffer.validate(SkIsValidRect(rect))) { uint32_t flags = buffer.readUInt(); fCropRect = CropRect(rect, flags); } diff --git a/core/SkImageInfo.cpp b/core/SkImageInfo.cpp new file mode 100644 index 00000000..461bdc03 --- /dev/null +++ b/core/SkImageInfo.cpp @@ -0,0 +1,29 @@ +/* + * Copyright 2010 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkImageInfo.h" +#include "SkFlattenableBuffers.h" + +void SkImageInfo::unflatten(SkFlattenableReadBuffer& buffer) { + fWidth = buffer.read32(); + fHeight = buffer.read32(); + + uint32_t packed = buffer.read32(); + SkASSERT(0 == (packed >> 16)); + fAlphaType = (SkAlphaType)((packed >> 8) & 0xFF); + fColorType = (SkColorType)((packed >> 0) & 0xFF); +} + +void SkImageInfo::flatten(SkFlattenableWriteBuffer& buffer) const { + buffer.write32(fWidth); + buffer.write32(fHeight); + + SkASSERT(0 == (fAlphaType & ~0xFF)); + SkASSERT(0 == (fColorType & ~0xFF)); + uint32_t packed = (fAlphaType << 8) | fColorType; + buffer.write32(packed); +} diff --git a/core/SkMaskFilter.cpp b/core/SkMaskFilter.cpp index cd257163..f062f135 100644 --- a/core/SkMaskFilter.cpp +++ b/core/SkMaskFilter.cpp @@ -21,8 +21,6 @@ #include "SkGrPixelRef.h" #endif -SK_DEFINE_INST_COUNT(SkMaskFilter) - bool SkMaskFilter::filterMask(SkMask*, const SkMask&, const SkMatrix&, SkIPoint*) const { return false; diff --git a/core/SkMaskGamma.h b/core/SkMaskGamma.h index 9d3fd9a3..1f2b73ca 100644 --- a/core/SkMaskGamma.h +++ b/core/SkMaskGamma.h @@ -92,8 +92,8 @@ void SkTMaskGamma_build_correcting_lut(uint8_t table[256], U8CPU srcI, SkScalar * @param B The number of luminance bits to use [1, 8] from the blue channel. */ template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskGamma : public SkRefCnt { + SK_DECLARE_INST_COUNT(SkTMaskGamma) public: - SK_DECLARE_INST_COUNT_TEMPLATE(SkTMaskGamma) /** Creates a linear SkTMaskGamma. */ SkTMaskGamma() : fIsLinear(true) { } @@ -147,11 +147,6 @@ private: }; -#define MacroComma , -SK_DEFINE_INST_COUNT_TEMPLATE( - template <int R_LUM_BITS MacroComma int G_LUM_BITS MacroComma int B_LUM_BITS>, - SkTMaskGamma<R_LUM_BITS MacroComma G_LUM_BITS MacroComma B_LUM_BITS>); - /** * SkTMaskPreBlend is a tear-off of SkTMaskGamma. It provides the tables to * convert a linear alpha value for a given channel to a gamma correcting alpha diff --git a/core/SkMatrix.cpp b/core/SkMatrix.cpp index 3caf51a0..474f2722 100644 --- a/core/SkMatrix.cpp +++ b/core/SkMatrix.cpp @@ -1857,45 +1857,71 @@ bool SkMatrix::setPolyToPoly(const SkPoint src[], const SkPoint dst[], /////////////////////////////////////////////////////////////////////////////// -SkScalar SkMatrix::getMaxStretch() const { - TypeMask mask = this->getType(); +enum MinOrMax { + kMin_MinOrMax, + kMax_MinOrMax +}; - if (this->hasPerspective()) { +template <MinOrMax MIN_OR_MAX> SkScalar get_stretch_factor(SkMatrix::TypeMask typeMask, + const SkScalar m[9]) { + if (typeMask & SkMatrix::kPerspective_Mask) { return -SK_Scalar1; } - if (this->isIdentity()) { + if (SkMatrix::kIdentity_Mask == typeMask) { return SK_Scalar1; } - if (!(mask & kAffine_Mask)) { - return SkMaxScalar(SkScalarAbs(fMat[kMScaleX]), - SkScalarAbs(fMat[kMScaleY])); + if (!(typeMask & SkMatrix::kAffine_Mask)) { + if (kMin_MinOrMax == MIN_OR_MAX) { + return SkMinScalar(SkScalarAbs(m[SkMatrix::kMScaleX]), + SkScalarAbs(m[SkMatrix::kMScaleY])); + } else { + return SkMaxScalar(SkScalarAbs(m[SkMatrix::kMScaleX]), + SkScalarAbs(m[SkMatrix::kMScaleY])); + } } // ignore the translation part of the matrix, just look at 2x2 portion. - // compute singular values, take largest abs value. + // compute singular values, take largest or smallest abs value. // [a b; b c] = A^T*A - SkScalar a = SkScalarMul(fMat[kMScaleX], fMat[kMScaleX]) + - SkScalarMul(fMat[kMSkewY], fMat[kMSkewY]); - SkScalar b = SkScalarMul(fMat[kMScaleX], fMat[kMSkewX]) + - SkScalarMul(fMat[kMScaleY], fMat[kMSkewY]); - SkScalar c = SkScalarMul(fMat[kMSkewX], fMat[kMSkewX]) + - SkScalarMul(fMat[kMScaleY], fMat[kMScaleY]); + SkScalar a = SkScalarMul(m[SkMatrix::kMScaleX], m[SkMatrix::kMScaleX]) + + SkScalarMul(m[SkMatrix::kMSkewY], m[SkMatrix::kMSkewY]); + SkScalar b = SkScalarMul(m[SkMatrix::kMScaleX], m[SkMatrix::kMSkewX]) + + SkScalarMul(m[SkMatrix::kMScaleY], m[SkMatrix::kMSkewY]); + SkScalar c = SkScalarMul(m[SkMatrix::kMSkewX], m[SkMatrix::kMSkewX]) + + SkScalarMul(m[SkMatrix::kMScaleY], m[SkMatrix::kMScaleY]); // eigenvalues of A^T*A are the squared singular values of A. // characteristic equation is det((A^T*A) - l*I) = 0 // l^2 - (a + c)l + (ac-b^2) // solve using quadratic equation (divisor is non-zero since l^2 has 1 coeff - // and roots are guaraunteed to be pos and real). - SkScalar largerRoot; + // and roots are guaranteed to be pos and real). + SkScalar chosenRoot; SkScalar bSqd = SkScalarMul(b,b); // if upper left 2x2 is orthogonal save some math if (bSqd <= SK_ScalarNearlyZero*SK_ScalarNearlyZero) { - largerRoot = SkMaxScalar(a, c); + if (kMin_MinOrMax == MIN_OR_MAX) { + chosenRoot = SkMinScalar(a, c); + } else { + chosenRoot = SkMaxScalar(a, c); + } } else { SkScalar aminusc = a - c; SkScalar apluscdiv2 = SkScalarHalf(a + c); SkScalar x = SkScalarHalf(SkScalarSqrt(SkScalarMul(aminusc, aminusc) + 4 * bSqd)); - largerRoot = apluscdiv2 + x; + if (kMin_MinOrMax == MIN_OR_MAX) { + chosenRoot = apluscdiv2 - x; + } else { + chosenRoot = apluscdiv2 + x; + } } - return SkScalarSqrt(largerRoot); + SkASSERT(chosenRoot >= 0); + return SkScalarSqrt(chosenRoot); +} + +SkScalar SkMatrix::getMinStretch() const { + return get_stretch_factor<kMin_MinOrMax>(this->getType(), fMat); +} + +SkScalar SkMatrix::getMaxStretch() const { + return get_stretch_factor<kMax_MinOrMax>(this->getType(), fMat); } static void reset_identity_matrix(SkMatrix* identity) { diff --git a/core/SkOnce.h b/core/SkOnce.h index a469e22c..89de1124 100644 --- a/core/SkOnce.h +++ b/core/SkOnce.h @@ -13,15 +13,15 @@ // is particularly useful for lazy singleton initialization. E.g. // // static void set_up_my_singleton(Singleton** singleton) { -// *singleton = new Singleton(...); +// *singleton = new Singleton(...); // } // ... // const Singleton& GetSingleton() { -// static Singleton* singleton = NULL; -// SK_DECLARE_STATIC_ONCE(once); -// SkOnce(&once, set_up_my_singleton, &singleton); -// SkASSERT(NULL != singleton); -// return *singleton; +// static Singleton* singleton = NULL; +// SK_DECLARE_STATIC_ONCE(once); +// SkOnce(&once, set_up_my_singleton, &singleton); +// SkASSERT(NULL != singleton); +// return *singleton; // } // // OnceTest.cpp also should serve as a few other simple examples. @@ -30,17 +30,17 @@ #include "SkTypes.h" #ifdef SK_USE_POSIX_THREADS -#define SK_DECLARE_STATIC_ONCE(name) \ - static SkOnceFlag name = { false, { PTHREAD_MUTEX_INITIALIZER } } +# define SK_ONCE_INIT { false, { PTHREAD_MUTEX_INITIALIZER } } #else -#define SK_DECLARE_STATIC_ONCE(name) \ - static SkOnceFlag name = { false, SkBaseMutex() } +# define SK_ONCE_INIT { false, SkBaseMutex() } #endif -struct SkOnceFlag; +#define SK_DECLARE_STATIC_ONCE(name) static SkOnceFlag name = SK_ONCE_INIT -template <typename Arg> -inline void SkOnce(SkOnceFlag* once, void (*f)(Arg), Arg arg); +struct SkOnceFlag; // If manually created, initialize with SkOnceFlag once = SK_ONCE_INIT + +template <typename Func, typename Arg> +inline void SkOnce(SkOnceFlag* once, Func f, Arg arg); // ---------------------- Implementation details below here. ----------------------------- @@ -96,19 +96,19 @@ inline static void acquire_barrier() { // This is the guts of the code, called when we suspect the one-time code hasn't been run yet. // This should be rarely called, so we separate it from SkOnce and don't mark it as inline. // (We don't mind if this is an actual function call, but odds are it'll be inlined anyway.) -template <typename Arg> -static void sk_once_slow(SkOnceFlag* once, void (*f)(Arg), Arg arg) { +template <typename Func, typename Arg> +static void sk_once_slow(SkOnceFlag* once, Func f, Arg arg) { const SkAutoMutexAcquire lock(once->mutex); if (!once->done) { f(arg); // Also known as a store-store/load-store barrier, this makes sure that the writes - // done before here---in particular, those done by calling once(arg)---are observable + // done before here---in particular, those done by calling f(arg)---are observable // before the writes after the line, *done = true. // // In version control terms this is like saying, "check in the work up - // to and including once(arg), then check in *done=true as a subsequent change". + // to and including f(arg), then check in *done=true as a subsequent change". // - // We'll use this in the fast path to make sure once(arg)'s effects are + // We'll use this in the fast path to make sure f(arg)'s effects are // observable whenever we observe *done == true. release_barrier(); once->done = true; @@ -131,8 +131,8 @@ void AnnotateBenignRace(const char* file, int line, const volatile void* mem, co #endif // This is our fast path, called all the time. We do really want it to be inlined. -template <typename Arg> -inline void SkOnce(SkOnceFlag* once, void (*f)(Arg), Arg arg) { +template <typename Func, typename Arg> +inline void SkOnce(SkOnceFlag* once, Func f, Arg arg) { ANNOTATE_BENIGN_RACE(&(once->done), "Don't worry TSAN, we're sure this is safe."); if (!once->done) { sk_once_slow(once, f, arg); diff --git a/core/SkPaint.cpp b/core/SkPaint.cpp index d6948dd8..91a76e15 100644 --- a/core/SkPaint.cpp +++ b/core/SkPaint.cpp @@ -2005,11 +2005,7 @@ enum FlatFlags { }; // The size of a flat paint's POD fields -// Include an SkScalar for hinting scale factor whether it is -// supported or not so that an SKP is valid whether it was -// created with support or not. - -static const uint32_t kPODPaintSize = 6 * sizeof(SkScalar) + +static const uint32_t kPODPaintSize = 5 * sizeof(SkScalar) + 1 * sizeof(SkColor) + 1 * sizeof(uint16_t) + 6 * sizeof(uint8_t); @@ -2046,8 +2042,6 @@ void SkPaint::flatten(SkFlattenableWriteBuffer& buffer) const { ptr = write_scalar(ptr, this->getTextSize()); ptr = write_scalar(ptr, this->getTextScaleX()); ptr = write_scalar(ptr, this->getTextSkewX()); - // Dummy value for obsolete hinting scale factor. TODO: remove with next picture version - ptr = write_scalar(ptr, SK_Scalar1); ptr = write_scalar(ptr, this->getStrokeWidth()); ptr = write_scalar(ptr, this->getStrokeMiter()); *ptr++ = this->getColor(); @@ -2064,8 +2058,6 @@ void SkPaint::flatten(SkFlattenableWriteBuffer& buffer) const { buffer.writeScalar(fTextSize); buffer.writeScalar(fTextScaleX); buffer.writeScalar(fTextSkewX); - // Dummy value for obsolete hinting scale factor. TODO: remove with next picture version - buffer.writeScalar(SK_Scalar1); buffer.writeScalar(fWidth); buffer.writeScalar(fMiterLimit); buffer.writeColor(fColor); @@ -2120,8 +2112,6 @@ void SkPaint::unflatten(SkFlattenableReadBuffer& buffer) { this->setTextSize(read_scalar(pod)); this->setTextScaleX(read_scalar(pod)); this->setTextSkewX(read_scalar(pod)); - // Skip the hinting scalar factor, which is not supported. - read_scalar(pod); this->setStrokeWidth(read_scalar(pod)); this->setStrokeMiter(read_scalar(pod)); this->setColor(*pod++); diff --git a/core/SkPath.cpp b/core/SkPath.cpp index d25ec3c1..eaa6c93d 100644 --- a/core/SkPath.cpp +++ b/core/SkPath.cpp @@ -15,8 +15,6 @@ #include "SkRRect.h" #include "SkThread.h" -SK_DEFINE_INST_COUNT(SkPath); - // This value is just made-up for now. When count is 4, calling memset was much // slower than just writing the loop. This seems odd, and hopefully in the // future this we appear to have been a fluke... @@ -42,22 +40,6 @@ static bool is_degenerate(const SkPath& path) { return SkPath::kDone_Verb == iter.next(pts); } -class SkAutoDisableOvalCheck { -public: - SkAutoDisableOvalCheck(SkPath* path) : fPath(path) { - fSaved = fPath->fIsOval; - } - - ~SkAutoDisableOvalCheck() { - fPath->fIsOval = fSaved; - } - -private: - SkPath* fPath; - bool fSaved; -}; -#define SkAutoDisableOvalCheck(...) SK_REQUIRE_LOCAL_VAR(SkAutoDisableOvalCheck) - class SkAutoDisableDirectionCheck { public: SkAutoDisableDirectionCheck(SkPath* path) : fPath(path) { @@ -84,7 +66,7 @@ private: It also notes if the path was originally degenerate, and if so, sets isConvex to true. Thus it can only be used if the contour being added is - convex (which is always true since we only allow the addition of rects). + convex. */ class SkAutoPathBoundsUpdate { public: @@ -164,7 +146,6 @@ void SkPath::resetFields() { fSegmentMask = 0; fConvexity = kUnknown_Convexity; fDirection = kUnknown_Direction; - fIsOval = false; // We don't touch Android's fSourcePath. It's used to track texture garbage collection, so we // don't want to muck with it if it's been set to something non-NULL. @@ -204,7 +185,6 @@ void SkPath::copyFields(const SkPath& that) { fSegmentMask = that.fSegmentMask; fConvexity = that.fConvexity; fDirection = that.fDirection; - fIsOval = that.fIsOval; } bool operator==(const SkPath& a, const SkPath& b) { @@ -230,7 +210,6 @@ void SkPath::swap(SkPath& that) { SkTSwap<uint8_t>(fSegmentMask, that.fSegmentMask); SkTSwap<uint8_t>(fConvexity, that.fConvexity); SkTSwap<uint8_t>(fDirection, that.fDirection); - SkTSwap<SkBool8>(fIsOval, that.fIsOval); #ifdef SK_BUILD_FOR_ANDROID SkTSwap<const SkPath*>(fSourcePath, that.fSourcePath); #endif @@ -631,7 +610,6 @@ void SkPath::setLastPt(SkScalar x, SkScalar y) { if (count == 0) { this->moveTo(x, y); } else { - fIsOval = false; SkPathRef::Editor ed(&fPathRef); ed.atPoint(count-1)->set(x, y); } @@ -650,7 +628,6 @@ void SkPath::setConvexity(Convexity c) { do { \ fConvexity = kUnknown_Convexity; \ fDirection = kUnknown_Direction; \ - fIsOval = false; \ } while (0) void SkPath::incReserve(U16CPU inc) { @@ -1263,14 +1240,13 @@ void SkPath::addOval(const SkRect& oval, Direction dir) { We can't simply check isEmpty() in this case, as additional moveTo() would mark the path non empty. */ - fIsOval = hasOnlyMoveTos(); - if (fIsOval) { + bool isOval = hasOnlyMoveTos(); + if (isOval) { fDirection = dir; } else { fDirection = kUnknown_Direction; } - SkAutoDisableOvalCheck adoc(this); SkAutoDisableDirectionCheck addc(this); SkAutoPathBoundsUpdate apbu(this, oval); @@ -1318,14 +1294,10 @@ void SkPath::addOval(const SkRect& oval, Direction dir) { this->quadTo( R, cy - sy, R, cy ); } this->close(); -} -bool SkPath::isOval(SkRect* rect) const { - if (fIsOval && rect) { - *rect = getBounds(); - } + SkPathRef::Editor ed(&fPathRef); - return fIsOval; + ed.setIsOval(isOval); } void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) { @@ -1459,8 +1431,6 @@ void SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy) { void SkPath::addPath(const SkPath& path, const SkMatrix& matrix) { SkPathRef::Editor(&fPathRef, path.countVerbs(), path.countPoints()); - fIsOval = false; - RawIter iter(path); SkPoint pts[4]; Verb verb; @@ -1525,8 +1495,6 @@ void SkPath::reversePathTo(const SkPath& path) { SkPathRef::Editor(&fPathRef, vcount, path.countPoints()); - fIsOval = false; - const uint8_t* verbs = path.fPathRef->verbs(); const SkPoint* pts = path.fPathRef->points(); const SkScalar* conicWeights = path.fPathRef->conicWeights(); @@ -1574,8 +1542,6 @@ void SkPath::reverseAddPath(const SkPath& src) { const uint8_t* verbsEnd = src.fPathRef->verbs(); // points just past the first verb const SkScalar* conicWeights = src.fPathRef->conicWeightsEnd(); - fIsOval = false; - bool needMove = true; bool needClose = false; while (verbs < verbsEnd) { @@ -1725,9 +1691,6 @@ void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const { } } - // It's an oval only if it stays a rect. - dst->fIsOval = fIsOval && matrix.rectStaysRect(); - SkDEBUGCODE(dst->validate();) } } @@ -2080,12 +2043,11 @@ size_t SkPath::writeToMemory(void* storage) const { SkWBuffer buffer(storage); - int32_t packed = ((fIsOval & 1) << kIsOval_SerializationShift) | - (fConvexity << kConvexity_SerializationShift) | + int32_t packed = (fConvexity << kConvexity_SerializationShift) | (fFillType << kFillType_SerializationShift) | (fSegmentMask << kSegmentMask_SerializationShift) | (fDirection << kDirection_SerializationShift) -#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TOO +#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V16_AND_ALL_OTHER_INSTANCES_TOO | (0x1 << kNewFormat_SerializationShift) #endif ; @@ -2106,17 +2068,16 @@ size_t SkPath::readFromMemory(const void* storage, size_t length) { return 0; } - fIsOval = (packed >> kIsOval_SerializationShift) & 1; fConvexity = (packed >> kConvexity_SerializationShift) & 0xFF; fFillType = (packed >> kFillType_SerializationShift) & 0xFF; fSegmentMask = (packed >> kSegmentMask_SerializationShift) & 0xF; fDirection = (packed >> kDirection_SerializationShift) & 0x3; -#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TOO +#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V16_AND_ALL_OTHER_INSTANCES_TOO bool newFormat = (packed >> kNewFormat_SerializationShift) & 1; #endif SkPathRef* pathRef = SkPathRef::CreateFromBuffer(&buffer -#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TOO +#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V16_AND_ALL_OTHER_INSTANCES_TOO , newFormat, packed #endif ); diff --git a/core/SkPathEffect.cpp b/core/SkPathEffect.cpp index e7f68225..59ba3ec3 100644 --- a/core/SkPathEffect.cpp +++ b/core/SkPathEffect.cpp @@ -12,8 +12,6 @@ /////////////////////////////////////////////////////////////////////////////// -SK_DEFINE_INST_COUNT(SkPathEffect) - void SkPathEffect::computeFastBounds(SkRect* dst, const SkRect& src) const { *dst = src; } diff --git a/core/SkPathHeap.cpp b/core/SkPathHeap.cpp index 12db3c48..c6e2129e 100644 --- a/core/SkPathHeap.cpp +++ b/core/SkPathHeap.cpp @@ -11,8 +11,6 @@ #include "SkFlattenableBuffers.h" #include <new> -SK_DEFINE_INST_COUNT(SkPathHeap) - #define kPathCount 64 SkPathHeap::SkPathHeap() : fHeap(kPathCount * sizeof(SkPath)) { diff --git a/core/SkPathRef.cpp b/core/SkPathRef.cpp index c66d75b3..a02df302 100644 --- a/core/SkPathRef.cpp +++ b/core/SkPathRef.cpp @@ -10,8 +10,6 @@ #include "SkPath.h" #include "SkPathRef.h" -SK_DEFINE_INST_COUNT(SkPathRef); - ////////////////////////////////////////////////////////////////////////////// SkPathRef::Editor::Editor(SkAutoTUnref<SkPathRef>* pathRef, int incReserveVerbs, @@ -26,6 +24,7 @@ SkPathRef::Editor::Editor(SkAutoTUnref<SkPathRef>* pathRef, } fPathRef = *pathRef; fPathRef->fGenerationID = 0; + fPathRef->fIsOval = false; SkDEBUGCODE(sk_atomic_inc(&fPathRef->fEditorsAttached);) } @@ -62,14 +61,20 @@ void SkPathRef::CreateTransformedCopy(SkAutoTUnref<SkPathRef>* dst, return; } - bool dstUnique = (*dst)->unique(); - if (!dstUnique) { + if (!(*dst)->unique()) { dst->reset(SkNEW(SkPathRef)); + } + + if (*dst != &src) { (*dst)->resetToSize(src.fVerbCnt, src.fPointCnt, src.fConicWeights.count()); memcpy((*dst)->verbsMemWritable(), src.verbsMemBegin(), src.fVerbCnt * sizeof(uint8_t)); (*dst)->fConicWeights = src.fConicWeights; } + SkASSERT((*dst)->countPoints() == src.countPoints()); + SkASSERT((*dst)->countVerbs() == src.countVerbs()); + SkASSERT((*dst)->fConicWeights.count() == src.fConicWeights.count()); + // Need to check this here in case (&src == dst) bool canXformBounds = !src.fBoundsIsDirty && matrix.rectStaysRect() && src.countPoints() > 1; @@ -100,28 +105,35 @@ void SkPathRef::CreateTransformedCopy(SkAutoTUnref<SkPathRef>* dst, (*dst)->fBoundsIsDirty = true; } + // It's an oval only if it stays a rect. + (*dst)->fIsOval = src.fIsOval && matrix.rectStaysRect(); + SkDEBUGCODE((*dst)->validate();) } SkPathRef* SkPathRef::CreateFromBuffer(SkRBuffer* buffer -#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TOO +#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V16_AND_ALL_OTHER_INSTANCES_TOO , bool newFormat, int32_t oldPacked #endif ) { SkPathRef* ref = SkNEW(SkPathRef); -#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TOO + bool isOval; + + int32_t packed; + if (!buffer->readS32(&packed)) { + SkDELETE(ref); + return NULL; + } + + ref->fIsFinite = (packed >> kIsFinite_SerializationShift) & 1; + +#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V16_AND_ALL_OTHER_INSTANCES_TOO if (newFormat) { #endif - int32_t packed; - if (!buffer->readS32(&packed)) { - SkDELETE(ref); - return NULL; - } - - ref->fIsFinite = (packed >> kIsFinite_SerializationShift) & 1; -#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TOO + isOval = (packed >> kIsOval_SerializationShift) & 1; +#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V16_AND_ALL_OTHER_INSTANCES_TOO } else { - ref->fIsFinite = (oldPacked >> SkPath::kOldIsFinite_SerializationShift) & 1; + isOval = (oldPacked >> SkPath::kOldIsOval_SerializationShift) & 1; } #endif @@ -147,6 +159,7 @@ SkPathRef* SkPathRef::CreateFromBuffer(SkRBuffer* buffer return NULL; } ref->fBoundsIsDirty = false; + ref->fIsOval = isOval; return ref; } @@ -159,6 +172,7 @@ void SkPathRef::Rewind(SkAutoTUnref<SkPathRef>* pathRef) { (*pathRef)->fFreeSpace = (*pathRef)->currSize(); (*pathRef)->fGenerationID = 0; (*pathRef)->fConicWeights.rewind(); + (*pathRef)->fIsOval = false; SkDEBUGCODE((*pathRef)->validate();) } else { int oldVCnt = (*pathRef)->countVerbs(); @@ -216,7 +230,8 @@ void SkPathRef::writeToBuffer(SkWBuffer* buffer) { // and fIsFinite are computed. const SkRect& bounds = this->getBounds(); - int32_t packed = ((fIsFinite & 1) << kIsFinite_SerializationShift); + int32_t packed = ((fIsFinite & 1) << kIsFinite_SerializationShift) | + ((fIsOval & 1) << kIsOval_SerializationShift); buffer->write32(packed); // TODO: write gen ID here. Problem: We don't know if we're cross process or not from @@ -258,15 +273,18 @@ void SkPathRef::copy(const SkPathRef& ref, fBounds = ref.fBounds; fIsFinite = ref.fIsFinite; } + fIsOval = ref.fIsOval; SkDEBUGCODE(this->validate();) } SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb) { SkDEBUGCODE(this->validate();) int pCnt; + bool dirtyAfterEdit = true; switch (verb) { case SkPath::kMove_Verb: pCnt = 1; + dirtyAfterEdit = false; break; case SkPath::kLine_Verb: pCnt = 1; @@ -281,12 +299,14 @@ SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb) { break; case SkPath::kClose_Verb: pCnt = 0; + dirtyAfterEdit = false; break; case SkPath::kDone_Verb: SkDEBUGFAIL("growForVerb called for kDone"); // fall through default: SkDEBUGFAIL("default is not reached"); + dirtyAfterEdit = false; pCnt = 0; } size_t space = sizeof(uint8_t) + pCnt * sizeof (SkPoint); @@ -297,6 +317,9 @@ SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb) { fPointCnt += pCnt; fFreeSpace -= space; fBoundsIsDirty = true; // this also invalidates fIsFinite + if (dirtyAfterEdit) { + fIsOval = false; + } SkDEBUGCODE(this->validate();) return ret; } diff --git a/core/SkPicture.cpp b/core/SkPicture.cpp index 4acc5493..2b9b9e93 100644 --- a/core/SkPicture.cpp +++ b/core/SkPicture.cpp @@ -26,8 +26,6 @@ #include "SkRTree.h" #include "SkBBoxHierarchyRecord.h" -SK_DEFINE_INST_COUNT(SkPicture) - #define DUMP_BUFFER_SIZE 65536 //#define ENABLE_TIME_DRAW // dumps milliseconds for each draw @@ -266,23 +264,29 @@ void SkPicture::draw(SkCanvas* surface, SkDrawPictureCallback* callback) { #include "SkStream.h" +static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' }; + bool SkPicture::StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) { if (NULL == stream) { return false; } + // Check magic bytes. + char magic[sizeof(kMagic)]; + stream->read(magic, sizeof(kMagic)); + if (0 != memcmp(magic, kMagic, sizeof(kMagic))) { + return false; + } + SkPictInfo info; if (!stream->read(&info, sizeof(SkPictInfo))) { return false; } + if (PICTURE_VERSION != info.fVersion -#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V13_AND_ALL_OTHER_INSTANCES_TOO - // V13 is backwards compatible with V12 - && PRIOR_PRIOR_PICTURE_VERSION != info.fVersion // TODO: remove when .skps regenerated -#endif -#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TOO - // V14 is backwards compatible with V13 - && PRIOR_PICTURE_VERSION2 != info.fVersion // TODO: remove when .skps regenerated +#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V16_AND_ALL_OTHER_INSTANCES_TOO + // V16 is backwards compatible with V15 + && PRIOR_PICTURE_VERSION != info.fVersion // TODO: remove when .skps regenerated #endif ) { return false; @@ -341,6 +345,10 @@ void SkPicture::serialize(SkWStream* stream, EncodeBitmap encoder) const { info.fFlags |= SkPictInfo::kPtrIs64Bit_Flag; } + // Write 8 magic bytes to ID this file format. + SkASSERT(sizeof(kMagic) == 8); + stream->write(kMagic, sizeof(kMagic)); + stream->write(&info, sizeof(info)); if (playback) { stream->writeBool(true); diff --git a/core/SkPictureFlat.cpp b/core/SkPictureFlat.cpp index 2a7d15a7..149cf7cc 100644 --- a/core/SkPictureFlat.cpp +++ b/core/SkPictureFlat.cpp @@ -16,8 +16,6 @@ #include "SkTypeface.h" #include "SkXfermode.h" -SK_DEFINE_INST_COUNT(SkFlatController) - /////////////////////////////////////////////////////////////////////////////// SkTypefacePlayback::SkTypefacePlayback() : fCount(0), fArray(NULL) {} diff --git a/core/SkPicturePlayback.cpp b/core/SkPicturePlayback.cpp index 5a016d48..82c7a03b 100644 --- a/core/SkPicturePlayback.cpp +++ b/core/SkPicturePlayback.cpp @@ -882,18 +882,7 @@ void SkPicturePlayback::draw(SkCanvas& canvas, SkDrawPictureCallback* callback) const SkRect* src = this->getRectPtr(reader); // may be null const SkRect& dst = reader.skipT<SkRect>(); // required SkCanvas::DrawBitmapRectFlags flags; -#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V13_AND_ALL_OTHER_INSTANCES_TOO - flags = SkCanvas::kNone_DrawBitmapRectFlag; - // TODO: remove this backwards compatibility code once the .skps are - // regenerated - SkASSERT(32 == size || 48 == size || // old sizes - 36 == size || 52 == size); // new sizes - if (36 == size || 52 == size) { -#endif - flags = (SkCanvas::DrawBitmapRectFlags) reader.readInt(); -#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V13_AND_ALL_OTHER_INSTANCES_TOO - } -#endif + flags = (SkCanvas::DrawBitmapRectFlags) reader.readInt(); canvas.drawBitmapRectToRect(bitmap, src, dst, paint, flags); } break; case DRAW_BITMAP_MATRIX: { diff --git a/core/SkPictureStateTree.cpp b/core/SkPictureStateTree.cpp index 9f2db258..5f4ed934 100644 --- a/core/SkPictureStateTree.cpp +++ b/core/SkPictureStateTree.cpp @@ -9,8 +9,6 @@ #include "SkPictureStateTree.h" #include "SkCanvas.h" -SK_DEFINE_INST_COUNT(SkPictureStateTree) - SkPictureStateTree::SkPictureStateTree() : fAlloc(2048) , fRoot(NULL) diff --git a/core/SkPixelRef.cpp b/core/SkPixelRef.cpp index 972474cc..b5daf0b5 100644 --- a/core/SkPixelRef.cpp +++ b/core/SkPixelRef.cpp @@ -9,9 +9,6 @@ #include "SkFlattenableBuffers.h" #include "SkThread.h" -SK_DEFINE_INST_COUNT(SkPixelRef) - - #ifdef SK_USE_POSIX_THREADS static SkBaseMutex gPixelRefMutexRing[] = { @@ -85,6 +82,28 @@ void SkPixelRef::setMutex(SkBaseMutex* mutex) { // just need a > 0 value, so pick a funny one to aid in debugging #define SKPIXELREF_PRELOCKED_LOCKCOUNT 123456789 +SkPixelRef::SkPixelRef(const SkImageInfo&, SkBaseMutex* mutex) { + this->setMutex(mutex); + fPixels = NULL; + fColorTable = NULL; // we do not track ownership of this + fLockCount = 0; + this->needsNewGenID(); + fIsImmutable = false; + fPreLocked = false; +} + +SkPixelRef::SkPixelRef(const SkImageInfo&) { + this->setMutex(NULL); + fPixels = NULL; + fColorTable = NULL; // we do not track ownership of this + fLockCount = 0; + this->needsNewGenID(); + fIsImmutable = false; + fPreLocked = false; +} + +#ifdef SK_SUPPORT_LEGACY_PIXELREF_CONSTRUCTOR +// THIS GUY IS DEPRECATED -- don't use me! SkPixelRef::SkPixelRef(SkBaseMutex* mutex) { this->setMutex(mutex); fPixels = NULL; @@ -94,6 +113,7 @@ SkPixelRef::SkPixelRef(SkBaseMutex* mutex) { fIsImmutable = false; fPreLocked = false; } +#endif SkPixelRef::SkPixelRef(SkFlattenableReadBuffer& buffer, SkBaseMutex* mutex) : INHERITED(buffer) { @@ -247,6 +267,10 @@ SkData* SkPixelRef::onRefEncodedData() { return NULL; } +size_t SkPixelRef::getAllocatedSizeInBytes() const { + return 0; +} + /////////////////////////////////////////////////////////////////////////////// #ifdef SK_BUILD_FOR_ANDROID diff --git a/core/SkPtrRecorder.cpp b/core/SkPtrRecorder.cpp index 2acb5af9..aae28d0e 100644 --- a/core/SkPtrRecorder.cpp +++ b/core/SkPtrRecorder.cpp @@ -8,9 +8,6 @@ #include "SkPtrRecorder.h" #include "SkTSearch.h" -SK_DEFINE_INST_COUNT(SkPtrSet) -SK_DEFINE_INST_COUNT(SkNamedFactorySet) - void SkPtrSet::reset() { Pair* p = fList.begin(); Pair* stop = fList.end(); diff --git a/core/SkRTree.cpp b/core/SkRTree.cpp index e3d2eb69..253a7ee1 100644 --- a/core/SkRTree.cpp +++ b/core/SkRTree.cpp @@ -16,8 +16,6 @@ static inline void join_no_empty_check(const SkIRect& joinWith, SkIRect* out); /////////////////////////////////////////////////////////////////////////////////////////////////// -SK_DEFINE_INST_COUNT(SkRTree) - SkRTree* SkRTree::Create(int minChildren, int maxChildren, SkScalar aspectRatio, bool sortWhenBulkLoading) { if (minChildren < maxChildren && (maxChildren + 1) / 2 >= minChildren && diff --git a/core/SkRasterizer.cpp b/core/SkRasterizer.cpp index a65d541a..3a7af955 100644 --- a/core/SkRasterizer.cpp +++ b/core/SkRasterizer.cpp @@ -12,8 +12,6 @@ #include "SkMaskFilter.h" #include "SkPath.h" -SK_DEFINE_INST_COUNT(SkRasterizer) - bool SkRasterizer::rasterize(const SkPath& fillPath, const SkMatrix& matrix, const SkIRect* clipBounds, SkMaskFilter* filter, SkMask* mask, SkMask::CreateMode mode) const { diff --git a/core/SkRefCnt.cpp b/core/SkRefCnt.cpp deleted file mode 100644 index b922e03e..00000000 --- a/core/SkRefCnt.cpp +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#include "SkRefCnt.h" -#include "SkWeakRefCnt.h" - -SK_DEFINE_INST_COUNT(SkRefCntBase) -SK_DEFINE_INST_COUNT(SkWeakRefCnt) diff --git a/core/SkScaledImageCache.cpp b/core/SkScaledImageCache.cpp index ea29843c..5b166887 100644 --- a/core/SkScaledImageCache.cpp +++ b/core/SkScaledImageCache.cpp @@ -11,6 +11,13 @@ #include "SkPixelRef.h" #include "SkRect.h" +// This can be defined by the caller's build system +//#define SK_USE_DISCARDABLE_SCALEDIMAGECACHE + +#ifndef SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT +# define SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT 1024 +#endif + #ifndef SK_DEFAULT_IMAGE_CACHE_LIMIT #define SK_DEFAULT_IMAGE_CACHE_LIMIT (2 * 1024 * 1024) #endif @@ -49,7 +56,7 @@ static uint32_t compute_hash(const uint32_t data[], int count) { return hash; } -struct Key { +struct SkScaledImageCache::Key { Key(uint32_t genID, SkScalar scaleX, SkScalar scaleY, @@ -129,22 +136,24 @@ struct SkScaledImageCache::Rec { #include "SkTDynamicHash.h" namespace { // can't use static functions w/ template parameters -const Key& key_from_rec(const SkScaledImageCache::Rec& rec) { +const SkScaledImageCache::Key& key_from_rec(const SkScaledImageCache::Rec& rec) { return rec.fKey; } -uint32_t hash_from_key(const Key& key) { +uint32_t hash_from_key(const SkScaledImageCache::Key& key) { return key.fHash; } -bool eq_rec_key(const SkScaledImageCache::Rec& rec, const Key& key) { +bool eq_rec_key(const SkScaledImageCache::Rec& rec, const SkScaledImageCache::Key& key) { return rec.fKey == key; } } class SkScaledImageCache::Hash : public SkTDynamicHash<SkScaledImageCache::Rec, - Key, key_from_rec, hash_from_key, - eq_rec_key> {}; + SkScaledImageCache::Key, + key_from_rec, + hash_from_key, + eq_rec_key> {}; /////////////////////////////////////////////////////////////////////////////// @@ -162,7 +171,7 @@ static inline SkScaledImageCache::Rec* find_rec_in_list( } #endif -SkScaledImageCache::SkScaledImageCache(size_t byteLimit) { +void SkScaledImageCache::init() { fHead = NULL; fTail = NULL; #ifdef USE_HASH @@ -171,11 +180,133 @@ SkScaledImageCache::SkScaledImageCache(size_t byteLimit) { fHash = NULL; #endif fBytesUsed = 0; - fByteLimit = byteLimit; fCount = 0; + fAllocator = NULL; + + // One of these should be explicit set by the caller after we return. + fByteLimit = 0; + fDiscardableFactory = NULL; +} + +#include "SkDiscardableMemory.h" + +class SkOneShotDiscardablePixelRef : public SkPixelRef { +public: + // Ownership of the discardablememory is transfered to the pixelref + SkOneShotDiscardablePixelRef(const SkImageInfo&, SkDiscardableMemory*, size_t rowBytes); + ~SkOneShotDiscardablePixelRef(); + + SK_DECLARE_UNFLATTENABLE_OBJECT() + +protected: + virtual void* onLockPixels(SkColorTable**) SK_OVERRIDE; + virtual void onUnlockPixels() SK_OVERRIDE; + virtual size_t getAllocatedSizeInBytes() const SK_OVERRIDE; + +private: + SkImageInfo fInfo; // remove when SkPixelRef gets this in baseclass + + SkDiscardableMemory* fDM; + size_t fRB; + bool fFirstTime; + + typedef SkPixelRef INHERITED; +}; + +SkOneShotDiscardablePixelRef::SkOneShotDiscardablePixelRef(const SkImageInfo& info, + SkDiscardableMemory* dm, + size_t rowBytes) + : INHERITED(info) + , fDM(dm) + , fRB(rowBytes) +{ + fInfo = info; // remove this redundant field when SkPixelRef has info + + SkASSERT(dm->data()); + fFirstTime = true; +} + +SkOneShotDiscardablePixelRef::~SkOneShotDiscardablePixelRef() { + SkDELETE(fDM); +} + +void* SkOneShotDiscardablePixelRef::onLockPixels(SkColorTable** ctable) { + if (fFirstTime) { + // we're already locked + fFirstTime = false; + return fDM->data(); + } + return fDM->lock() ? fDM->data() : NULL; +} + +void SkOneShotDiscardablePixelRef::onUnlockPixels() { + SkASSERT(!fFirstTime); + fDM->unlock(); +} + +size_t SkOneShotDiscardablePixelRef::getAllocatedSizeInBytes() const { + return fInfo.fHeight * fRB; +} + +class SkScaledImageCacheDiscardableAllocator : public SkBitmap::Allocator { +public: + SkScaledImageCacheDiscardableAllocator( + SkScaledImageCache::DiscardableFactory factory) { + SkASSERT(factory); + fFactory = factory; + } + + virtual bool allocPixelRef(SkBitmap*, SkColorTable*) SK_OVERRIDE; + +private: + SkScaledImageCache::DiscardableFactory fFactory; +}; + +bool SkScaledImageCacheDiscardableAllocator::allocPixelRef(SkBitmap* bitmap, + SkColorTable* ctable) { + size_t size = bitmap->getSize(); + if (0 == size) { + return false; + } + + SkDiscardableMemory* dm = fFactory(size); + if (NULL == dm) { + return false; + } + + // can relax when we have bitmap::asImageInfo + if (SkBitmap::kARGB_8888_Config != bitmap->config()) { + return false; + } + + SkImageInfo info = { + bitmap->width(), + bitmap->height(), + kPMColor_SkColorType, + bitmap->alphaType() + }; + + bitmap->setPixelRef(SkNEW_ARGS(SkOneShotDiscardablePixelRef, + (info, dm, bitmap->rowBytes())))->unref(); + bitmap->lockPixels(); + return bitmap->readyToDraw(); +} + +SkScaledImageCache::SkScaledImageCache(DiscardableFactory factory) { + this->init(); + fDiscardableFactory = factory; + + fAllocator = SkNEW_ARGS(SkScaledImageCacheDiscardableAllocator, (factory)); +} + +SkScaledImageCache::SkScaledImageCache(size_t byteLimit) { + this->init(); + fByteLimit = byteLimit; } SkScaledImageCache::~SkScaledImageCache() { + SkSafeUnref(fAllocator); + Rec* rec = fHead; while (rec) { Rec* next = rec->fNext; @@ -187,17 +318,22 @@ SkScaledImageCache::~SkScaledImageCache() { //////////////////////////////////////////////////////////////////////////////// -/** - This private method is the fully general record finder. All other - record finders should call this funtion. */ + SkScaledImageCache::Rec* SkScaledImageCache::findAndLock(uint32_t genID, SkScalar scaleX, SkScalar scaleY, const SkIRect& bounds) { - if (bounds.isEmpty()) { + const Key key(genID, scaleX, scaleY, bounds); + return this->findAndLock(key); +} + +/** + This private method is the fully general record finder. All other + record finders should call this function or the one above. */ +SkScaledImageCache::Rec* SkScaledImageCache::findAndLock(const SkScaledImageCache::Key& key) { + if (key.fBounds.isEmpty()) { return NULL; } - Key key(genID, scaleX, scaleY, bounds); #ifdef USE_HASH Rec* rec = fHash->find(key); #else @@ -275,8 +411,14 @@ SkScaledImageCache::ID* SkScaledImageCache::findAndLockMip(const SkBitmap& orig, /** This private method is the fully general record adder. All other record adders should call this funtion. */ -void SkScaledImageCache::addAndLock(SkScaledImageCache::Rec* rec) { +SkScaledImageCache::ID* SkScaledImageCache::addAndLock(SkScaledImageCache::Rec* rec) { SkASSERT(rec); + // See if we already have this key (racy inserts, etc.) + Rec* existing = this->findAndLock(rec->fKey); + if (existing != NULL) { + return rec_to_id(existing); + } + this->addToHead(rec); SkASSERT(1 == rec->fLockCount); #ifdef USE_HASH @@ -285,6 +427,7 @@ void SkScaledImageCache::addAndLock(SkScaledImageCache::Rec* rec) { #endif // We may (now) be overbudget, so see if we need to purge something. this->purgeAsNeeded(); + return rec_to_id(rec); } SkScaledImageCache::ID* SkScaledImageCache::addAndLock(uint32_t genID, @@ -293,8 +436,7 @@ SkScaledImageCache::ID* SkScaledImageCache::addAndLock(uint32_t genID, const SkBitmap& bitmap) { Key key(genID, SK_Scalar1, SK_Scalar1, SkIRect::MakeWH(width, height)); Rec* rec = SkNEW_ARGS(Rec, (key, bitmap)); - this->addAndLock(rec); - return rec_to_id(rec); + return this->addAndLock(rec); } SkScaledImageCache::ID* SkScaledImageCache::addAndLock(const SkBitmap& orig, @@ -311,8 +453,7 @@ SkScaledImageCache::ID* SkScaledImageCache::addAndLock(const SkBitmap& orig, } Key key(orig.getGenerationID(), scaleX, scaleY, bounds); Rec* rec = SkNEW_ARGS(Rec, (key, scaled)); - this->addAndLock(rec); - return rec_to_id(rec); + return this->addAndLock(rec); } SkScaledImageCache::ID* SkScaledImageCache::addAndLockMip(const SkBitmap& orig, @@ -323,8 +464,7 @@ SkScaledImageCache::ID* SkScaledImageCache::addAndLockMip(const SkBitmap& orig, } Key key(orig.getGenerationID(), 0, 0, bounds); Rec* rec = SkNEW_ARGS(Rec, (key, mip)); - this->addAndLock(rec); - return rec_to_id(rec); + return this->addAndLock(rec); } void SkScaledImageCache::unlock(SkScaledImageCache::ID* id) { @@ -356,30 +496,45 @@ void SkScaledImageCache::unlock(SkScaledImageCache::ID* id) { } void SkScaledImageCache::purgeAsNeeded() { - size_t byteLimit = fByteLimit; + size_t byteLimit; + int countLimit; + + if (fDiscardableFactory) { + countLimit = SK_DISCARDABLEMEMORY_SCALEDIMAGECACHE_COUNT_LIMIT; + byteLimit = SK_MaxU32; // no limit based on bytes + } else { + countLimit = SK_MaxS32; // no limit based on count + byteLimit = fByteLimit; + } + size_t bytesUsed = fBytesUsed; + int countUsed = fCount; Rec* rec = fTail; while (rec) { - if (bytesUsed < byteLimit) { + if (bytesUsed < byteLimit && countUsed < countLimit) { break; } + Rec* prev = rec->fPrev; if (0 == rec->fLockCount) { size_t used = rec->bytesUsed(); SkASSERT(used <= bytesUsed); - bytesUsed -= used; this->detach(rec); #ifdef USE_HASH fHash->remove(rec->fKey); #endif SkDELETE(rec); - fCount -= 1; + + bytesUsed -= used; + countUsed -= 1; } rec = prev; } + fBytesUsed = bytesUsed; + fCount = countUsed; } size_t SkScaledImageCache::setByteLimit(size_t newLimit) { @@ -502,13 +657,17 @@ void SkScaledImageCache::validate() const { SK_DECLARE_STATIC_MUTEX(gMutex); static void create_cache(SkScaledImageCache** cache) { +#ifdef SK_USE_DISCARDABLE_SCALEDIMAGECACHE + *cache = SkNEW_ARGS(SkScaledImageCache, (SkDiscardableMemory::Create)); +#else *cache = SkNEW_ARGS(SkScaledImageCache, (SK_DEFAULT_IMAGE_CACHE_LIMIT)); +#endif } static SkScaledImageCache* get_cache() { static SkScaledImageCache* gCache(NULL); SK_DECLARE_STATIC_ONCE(create_cache_once); - SkOnce<SkScaledImageCache**>(&create_cache_once, create_cache, &gCache); + SkOnce(&create_cache_once, create_cache, &gCache); SkASSERT(NULL != gCache); return gCache; } @@ -581,6 +740,11 @@ size_t SkScaledImageCache::SetByteLimit(size_t newLimit) { return get_cache()->setByteLimit(newLimit); } +SkBitmap::Allocator* SkScaledImageCache::GetAllocator() { + SkAutoMutexAcquire am(gMutex); + return get_cache()->allocator(); +} + /////////////////////////////////////////////////////////////////////////////// #include "SkGraphics.h" diff --git a/core/SkScaledImageCache.h b/core/SkScaledImageCache.h index fee69d2d..311db325 100644 --- a/core/SkScaledImageCache.h +++ b/core/SkScaledImageCache.h @@ -10,6 +10,7 @@ #include "SkBitmap.h" +class SkDiscardableMemory; class SkMipMap; /** @@ -26,6 +27,12 @@ class SkScaledImageCache { public: struct ID; + /** + * Returns a locked/pinned SkDiscardableMemory instance for the specified + * number of bytes, or NULL on failure. + */ + typedef SkDiscardableMemory* (*DiscardableFactory)(size_t bytes); + /* * The following static methods are thread-safe wrappers around a global * instance of this cache. @@ -57,9 +64,27 @@ public: static size_t GetByteLimit(); static size_t SetByteLimit(size_t newLimit); + static SkBitmap::Allocator* GetAllocator(); + /////////////////////////////////////////////////////////////////////////// + /** + * Construct the cache to call DiscardableFactory when it + * allocates memory for the pixels. In this mode, the cache has + * not explicit budget, and so methods like getBytesUsed() and + * getByteLimit() will return 0, and setByteLimit will ignore its argument + * and return 0. + */ + SkScaledImageCache(DiscardableFactory); + + /** + * Construct the cache, allocating memory with malloc, and respect the + * byteLimit, purging automatically when a new image is added to the cache + * that pushes the total bytesUsed over the limit. Note: The limit can be + * changed at runtime with setByteLimit. + */ SkScaledImageCache(size_t byteLimit); + ~SkScaledImageCache(); /** @@ -124,8 +149,11 @@ public: */ size_t setByteLimit(size_t newLimit); + SkBitmap::Allocator* allocator() const { return fAllocator; }; + public: struct Rec; + struct Key; private: Rec* fHead; Rec* fTail; @@ -133,13 +161,18 @@ private: class Hash; Hash* fHash; + DiscardableFactory fDiscardableFactory; + // the allocator is NULL or one that matches discardables + SkBitmap::Allocator* fAllocator; + size_t fBytesUsed; size_t fByteLimit; int fCount; Rec* findAndLock(uint32_t generationID, SkScalar sx, SkScalar sy, const SkIRect& bounds); - void addAndLock(Rec* rec); + Rec* findAndLock(const Key& key); + ID* addAndLock(Rec* rec); void purgeAsNeeded(); @@ -147,6 +180,9 @@ private: void moveToHead(Rec*); void addToHead(Rec*); void detach(Rec*); + + void init(); // called by constructors + #ifdef SK_DEBUG void validate() const; #else diff --git a/core/SkScalerContext.cpp b/core/SkScalerContext.cpp index 1d6c2f79..04ef2a92 100644 --- a/core/SkScalerContext.cpp +++ b/core/SkScalerContext.cpp @@ -352,16 +352,16 @@ void SkScalerContext::getMetrics(SkGlyph* glyph) { glyph->fHeight = SkToU16(ir.height()); if (glyph->fWidth > 0) { - switch (fRec.fMaskFormat) { - case SkMask::kLCD16_Format: - case SkMask::kLCD32_Format: - glyph->fWidth += 2; - glyph->fLeft -= 1; - break; - default: - break; + switch (fRec.fMaskFormat) { + case SkMask::kLCD16_Format: + case SkMask::kLCD32_Format: + glyph->fWidth += 2; + glyph->fLeft -= 1; + break; + default: + break; + } } - } } } @@ -523,10 +523,54 @@ static void pack4xHToLCD32(const SkBitmap& src, const SkMask& dst, } } +static inline int convert_8_to_1(unsigned byte) { + SkASSERT(byte <= 0xFF); + return byte >> 7; +} + +static uint8_t pack_8_to_1(const uint8_t alpha[8]) { + unsigned bits = 0; + for (int i = 0; i < 8; ++i) { + bits <<= 1; + bits |= convert_8_to_1(alpha[i]); + } + return SkToU8(bits); +} + +static void packA8ToA1(const SkMask& mask, const uint8_t* src, size_t srcRB) { + const int height = mask.fBounds.height(); + const int width = mask.fBounds.width(); + const int octs = width >> 3; + const int leftOverBits = width & 7; + + uint8_t* dst = mask.fImage; + const int dstPad = mask.fRowBytes - SkAlign8(width)/8; + SkASSERT(dstPad >= 0); + + const int srcPad = srcRB - width; + SkASSERT(srcPad >= 0); + + for (int y = 0; y < height; ++y) { + for (int i = 0; i < octs; ++i) { + *dst++ = pack_8_to_1(src); + src += 8; + } + if (leftOverBits > 0) { + unsigned bits = 0; + int shift = 7; + for (int i = 0; i < leftOverBits; ++i, --shift) { + bits |= convert_8_to_1(*src++) << shift; + } + *dst++ = bits; + } + src += srcPad; + dst += dstPad; + } +} + static void generateMask(const SkMask& mask, const SkPath& path, const SkMaskGamma::PreBlend& maskPreBlend) { - SkBitmap::Config config; - SkPaint paint; + SkPaint paint; int srcW = mask.fBounds.width(); int srcH = mask.fBounds.height(); @@ -538,27 +582,25 @@ static void generateMask(const SkMask& mask, const SkPath& path, matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft), -SkIntToScalar(mask.fBounds.fTop)); - if (SkMask::kBW_Format == mask.fFormat) { - config = SkBitmap::kA1_Config; - paint.setAntiAlias(false); - } else { - config = SkBitmap::kA8_Config; - paint.setAntiAlias(true); - switch (mask.fFormat) { - case SkMask::kA8_Format: - break; - case SkMask::kLCD16_Format: - case SkMask::kLCD32_Format: - // TODO: trigger off LCD orientation - dstW = 4*dstW - 8; - matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft + 1), - -SkIntToScalar(mask.fBounds.fTop)); - matrix.postScale(SkIntToScalar(4), SK_Scalar1); - dstRB = 0; // signals we need a copy - break; - default: - SkDEBUGFAIL("unexpected mask format"); - } + SkBitmap::Config config = SkBitmap::kA8_Config; + paint.setAntiAlias(SkMask::kBW_Format != mask.fFormat); + switch (mask.fFormat) { + case SkMask::kBW_Format: + dstRB = 0; // signals we need a copy + break; + case SkMask::kA8_Format: + break; + case SkMask::kLCD16_Format: + case SkMask::kLCD32_Format: + // TODO: trigger off LCD orientation + dstW = 4*dstW - 8; + matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft + 1), + -SkIntToScalar(mask.fBounds.fTop)); + matrix.postScale(SkIntToScalar(4), SK_Scalar1); + dstRB = 0; // signals we need a copy + break; + default: + SkDEBUGFAIL("unexpected mask format"); } SkRasterClip clip; @@ -587,6 +629,9 @@ static void generateMask(const SkMask& mask, const SkPath& path, draw.drawPath(path, paint); switch (mask.fFormat) { + case SkMask::kBW_Format: + packA8ToA1(mask, bm.getAddr8(0, 0), bm.rowBytes()); + break; case SkMask::kA8_Format: if (maskPreBlend.isApplicable()) { applyLUTToA8Mask(mask, maskPreBlend.fG); diff --git a/core/SkScalerContext.h b/core/SkScalerContext.h index 2820b5ac..e4950ede 100644 --- a/core/SkScalerContext.h +++ b/core/SkScalerContext.h @@ -202,16 +202,57 @@ protected: Rec fRec; unsigned fBaseGlyphCount; - virtual unsigned generateGlyphCount() = 0; - virtual uint16_t generateCharToGlyph(SkUnichar) = 0; - virtual void generateAdvance(SkGlyph*) = 0; - virtual void generateMetrics(SkGlyph*) = 0; - virtual void generateImage(const SkGlyph&) = 0; - virtual void generatePath(const SkGlyph&, SkPath*) = 0; + /** Generates the contents of glyph.fAdvanceX and glyph.fAdvanceY. + * May call getMetrics if that would be just as fast. + */ + virtual void generateAdvance(SkGlyph* glyph) = 0; + + /** Generates the contents of glyph.fWidth, fHeight, fTop, fLeft, + * as well as fAdvanceX and fAdvanceY if not already set. + * + * TODO: fMaskFormat is set by getMetrics later; cannot be set here. + */ + virtual void generateMetrics(SkGlyph* glyph) = 0; + + /** Generates the contents of glyph.fImage. + * When called, glyph.fImage will be pointing to a pre-allocated, + * uninitialized region of memory of size glyph.computeImageSize(). + * This method may change glyph.fMaskFormat if the new image size is + * less than or equal to the old image size. + * + * Because glyph.computeImageSize() will determine the size of fImage, + * generateMetrics will be called before generateImage. + */ + virtual void generateImage(const SkGlyph& glyph) = 0; + + /** Sets the passed path to the glyph outline. + * If this cannot be done the path is set to empty; + * this is indistinguishable from a glyph with an empty path. + * This does not set glyph.fPath. + * + * TODO: path is always glyph.fPath, no reason to pass separately. + */ + virtual void generatePath(const SkGlyph& glyph, SkPath* path) = 0; + + /** Retrieves font metrics. + * TODO: there is now a vertical bit, no need for two parameters. + */ virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY) = 0; - // default impl returns 0, indicating failure. - virtual SkUnichar generateGlyphToChar(uint16_t); + + /** Returns the number of glyphs in the font. */ + virtual unsigned generateGlyphCount() = 0; + + /** Returns the glyph id for the given unichar. + * If there is no 1:1 mapping from the unichar to a glyph id, returns 0. + */ + virtual uint16_t generateCharToGlyph(SkUnichar unichar) = 0; + + /** Returns the unichar for the given glyph id. + * If there is no 1:1 mapping from the glyph id to a unichar, returns 0. + * The default implementation always returns 0, indicating failure. + */ + virtual SkUnichar generateGlyphToChar(uint16_t glyphId); void forceGenerateImageFromPath() { fGenerateImageFromPath = true; } diff --git a/core/SkShader.cpp b/core/SkShader.cpp index 8f6bfb5d..33fddb11 100644 --- a/core/SkShader.cpp +++ b/core/SkShader.cpp @@ -12,8 +12,6 @@ #include "SkPaint.h" #include "SkMallocPixelRef.h" -SK_DEFINE_INST_COUNT(SkShader) - SkShader::SkShader() { fLocalMatrix.reset(); SkDEBUGCODE(fInSetContext = false;) diff --git a/core/SkStream.cpp b/core/SkStream.cpp index a7e92cf6..3350f82f 100644 --- a/core/SkStream.cpp +++ b/core/SkStream.cpp @@ -13,15 +13,6 @@ #include "SkString.h" #include "SkOSFile.h" -SK_DEFINE_INST_COUNT(SkStream) -SK_DEFINE_INST_COUNT(SkWStream) -SK_DEFINE_INST_COUNT(SkFILEStream) -SK_DEFINE_INST_COUNT(SkMemoryStream) -SK_DEFINE_INST_COUNT(SkFILEWStream) -SK_DEFINE_INST_COUNT(SkMemoryWStream) -SK_DEFINE_INST_COUNT(SkDynamicMemoryWStream) -SK_DEFINE_INST_COUNT(SkDebugWStream) - /////////////////////////////////////////////////////////////////////////////// diff --git a/core/SkString.cpp b/core/SkString.cpp index e30b89f1..643dfb13 100644 --- a/core/SkString.cpp +++ b/core/SkString.cpp @@ -634,5 +634,17 @@ SkString SkStringPrintf(const char* format, ...) { return formattedOutput; } +void SkStrSplit(const char* str, const char* delimiters, SkTArray<SkString>* out) { + const char* end = str + strlen(str); + while (str != end) { + // Find a token. + const size_t len = strcspn(str, delimiters); + out->push_back().set(str, len); + str += len; + // Skip any delimiters. + str += strspn(str, delimiters); + } +} + #undef VSNPRINTF #undef SNPRINTF diff --git a/core/SkTypeface.cpp b/core/SkTypeface.cpp index 23627616..09a64324 100644 --- a/core/SkTypeface.cpp +++ b/core/SkTypeface.cpp @@ -11,8 +11,6 @@ #include "SkStream.h" #include "SkTypeface.h" -SK_DEFINE_INST_COUNT(SkTypeface) - //#define TRACE_LIFECYCLE #ifdef TRACE_LIFECYCLE diff --git a/core/SkValidatingReadBuffer.cpp b/core/SkValidatingReadBuffer.cpp index a92c1b9b..692d0dd4 100644 --- a/core/SkValidatingReadBuffer.cpp +++ b/core/SkValidatingReadBuffer.cpp @@ -29,6 +29,10 @@ bool SkValidatingReadBuffer::validate(bool isValid) { return !fError; } +bool SkValidatingReadBuffer::isValid() const { + return !fError; +} + void SkValidatingReadBuffer::setMemory(const void* data, size_t size) { this->validate(IsPtrAlign4(data) && (SkAlign4(size) == size)); if (!fError) { @@ -203,7 +207,7 @@ bool SkValidatingReadBuffer::readScalarArray(SkScalar* values, size_t size) { uint32_t SkValidatingReadBuffer::getArrayCount() { const size_t inc = sizeof(uint32_t); fError = fError || !IsPtrAlign4(fReader.peek()) || !fReader.isAvailable(inc); - return *(uint32_t*)fReader.peek(); + return fError ? 0 : *(uint32_t*)fReader.peek(); } void SkValidatingReadBuffer::readBitmap(SkBitmap* bitmap) { diff --git a/core/SkValidatingReadBuffer.h b/core/SkValidatingReadBuffer.h index 2bcdc43e..febf0c0b 100644 --- a/core/SkValidatingReadBuffer.h +++ b/core/SkValidatingReadBuffer.h @@ -61,6 +61,7 @@ public: virtual SkTypeface* readTypeface() SK_OVERRIDE; virtual bool validate(bool isValid) SK_OVERRIDE; + virtual bool isValid() const SK_OVERRIDE; private: bool readArray(void* value, size_t size, size_t elementSize); diff --git a/core/SkXfermode.cpp b/core/SkXfermode.cpp index 20d88162..8cb79c2d 100644 --- a/core/SkXfermode.cpp +++ b/core/SkXfermode.cpp @@ -19,8 +19,6 @@ #include "SkXfermode_opts_arm_neon.h" #endif -SK_DEFINE_INST_COUNT(SkXfermode) - #define SkAlphaMulAlpha(a, b) SkMulDiv255Round(a, b) #if 0 @@ -1671,6 +1669,7 @@ void SkXfermode::Term() { extern SkProcCoeffXfermode* SkPlatformXfermodeFactory(const ProcCoeff& rec, SkXfermode::Mode mode); +extern SkXfermodeProc SkPlatformXfermodeProcFactory(SkXfermode::Mode mode); SkXfermode* SkXfermode::Create(Mode mode) { SkASSERT(SK_ARRAY_COUNT(gProcCoeffs) == kModeCount); @@ -1692,7 +1691,13 @@ SkXfermode* SkXfermode::Create(Mode mode) { SkXfermode* xfer = gCachedXfermodes[mode]; if (NULL == xfer) { - const ProcCoeff& rec = gProcCoeffs[mode]; + ProcCoeff rec = gProcCoeffs[mode]; + + SkXfermodeProc pp = SkPlatformXfermodeProcFactory(mode); + + if (pp != NULL) { + rec.fProc = pp; + } // check if we have a platform optim for that SkProcCoeffXfermode* xfm = SkPlatformXfermodeFactory(rec, mode); diff --git a/doc/SkDocument.cpp b/doc/SkDocument.cpp index 85a2bad5..5b2237d1 100644 --- a/doc/SkDocument.cpp +++ b/doc/SkDocument.cpp @@ -8,8 +8,6 @@ #include "SkDocument.h" #include "SkStream.h" -SK_DEFINE_INST_COUNT(SkDocument) - SkDocument::SkDocument(SkWStream* stream, void (*doneProc)(SkWStream*, bool)) { fStream = stream; // we do not own this object. fDoneProc = doneProc; diff --git a/effects/SkBitmapAlphaThresholdShader.cpp b/effects/SkBitmapAlphaThresholdShader.cpp index 05d7ba30..44db167d 100644 --- a/effects/SkBitmapAlphaThresholdShader.cpp +++ b/effects/SkBitmapAlphaThresholdShader.cpp @@ -34,8 +34,6 @@ private: typedef SkShader INHERITED; }; -SK_DEFINE_INST_COUNT(BATShader) - SkShader* SkBitmapAlphaThresholdShader::Create(const SkBitmap& bitmap, const SkRegion& region, U8CPU threshold) { diff --git a/effects/SkBitmapSource.cpp b/effects/SkBitmapSource.cpp index 72f51f84..ef5ea869 100644 --- a/effects/SkBitmapSource.cpp +++ b/effects/SkBitmapSource.cpp @@ -6,24 +6,74 @@ */ #include "SkBitmapSource.h" +#include "SkDevice.h" +#include "SkCanvas.h" +#include "SkFlattenableBuffers.h" +#include "SkValidationUtils.h" SkBitmapSource::SkBitmapSource(const SkBitmap& bitmap) : INHERITED(0, 0), - fBitmap(bitmap) { + fBitmap(bitmap), + fSrcRect(SkRect::MakeWH(SkIntToScalar(bitmap.width()), + SkIntToScalar(bitmap.height()))), + fDstRect(fSrcRect) { +} + +SkBitmapSource::SkBitmapSource(const SkBitmap& bitmap, const SkRect& srcRect, const SkRect& dstRect) + : INHERITED(0, 0), + fBitmap(bitmap), + fSrcRect(srcRect), + fDstRect(dstRect) { } SkBitmapSource::SkBitmapSource(SkFlattenableReadBuffer& buffer) : INHERITED(0, buffer) { fBitmap.unflatten(buffer); + buffer.readRect(&fSrcRect); + buffer.readRect(&fDstRect); + buffer.validate(SkIsValidRect(fSrcRect) && SkIsValidRect(fDstRect)); } void SkBitmapSource::flatten(SkFlattenableWriteBuffer& buffer) const { this->INHERITED::flatten(buffer); fBitmap.flatten(buffer); + buffer.writeRect(fSrcRect); + buffer.writeRect(fDstRect); } -bool SkBitmapSource::onFilterImage(Proxy*, const SkBitmap&, const SkMatrix&, +bool SkBitmapSource::onFilterImage(Proxy* proxy, const SkBitmap&, const SkMatrix& matrix, SkBitmap* result, SkIPoint* offset) { - *result = fBitmap; + SkRect bounds, dstRect; + fBitmap.getBounds(&bounds); + matrix.mapRect(&dstRect, fDstRect); + if (fSrcRect == bounds && dstRect == bounds) { + // No regions cropped out or resized; return entire bitmap. + *result = fBitmap; + return true; + } + SkIRect dstIRect; + dstRect.roundOut(&dstIRect); + + SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(dstIRect.width(), dstIRect.height())); + if (NULL == device.get()) { + return false; + } + + SkCanvas canvas(device.get()); + SkPaint paint; + + // Subtract off the integer component of the translation (will be applied in loc, below). + dstRect.offset(-SkIntToScalar(dstIRect.fLeft), -SkIntToScalar(dstIRect.fTop)); + paint.setXfermodeMode(SkXfermode::kSrc_Mode); + // FIXME: this probably shouldn't be necessary, but drawBitmapRectToRect asserts + // None filtering when it's translate-only + paint.setFilterLevel( + fSrcRect.width() == dstRect.width() && fSrcRect.height() == dstRect.height() ? + SkPaint::kNone_FilterLevel : SkPaint::kMedium_FilterLevel); + canvas.drawBitmapRectToRect(fBitmap, &fSrcRect, dstRect, &paint); + + *result = device.get()->accessBitmap(false); + offset->fX += dstIRect.fLeft; + offset->fY += dstIRect.fTop; return true; } diff --git a/effects/SkBlurImageFilter.cpp b/effects/SkBlurImageFilter.cpp index 9bceda7d..2795f3a8 100644 --- a/effects/SkBlurImageFilter.cpp +++ b/effects/SkBlurImageFilter.cpp @@ -159,6 +159,10 @@ bool SkBlurImageFilter::onFilterImage(Proxy* proxy, dst->setConfig(src.config(), srcBounds.width(), srcBounds.height()); dst->getBounds(&dstBounds); dst->allocPixels(); + if (!dst->getPixels()) { + return false; + } + int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX; int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY; getBox3Params(fSigma.width(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX); diff --git a/effects/SkBlurMaskFilter.cpp b/effects/SkBlurMaskFilter.cpp index 9ed6d0c0..d2c43d71 100644 --- a/effects/SkBlurMaskFilter.cpp +++ b/effects/SkBlurMaskFilter.cpp @@ -483,18 +483,10 @@ void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src, SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkFlattenableReadBuffer& buffer) : SkMaskFilter(buffer) { - fSigma = buffer.readScalar(); -#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V13_AND_ALL_OTHER_INSTANCES_TOO - // Fixing this must be done in two stages. When the skps are recaptured in V13, - // remove the ConvertRadiusToSigma but retain the absolute value. - // At the same time, switch the code in flatten to write a positive value. - // When the skps are captured in V14 the absolute value can be removed. - if (fSigma > 0) { - fSigma = SkBlurMask::ConvertRadiusToSigma(fSigma); - } else { - fSigma = -fSigma; - } +#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V16_AND_ALL_OTHER_INSTANCES_TOO + // TODO: when the skps are recaptured at > v15 the SkScalarAbs can be removed #endif + fSigma = SkScalarAbs(buffer.readScalar()); fBlurStyle = (SkBlurMaskFilter::BlurStyle)buffer.readInt(); fBlurFlags = buffer.readUInt() & SkBlurMaskFilter::kAll_BlurFlag; SkASSERT(fSigma >= 0); @@ -503,7 +495,7 @@ SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkFlattenableReadBuffer& buffer) void SkBlurMaskFilterImpl::flatten(SkFlattenableWriteBuffer& buffer) const { this->INHERITED::flatten(buffer); - buffer.writeScalar(-fSigma); + buffer.writeScalar(fSigma); buffer.writeInt(fBlurStyle); buffer.writeUInt(fBlurFlags); } diff --git a/effects/SkColorFilterImageFilter.cpp b/effects/SkColorFilterImageFilter.cpp index 2042c126..8b7b390e 100755 --- a/effects/SkColorFilterImageFilter.cpp +++ b/effects/SkColorFilterImageFilter.cpp @@ -113,6 +113,9 @@ bool SkColorFilterImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& sourc } SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height())); + if (NULL == device.get()) { + return false; + } SkCanvas canvas(device.get()); SkPaint paint; diff --git a/effects/SkColorFilters.cpp b/effects/SkColorFilters.cpp index 8ef9edf2..d482a09d 100644 --- a/effects/SkColorFilters.cpp +++ b/effects/SkColorFilters.cpp @@ -101,8 +101,10 @@ protected: SkModeColorFilter(SkFlattenableReadBuffer& buffer) { fColor = buffer.readColor(); fMode = (SkXfermode::Mode)buffer.readUInt(); - this->updateCache(); - buffer.validate(SkIsValidMode(fMode)); + if (buffer.isValid()) { + this->updateCache(); + buffer.validate(SkIsValidMode(fMode)); + } } private: diff --git a/effects/SkColorMatrixFilter.cpp b/effects/SkColorMatrixFilter.cpp index 5b36a8f7..fc1b77b7 100644 --- a/effects/SkColorMatrixFilter.cpp +++ b/effects/SkColorMatrixFilter.cpp @@ -308,10 +308,8 @@ void SkColorMatrixFilter::flatten(SkFlattenableWriteBuffer& buffer) const { SkColorMatrixFilter::SkColorMatrixFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) { SkASSERT(buffer.getArrayCount() == 20); - buffer.readScalarArray(fMatrix.fMat, 20); - this->initState(fMatrix.fMat); - for (int i = 0; i < 20; ++i) { - buffer.validate(SkScalarIsFinite(fMatrix.fMat[i])); + if (buffer.readScalarArray(fMatrix.fMat, 20)) { + this->initState(fMatrix.fMat); } } diff --git a/effects/SkDropShadowImageFilter.cpp b/effects/SkDropShadowImageFilter.cpp index b4d8689f..24a910d0 100644 --- a/effects/SkDropShadowImageFilter.cpp +++ b/effects/SkDropShadowImageFilter.cpp @@ -15,10 +15,21 @@ #include "SkFlattenableBuffers.h" SkDropShadowImageFilter::SkDropShadowImageFilter(SkScalar dx, SkScalar dy, SkScalar sigma, SkColor color, SkImageFilter* input) - : SkImageFilter(input) + : INHERITED(input) , fDx(dx) , fDy(dy) - , fSigma(sigma) + , fSigmaX(sigma) + , fSigmaY(sigma) + , fColor(color) +{ +} + +SkDropShadowImageFilter::SkDropShadowImageFilter(SkScalar dx, SkScalar dy, SkScalar sigmaX, SkScalar sigmaY, SkColor color, SkImageFilter* input, const CropRect* cropRect) + : INHERITED(input, cropRect) + , fDx(dx) + , fDy(dy) + , fSigmaX(sigmaX) + , fSigmaY(sigmaY) , fColor(color) { } @@ -27,11 +38,13 @@ SkDropShadowImageFilter::SkDropShadowImageFilter(SkFlattenableReadBuffer& buffer : INHERITED(1, buffer) { fDx = buffer.readScalar(); fDy = buffer.readScalar(); - fSigma = buffer.readScalar(); + fSigmaX = buffer.readScalar(); + fSigmaY = buffer.readScalar(); fColor = buffer.readColor(); buffer.validate(SkScalarIsFinite(fDx) && SkScalarIsFinite(fDy) && - SkScalarIsFinite(fSigma)); + SkScalarIsFinite(fSigmaX) && + SkScalarIsFinite(fSigmaY)); } void SkDropShadowImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const @@ -39,7 +52,8 @@ void SkDropShadowImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const this->INHERITED::flatten(buffer); buffer.writeScalar(fDx); buffer.writeScalar(fDy); - buffer.writeScalar(fSigma); + buffer.writeScalar(fSigmaX); + buffer.writeScalar(fSigmaY); buffer.writeColor(fColor); } @@ -49,17 +63,29 @@ bool SkDropShadowImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source if (getInput(0) && !getInput(0)->filterImage(proxy, source, matrix, &src, loc)) return false; - SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(src.width(), src.height())); + SkIRect bounds; + src.getBounds(&bounds); + if (!this->applyCropRect(&bounds, matrix)) { + return false; + } + + SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height())); + if (NULL == device.get()) { + return false; + } SkCanvas canvas(device.get()); - SkAutoTUnref<SkImageFilter> blurFilter(new SkBlurImageFilter(fSigma, fSigma)); + SkAutoTUnref<SkImageFilter> blurFilter(new SkBlurImageFilter(fSigmaX, fSigmaY)); SkAutoTUnref<SkColorFilter> colorFilter(SkColorFilter::CreateModeFilter(fColor, SkXfermode::kSrcIn_Mode)); SkPaint paint; paint.setImageFilter(blurFilter.get()); paint.setColorFilter(colorFilter.get()); paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); + canvas.translate(-SkIntToScalar(bounds.fLeft), -SkIntToScalar(bounds.fTop)); canvas.drawBitmap(src, fDx, fDy, &paint); canvas.drawBitmap(src, 0, 0); *result = device->accessBitmap(false); + loc->fX += bounds.fLeft; + loc->fY += bounds.fTop; return true; } diff --git a/effects/SkEmbossMaskFilter.cpp b/effects/SkEmbossMaskFilter.cpp index 2d312500..905cfab9 100644 --- a/effects/SkEmbossMaskFilter.cpp +++ b/effects/SkEmbossMaskFilter.cpp @@ -134,18 +134,10 @@ SkEmbossMaskFilter::SkEmbossMaskFilter(SkFlattenableReadBuffer& buffer) SkASSERT(buffer.getArrayCount() == sizeof(Light)); buffer.readByteArray(&fLight, sizeof(Light)); SkASSERT(fLight.fPad == 0); // for the font-cache lookup to be clean - fBlurSigma = buffer.readScalar(); -#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V13_AND_ALL_OTHER_INSTANCES_TOO - // Fixing this must be done in two stages. When the skps are recaptured in V13, - // remove the ConvertRadiusToSigma but retain the absolute value. - // At the same time, switch the code in flatten to write a positive value. - // When the skps are captured in V14 the absolute value can be removed. - if (fBlurSigma > 0) { - fBlurSigma = SkBlurMask::ConvertRadiusToSigma(fBlurSigma); - } else { - fBlurSigma = -fBlurSigma; - } +#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V16_AND_ALL_OTHER_INSTANCES_TOO + // TODO: Once skps are recaptured in > v15 this SkScalarAbs can be removed #endif + fBlurSigma = SkScalarAbs(buffer.readScalar()); } void SkEmbossMaskFilter::flatten(SkFlattenableWriteBuffer& buffer) const { @@ -154,7 +146,7 @@ void SkEmbossMaskFilter::flatten(SkFlattenableWriteBuffer& buffer) const { Light tmpLight = fLight; tmpLight.fPad = 0; // for the font-cache lookup to be clean buffer.writeByteArray(&tmpLight, sizeof(tmpLight)); - buffer.writeScalar(-fBlurSigma); + buffer.writeScalar(fBlurSigma); } #ifdef SK_DEVELOPER diff --git a/effects/SkGpuBlurUtils.cpp b/effects/SkGpuBlurUtils.cpp index 02fdf485..6133db11 100644 --- a/effects/SkGpuBlurUtils.cpp +++ b/effects/SkGpuBlurUtils.cpp @@ -11,7 +11,7 @@ #if SK_SUPPORT_GPU #include "effects/GrConvolutionEffect.h" -#include "effects/GrTextureDomainEffect.h" +#include "effects/GrTextureDomain.h" #include "GrContext.h" #endif @@ -173,7 +173,7 @@ GrTexture* GaussianBlur(GrContext* context, srcTexture, matrix, domain, - GrTextureDomainEffect::kDecal_WrapMode, + GrTextureDomain::kDecal_Mode, GrTextureParams::kBilerp_FilterMode)); paint.addColorEffect(effect); } else { diff --git a/effects/SkLayerDrawLooper.cpp b/effects/SkLayerDrawLooper.cpp index 998c4bcd..65f058b9 100644 --- a/effects/SkLayerDrawLooper.cpp +++ b/effects/SkLayerDrawLooper.cpp @@ -13,8 +13,6 @@ #include "SkStringUtils.h" #include "SkUnPreMultiply.h" -SK_DEFINE_INST_COUNT(SkLayerDrawLooper) - SkLayerDrawLooper::LayerInfo::LayerInfo() { fFlagsMask = 0; // ignore our paint flags fPaintBits = 0; // ignore our paint fields diff --git a/effects/SkLightingImageFilter.cpp b/effects/SkLightingImageFilter.cpp index 8c8798f0..50cca07d 100644 --- a/effects/SkLightingImageFilter.cpp +++ b/effects/SkLightingImageFilter.cpp @@ -549,8 +549,6 @@ private: SkPoint3 fColor; }; -SK_DEFINE_INST_COUNT(SkLight) - /////////////////////////////////////////////////////////////////////////////// class SkDistantLight : public SkLight { @@ -814,6 +812,7 @@ void SkLight::flattenLight(SkFlattenableWriteBuffer& buffer) const { case SkLight::kSpot_LightType: return SkNEW_ARGS(SkSpotLight, (buffer)); default: SkDEBUGFAIL("Unknown LightType."); + buffer.validate(false); return NULL; } } @@ -952,6 +951,9 @@ bool SkDiffuseLightingImageFilter::onFilterImage(Proxy* proxy, dst->setConfig(src.config(), bounds.width(), bounds.height()); dst->allocPixels(); + if (!dst->getPixels()) { + return false; + } SkAutoTUnref<SkLight> transformedLight(light()->transform(ctm)); @@ -1040,6 +1042,9 @@ bool SkSpecularLightingImageFilter::onFilterImage(Proxy* proxy, dst->setConfig(src.config(), bounds.width(), bounds.height()); dst->allocPixels(); + if (!dst->getPixels()) { + return false; + } SpecularLightingType lightingType(fKS, fShininess); SkAutoTUnref<SkLight> transformedLight(light()->transform(ctm)); diff --git a/effects/SkMagnifierImageFilter.cpp b/effects/SkMagnifierImageFilter.cpp index d4120598..e6f3984b 100644 --- a/effects/SkMagnifierImageFilter.cpp +++ b/effects/SkMagnifierImageFilter.cpp @@ -240,7 +240,9 @@ SkMagnifierImageFilter::SkMagnifierImageFilter(SkFlattenableReadBuffer& buffer) fSrcRect = SkRect::MakeXYWH(x, y, width, height); fInset = buffer.readScalar(); - buffer.validate(SkIsValidRect(fSrcRect) && SkScalarIsFinite(fInset)); + buffer.validate(SkScalarIsFinite(fInset) && SkIsValidRect(fSrcRect) && + // Negative numbers in src rect are not supported + (fSrcRect.fLeft >= 0) && (fSrcRect.fTop >= 0)); } // FIXME: implement single-input semantics @@ -283,7 +285,9 @@ bool SkMagnifierImageFilter::onFilterImage(Proxy*, const SkBitmap& src, SkASSERT(fSrcRect.width() < src.width()); SkASSERT(fSrcRect.height() < src.height()); - if (src.config() != SkBitmap::kARGB_8888_Config) { + if ((src.config() != SkBitmap::kARGB_8888_Config) || + (fSrcRect.width() >= src.width()) || + (fSrcRect.height() >= src.height())) { return false; } @@ -293,13 +297,17 @@ bool SkMagnifierImageFilter::onFilterImage(Proxy*, const SkBitmap& src, return false; } + dst->setConfig(src.config(), src.width(), src.height()); + dst->allocPixels(); + if (!dst->getPixels()) { + return false; + } + SkScalar inv_inset = fInset > 0 ? SkScalarInvert(fInset) : SK_Scalar1; SkScalar inv_x_zoom = fSrcRect.width() / src.width(); SkScalar inv_y_zoom = fSrcRect.height() / src.height(); - dst->setConfig(src.config(), src.width(), src.height()); - dst->allocPixels(); SkColor* sptr = src.getAddr32(0, 0); SkColor* dptr = dst->getAddr32(0, 0); int width = src.width(), height = src.height(); @@ -332,8 +340,8 @@ bool SkMagnifierImageFilter::onFilterImage(Proxy*, const SkBitmap& src, SkScalar y_interp = SkScalarMul(weight, (fSrcRect.y() + y * inv_y_zoom)) + (SK_Scalar1 - weight) * y; - int x_val = SkMin32(SkScalarFloorToInt(x_interp), width - 1); - int y_val = SkMin32(SkScalarFloorToInt(y_interp), height - 1); + int x_val = SkPin32(SkScalarFloorToInt(x_interp), 0, width - 1); + int y_val = SkPin32(SkScalarFloorToInt(y_interp), 0, height - 1); *dptr = sptr[y_val * width + x_val]; dptr++; diff --git a/effects/SkMatrixConvolutionImageFilter.cpp b/effects/SkMatrixConvolutionImageFilter.cpp index cef0450f..3da27cef 100644 --- a/effects/SkMatrixConvolutionImageFilter.cpp +++ b/effects/SkMatrixConvolutionImageFilter.cpp @@ -279,6 +279,9 @@ bool SkMatrixConvolutionImageFilter::onFilterImage(Proxy* proxy, result->setConfig(src.config(), bounds.width(), bounds.height()); result->allocPixels(); + if (!result->getPixels()) { + return false; + } SkIRect interior = SkIRect::MakeXYWH(bounds.left() + fTarget.fX, bounds.top() + fTarget.fY, diff --git a/effects/SkMergeImageFilter.cpp b/effects/SkMergeImageFilter.cpp index a755fe68..33673b06 100755 --- a/effects/SkMergeImageFilter.cpp +++ b/effects/SkMergeImageFilter.cpp @@ -11,9 +11,6 @@ #include "SkFlattenableBuffers.h" #include "SkValidationUtils.h" -// Use 65535 as an arbitrary large number of inputs that this image filter should never overflow -static const int kMaxInputs = 65535; - /////////////////////////////////////////////////////////////////////////////// void SkMergeImageFilter::initAllocModes() { @@ -56,7 +53,7 @@ SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* first, SkImageFilter* seco SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* filters[], int count, const SkXfermode::Mode modes[], const CropRect* cropRect) : INHERITED(count, filters, cropRect) { - SkASSERT(count <= kMaxInputs); + SkASSERT(count >= 0); this->initModes(modes); } @@ -161,16 +158,17 @@ void SkMergeImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const { } SkMergeImageFilter::SkMergeImageFilter(SkFlattenableReadBuffer& buffer) - : INHERITED(kMaxInputs, buffer) { + : INHERITED(-1, buffer) { bool hasModes = buffer.readBool(); if (hasModes) { this->initAllocModes(); int nbInputs = countInputs(); size_t size = nbInputs * sizeof(fModes[0]); SkASSERT(buffer.getArrayCount() == size); - buffer.readByteArray(fModes, size); - for (int i = 0; i < nbInputs; ++i) { - buffer.validate(SkIsValidMode((SkXfermode::Mode)fModes[i])); + if (buffer.readByteArray(fModes, size)) { + for (int i = 0; i < nbInputs; ++i) { + buffer.validate(SkIsValidMode((SkXfermode::Mode)fModes[i])); + } } } else { fModes = 0; diff --git a/effects/SkMorphologyImageFilter.cpp b/effects/SkMorphologyImageFilter.cpp index 3f2f9855..0d00c359 100644 --- a/effects/SkMorphologyImageFilter.cpp +++ b/effects/SkMorphologyImageFilter.cpp @@ -188,6 +188,9 @@ bool SkErodeImageFilter::onFilterImage(Proxy* proxy, dst->setConfig(src.config(), bounds.width(), bounds.height()); dst->allocPixels(); + if (!dst->getPixels()) { + return false; + } int width = radius().width(); int height = radius().height(); @@ -247,6 +250,9 @@ bool SkDilateImageFilter::onFilterImage(Proxy* proxy, dst->setConfig(src.config(), bounds.width(), bounds.height()); dst->allocPixels(); + if (!dst->getPixels()) { + return false; + } int width = radius().width(); int height = radius().height(); diff --git a/effects/SkOffsetImageFilter.cpp b/effects/SkOffsetImageFilter.cpp index aefbcba8..59318e39 100644 --- a/effects/SkOffsetImageFilter.cpp +++ b/effects/SkOffsetImageFilter.cpp @@ -48,6 +48,9 @@ bool SkOffsetImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source, } SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height())); + if (NULL == device.get()) { + return false; + } SkCanvas canvas(device); SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); diff --git a/effects/SkRectShaderImageFilter.cpp b/effects/SkRectShaderImageFilter.cpp index ab38fc48..5c34547c 100644 --- a/effects/SkRectShaderImageFilter.cpp +++ b/effects/SkRectShaderImageFilter.cpp @@ -62,6 +62,9 @@ bool SkRectShaderImageFilter::onFilterImage(Proxy* proxy, SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height())); + if (NULL == device.get()) { + return false; + } SkCanvas canvas(device.get()); SkPaint paint; paint.setShader(fShader); diff --git a/effects/SkTileImageFilter.cpp b/effects/SkTileImageFilter.cpp index ccca4ff7..73e5304a 100644 --- a/effects/SkTileImageFilter.cpp +++ b/effects/SkTileImageFilter.cpp @@ -24,22 +24,31 @@ bool SkTileImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src, const S return false; } - int w = SkScalarTruncToInt(fDstRect.width()); - int h = SkScalarTruncToInt(fDstRect.height()); + SkRect dstRect; + ctm.mapRect(&dstRect, fDstRect); + int w = SkScalarCeilToInt(dstRect.width()); + int h = SkScalarCeilToInt(dstRect.height()); if (!fSrcRect.width() || !fSrcRect.height() || !w || !h) { return false; } - SkIRect srcRect; - fSrcRect.roundOut(&srcRect); + SkRect srcRect; + ctm.mapRect(&srcRect, fSrcRect); + SkIRect srcIRect; + srcRect.roundOut(&srcIRect); SkBitmap subset; - if (!source.extractSubset(&subset, srcRect)) { + SkIRect bounds; + source.getBounds(&bounds); + if (!srcIRect.intersect(bounds)) { + return true; + } else if (!source.extractSubset(&subset, srcIRect)) { return false; } SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(w, h)); - SkIRect bounds; - source.getBounds(&bounds); + if (NULL == device.get()) { + return false; + } SkCanvas canvas(device); SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); @@ -47,7 +56,6 @@ bool SkTileImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src, const S SkAutoTUnref<SkShader> shader(SkShader::CreateBitmapShader(subset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode)); paint.setShader(shader); - SkRect dstRect = fDstRect; dstRect.offset(SkIntToScalar(localOffset.fX), SkIntToScalar(localOffset.fY)); canvas.drawRect(dstRect, paint); *dst = device->accessBitmap(false); @@ -58,7 +66,7 @@ SkTileImageFilter::SkTileImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(1, buffer) { buffer.readRect(&fSrcRect); buffer.readRect(&fDstRect); - buffer.validate(SkIsValidRect(fSrcRect) && SkIsValidRect(fDstRect)); + buffer.validate(buffer.isValid() && SkIsValidRect(fSrcRect) && SkIsValidRect(fDstRect)); } void SkTileImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const { diff --git a/effects/SkTransparentShader.cpp b/effects/SkTransparentShader.cpp index 970e74fa..1d7e8087 100644 --- a/effects/SkTransparentShader.cpp +++ b/effects/SkTransparentShader.cpp @@ -94,9 +94,6 @@ void SkTransparentShader::shadeSpan(int x, int y, SkPMColor span[], int count) { } break; } - case SkBitmap::kA1_Config: - SkDEBUGFAIL("kA1_Config umimplemented at this time"); - break; default: // to avoid warnings break; } diff --git a/effects/SkXfermodeImageFilter.cpp b/effects/SkXfermodeImageFilter.cpp index 620bde9f..3ab52950 100644 --- a/effects/SkXfermodeImageFilter.cpp +++ b/effects/SkXfermodeImageFilter.cpp @@ -72,6 +72,9 @@ bool SkXfermodeImageFilter::onFilterImage(Proxy* proxy, foregroundOffset.fY -= bounds.top(); SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height())); + if (NULL == device.get()) { + return false; + } SkCanvas canvas(device); SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); diff --git a/fonts/SkGScalerContext.cpp b/fonts/SkGScalerContext.cpp index f0543c57..551b01c9 100644 --- a/fonts/SkGScalerContext.cpp +++ b/fonts/SkGScalerContext.cpp @@ -176,6 +176,8 @@ SkScalerContext* SkGTypeface::onCreateScalerContext( void SkGTypeface::onFilterRec(SkScalerContextRec* rec) const { fProxy->filterRec(rec); + rec->setHinting(SkPaint::kNo_Hinting); + rec->fMaskFormat = SkMask::kARGB32_Format; } SkAdvancedTypefaceMetrics* SkGTypeface::onGetAdvancedTypefaceMetrics( diff --git a/gpu/GrAARectRenderer.cpp b/gpu/GrAARectRenderer.cpp index aa599ca8..2c21f09f 100644 --- a/gpu/GrAARectRenderer.cpp +++ b/gpu/GrAARectRenderer.cpp @@ -13,8 +13,6 @@ #include "SkColorPriv.h" #include "effects/GrVertexEffect.h" -SK_DEFINE_INST_COUNT(GrAARectRenderer) - /////////////////////////////////////////////////////////////////////////////// class GrGLAlignedRectEffect; diff --git a/gpu/GrAllocator.h b/gpu/GrAllocator.h index 23bb6b76..f2afec8e 100755 --- a/gpu/GrAllocator.h +++ b/gpu/GrAllocator.h @@ -39,6 +39,22 @@ public: SkDEBUGCODE(if (!fOwnFirstBlock) {*((char*)initialBlock+fBlockSize-1)='a';} ); } + /* + * Set first block of memory to write into. Must be called before any other methods. + * This requires that you have passed NULL in the constructor. + * + * @param initialBlock optional memory to use for the first block. + * Must be at least itemSize*itemsPerBlock sized. + * Caller is responsible for freeing this memory. + */ + void setInitialBlock(void* initialBlock) { + SkASSERT(0 == fCount); + SkASSERT(1 == fBlocks.count()); + SkASSERT(NULL == fBlocks.back()); + fOwnFirstBlock = false; + fBlocks.back() = initialBlock; + } + /** * Adds an item and returns pointer to it. * @@ -145,9 +161,6 @@ public: * Create an allocator * * @param itemsPerBlock the number of items to allocate at once - * @param initialBlock optional memory to use for the first block. - * Must be at least size(T)*itemsPerBlock sized. - * Caller is responsible for freeing this memory. */ explicit GrTAllocator(int itemsPerBlock) : fAllocator(sizeof(T), itemsPerBlock, NULL) {} @@ -223,8 +236,15 @@ public: } protected: - GrTAllocator(int itemsPerBlock, void* initialBlock) - : fAllocator(sizeof(T), itemsPerBlock, initialBlock) { + /* + * Set first block of memory to write into. Must be called before any other methods. + * + * @param initialBlock optional memory to use for the first block. + * Must be at least size(T)*itemsPerBlock sized. + * Caller is responsible for freeing this memory. + */ + void setInitialBlock(void* initialBlock) { + fAllocator.setInitialBlock(initialBlock); } private: @@ -237,7 +257,8 @@ private: typedef GrTAllocator<T> INHERITED; public: - GrSTAllocator() : INHERITED(N, fStorage.get()) { + GrSTAllocator() : INHERITED(N) { + this->setInitialBlock(fStorage.get()); } private: diff --git a/gpu/GrBinHashKey.h b/gpu/GrBinHashKey.h index 7d4aa0fb..585a1a1c 100644 --- a/gpu/GrBinHashKey.h +++ b/gpu/GrBinHashKey.h @@ -13,37 +13,19 @@ #include "GrTypes.h" /** - * Hash function class that can take a data chunk of any predetermined length. The hash function - * used is the One-at-a-Time Hash (http://burtleburtle.net/bob/hash/doobs.html). - * - * Keys are computed from ENTRY objects. ENTRY must be fully ordered by a member: - * int compare(const GrTBinHashKey<ENTRY, ..>& k); - * which returns negative if the ENTRY < k, 0 if it equals k, and positive if k < the ENTRY. - * Additionally, ENTRY must be flattenable into the key using setKeyData. - * - * This class satisfies the requirements to be a key for a GrTHashTable. + * GrBinHashKey is a hash key class that can take a data chunk of any predetermined + * length. The hash function used is the One-at-a-Time Hash + * (http://burtleburtle.net/bob/hash/doobs.html). */ -template<typename ENTRY, size_t KEY_SIZE> -class GrTBinHashKey { +template<size_t KEY_SIZE> +class GrBinHashKey { public: enum { kKeySize = KEY_SIZE }; - GrTBinHashKey() { + GrBinHashKey() { this->reset(); } - GrTBinHashKey(const GrTBinHashKey<ENTRY, KEY_SIZE>& other) { - *this = other; - } - - GrTBinHashKey<ENTRY, KEY_SIZE>& operator=(const GrTBinHashKey<ENTRY, KEY_SIZE>& other) { - memcpy(this, &other, sizeof(*this)); - return *this; - } - - ~GrTBinHashKey() { - } - void reset() { fHash = 0; #ifdef SK_DEBUG @@ -52,39 +34,49 @@ public: } void setKeyData(const uint32_t* SK_RESTRICT data) { - SkASSERT(GrIsALIGN4(KEY_SIZE)); + SK_COMPILE_ASSERT(KEY_SIZE % 4 == 0, key_size_mismatch); memcpy(&fData, data, KEY_SIZE); uint32_t hash = 0; size_t len = KEY_SIZE; while (len >= 4) { hash += *data++; - hash += (fHash << 10); + hash += (hash << 10); hash ^= (hash >> 6); len -= 4; } - hash += (fHash << 3); - hash ^= (fHash >> 11); - hash += (fHash << 15); + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); #ifdef SK_DEBUG fIsValid = true; #endif fHash = hash; } - int compare(const GrTBinHashKey<ENTRY, KEY_SIZE>& key) const { + bool operator==(const GrBinHashKey<KEY_SIZE>& key) const { SkASSERT(fIsValid && key.fIsValid); - return memcmp(fData, key.fData, KEY_SIZE); - } - - static bool EQ(const ENTRY& entry, const GrTBinHashKey<ENTRY, KEY_SIZE>& key) { - SkASSERT(key.fIsValid); - return 0 == entry.compare(key); + if (fHash != key.fHash) { + return false; + } + for (size_t i = 0; i < SK_ARRAY_COUNT(fData); ++i) { + if (fData[i] != key.fData[i]) { + return false; + } + } + return true; } - static bool LT(const ENTRY& entry, const GrTBinHashKey<ENTRY, KEY_SIZE>& key) { - SkASSERT(key.fIsValid); - return entry.compare(key) < 0; + bool operator<(const GrBinHashKey<KEY_SIZE>& key) const { + SkASSERT(fIsValid && key.fIsValid); + for (size_t i = 0; i < SK_ARRAY_COUNT(fData); ++i) { + if (fData[i] < key.fData[i]) { + return true; + } else if (fData[i] > key.fData[i]) { + return false; + } + } + return false; } uint32_t getHash() const { @@ -94,12 +86,12 @@ public: const uint8_t* getData() const { SkASSERT(fIsValid); - return fData; + return reinterpret_cast<const uint8_t*>(fData); } private: uint32_t fHash; - uint8_t fData[KEY_SIZE]; // Buffer for key storage + uint32_t fData[KEY_SIZE / sizeof(uint32_t)]; // Buffer for key storage. #ifdef SK_DEBUG public: diff --git a/gpu/GrBitmapTextContext.cpp b/gpu/GrBitmapTextContext.cpp index 8d955bbb..a43c4a28 100755 --- a/gpu/GrBitmapTextContext.cpp +++ b/gpu/GrBitmapTextContext.cpp @@ -70,7 +70,10 @@ void GrBitmapTextContext::flushGlyphs() { GrCustomCoordsTextureEffect::Create(fCurrTexture, params), kGlyphCoordsAttributeIndex)->unref(); - if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) { + if (NULL != fStrike && kARGB_GrMaskFormat == fStrike->getMaskFormat()) { + drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff()); + drawState->setColor(0xffffffff); + } else if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) { if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() || kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() || fPaint.numColorStages()) { diff --git a/gpu/GrClipMaskManager.cpp b/gpu/GrClipMaskManager.cpp index 3aef3dee..4fd746f3 100644 --- a/gpu/GrClipMaskManager.cpp +++ b/gpu/GrClipMaskManager.cpp @@ -17,7 +17,7 @@ #include "GrRenderTarget.h" #include "GrStencilBuffer.h" #include "GrSWMaskHelper.h" -#include "effects/GrTextureDomainEffect.h" +#include "effects/GrTextureDomain.h" #include "SkRasterClip.h" #include "SkStrokeRec.h" #include "SkTLazy.h" @@ -52,8 +52,8 @@ void setup_drawstate_aaclip(GrGpu* gpu, drawState->addCoverageEffect( GrTextureDomainEffect::Create(result, mat, - GrTextureDomainEffect::MakeTexelDomain(result, domainTexels), - GrTextureDomainEffect::kDecal_WrapMode, + GrTextureDomain::MakeTexelDomain(result, domainTexels), + GrTextureDomain::kDecal_Mode, GrTextureParams::kNone_FilterMode, kPosition_GrCoordSet))->unref(); } @@ -365,8 +365,8 @@ void GrClipMaskManager::mergeMask(GrTexture* dstMask, drawState->addColorEffect( GrTextureDomainEffect::Create(srcMask, sampleM, - GrTextureDomainEffect::MakeTexelDomain(srcMask, srcBound), - GrTextureDomainEffect::kDecal_WrapMode, + GrTextureDomain::MakeTexelDomain(srcMask, srcBound), + GrTextureDomain::kDecal_Mode, GrTextureParams::kNone_FilterMode))->unref(); fGpu->drawSimpleRect(SkRect::Make(dstBound), NULL); } diff --git a/gpu/GrContext.cpp b/gpu/GrContext.cpp index cab4414a..0eb8c5b7 100644 --- a/gpu/GrContext.cpp +++ b/gpu/GrContext.cpp @@ -32,9 +32,6 @@ #include "SkTLS.h" #include "SkTrace.h" -SK_DEFINE_INST_COUNT(GrContext) -SK_DEFINE_INST_COUNT(GrDrawState) - // It can be useful to set this to false to test whether a bug is caused by using the // InOrderDrawBuffer, to compare performance of using/not using InOrderDrawBuffer, or to make // debugging simpler. @@ -88,19 +85,7 @@ GrContext* GrContext::Create(GrBackend backend, GrBackendContext backendContext) } } -namespace { -void* CreateThreadInstanceCount() { - return SkNEW_ARGS(int, (0)); -} -void DeleteThreadInstanceCount(void* v) { - delete reinterpret_cast<int*>(v); -} -#define THREAD_INSTANCE_COUNT \ - (*reinterpret_cast<int*>(SkTLS::Get(CreateThreadInstanceCount, DeleteThreadInstanceCount))) -} - GrContext::GrContext() { - ++THREAD_INSTANCE_COUNT; fDrawState = NULL; fGpu = NULL; fClip = NULL; @@ -148,10 +133,6 @@ bool GrContext::init(GrBackend backend, GrBackendContext backendContext) { return true; } -int GrContext::GetThreadInstanceCount() { - return THREAD_INSTANCE_COUNT; -} - GrContext::~GrContext() { if (NULL == fGpu) { return; @@ -181,8 +162,6 @@ GrContext::~GrContext() { SkSafeUnref(fPathRendererChain); SkSafeUnref(fSoftwarePathRenderer); fDrawState->unref(); - - --THREAD_INSTANCE_COUNT; } void GrContext::contextLost() { diff --git a/gpu/GrDrawTarget.cpp b/gpu/GrDrawTarget.cpp index 6a1c4544..0b4d96af 100644 --- a/gpu/GrDrawTarget.cpp +++ b/gpu/GrDrawTarget.cpp @@ -18,8 +18,6 @@ #include "SkStrokeRec.h" -SK_DEFINE_INST_COUNT(GrDrawTarget) - //////////////////////////////////////////////////////////////////////////////// GrDrawTarget::DrawInfo& GrDrawTarget::DrawInfo::operator =(const DrawInfo& di) { @@ -962,8 +960,6 @@ void GrDrawTarget::initCopySurfaceDstDesc(const GrSurface* src, GrTextureDesc* d /////////////////////////////////////////////////////////////////////////////// -SK_DEFINE_INST_COUNT(GrDrawTargetCaps) - void GrDrawTargetCaps::reset() { f8BitPaletteSupport = false; fNPOTTextureTileSupport = false; diff --git a/gpu/GrEffect.cpp b/gpu/GrEffect.cpp index a64fd7c4..986e80a5 100644 --- a/gpu/GrEffect.cpp +++ b/gpu/GrEffect.cpp @@ -12,8 +12,6 @@ #include "GrMemoryPool.h" #include "SkTLS.h" -SK_DEFINE_INST_COUNT(GrEffect) - #if SK_ALLOW_STATIC_GLOBAL_INITIALIZERS SkTArray<GrEffectTestFactory*, true>* GrEffectTestFactory::GetFactories() { static SkTArray<GrEffectTestFactory*, true> gFactories; @@ -61,8 +59,6 @@ int32_t GrBackendEffectFactory::fCurrEffectClassID = GrBackendEffectFactory::kIl /////////////////////////////////////////////////////////////////////////////// -SK_DEFINE_INST_COUNT(GrEffectRef) - GrEffectRef::~GrEffectRef() { SkASSERT(this->unique()); fEffect->EffectRefDestroyed(); diff --git a/gpu/GrGeometryBuffer.cpp b/gpu/GrGeometryBuffer.cpp deleted file mode 100644 index 202d0c33..00000000 --- a/gpu/GrGeometryBuffer.cpp +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "GrGeometryBuffer.h" - -SK_DEFINE_INST_COUNT(GrGeometryBuffer) diff --git a/gpu/GrOvalRenderer.cpp b/gpu/GrOvalRenderer.cpp index 8f078dbc..ac33a5cc 100644 --- a/gpu/GrOvalRenderer.cpp +++ b/gpu/GrOvalRenderer.cpp @@ -22,8 +22,6 @@ #include "effects/GrVertexEffect.h" -SK_DEFINE_INST_COUNT(GrOvalRenderer) - namespace { struct CircleVertex { diff --git a/gpu/GrPath.cpp b/gpu/GrPath.cpp index f928dffa..adb3fe63 100644 --- a/gpu/GrPath.cpp +++ b/gpu/GrPath.cpp @@ -7,8 +7,6 @@ #include "GrPath.h" -SK_DEFINE_INST_COUNT(GrPath) - GrResourceKey GrPath::ComputeKey(const SkPath& path, const SkStrokeRec& stroke) { static const GrResourceKey::ResourceType gPathResourceType = GrResourceKey::GenerateResourceType(); static const GrCacheID::Domain gPathDomain = GrCacheID::GenerateDomain(); diff --git a/gpu/GrPathRenderer.cpp b/gpu/GrPathRenderer.cpp index e88db22b..3dcedf9f 100644 --- a/gpu/GrPathRenderer.cpp +++ b/gpu/GrPathRenderer.cpp @@ -8,8 +8,6 @@ #include "GrPathRenderer.h" -SK_DEFINE_INST_COUNT(GrPathRenderer) - GrPathRenderer::GrPathRenderer() { } diff --git a/gpu/GrPathRendererChain.cpp b/gpu/GrPathRendererChain.cpp index d6de12b3..cac0475b 100644 --- a/gpu/GrPathRendererChain.cpp +++ b/gpu/GrPathRendererChain.cpp @@ -14,8 +14,6 @@ #include "GrDrawTargetCaps.h" #include "GrGpu.h" -SK_DEFINE_INST_COUNT(GrPathRendererChain) - GrPathRendererChain::GrPathRendererChain(GrContext* context) : fInit(false) , fOwner(context) { diff --git a/gpu/GrPathUtils.cpp b/gpu/GrPathUtils.cpp index 81348ec2..e2b1ac76 100644 --- a/gpu/GrPathUtils.cpp +++ b/gpu/GrPathUtils.cpp @@ -187,8 +187,6 @@ int GrPathUtils::worstCasePointCount(const SkPath& path, int* subpaths, } void GrPathUtils::QuadUVMatrix::set(const GrPoint qPts[3]) { - // can't make this static, no cons :( - SkMatrix UVpts; #ifndef SK_SCALAR_IS_FLOAT GrCrash("Expected scalar is float."); #endif @@ -197,18 +195,23 @@ void GrPathUtils::QuadUVMatrix::set(const GrPoint qPts[3]) { // We know M * control_pts = [0 1/2 1] // [0 0 1] // [1 1 1] + // And control_pts = [x0 x1 x2] + // [y0 y1 y2] + // [1 1 1 ] // We invert the control pt matrix and post concat to both sides to get M. - UVpts.setAll(0, SK_ScalarHalf, SK_Scalar1, - 0, 0, SK_Scalar1, - SkScalarToPersp(SK_Scalar1), - SkScalarToPersp(SK_Scalar1), - SkScalarToPersp(SK_Scalar1)); - m.setAll(qPts[0].fX, qPts[1].fX, qPts[2].fX, - qPts[0].fY, qPts[1].fY, qPts[2].fY, - SkScalarToPersp(SK_Scalar1), - SkScalarToPersp(SK_Scalar1), - SkScalarToPersp(SK_Scalar1)); - if (!m.invert(&m)) { + // Using the known form of the control point matrix and the result, we can + // optimize and improve precision. + + double x0 = qPts[0].fX; + double y0 = qPts[0].fY; + double x1 = qPts[1].fX; + double y1 = qPts[1].fY; + double x2 = qPts[2].fX; + double y2 = qPts[2].fY; + double det = x0*y1 - y0*x1 + x2*y0 - y2*x0 + x1*y2 - y1*x2; + + if (!sk_float_isfinite(det) + || SkScalarNearlyZero((float)det, SK_ScalarNearlyZero * SK_ScalarNearlyZero)) { // The quad is degenerate. Hopefully this is rare. Find the pts that are // farthest apart to compute a line (unless it is really a pt). SkScalar maxD = qPts[0].distanceToSqd(qPts[1]); @@ -247,7 +250,35 @@ void GrPathUtils::QuadUVMatrix::set(const GrPoint qPts[3]) { fM[3] = 0; fM[4] = 0; fM[5] = 100.f; } } else { - m.postConcat(UVpts); + double scale = 1.0/det; + + // compute adjugate matrix + double a0, a1, a2, a3, a4, a5, a6, a7, a8; + a0 = y1-y2; + a1 = x2-x1; + a2 = x1*y2-x2*y1; + + a3 = y2-y0; + a4 = x0-x2; + a5 = x2*y0-x0*y2; + + a6 = y0-y1; + a7 = x1-x0; + a8 = x0*y1-x1*y0; + + // this performs the uv_pts*adjugate(control_pts) multiply, + // then does the scale by 1/det afterwards to improve precision + m[SkMatrix::kMScaleX] = (float)((0.5*a3 + a6)*scale); + m[SkMatrix::kMSkewX] = (float)((0.5*a4 + a7)*scale); + m[SkMatrix::kMTransX] = (float)((0.5*a5 + a8)*scale); + + m[SkMatrix::kMSkewY] = (float)(a6*scale); + m[SkMatrix::kMScaleY] = (float)(a7*scale); + m[SkMatrix::kMTransY] = (float)(a8*scale); + + m[SkMatrix::kMPersp0] = (float)((a0 + a3 + a6)*scale); + m[SkMatrix::kMPersp1] = (float)((a1 + a4 + a7)*scale); + m[SkMatrix::kMPersp2] = (float)((a2 + a5 + a8)*scale); // The matrix should not have perspective. SkDEBUGCODE(static const SkScalar gTOL = 1.f / 100.f); diff --git a/gpu/GrRenderTarget.cpp b/gpu/GrRenderTarget.cpp index 49a76149..f18df2cf 100644 --- a/gpu/GrRenderTarget.cpp +++ b/gpu/GrRenderTarget.cpp @@ -13,8 +13,6 @@ #include "GrGpu.h" #include "GrStencilBuffer.h" -SK_DEFINE_INST_COUNT(GrRenderTarget) - bool GrRenderTarget::readPixels(int left, int top, int width, int height, GrPixelConfig config, void* buffer, diff --git a/gpu/GrResource.cpp b/gpu/GrResource.cpp index 8b439061..e20a30ff 100644 --- a/gpu/GrResource.cpp +++ b/gpu/GrResource.cpp @@ -10,8 +10,6 @@ #include "GrResource.h" #include "GrGpu.h" -SK_DEFINE_INST_COUNT(GrResource) - GrResource::GrResource(GrGpu* gpu, bool isWrapped) { fGpu = gpu; fCacheEntry = NULL; diff --git a/gpu/GrResourceCache.h b/gpu/GrResourceCache.h index 38378ac7..ca30732b 100644 --- a/gpu/GrResourceCache.h +++ b/gpu/GrResourceCache.h @@ -54,7 +54,7 @@ public: } GrResourceKey() { - fKey.fHashedKey.reset(); + fKey.reset(); } void reset(const GrCacheID& id, ResourceType type, ResourceFlags flags) { @@ -63,41 +63,34 @@ public: //!< returns hash value [0..kHashMask] for the key int getHash() const { - return fKey.fHashedKey.getHash() & kHashMask; + return fKey.getHash() & kHashMask; } bool isScratch() const { return ScratchDomain() == - *reinterpret_cast<const GrCacheID::Domain*>(fKey.fHashedKey.getData() + + *reinterpret_cast<const GrCacheID::Domain*>(fKey.getData() + kCacheIDDomainOffset); } ResourceType getResourceType() const { - return *reinterpret_cast<const ResourceType*>(fKey.fHashedKey.getData() + + return *reinterpret_cast<const ResourceType*>(fKey.getData() + kResourceTypeOffset); } ResourceFlags getResourceFlags() const { - return *reinterpret_cast<const ResourceFlags*>(fKey.fHashedKey.getData() + + return *reinterpret_cast<const ResourceFlags*>(fKey.getData() + kResourceFlagsOffset); } - int compare(const GrResourceKey& other) const { - return fKey.fHashedKey.compare(other.fKey.fHashedKey); - } - - static bool LT(const GrResourceKey& a, const GrResourceKey& b) { - return a.compare(b) < 0; - } - - static bool EQ(const GrResourceKey& a, const GrResourceKey& b) { - return 0 == a.compare(b); - } + bool operator==(const GrResourceKey& other) const { return fKey == other.fKey; } + bool operator<(const GrResourceKey& other) const { return fKey < other.fKey; } - inline static bool LT(const GrResourceEntry& entry, const GrResourceKey& key); - inline static bool EQ(const GrResourceEntry& entry, const GrResourceKey& key); - inline static bool LT(const GrResourceEntry& a, const GrResourceEntry& b); - inline static bool EQ(const GrResourceEntry& a, const GrResourceEntry& b); + static bool LessThan(const GrResourceEntry& entry, const GrResourceKey& key); + static bool Equals(const GrResourceEntry& entry, const GrResourceKey& key); +#ifdef SK_DEBUG + static bool LessThan(const GrResourceEntry& a, const GrResourceEntry& b); + static bool Equals(const GrResourceEntry& a, const GrResourceEntry& b); +#endif private: enum { @@ -125,21 +118,9 @@ private: memcpy(k + kResourceTypeOffset, &type, sizeof(ResourceType)); memcpy(k + kResourceFlagsOffset, &flags, sizeof(ResourceFlags)); memset(k + kPadOffset, 0, kPadSize); - fKey.fHashedKey.setKeyData(keyData.fKey32); + fKey.setKeyData(keyData.fKey32); } - - struct Key; - typedef GrTBinHashKey<Key, kKeySize> HashedKey; - - struct Key { - int compare(const HashedKey& hashedKey) const { - return fHashedKey.compare(hashedKey); - } - - HashedKey fHashedKey; - }; - - Key fKey; + GrBinHashKey<kKeySize> fKey; }; // The cache listens for these messages to purge junk resources proactively. @@ -174,21 +155,23 @@ private: friend class GrDLinkedList; }; -bool GrResourceKey::LT(const GrResourceEntry& entry, const GrResourceKey& key) { - return LT(entry.key(), key); +inline bool GrResourceKey::LessThan(const GrResourceEntry& entry, const GrResourceKey& key) { + return entry.key() < key; } -bool GrResourceKey::EQ(const GrResourceEntry& entry, const GrResourceKey& key) { - return EQ(entry.key(), key); +inline bool GrResourceKey::Equals(const GrResourceEntry& entry, const GrResourceKey& key) { + return entry.key() == key; } -bool GrResourceKey::LT(const GrResourceEntry& a, const GrResourceEntry& b) { - return LT(a.key(), b.key()); +#ifdef SK_DEBUG +inline bool GrResourceKey::LessThan(const GrResourceEntry& a, const GrResourceEntry& b) { + return a.key() < b.key(); } -bool GrResourceKey::EQ(const GrResourceEntry& a, const GrResourceEntry& b) { - return EQ(a.key(), b.key()); +inline bool GrResourceKey::Equals(const GrResourceEntry& a, const GrResourceEntry& b) { + return a.key() == b.key(); } +#endif /////////////////////////////////////////////////////////////////////////////// diff --git a/gpu/GrStencilBuffer.cpp b/gpu/GrStencilBuffer.cpp index c12de523..ea7b4fa1 100644 --- a/gpu/GrStencilBuffer.cpp +++ b/gpu/GrStencilBuffer.cpp @@ -12,8 +12,6 @@ #include "GrGpu.h" #include "GrResourceCache.h" -SK_DEFINE_INST_COUNT(GrStencilBuffer) - void GrStencilBuffer::transferToCache() { SkASSERT(NULL == this->getCacheEntry()); diff --git a/gpu/GrSurface.cpp b/gpu/GrSurface.cpp index 3ac8bc22..fed95f23 100644 --- a/gpu/GrSurface.cpp +++ b/gpu/GrSurface.cpp @@ -11,8 +11,6 @@ #include "SkImageEncoder.h" #include <stdio.h> -SK_DEFINE_INST_COUNT(GrSurface) - bool GrSurface::savePixels(const char* filename) { SkBitmap bm; bm.setConfig(SkBitmap::kARGB_8888_Config, this->width(), this->height()); diff --git a/gpu/GrTHashTable.h b/gpu/GrTHashTable.h index 3b329778..83462c70 100644 --- a/gpu/GrTHashTable.h +++ b/gpu/GrTHashTable.h @@ -16,8 +16,10 @@ /** * Key needs - * static bool EQ(const Entry&, const HashKey&); - * static bool LT(const Entry&, const HashKey&); + * static bool Equals(const Entry&, const Key&); + * static bool LessThan(const Entry&, const Key&); + * static bool Equals(const Entry&, const Entry&); for SK_DEBUG if GrTHashTable::validate() is called + * static bool LessThan(const Entry&, const Entry&); for SK_DEBUG if GrTHashTable::validate() is called * uint32_t getHash() const; * * Allows duplicate key entries but on find you may get @@ -90,7 +92,7 @@ int GrTHashTable<T, Key, kHashBits>::searchArray(const Key& key) const { int low = 0; while (high > low) { int index = (low + high) >> 1; - if (Key::LT(*array[index], key)) { + if (Key::LessThan(*array[index], key)) { low = index + 1; } else { high = index; @@ -98,15 +100,15 @@ int GrTHashTable<T, Key, kHashBits>::searchArray(const Key& key) const { } // check if we found it - if (Key::EQ(*array[high], key)) { + if (Key::Equals(*array[high], key)) { // above search should have found the first occurrence if there // are multiple. - SkASSERT(0 == high || Key::LT(*array[high - 1], key)); + SkASSERT(0 == high || Key::LessThan(*array[high - 1], key)); return high; } // now return the ~ of where we should insert it - if (Key::LT(*array[high], key)) { + if (Key::LessThan(*array[high], key)) { high += 1; } return ~high; @@ -119,7 +121,7 @@ T* GrTHashTable<T, Key, kHashBits>::find(const Key& key, Filter filter) const { int hashIndex = hash2Index(key.getHash()); T* elem = fHash[hashIndex]; - if (NULL != elem && Key::EQ(*elem, key) && filter(elem)) { + if (NULL != elem && Key::Equals(*elem, key) && filter(elem)) { return elem; } @@ -133,9 +135,9 @@ T* GrTHashTable<T, Key, kHashBits>::find(const Key& key, Filter filter) const { // above search should have found the first occurrence if there // are multiple. - SkASSERT(0 == index || Key::LT(*array[index - 1], key)); + SkASSERT(0 == index || Key::LessThan(*array[index - 1], key)); - for ( ; index < count() && Key::EQ(*array[index], key); ++index) { + for ( ; index < count() && Key::Equals(*array[index], key); ++index) { if (filter(fSorted[index])) { // update the hash fHash[hashIndex] = fSorted[index]; @@ -192,8 +194,8 @@ template <typename T, typename Key, size_t kHashBits> void GrTHashTable<T, Key, kHashBits>::validate() const { int count = fSorted.count(); for (int i = 1; i < count; i++) { - SkASSERT(Key::LT(*fSorted[i - 1], *fSorted[i]) || - Key::EQ(*fSorted[i - 1], *fSorted[i])); + SkASSERT(Key::LessThan(*fSorted[i - 1], *fSorted[i]) || + Key::Equals(*fSorted[i - 1], *fSorted[i])); } } diff --git a/gpu/GrTextStrike.cpp b/gpu/GrTextStrike.cpp index ddab1e95..c70e822c 100644 --- a/gpu/GrTextStrike.cpp +++ b/gpu/GrTextStrike.cpp @@ -16,9 +16,6 @@ #include "edtaa3.h" #endif -SK_DEFINE_INST_COUNT(GrFontScaler) -SK_DEFINE_INST_COUNT(GrKey) - /////////////////////////////////////////////////////////////////////////////// #define FONT_CACHE_STATS 0 @@ -28,7 +25,7 @@ static int g_PurgeCount = 0; GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) { gpu->ref(); - for (int i = 0; i < kMaskFormatCount; ++i) { + for (int i = 0; i < kAtlasCount; ++i) { fAtlasMgr[i] = NULL; } @@ -37,7 +34,7 @@ GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) { GrFontCache::~GrFontCache() { fCache.deleteAll(); - for (int i = 0; i < kMaskFormatCount; ++i) { + for (int i = 0; i < kAtlasCount; ++i) { delete fAtlasMgr[i]; } fGpu->unref(); @@ -47,28 +44,40 @@ GrFontCache::~GrFontCache() { } static GrPixelConfig mask_format_to_pixel_config(GrMaskFormat format) { - switch (format) { - case kA8_GrMaskFormat: - return kAlpha_8_GrPixelConfig; - case kA565_GrMaskFormat: - return kRGB_565_GrPixelConfig; - case kA888_GrMaskFormat: - return kSkia8888_GrPixelConfig; - default: - SkDEBUGFAIL("unknown maskformat"); - } - return kUnknown_GrPixelConfig; + static const GrPixelConfig sPixelConfigs[] = { + kAlpha_8_GrPixelConfig, + kRGB_565_GrPixelConfig, + kSkia8888_GrPixelConfig, + kSkia8888_GrPixelConfig + }; + SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sPixelConfigs) == kMaskFormatCount, array_size_mismatch); + + return sPixelConfigs[format]; +} + +static int mask_format_to_atlas_index(GrMaskFormat format) { + static const int sAtlasIndices[] = { + GrFontCache::kA8_AtlasType, + GrFontCache::k565_AtlasType, + GrFontCache::k8888_AtlasType, + GrFontCache::k8888_AtlasType + }; + SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch); + + SkASSERT(sAtlasIndices[format] < GrFontCache::kAtlasCount); + return sAtlasIndices[format]; } GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler, const Key& key) { GrMaskFormat format = scaler->getMaskFormat(); GrPixelConfig config = mask_format_to_pixel_config(format); - if (NULL == fAtlasMgr[format]) { - fAtlasMgr[format] = SkNEW_ARGS(GrAtlasMgr, (fGpu, config)); + int atlasIndex = mask_format_to_atlas_index(format); + if (NULL == fAtlasMgr[atlasIndex]) { + fAtlasMgr[atlasIndex] = SkNEW_ARGS(GrAtlasMgr, (fGpu, config)); } GrTextStrike* strike = SkNEW_ARGS(GrTextStrike, - (this, scaler->getKey(), format, fAtlasMgr[format])); + (this, scaler->getKey(), format, fAtlasMgr[atlasIndex])); fCache.insert(key, strike); if (fHead) { @@ -86,7 +95,7 @@ GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler, void GrFontCache::freeAll() { fCache.deleteAll(); - for (int i = 0; i < kMaskFormatCount; ++i) { + for (int i = 0; i < kAtlasCount; ++i) { delete fAtlasMgr[i]; fAtlasMgr[i] = NULL; } @@ -177,7 +186,7 @@ void GrFontCache::validate() const { #ifdef SK_DEVELOPER void GrFontCache::dump() const { static int gDumpCount = 0; - for (int i = 0; i < kMaskFormatCount; ++i) { + for (int i = 0; i < kAtlasCount; ++i) { if (NULL != fAtlasMgr[i]) { GrTexture* texture = fAtlasMgr[i]->getTexture(); if (NULL != texture) { diff --git a/gpu/GrTextStrike.h b/gpu/GrTextStrike.h index 422ae0c3..c5a3f656 100644 --- a/gpu/GrTextStrike.h +++ b/gpu/GrTextStrike.h @@ -108,6 +108,15 @@ public: void dump() const; #endif + enum AtlasType { + kA8_AtlasType, //!< 1-byte per pixel + k565_AtlasType, //!< 2-bytes per pixel + k8888_AtlasType, //!< 4-bytes per pixel + + kLast_AtlasType = k8888_AtlasType + }; + static const int kAtlasCount = kLast_AtlasType + 1; + private: friend class GrFontPurgeListener; @@ -118,7 +127,7 @@ private: GrTextStrike* fTail; GrGpu* fGpu; - GrAtlasMgr* fAtlasMgr[kMaskFormatCount]; + GrAtlasMgr* fAtlasMgr[kAtlasCount]; GrTextStrike* generateStrike(GrFontScaler*, const Key&); inline void detachStrikeFromList(GrTextStrike*); diff --git a/gpu/GrTextStrike_impl.h b/gpu/GrTextStrike_impl.h index 42971855..0691eaa6 100644 --- a/gpu/GrTextStrike_impl.h +++ b/gpu/GrTextStrike_impl.h @@ -19,10 +19,10 @@ public: intptr_t getHash() const { return fFontScalerKey->getHash(); } - static bool LT(const GrTextStrike& strike, const Key& key) { + static bool LessThan(const GrTextStrike& strike, const Key& key) { return *strike.getFontScalerKey() < *key.fFontScalerKey; } - static bool EQ(const GrTextStrike& strike, const Key& key) { + static bool Equals(const GrTextStrike& strike, const Key& key) { return *strike.getFontScalerKey() == *key.fFontScalerKey; } @@ -88,10 +88,10 @@ public: uint32_t getHash() const { return fPackedID; } - static bool LT(const GrGlyph& glyph, const Key& key) { + static bool LessThan(const GrGlyph& glyph, const Key& key) { return glyph.fPackedID < key.fPackedID; } - static bool EQ(const GrGlyph& glyph, const Key& key) { + static bool Equals(const GrGlyph& glyph, const Key& key) { return glyph.fPackedID == key.fPackedID; } diff --git a/gpu/GrTexture.cpp b/gpu/GrTexture.cpp index b5a0195d..f8515153 100644 --- a/gpu/GrTexture.cpp +++ b/gpu/GrTexture.cpp @@ -15,8 +15,6 @@ #include "GrRenderTarget.h" #include "GrResourceCache.h" -SK_DEFINE_INST_COUNT(GrTexture) - GrTexture::~GrTexture() { if (NULL != fRenderTarget.get()) { fRenderTarget.get()->owningTextureDestroyed(); diff --git a/gpu/SkGpuDevice.cpp b/gpu/SkGpuDevice.cpp index 6fb1403c..ce02f2c5 100644 --- a/gpu/SkGpuDevice.cpp +++ b/gpu/SkGpuDevice.cpp @@ -7,7 +7,8 @@ #include "SkGpuDevice.h" -#include "effects/GrTextureDomainEffect.h" +#include "effects/GrBicubicEffect.h" +#include "effects/GrTextureDomain.h" #include "effects/GrSimpleTextureEffect.h" #include "GrContext.h" @@ -154,6 +155,20 @@ static SkBitmap make_bitmap(GrContext* context, GrRenderTarget* renderTarget) { return bitmap; } +/* + * Calling SkBitmapDevice with individual params asks it to allocate pixel memory. + * We never want that, so we always need to call it with a bitmap argument + * (which says take my allocate (or lack thereof)). + * + * This is a REALLY good reason to finish the clean-up of SkBaseDevice, and have + * SkGpuDevice inherit from that instead of SkBitmapDevice. + */ +static SkBitmap make_bitmap(SkBitmap::Config config, int width, int height, bool isOpaque) { + SkBitmap bm; + bm.setConfig(config, width, height, isOpaque); + return bm; +} + SkGpuDevice* SkGpuDevice::Create(GrSurface* surface) { SkASSERT(NULL != surface); if (NULL == surface->asRenderTarget() || NULL == surface->getContext()) { @@ -209,7 +224,7 @@ SkGpuDevice::SkGpuDevice(GrContext* context, int width, int height, int sampleCount) - : SkBitmapDevice(config, width, height, false /*isOpaque*/) { + : SkBitmapDevice(make_bitmap(config, width, height, false /*isOpaque*/)) { fDrawProcs = NULL; @@ -1057,22 +1072,29 @@ void SkGpuDevice::drawBitmap(const SkDraw& draw, SkCanvas::kNone_DrawBitmapRectFlag); } -// This method outsets 'iRect' by 1 all around and then clamps its extents to +// This method outsets 'iRect' by 'outset' all around and then clamps its extents to // 'clamp'. 'offset' is adjusted to remain positioned over the top-left corner // of 'iRect' for all possible outsets/clamps. -static inline void clamped_unit_outset_with_offset(SkIRect* iRect, SkPoint* offset, - const SkIRect& clamp) { - iRect->outset(1, 1); - - if (iRect->fLeft < clamp.fLeft) { +static inline void clamped_outset_with_offset(SkIRect* iRect, + int outset, + SkPoint* offset, + const SkIRect& clamp) { + iRect->outset(outset, outset); + + int leftClampDelta = clamp.fLeft - iRect->fLeft; + if (leftClampDelta > 0) { + offset->fX -= outset - leftClampDelta; iRect->fLeft = clamp.fLeft; } else { - offset->fX -= SK_Scalar1; + offset->fX -= outset; } - if (iRect->fTop < clamp.fTop) { + + int topClampDelta = clamp.fTop - iRect->fTop; + if (topClampDelta > 0) { + offset->fY -= outset - topClampDelta; iRect->fTop = clamp.fTop; } else { - offset->fY -= SK_Scalar1; + offset->fY -= outset; } if (iRect->fRight > clamp.fRight) { @@ -1092,10 +1114,17 @@ void SkGpuDevice::drawBitmapCommon(const SkDraw& draw, CHECK_SHOULD_DRAW(draw, false); SkRect srcRect; + // If there is no src rect, or the src rect contains the entire bitmap then we're effectively + // in the (easier) bleed case, so update flags. if (NULL == srcRectPtr) { srcRect.set(0, 0, SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height())); + flags = (SkCanvas::DrawBitmapRectFlags) (flags | SkCanvas::kBleed_DrawBitmapRectFlag); } else { srcRect = *srcRectPtr; + if (srcRect.fLeft <= 0 && srcRect.fTop <= 0 && + srcRect.fRight >= bitmap.width() && srcRect.fBottom >= bitmap.height()) { + flags = (SkCanvas::DrawBitmapRectFlags) (flags | SkCanvas::kBleed_DrawBitmapRectFlag); + } } if (paint.getMaskFilter()){ @@ -1148,47 +1177,62 @@ void SkGpuDevice::drawBitmapCommon(const SkDraw& draw, GrTextureParams params; SkPaint::FilterLevel paintFilterLevel = paint.getFilterLevel(); GrTextureParams::FilterMode textureFilterMode; + + int tileFilterPad; + bool doBicubic = false; + switch(paintFilterLevel) { case SkPaint::kNone_FilterLevel: + tileFilterPad = 0; textureFilterMode = GrTextureParams::kNone_FilterMode; break; case SkPaint::kLow_FilterLevel: + tileFilterPad = 1; textureFilterMode = GrTextureParams::kBilerp_FilterMode; break; case SkPaint::kMedium_FilterLevel: + tileFilterPad = 1; textureFilterMode = GrTextureParams::kMipMap_FilterMode; break; - case SkPaint::kHigh_FilterLevel: - // Fall back to mips for now - textureFilterMode = GrTextureParams::kMipMap_FilterMode; + case SkPaint::kHigh_FilterLevel: { + // Minification can look bad with the bicubic effect. + if (fContext->getMatrix().getMinStretch() >= SK_Scalar1 && + (flags & SkCanvas::kBleed_DrawBitmapRectFlag)) { + // We will install an effect that does the filtering in the shader. + textureFilterMode = GrTextureParams::kNone_FilterMode; + tileFilterPad = GrBicubicEffect::kFilterTexelPad; + doBicubic = true; + } else { + // We don't yet support doing bicubic filtering with an interior clamp. Fall back + // to MIPs + textureFilterMode = GrTextureParams::kMipMap_FilterMode; + tileFilterPad = 1; + } break; + } default: SkErrorInternals::SetError( kInvalidPaint_SkError, "Sorry, I don't understand the filtering " "mode you asked for. Falling back to " "MIPMaps."); + tileFilterPad = 1; textureFilterMode = GrTextureParams::kMipMap_FilterMode; break; - } params.setFilterMode(textureFilterMode); - int maxTileSize = fContext->getMaxTextureSize(); - if (SkPaint::kNone_FilterLevel != paint.getFilterLevel()) { - // We may need a skosh more room if we have to bump out the tile - // by 1 pixel all around - maxTileSize -= 2; - } + int maxTileSize = fContext->getMaxTextureSize() - 2 * tileFilterPad; int tileSize; SkIRect clippedSrcRect; if (this->shouldTileBitmap(bitmap, params, srcRectPtr, maxTileSize, &tileSize, &clippedSrcRect)) { - this->drawTiledBitmap(bitmap, srcRect, clippedSrcRect, params, paint, flags, tileSize); + this->drawTiledBitmap(bitmap, srcRect, clippedSrcRect, params, paint, flags, tileSize, + doBicubic); } else { // take the simple case - this->internalDrawBitmap(bitmap, srcRect, params, paint, flags); + this->internalDrawBitmap(bitmap, srcRect, params, paint, flags, doBicubic); } } @@ -1200,7 +1244,8 @@ void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap, const GrTextureParams& params, const SkPaint& paint, SkCanvas::DrawBitmapRectFlags flags, - int tileSize) { + int tileSize, + bool bicubic) { SkRect clippedSrcRect = SkRect::Make(clippedSrcIRect); int nx = bitmap.width() / tileSize; @@ -1227,7 +1272,7 @@ void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap, SkPoint offset = SkPoint::Make(SkIntToScalar(iTileR.fLeft), SkIntToScalar(iTileR.fTop)); - if (SkPaint::kNone_FilterLevel != paint.getFilterLevel()) { + if (SkPaint::kNone_FilterLevel != paint.getFilterLevel() || bicubic) { SkIRect iClampRect; if (SkCanvas::kBleed_DrawBitmapRectFlag & flags) { @@ -1235,13 +1280,15 @@ void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap, // but stay within the bitmap bounds iClampRect = SkIRect::MakeWH(bitmap.width(), bitmap.height()); } else { + SkASSERT(!bicubic); // Bicubic is not supported with non-bleed yet. + // In texture-domain/clamp mode we only want to expand the // tile on edges interior to "srcRect" (i.e., we want to // not bleed across the original clamped edges) srcRect.roundOut(&iClampRect); } - - clamped_unit_outset_with_offset(&iTileR, &offset, iClampRect); + int outset = bicubic ? GrBicubicEffect::kFilterTexelPad : 1; + clamped_outset_with_offset(&iTileR, outset, &offset, iClampRect); } if (bitmap.extractSubset(&tmpB, iTileR)) { @@ -1251,7 +1298,7 @@ void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap, tmpM.setTranslate(offset.fX, offset.fY); GrContext::AutoMatrix am; am.setPreConcat(fContext, tmpM); - this->internalDrawBitmap(tmpB, tileR, params, paint, flags); + this->internalDrawBitmap(tmpB, tileR, params, paint, flags, bicubic); } } } @@ -1310,7 +1357,8 @@ void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap, const SkRect& srcRect, const GrTextureParams& params, const SkPaint& paint, - SkCanvas::DrawBitmapRectFlags flags) { + SkCanvas::DrawBitmapRectFlags flags, + bool bicubic) { SkASSERT(bitmap.width() <= fContext->getMaxTextureSize() && bitmap.height() <= fContext->getMaxTextureSize()); @@ -1332,6 +1380,7 @@ void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap, bool needsTextureDomain = false; if (!(flags & SkCanvas::kBleed_DrawBitmapRectFlag) && params.filterMode() != GrTextureParams::kNone_FilterMode) { + SkASSERT(!bicubic); // Need texture domain if drawing a sub rect. needsTextureDomain = srcRect.width() < bitmap.width() || srcRect.height() < bitmap.height(); @@ -1374,8 +1423,12 @@ void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap, effect.reset(GrTextureDomainEffect::Create(texture, SkMatrix::I(), textureDomain, - GrTextureDomainEffect::kClamp_WrapMode, + GrTextureDomain::kClamp_Mode, params.filterMode())); + } else if (bicubic) { + SkASSERT(GrTextureParams::kNone_FilterMode == params.filterMode()); + SkShader::TileMode tileModes[2] = { params.getTileModeX(), params.getTileModeY() }; + effect.reset(GrBicubicEffect::Create(texture, SkMatrix::I(), tileModes)); } else { effect.reset(GrSimpleTextureEffect::Create(texture, SkMatrix::I(), params)); } diff --git a/gpu/SkGrFontScaler.cpp b/gpu/SkGrFontScaler.cpp index 65148666..8fdae48a 100644 --- a/gpu/SkGrFontScaler.cpp +++ b/gpu/SkGrFontScaler.cpp @@ -87,6 +87,8 @@ GrMaskFormat SkGrFontScaler::getMaskFormat() { return kA565_GrMaskFormat; case SkMask::kLCD32_Format: return kA888_GrMaskFormat; + case SkMask::kARGB32_Format: + return kARGB_GrMaskFormat; default: SkDEBUGFAIL("unsupported SkMask::Format"); return kA8_GrMaskFormat; @@ -172,8 +174,8 @@ bool SkGrFontScaler::getPackedGlyphImage(GrGlyph::PackedID packed, expand_bits(rgba8888, bits, width, height, dstRB, srcRB); break; } - default: - GrCrash("Unknown GrMaskFormat"); + default: + GrCrash("Invalid GrMaskFormat"); } } else if (srcRB == dstRB) { memcpy(dst, src, dstRB * height); diff --git a/gpu/SkGrPixelRef.cpp b/gpu/SkGrPixelRef.cpp index dc5d7558..01294266 100644 --- a/gpu/SkGrPixelRef.cpp +++ b/gpu/SkGrPixelRef.cpp @@ -117,6 +117,23 @@ SkGrPixelRef::SkGrPixelRef(GrSurface* surface, bool transferCacheLock) { SkSafeRef(surface); } +SkGrPixelRef::SkGrPixelRef(const SkImageInfo&, GrSurface* surface, bool transferCacheLock) { + // TODO: figure out if this is responsible for Chrome canvas errors +#if 0 + // The GrTexture has a ref to the GrRenderTarget but not vice versa. + // If the GrTexture exists take a ref to that (rather than the render + // target) + fSurface = surface->asTexture(); +#else + fSurface = NULL; +#endif + if (NULL == fSurface) { + fSurface = surface; + } + fUnlock = transferCacheLock; + SkSafeRef(surface); +} + SkGrPixelRef::~SkGrPixelRef() { if (fUnlock) { GrContext* context = fSurface->getContext(); diff --git a/gpu/effects/GrBicubicEffect.cpp b/gpu/effects/GrBicubicEffect.cpp index f6cc37dc..a6e08f20 100644 --- a/gpu/effects/GrBicubicEffect.cpp +++ b/gpu/effects/GrBicubicEffect.cpp @@ -73,8 +73,14 @@ void GrGLBicubicEffect::emitCode(GrGLShaderBuilder* builder, "\tvec4 c = coefficients * ts;\n" "\treturn c.x * c0 + c.y * c1 + c.z * c2 + c.w * c3;\n", &cubicBlendName); - builder->fsCodeAppendf("\tvec2 coord = %s - %s * vec2(0.5, 0.5);\n", coords2D.c_str(), imgInc); - builder->fsCodeAppendf("\tvec2 f = fract(coord / %s);\n", imgInc); + builder->fsCodeAppendf("\tvec2 coord = %s - %s * vec2(0.5);\n", coords2D.c_str(), imgInc); + // We unnormalize the coord in order to determine our fractional offset (f) within the texel + // We then snap coord to a texel center and renormalize. The snap prevents cases where the + // starting coords are near a texel boundary and accumulations of imgInc would cause us to skip/ + // double hit a texel. + builder->fsCodeAppendf("\tcoord /= %s;\n", imgInc); + builder->fsCodeAppend("\tvec2 f = fract(coord);\n"); + builder->fsCodeAppendf("\tcoord = (coord - f + vec2(0.5)) * %s;\n", imgInc); for (int y = 0; y < 4; ++y) { for (int x = 0; x < 4; ++x) { SkString coord; @@ -100,23 +106,10 @@ void GrGLBicubicEffect::setData(const GrGLUniformManager& uman, } GrBicubicEffect::GrBicubicEffect(GrTexture* texture, - const SkScalar coefficients[16]) - : INHERITED(texture, MakeDivByTextureWHMatrix(texture)) { - for (int y = 0; y < 4; y++) { - for (int x = 0; x < 4; x++) { - // Convert from row-major scalars to column-major floats. - fCoefficients[x * 4 + y] = SkScalarToFloat(coefficients[y * 4 + x]); - } - } - this->setWillNotUseInputColor(); -} - -GrBicubicEffect::GrBicubicEffect(GrTexture* texture, const SkScalar coefficients[16], const SkMatrix &matrix, - const GrTextureParams ¶ms, - GrCoordSet coordSet) - : INHERITED(texture, matrix, params, coordSet) { + const SkShader::TileMode tileModes[2]) + : INHERITED(texture, matrix, GrTextureParams(tileModes, GrTextureParams::kNone_FilterMode)) { for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { // Convert from row-major scalars to column-major floats. diff --git a/gpu/effects/GrBicubicEffect.h b/gpu/effects/GrBicubicEffect.h index eabc79fe..85bec771 100644 --- a/gpu/effects/GrBicubicEffect.h +++ b/gpu/effects/GrBicubicEffect.h @@ -17,6 +17,10 @@ class GrGLBicubicEffect; class GrBicubicEffect : public GrSingleTextureEffect { public: + enum { + kFilterTexelPad = 2, // Given a src rect in texels to be filtered, this number of + // surrounding texels are needed by the kernel in x and y. + }; virtual ~GrBicubicEffect(); static const char* Name() { return "Bicubic"; } @@ -27,34 +31,44 @@ public: virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE; - static GrEffectRef* Create(GrTexture* tex, const SkScalar coefficients[16]) { - AutoEffectUnref effect(SkNEW_ARGS(GrBicubicEffect, (tex, coefficients))); - return CreateEffectRef(effect); + /** + * Create a simple Mitchell filter effect. + */ + static GrEffectRef* Create(GrTexture* tex) { + return Create(tex, gMitchellCoefficients); } - static GrEffectRef* Create(GrTexture* tex, const SkScalar coefficients[16], - const SkMatrix& matrix, - const GrTextureParams& p, - GrCoordSet coordSet = kLocal_GrCoordSet) { - AutoEffectUnref effect(SkNEW_ARGS(GrBicubicEffect, (tex, coefficients, matrix, p, coordSet))); - return CreateEffectRef(effect); + /** + * Create a simple filter effect with custom bicubic coefficients. + */ + static GrEffectRef* Create(GrTexture* tex, const SkScalar coefficients[16]) { + const SkShader::TileMode tm[] = { SkShader::kClamp_TileMode, SkShader::kClamp_TileMode }; + return Create(tex, coefficients, MakeDivByTextureWHMatrix(tex), tm); } - static GrEffectRef* Create(GrTexture* tex) { - return Create(tex, gMitchellCoefficients); + /** + * Create a Mitchell filter effect with specified texture matrix and x/y tile modes. + */ + static GrEffectRef* Create(GrTexture* tex, + const SkMatrix& matrix, + SkShader::TileMode tileModes[2]) { + return Create(tex, gMitchellCoefficients, matrix, tileModes); } - static GrEffectRef* Create(GrTexture* tex, + /** + * The most general Create method. This allows specification of the bicubic coefficients, the + * texture matrix, and the x/y tilemodes. + */ + static GrEffectRef* Create(GrTexture* tex, const SkScalar coefficients[16], const SkMatrix& matrix, - const GrTextureParams& p, - GrCoordSet coordSet = kLocal_GrCoordSet) { - return Create(tex, gMitchellCoefficients, matrix, p, coordSet); + const SkShader::TileMode tileModes[2]) { + AutoEffectUnref effect(SkNEW_ARGS(GrBicubicEffect, (tex, coefficients, matrix, tileModes))); + return CreateEffectRef(effect); } private: - GrBicubicEffect(GrTexture*, const SkScalar coefficients[16]); GrBicubicEffect(GrTexture*, const SkScalar coefficients[16], - const SkMatrix &matrix, const GrTextureParams &p, GrCoordSet coordSet); + const SkMatrix &matrix, const SkShader::TileMode tileModes[2]); virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE; float fCoefficients[16]; diff --git a/gpu/effects/GrTextureDomainEffect.cpp b/gpu/effects/GrTextureDomain.cpp index 699aa729..70d61592 100644 --- a/gpu/effects/GrTextureDomainEffect.cpp +++ b/gpu/effects/GrTextureDomain.cpp @@ -5,12 +5,141 @@ * found in the LICENSE file. */ -#include "GrTextureDomainEffect.h" +#include "GrTextureDomain.h" #include "GrSimpleTextureEffect.h" #include "GrTBackendEffectFactory.h" #include "gl/GrGLEffect.h" #include "SkFloatingPoint.h" + +GrTextureDomain::GrTextureDomain(const SkRect& domain, Mode mode, int index) + : fIndex(index) { + + static const SkRect kFullRect = {0, 0, SK_Scalar1, SK_Scalar1}; + if (domain.contains(kFullRect)) { + fMode = kIgnore_Mode; + } else { + fMode = mode; + } + + if (fMode != kIgnore_Mode) { + // We don't currently handle domains that are empty or don't intersect the texture. + // It is OK if the domain rect is a line or point, but it should not be inverted. We do not + // handle rects that do not intersect the [0..1]x[0..1] rect. + SkASSERT(domain.fLeft <= domain.fRight); + SkASSERT(domain.fTop <= domain.fBottom); + fDomain.fLeft = SkMaxScalar(domain.fLeft, kFullRect.fLeft); + fDomain.fRight = SkMinScalar(domain.fRight, kFullRect.fRight); + fDomain.fTop = SkMaxScalar(domain.fTop, kFullRect.fTop); + fDomain.fBottom = SkMinScalar(domain.fBottom, kFullRect.fBottom); + SkASSERT(fDomain.fLeft <= fDomain.fRight); + SkASSERT(fDomain.fTop <= fDomain.fBottom); + } +} + +////////////////////////////////////////////////////////////////////////////// + +void GrTextureDomain::GLDomain::sampleTexture(GrGLShaderBuilder* builder, + const GrTextureDomain& textureDomain, + const char* outColor, + const SkString& inCoords, + const GrGLEffect::TextureSampler sampler, + const char* inModulateColor) { + SkASSERT((Mode)-1 == fMode || textureDomain.mode() == fMode); + SkDEBUGCODE(fMode = textureDomain.mode();) + + if (kIgnore_Mode == textureDomain.mode()) { + builder->fsCodeAppendf("\t%s = ", outColor); + builder->fsAppendTextureLookupAndModulate(inModulateColor, sampler, + inCoords.c_str()); + builder->fsCodeAppend(";\n"); + return; + } + + if (!fDomainUni.isValid()) { + const char* name; + SkString uniName("TexDom"); + if (textureDomain.fIndex >= 0) { + uniName.appendS32(textureDomain.fIndex); + } + fDomainUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, + kVec4f_GrSLType, uniName.c_str(), &name); + fDomainName = name; + } + if (kClamp_Mode == textureDomain.mode()) { + SkString clampedCoords; + clampedCoords.appendf("\tclamp(%s, %s.xy, %s.zw)", + inCoords.c_str(), fDomainName.c_str(), fDomainName.c_str()); + + builder->fsCodeAppendf("\t%s = ", outColor); + builder->fsAppendTextureLookupAndModulate(inModulateColor, sampler, clampedCoords.c_str()); + builder->fsCodeAppend(";\n"); + } else { + SkASSERT(GrTextureDomain::kDecal_Mode == textureDomain.mode()); + // Add a block since we're going to declare variables. + GrGLShaderBuilder::FSBlock block(builder); + + const char* domain = fDomainName.c_str(); + if (kImagination_GrGLVendor == builder->ctxInfo().vendor()) { + // On the NexusS and GalaxyNexus, the other path (with the 'any' + // call) causes the compilation error "Calls to any function that + // may require a gradient calculation inside a conditional block + // may return undefined results". This appears to be an issue with + // the 'any' call since even the simple "result=black; if (any()) + // result=white;" code fails to compile. + builder->fsCodeAppend("\tvec4 outside = vec4(0.0, 0.0, 0.0, 0.0);\n"); + builder->fsCodeAppend("\tvec4 inside = "); + builder->fsAppendTextureLookupAndModulate(inModulateColor, sampler, inCoords.c_str()); + builder->fsCodeAppend(";\n"); + + builder->fsCodeAppendf("\tfloat x = abs(2.0*(%s.x - %s.x)/(%s.z - %s.x) - 1.0);\n", + inCoords.c_str(), domain, domain, domain); + builder->fsCodeAppendf("\tfloat y = abs(2.0*(%s.y - %s.y)/(%s.w - %s.y) - 1.0);\n", + inCoords.c_str(), domain, domain, domain); + builder->fsCodeAppend("\tfloat blend = step(1.0, max(x, y));\n"); + builder->fsCodeAppendf("\t%s = mix(inside, outside, blend);\n", outColor); + } else { + builder->fsCodeAppend("\tbvec4 outside;\n"); + builder->fsCodeAppendf("\toutside.xy = lessThan(%s, %s.xy);\n", inCoords.c_str(), + domain); + builder->fsCodeAppendf("\toutside.zw = greaterThan(%s, %s.zw);\n", inCoords.c_str(), + domain); + builder->fsCodeAppendf("\t%s = any(outside) ? vec4(0.0, 0.0, 0.0, 0.0) : ", outColor); + builder->fsAppendTextureLookupAndModulate(inModulateColor, sampler, inCoords.c_str()); + builder->fsCodeAppend(";\n"); + } + } +} + +void GrTextureDomain::GLDomain::setData(const GrGLUniformManager& uman, + const GrTextureDomain& textureDomain, + GrSurfaceOrigin textureOrigin) { + SkASSERT(textureDomain.mode() == fMode); + if (kIgnore_Mode != textureDomain.mode()) { + GrGLfloat values[4] = { + SkScalarToFloat(textureDomain.domain().left()), + SkScalarToFloat(textureDomain.domain().top()), + SkScalarToFloat(textureDomain.domain().right()), + SkScalarToFloat(textureDomain.domain().bottom()) + }; + // vertical flip if necessary + if (kBottomLeft_GrSurfaceOrigin == textureOrigin) { + values[1] = 1.0f - values[1]; + values[3] = 1.0f - values[3]; + // The top and bottom were just flipped, so correct the ordering + // of elements so that values = (l, t, r, b). + SkTSwap(values[1], values[3]); + } + if (0 != memcmp(values, fPrevDomain, 4 * sizeof(GrGLfloat))) { + uman.set4fv(fDomainUni, 1, values); + memcpy(fPrevDomain, values, 4 * sizeof(GrGLfloat)); + } + } +} + + +////////////////////////////////////////////////////////////////////////////// + class GrGLTextureDomainEffect : public GrGLEffect { public: GrGLTextureDomainEffect(const GrBackendEffectFactory&, const GrDrawEffect&); @@ -28,16 +157,13 @@ public: static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&); private: - GrGLUniformManager::UniformHandle fNameUni; - GrGLfloat fPrevDomain[4]; - + GrTextureDomain::GLDomain fGLDomain; typedef GrGLEffect INHERITED; }; GrGLTextureDomainEffect::GrGLTextureDomainEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&) : INHERITED(factory) { - fPrevDomain[0] = SK_FloatNaN; } void GrGLTextureDomainEffect::emitCode(GrGLShaderBuilder* builder, @@ -47,80 +173,24 @@ void GrGLTextureDomainEffect::emitCode(GrGLShaderBuilder* builder, const char* inputColor, const TransformedCoordsArray& coords, const TextureSamplerArray& samplers) { - const GrTextureDomainEffect& texDom = drawEffect.castEffect<GrTextureDomainEffect>(); + const GrTextureDomainEffect& effect = drawEffect.castEffect<GrTextureDomainEffect>(); + const GrTextureDomain& domain = effect.textureDomain(); SkString coords2D = builder->ensureFSCoords2D(coords, 0); - const char* domain; - fNameUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, - kVec4f_GrSLType, "TexDom", &domain); - if (GrTextureDomainEffect::kClamp_WrapMode == texDom.wrapMode()) { - - builder->fsCodeAppendf("\tvec2 clampCoord = clamp(%s, %s.xy, %s.zw);\n", - coords2D.c_str(), domain, domain); - - builder->fsCodeAppendf("\t%s = ", outputColor); - builder->fsAppendTextureLookupAndModulate(inputColor, samplers[0], "clampCoord"); - builder->fsCodeAppend(";\n"); - } else { - SkASSERT(GrTextureDomainEffect::kDecal_WrapMode == texDom.wrapMode()); - - if (kImagination_GrGLVendor == builder->ctxInfo().vendor()) { - // On the NexusS and GalaxyNexus, the other path (with the 'any' - // call) causes the compilation error "Calls to any function that - // may require a gradient calculation inside a conditional block - // may return undefined results". This appears to be an issue with - // the 'any' call since even the simple "result=black; if (any()) - // result=white;" code fails to compile. - builder->fsCodeAppend("\tvec4 outside = vec4(0.0, 0.0, 0.0, 0.0);\n"); - builder->fsCodeAppend("\tvec4 inside = "); - builder->fsAppendTextureLookupAndModulate(inputColor, samplers[0], coords2D.c_str()); - builder->fsCodeAppend(";\n"); - - builder->fsCodeAppendf("\tfloat x = abs(2.0*(%s.x - %s.x)/(%s.z - %s.x) - 1.0);\n", - coords2D.c_str(), domain, domain, domain); - builder->fsCodeAppendf("\tfloat y = abs(2.0*(%s.y - %s.y)/(%s.w - %s.y) - 1.0);\n", - coords2D.c_str(), domain, domain, domain); - builder->fsCodeAppend("\tfloat blend = step(1.0, max(x, y));\n"); - builder->fsCodeAppendf("\t%s = mix(inside, outside, blend);\n", outputColor); - } else { - builder->fsCodeAppend("\tbvec4 outside;\n"); - builder->fsCodeAppendf("\toutside.xy = lessThan(%s, %s.xy);\n", coords2D.c_str(), domain); - builder->fsCodeAppendf("\toutside.zw = greaterThan(%s, %s.zw);\n", coords2D.c_str(), domain); - builder->fsCodeAppendf("\t%s = any(outside) ? vec4(0.0, 0.0, 0.0, 0.0) : ", outputColor); - builder->fsAppendTextureLookupAndModulate(inputColor, samplers[0], coords2D.c_str()); - builder->fsCodeAppend(";\n"); - } - } + fGLDomain.sampleTexture(builder, domain, outputColor, coords2D, samplers[0], inputColor); } void GrGLTextureDomainEffect::setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) { - const GrTextureDomainEffect& texDom = drawEffect.castEffect<GrTextureDomainEffect>(); - const SkRect& domain = texDom.domain(); - - float values[4] = { - SkScalarToFloat(domain.left()), - SkScalarToFloat(domain.top()), - SkScalarToFloat(domain.right()), - SkScalarToFloat(domain.bottom()) - }; - // vertical flip if necessary - if (kBottomLeft_GrSurfaceOrigin == texDom.texture(0)->origin()) { - values[1] = 1.0f - values[1]; - values[3] = 1.0f - values[3]; - // The top and bottom were just flipped, so correct the ordering - // of elements so that values = (l, t, r, b). - SkTSwap(values[1], values[3]); - } - if (0 != memcmp(values, fPrevDomain, 4 * sizeof(GrGLfloat))) { - uman.set4fv(fNameUni, 1, values); - memcpy(fPrevDomain, values, 4 * sizeof(GrGLfloat)); - } + const GrTextureDomainEffect& effect = drawEffect.castEffect<GrTextureDomainEffect>(); + const GrTextureDomain& domain = effect.textureDomain(); + fGLDomain.setData(uman, domain, effect.texture(0)->origin()); } GrGLEffect::EffectKey GrGLTextureDomainEffect::GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { - return drawEffect.castEffect<GrTextureDomainEffect>().wrapMode(); + const GrTextureDomain& domain = drawEffect.castEffect<GrTextureDomainEffect>().textureDomain(); + return GrTextureDomain::GLDomain::DomainKey(domain); } @@ -129,30 +199,19 @@ GrGLEffect::EffectKey GrGLTextureDomainEffect::GenKey(const GrDrawEffect& drawEf GrEffectRef* GrTextureDomainEffect::Create(GrTexture* texture, const SkMatrix& matrix, const SkRect& domain, - WrapMode wrapMode, + GrTextureDomain::Mode mode, GrTextureParams::FilterMode filterMode, GrCoordSet coordSet) { static const SkRect kFullRect = {0, 0, SK_Scalar1, SK_Scalar1}; - if (kClamp_WrapMode == wrapMode && domain.contains(kFullRect)) { + if (GrTextureDomain::kIgnore_Mode == mode || + (GrTextureDomain::kClamp_Mode == mode && domain.contains(kFullRect))) { return GrSimpleTextureEffect::Create(texture, matrix, filterMode); } else { - SkRect clippedDomain; - // We don't currently handle domains that are empty or don't intersect the texture. - // It is OK if the domain rect is a line or point, but it should not be inverted. We do not - // handle rects that do not intersect the [0..1]x[0..1] rect. - SkASSERT(domain.fLeft <= domain.fRight); - SkASSERT(domain.fTop <= domain.fBottom); - clippedDomain.fLeft = SkMaxScalar(domain.fLeft, kFullRect.fLeft); - clippedDomain.fRight = SkMinScalar(domain.fRight, kFullRect.fRight); - clippedDomain.fTop = SkMaxScalar(domain.fTop, kFullRect.fTop); - clippedDomain.fBottom = SkMinScalar(domain.fBottom, kFullRect.fBottom); - SkASSERT(clippedDomain.fLeft <= clippedDomain.fRight); - SkASSERT(clippedDomain.fTop <= clippedDomain.fBottom); AutoEffectUnref effect(SkNEW_ARGS(GrTextureDomainEffect, (texture, matrix, - clippedDomain, - wrapMode, + domain, + mode, filterMode, coordSet))); return CreateEffectRef(effect); @@ -163,12 +222,11 @@ GrEffectRef* GrTextureDomainEffect::Create(GrTexture* texture, GrTextureDomainEffect::GrTextureDomainEffect(GrTexture* texture, const SkMatrix& matrix, const SkRect& domain, - WrapMode wrapMode, + GrTextureDomain::Mode mode, GrTextureParams::FilterMode filterMode, GrCoordSet coordSet) : GrSingleTextureEffect(texture, matrix, filterMode, coordSet) - , fWrapMode(wrapMode) - , fTextureDomain(domain) { + , fTextureDomain(domain, mode) { } GrTextureDomainEffect::~GrTextureDomainEffect() { @@ -186,7 +244,7 @@ bool GrTextureDomainEffect::onIsEqual(const GrEffect& sBase) const { } void GrTextureDomainEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const { - if (kDecal_WrapMode == fWrapMode) { + if (GrTextureDomain::kDecal_Mode == fTextureDomain.mode()) { // TODO: helper *validFlags = 0; } else { this->updateConstantColorComponentsForModulation(color, validFlags); @@ -208,14 +266,15 @@ GrEffectRef* GrTextureDomainEffect::TestCreate(SkRandom* random, domain.fRight = random->nextRangeScalar(domain.fLeft, SK_Scalar1); domain.fTop = random->nextUScalar1(); domain.fBottom = random->nextRangeScalar(domain.fTop, SK_Scalar1); - WrapMode wrapMode = random->nextBool() ? kClamp_WrapMode : kDecal_WrapMode; + GrTextureDomain::Mode mode = + (GrTextureDomain::Mode) random->nextULessThan(GrTextureDomain::kModeCount); const SkMatrix& matrix = GrEffectUnitTest::TestMatrix(random); bool bilerp = random->nextBool(); GrCoordSet coords = random->nextBool() ? kLocal_GrCoordSet : kPosition_GrCoordSet; return GrTextureDomainEffect::Create(textures[texIdx], matrix, domain, - wrapMode, + mode, bilerp ? GrTextureParams::kBilerp_FilterMode : GrTextureParams::kNone_FilterMode, coords); } diff --git a/gpu/effects/GrTextureDomain.h b/gpu/effects/GrTextureDomain.h new file mode 100644 index 00000000..f64d5c3c --- /dev/null +++ b/gpu/effects/GrTextureDomain.h @@ -0,0 +1,169 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrTextureDomainEffect_DEFINED +#define GrTextureDomainEffect_DEFINED + +#include "GrSingleTextureEffect.h" +#include "gl/GrGLEffect.h" + +class GrGLShaderBuilder; +struct SkRect; + +/** + * Limits a texture's lookup coordinates to a domain. Samples outside the domain are either clamped + * the edge of the domain or result in a vec4 of zeros (decal mode). The domain is clipped to + * normalized texture coords ([0,1]x[0,1] square). Bilinear filtering can cause texels outside the + * domain to affect the read value unless the caller considers this when calculating the domain. + */ +class GrTextureDomain { +public: + enum Mode { + kIgnore_Mode, // Ignore the texture domain rectangle. + kClamp_Mode, // Clamp texture coords to the domain rectangle. + kDecal_Mode, // Treat the area outside the domain rectangle as fully transparent. + + kLastMode = kDecal_Mode + }; + static const int kModeCount = kLastMode + 1; + + /** + * @param index Pass a value >= 0 if using multiple texture domains in the same effect. + * It is used to keep inserted variables from causing name collisions. + */ + GrTextureDomain(const SkRect& domain, Mode, int index = -1); + + const SkRect& domain() const { return fDomain; } + Mode mode() const { return fMode; } + + /* Computes a domain that bounds all the texels in texelRect. Note that with bilerp enabled + texels neighboring the domain may be read. */ + static const SkRect MakeTexelDomain(const GrTexture* texture, const SkIRect& texelRect) { + SkScalar wInv = SK_Scalar1 / texture->width(); + SkScalar hInv = SK_Scalar1 / texture->height(); + SkRect result = { + texelRect.fLeft * wInv, + texelRect.fTop * hInv, + texelRect.fRight * wInv, + texelRect.fBottom * hInv + }; + return result; + } + + bool operator== (const GrTextureDomain& that) const { + return fMode == that.fMode && fDomain == that.fDomain; + } + + /** + * A GrGLEffect subclass that corresponds to a GrEffect subclass that uses GrTextureDomain + * should include this helper. It generates the texture domain GLSL, produces the part of the + * effect key that reflects the texture domain code, and performs the uniform uploads necessary + * for texture domains. + */ + class GLDomain { + public: + GLDomain() { + fPrevDomain[0] = SK_FloatNaN; + SkDEBUGCODE(fMode = (Mode) -1;) + } + + /** + * Call this from GrGLEffect::emitCode() to sample the texture W.R.T. the domain and mode. + * + * @param outcolor name of vec4 variable to hold the sampled color. + * @param inCoords name of vec2 variable containing the coords to be used with the domain. + * It is assumed that this is a variable and not an expression. + * @param inModulateColor if non-NULL the sampled color will be modulated with this + * expression before being written to outColor. + */ + void sampleTexture(GrGLShaderBuilder* builder, + const GrTextureDomain& textureDomain, + const char* outColor, + const SkString& inCoords, + const GrGLEffect::TextureSampler sampler, + const char* inModulateColor = NULL); + + /** + * Call this from GrGLEffect::setData() to upload uniforms necessary for the texture domain. + * The rectangle is automatically adjusted to account for the texture's origin. + */ + void setData(const GrGLUniformManager& uman, const GrTextureDomain& textureDomain, + GrSurfaceOrigin textureOrigin); + + enum { + kDomainKeyBits = 2, // See DomainKey(). + }; + + /** + * GrGLEffect::GenKey() must call this and include the returned value in it's computed key. + * The returned will be limited to the lower kDomainKeyBits bits. + */ + static GrGLEffect::EffectKey DomainKey(const GrTextureDomain& domain) { + GR_STATIC_ASSERT(kModeCount <= 4); + return domain.mode(); + } + + private: + SkDEBUGCODE(Mode fMode;) + GrGLUniformManager::UniformHandle fDomainUni; + SkString fDomainName; + GrGLfloat fPrevDomain[4]; + }; + +protected: + Mode fMode; + SkRect fDomain; + int fIndex; + + typedef GrSingleTextureEffect INHERITED; +}; + +class GrGLTextureDomainEffect; + +/** + * A basic texture effect that uses GrTextureDomain. + */ +class GrTextureDomainEffect : public GrSingleTextureEffect { + +public: + static GrEffectRef* Create(GrTexture*, + const SkMatrix&, + const SkRect& domain, + GrTextureDomain::Mode, + GrTextureParams::FilterMode filterMode, + GrCoordSet = kLocal_GrCoordSet); + + virtual ~GrTextureDomainEffect(); + + static const char* Name() { return "TextureDomain"; } + + typedef GrGLTextureDomainEffect GLEffect; + + virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; + virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE; + + const GrTextureDomain& textureDomain() const { return fTextureDomain; } + +protected: + GrTextureDomain fTextureDomain; + +private: + GrTextureDomainEffect(GrTexture*, + const SkMatrix&, + const SkRect& domain, + GrTextureDomain::Mode, + GrTextureParams::FilterMode, + GrCoordSet); + + virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE; + + GR_DECLARE_EFFECT_TEST; + + typedef GrSingleTextureEffect INHERITED; +}; + +#endif diff --git a/gpu/effects/GrTextureDomainEffect.h b/gpu/effects/GrTextureDomainEffect.h deleted file mode 100644 index 46ee2a65..00000000 --- a/gpu/effects/GrTextureDomainEffect.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef GrTextureDomainEffect_DEFINED -#define GrTextureDomainEffect_DEFINED - -#include "GrSingleTextureEffect.h" - -class GrGLTextureDomainEffect; -struct SkRect; - -/** - * Limits a texture's lookup coordinates to a domain. Samples outside the domain are either clamped - * the edge of the domain or result in a vec4 of zeros. The domain is clipped to normalized texture - * coords ([0,1]x[0,1] square). Bilinear filtering can cause texels outside the domain to affect the - * read value unless the caller considers this when calculating the domain. TODO: This should be a - * helper that can assist an effect rather than effect unto itself. - */ -class GrTextureDomainEffect : public GrSingleTextureEffect { - -public: - /** - * If SkShader::kDecal_TileMode sticks then this enum could be replaced by SkShader::TileMode. - * We could also consider replacing/augmenting Decal mode with Border mode where the color - * outside of the domain is user-specifiable. Decal mode currently has a hard (non-lerped) - * transition between the border and the interior. - */ - enum WrapMode { - kClamp_WrapMode, - kDecal_WrapMode, - }; - - static GrEffectRef* Create(GrTexture*, - const SkMatrix&, - const SkRect& domain, - WrapMode, - GrTextureParams::FilterMode filterMode, - GrCoordSet = kLocal_GrCoordSet); - - virtual ~GrTextureDomainEffect(); - - static const char* Name() { return "TextureDomain"; } - - typedef GrGLTextureDomainEffect GLEffect; - - virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; - virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE; - - const SkRect& domain() const { return fTextureDomain; } - WrapMode wrapMode() const { return fWrapMode; } - - /* Computes a domain that bounds all the texels in texelRect. Note that with bilerp enabled - texels neighboring the domain may be read. */ - static const SkRect MakeTexelDomain(const GrTexture* texture, const SkIRect& texelRect) { - SkScalar wInv = SK_Scalar1 / texture->width(); - SkScalar hInv = SK_Scalar1 / texture->height(); - SkRect result = { - texelRect.fLeft * wInv, - texelRect.fTop * hInv, - texelRect.fRight * wInv, - texelRect.fBottom * hInv - }; - return result; - } - -protected: - WrapMode fWrapMode; - SkRect fTextureDomain; - -private: - GrTextureDomainEffect(GrTexture*, - const SkMatrix&, - const SkRect& domain, - WrapMode, - GrTextureParams::FilterMode filterMode, - GrCoordSet); - - virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE; - - GR_DECLARE_EFFECT_TEST; - - typedef GrSingleTextureEffect INHERITED; -}; - -#endif diff --git a/gpu/effects/GrTextureStripAtlas.h b/gpu/effects/GrTextureStripAtlas.h index e56e736d..e06e273e 100644 --- a/gpu/effects/GrTextureStripAtlas.h +++ b/gpu/effects/GrTextureStripAtlas.h @@ -136,12 +136,15 @@ private: // Hash table entry for atlases class AtlasEntry; - typedef GrTBinHashKey<AtlasEntry, sizeof(GrTextureStripAtlas::Desc)> AtlasHashKey; + class AtlasHashKey : public GrBinHashKey<sizeof(GrTextureStripAtlas::Desc)> { + public: + static bool Equals(const AtlasEntry& entry, const AtlasHashKey& key); + static bool LessThan(const AtlasEntry& entry, const AtlasHashKey& key); + }; class AtlasEntry : public ::SkNoncopyable { public: AtlasEntry() : fAtlas(NULL) {} ~AtlasEntry() { SkDELETE(fAtlas); } - int compare(const AtlasHashKey& key) const { return fKey.compare(key); } AtlasHashKey fKey; GrTextureStripAtlas* fAtlas; }; @@ -178,4 +181,14 @@ private: SkTDArray<AtlasRow*> fKeyTable; }; +inline bool GrTextureStripAtlas::AtlasHashKey::Equals(const AtlasEntry& entry, + const AtlasHashKey& key) { + return entry.fKey == key; +} + +inline bool GrTextureStripAtlas::AtlasHashKey::LessThan(const AtlasEntry& entry, + const AtlasHashKey& key) { + return entry.fKey < key; +} + #endif diff --git a/gpu/gl/GrGLCaps.cpp b/gpu/gl/GrGLCaps.cpp index 1a39ba59..8d8c0225 100644 --- a/gpu/gl/GrGLCaps.cpp +++ b/gpu/gl/GrGLCaps.cpp @@ -11,8 +11,6 @@ #include "SkTSearch.h" #include "SkTSort.h" -SK_DEFINE_INST_COUNT(GrGLCaps) - GrGLCaps::GrGLCaps() { this->reset(); } diff --git a/gpu/gl/GrGLEffect.h b/gpu/gl/GrGLEffect.h index b6807383..1cc3df24 100644 --- a/gpu/gl/GrGLEffect.h +++ b/gpu/gl/GrGLEffect.h @@ -40,6 +40,7 @@ class GrGLEffect { public: typedef GrBackendEffectFactory::EffectKey EffectKey; typedef GrGLProgramEffects::TransformedCoordsArray TransformedCoordsArray; + typedef GrGLProgramEffects::TextureSampler TextureSampler; typedef GrGLProgramEffects::TextureSamplerArray TextureSamplerArray; enum { diff --git a/gpu/gl/GrGLInterface.cpp b/gpu/gl/GrGLInterface.cpp index 09f6a65e..e1c69e18 100644 --- a/gpu/gl/GrGLInterface.cpp +++ b/gpu/gl/GrGLInterface.cpp @@ -12,8 +12,6 @@ #include <stdio.h> -SK_DEFINE_INST_COUNT(GrGLInterface) - #if GR_GL_PER_GL_FUNC_CALLBACK namespace { void GrGLDefaultInterfaceCallback(const GrGLInterface*) {} diff --git a/gpu/gl/GrGLProgram.cpp b/gpu/gl/GrGLProgram.cpp index 5b030fa3..cac38b4b 100644 --- a/gpu/gl/GrGLProgram.cpp +++ b/gpu/gl/GrGLProgram.cpp @@ -17,8 +17,6 @@ #include "GrGLSL.h" #include "SkXfermode.h" -SK_DEFINE_INST_COUNT(GrGLProgram) - #define GL_CALL(X) GR_GL_CALL(fGpu->glInterface(), X) #define GL_CALL_RET(R, X) GR_GL_CALL_RET(fGpu->glInterface(), R, X) diff --git a/gpu/gl/GrGLShaderBuilder.h b/gpu/gl/GrGLShaderBuilder.h index 52c24ae3..103efa5a 100644 --- a/gpu/gl/GrGLShaderBuilder.h +++ b/gpu/gl/GrGLShaderBuilder.h @@ -208,6 +208,25 @@ public: const GrGLContextInfo& ctxInfo() const; + /** + * Helper for begining and ending a block in the fragment code. TODO: Make GrGLShaderBuilder + * aware of all blocks and turn single \t's into the correct number of tabs (or spaces) so that + * our shaders print pretty without effect writers tracking indentation. + */ + class FSBlock { + public: + FSBlock(GrGLShaderBuilder* builder) : fBuilder(builder) { + SkASSERT(NULL != builder); + fBuilder->fsCodeAppend("\t{\n"); + } + + ~FSBlock() { + fBuilder->fsCodeAppend("\t}\n"); + } + private: + GrGLShaderBuilder* fBuilder; + }; + protected: GrGpuGL* gpu() const { return fGpu; } diff --git a/gpu/gl/GrGLTexture.cpp b/gpu/gl/GrGLTexture.cpp index 97721225..856cfb12 100644 --- a/gpu/gl/GrGLTexture.cpp +++ b/gpu/gl/GrGLTexture.cpp @@ -8,8 +8,6 @@ #include "GrGLTexture.h" #include "GrGpuGL.h" -SK_DEFINE_INST_COUNT(GrGLTexID) - #define GPUGL static_cast<GrGpuGL*>(getGpu()) #define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X) diff --git a/gpu/gl/GrGpuGL.cpp b/gpu/gl/GrGpuGL.cpp index 9cf39b65..4b5221c3 100644 --- a/gpu/gl/GrGpuGL.cpp +++ b/gpu/gl/GrGpuGL.cpp @@ -552,7 +552,12 @@ bool GrGpuGL::uploadTexData(const GrGLTexture::Desc& desc, SkAutoSMalloc<128 * 128> tempStorage; // paletted textures cannot be partially updated - bool useTexStorage = isNewTexture && + // We currently lazily create MIPMAPs when the we see a draw with + // GrTextureParams::kMipMap_FilterMode. Using texture storage requires that the + // MIP levels are all created when the texture is created. So for now we don't use + // texture storage. + bool useTexStorage = false && + isNewTexture && desc.fConfig != kIndex_8_GrPixelConfig && this->glCaps().texStorageSupport(); @@ -638,8 +643,7 @@ bool GrGpuGL::uploadTexData(const GrGLTexture::Desc& desc, desc.fWidth == width && desc.fHeight == height) { CLEAR_ERROR_BEFORE_ALLOC(this->glInterface()); if (useTexStorage) { - // We never resize or change formats of textures. We don't use - // mipmaps currently. + // We never resize or change formats of textures. GL_ALLOC_CALL(this->glInterface(), TexStorage2D(GR_GL_TEXTURE_2D, 1, // levels @@ -1276,6 +1280,7 @@ void GrGpuGL::onClear(const SkIRect* rect, GrColor color, bool canIgnoreRect) { return; } } + this->flushRenderTarget(rect); GrAutoTRestore<ScissorState> asr(&fScissorState); fScissorState.fEnabled = (NULL != rect); @@ -1522,10 +1527,16 @@ void GrGpuGL::flushRenderTarget(const SkIRect* bound) { if (fHWBoundRenderTarget != rt) { GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, rt->renderFBOID())); #ifdef SK_DEBUG - GrGLenum status; - GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER)); - if (status != GR_GL_FRAMEBUFFER_COMPLETE) { - GrPrintf("GrGpuGL::flushRenderTarget glCheckFramebufferStatus %x\n", status); + // don't do this check in Chromium -- this is causing + // lots of repeated command buffer flushes when the compositor is + // rendering with Ganesh, which is really slow; even too slow for + // Debug mode. + if (!this->glContext().info().isChromium()) { + GrGLenum status; + GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER)); + if (status != GR_GL_FRAMEBUFFER_COMPLETE) { + GrPrintf("GrGpuGL::flushRenderTarget glCheckFramebufferStatus %x\n", status); + } } #endif fHWBoundRenderTarget = rt; diff --git a/gpu/gl/GrGpuGL_program.cpp b/gpu/gl/GrGpuGL_program.cpp index a777e8f3..a3beab1a 100644 --- a/gpu/gl/GrGpuGL_program.cpp +++ b/gpu/gl/GrGpuGL_program.cpp @@ -27,8 +27,6 @@ struct GrGpuGL::ProgramCache::Entry { unsigned int fLRUStamp; }; -SK_DEFINE_INST_COUNT(GrGpuGL::ProgramCache::Entry); - struct GrGpuGL::ProgramCache::ProgDescLess { bool operator() (const GrGLProgramDesc& desc, const Entry* entry) { SkASSERT(NULL != entry->fProgram.get()); diff --git a/gpu/gl/SkGLContextHelper.cpp b/gpu/gl/SkGLContextHelper.cpp index 6f0372dd..da446be0 100644 --- a/gpu/gl/SkGLContextHelper.cpp +++ b/gpu/gl/SkGLContextHelper.cpp @@ -8,8 +8,6 @@ #include "gl/SkGLContextHelper.h" #include "GrGLUtil.h" -SK_DEFINE_INST_COUNT(SkGLContextHelper) - SkGLContextHelper::SkGLContextHelper() : fFBO(0) , fColorBufferID(0) diff --git a/gpu/gl/debug/GrGLCreateDebugInterface.cpp b/gpu/gl/debug/GrGLCreateDebugInterface.cpp index 2ccd1584..1a0e7acc 100644 --- a/gpu/gl/debug/GrGLCreateDebugInterface.cpp +++ b/gpu/gl/debug/GrGLCreateDebugInterface.cpp @@ -786,8 +786,6 @@ private: typedef GrGLInterface INHERITED; }; -SK_DEFINE_INST_COUNT(GrDebugGLInterface) - //////////////////////////////////////////////////////////////////////////////// const GrGLInterface* GrGLCreateDebugInterface() { GrGLInterface* interface = SkNEW(GrDebugGLInterface); diff --git a/gpu/gl/win/GrGLCreateNativeInterface_win.cpp b/gpu/gl/win/GrGLCreateNativeInterface_win.cpp index e9207b16..06e406f8 100644 --- a/gpu/gl/win/GrGLCreateNativeInterface_win.cpp +++ b/gpu/gl/win/GrGLCreateNativeInterface_win.cpp @@ -11,7 +11,7 @@ #include "gl/GrGLInterface.h" #include "gl/GrGLUtil.h" #define WIN32_LEAN_AND_MEAN -#include <Windows.h> +#include <windows.h> /* * Windows makes the GL funcs all be __stdcall instead of __cdecl :( diff --git a/gpu/gl/win/SkNativeGLContext_win.cpp b/gpu/gl/win/SkNativeGLContext_win.cpp index d8777274..392c2bc9 100644 --- a/gpu/gl/win/SkNativeGLContext_win.cpp +++ b/gpu/gl/win/SkNativeGLContext_win.cpp @@ -10,7 +10,7 @@ #include "SkWGL.h" #define WIN32_LEAN_AND_MEAN -#include <Windows.h> +#include <windows.h> SkNativeGLContext::AutoContextRestore::AutoContextRestore() { fOldHGLRC = wglGetCurrentContext(); diff --git a/gpu/gr_unittests.cpp b/gpu/gr_unittests.cpp deleted file mode 100644 index ae9f67f2..00000000 --- a/gpu/gr_unittests.cpp +++ /dev/null @@ -1,80 +0,0 @@ - -/* - * Copyright 2010 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "GrBinHashKey.h" -#include "GrDrawTarget.h" -#include "SkMatrix.h" -#include "GrRedBlackTree.h" - -// FIXME: needs to be in a header -void gr_run_unittests(); - -// If we aren't inheriting these as #defines from elsewhere, -// clang demands they be declared before we #include the template -// that relies on them. -#ifdef SK_DEBUG -static bool LT(const int& elem, int value) { - return elem < value; -} -static bool EQ(const int& elem, int value) { - return elem == value; -} -#include "GrTBSearch.h" - -static void test_bsearch() { - const int array[] = { - 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99 - }; - - for (int n = 0; n < static_cast<int>(GR_ARRAY_COUNT(array)); ++n) { - for (int i = 0; i < n; i++) { - int index = GrTBSearch<int, int>(array, n, array[i]); - SkASSERT(index == (int) i); - index = GrTBSearch<int, int>(array, n, -array[i]); - SkASSERT(index < 0); - } - } -} -#endif - -// bogus empty class for GrBinHashKey -class BogusEntry {}; - -static void test_binHashKey() -{ - const char* testStringA_ = "abcdABCD"; - const char* testStringB_ = "abcdBBCD"; - const uint32_t* testStringA = reinterpret_cast<const uint32_t*>(testStringA_); - const uint32_t* testStringB = reinterpret_cast<const uint32_t*>(testStringB_); - enum { - kDataLenUsedForKey = 8 - }; - - GrTBinHashKey<BogusEntry, kDataLenUsedForKey> keyA; - keyA.setKeyData(testStringA); - // test copy constructor and comparison - GrTBinHashKey<BogusEntry, kDataLenUsedForKey> keyA2(keyA); - SkASSERT(keyA.compare(keyA2) == 0); - SkASSERT(keyA.getHash() == keyA2.getHash()); - // test re-init - keyA2.setKeyData(testStringA); - SkASSERT(keyA.compare(keyA2) == 0); - SkASSERT(keyA.getHash() == keyA2.getHash()); - // test sorting - GrTBinHashKey<BogusEntry, kDataLenUsedForKey> keyB; - keyB.setKeyData(testStringB); - SkASSERT(keyA.compare(keyB) < 0); - SkASSERT(keyA.getHash() != keyB.getHash()); -} - - -void gr_run_unittests() { - SkDEBUGCODE(test_bsearch();) - test_binHashKey(); - GrRedBlackTree<int>::UnitTest(); -} diff --git a/image/SkDataPixelRef.cpp b/image/SkDataPixelRef.cpp index 05242433..7897bf93 100644 --- a/image/SkDataPixelRef.cpp +++ b/image/SkDataPixelRef.cpp @@ -27,6 +27,10 @@ void SkDataPixelRef::onUnlockPixels() { // nothing to do } +size_t SkDataPixelRef::getAllocatedSizeInBytes() const { + return fData ? fData->size() : 0; +} + void SkDataPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const { this->INHERITED::flatten(buffer); buffer.writeDataAsByteArray(fData); diff --git a/image/SkDataPixelRef.h b/image/SkDataPixelRef.h index 6b15802b..50c88571 100644 --- a/image/SkDataPixelRef.h +++ b/image/SkDataPixelRef.h @@ -22,6 +22,7 @@ public: protected: virtual void* onLockPixels(SkColorTable**) SK_OVERRIDE; virtual void onUnlockPixels() SK_OVERRIDE; + virtual size_t getAllocatedSizeInBytes() const SK_OVERRIDE; SkDataPixelRef(SkFlattenableReadBuffer& buffer); virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE; diff --git a/image/SkImage.cpp b/image/SkImage.cpp index 39fd93ac..620922f6 100644 --- a/image/SkImage.cpp +++ b/image/SkImage.cpp @@ -10,8 +10,6 @@ #include "SkImagePriv.h" #include "SkImage_Base.h" -SK_DEFINE_INST_COUNT(SkImage) - static SkImage_Base* as_IB(SkImage* image) { return static_cast<SkImage_Base*>(image); } diff --git a/image/SkImagePriv.cpp b/image/SkImagePriv.cpp index f1916c6c..976a5b33 100644 --- a/image/SkImagePriv.cpp +++ b/image/SkImagePriv.cpp @@ -14,13 +14,16 @@ SkBitmap::Config SkImageInfoToBitmapConfig(const SkImageInfo& info) { case kAlpha_8_SkColorType: return SkBitmap::kA8_Config; + case kARGB_4444_SkColorType: + return SkBitmap::kARGB_4444_Config; + case kRGB_565_SkColorType: return SkBitmap::kRGB_565_Config; case kPMColor_SkColorType: return SkBitmap::kARGB_8888_Config; - case kIndex8_SkColorType: + case kIndex_8_SkColorType: return SkBitmap::kIndex8_Config; default: @@ -30,52 +33,9 @@ SkBitmap::Config SkImageInfoToBitmapConfig(const SkImageInfo& info) { return SkBitmap::kNo_Config; } -int SkImageBytesPerPixel(SkColorType ct) { - static const uint8_t gColorTypeBytesPerPixel[] = { - 1, // kAlpha_8_SkColorType - 2, // kRGB_565_SkColorType - 4, // kRGBA_8888_SkColorType - 4, // kBGRA_8888_SkColorType - 4, // kPMColor_SkColorType - 1, // kIndex8_SkColorType - }; - - SkASSERT((size_t)ct < SK_ARRAY_COUNT(gColorTypeBytesPerPixel)); - return gColorTypeBytesPerPixel[ct]; -} - -bool SkBitmapToImageInfo(const SkBitmap& bm, SkImageInfo* info) { - switch (bm.config()) { - case SkBitmap::kA8_Config: - info->fColorType = kAlpha_8_SkColorType; - break; - - case SkBitmap::kIndex8_Config: - info->fColorType = kIndex8_SkColorType; - break; - - case SkBitmap::kRGB_565_Config: - info->fColorType = kRGB_565_SkColorType; - break; - - case SkBitmap::kARGB_8888_Config: - info->fColorType = kPMColor_SkColorType; - break; - - default: - return false; - } - - info->fWidth = bm.width(); - info->fHeight = bm.height(); - info->fAlphaType = bm.isOpaque() ? kOpaque_SkAlphaType : - kPremul_SkAlphaType; - return true; -} - SkImage* SkNewImageFromBitmap(const SkBitmap& bm, bool canSharePixelRef) { SkImageInfo info; - if (!SkBitmapToImageInfo(bm, &info)) { + if (!bm.asImageInfo(&info)) { return NULL; } diff --git a/image/SkImagePriv.h b/image/SkImagePriv.h index 188b16d5..bf28f598 100644 --- a/image/SkImagePriv.h +++ b/image/SkImagePriv.h @@ -15,10 +15,6 @@ class SkPicture; extern SkBitmap::Config SkImageInfoToBitmapConfig(const SkImageInfo&); -extern int SkImageBytesPerPixel(SkColorType); - -extern bool SkBitmapToImageInfo(const SkBitmap&, SkImageInfo*); - // Call this if you explicitly want to use/share this pixelRef in the image extern SkImage* SkNewImageFromPixelRef(const SkImageInfo&, SkPixelRef*, size_t rowBytes); @@ -48,8 +44,7 @@ extern void SkImagePrivDrawPicture(SkCanvas*, SkPicture*, extern SkImage* SkNewImageFromPicture(const SkPicture*); static inline size_t SkImageMinRowBytes(const SkImageInfo& info) { - size_t rb = info.fWidth * SkImageBytesPerPixel(info.fColorType); - return SkAlign4(rb); + return SkAlign4(info.minRowBytes()); } // Given an image created from SkNewImageFromBitmap, return its pixelref. This diff --git a/image/SkImage_Gpu.cpp b/image/SkImage_Gpu.cpp index 036e45bb..ab0a5237 100644 --- a/image/SkImage_Gpu.cpp +++ b/image/SkImage_Gpu.cpp @@ -36,8 +36,6 @@ private: typedef SkImage_Base INHERITED; }; -SK_DEFINE_INST_COUNT(SkImage_Gpu) - /////////////////////////////////////////////////////////////////////////////// SkImage_Gpu::SkImage_Gpu(const SkBitmap& bitmap) diff --git a/image/SkSurface.cpp b/image/SkSurface.cpp index 2fd4e104..2dde36b9 100644 --- a/image/SkSurface.cpp +++ b/image/SkSurface.cpp @@ -9,8 +9,6 @@ #include "SkImagePriv.h" #include "SkCanvas.h" -SK_DEFINE_INST_COUNT(SkSurface) - /////////////////////////////////////////////////////////////////////////////// SkSurface_Base::SkSurface_Base(int width, int height) : INHERITED(width, height) { diff --git a/image/SkSurface_Gpu.cpp b/image/SkSurface_Gpu.cpp index e9049aef..e673cef6 100644 --- a/image/SkSurface_Gpu.cpp +++ b/image/SkSurface_Gpu.cpp @@ -31,8 +31,6 @@ private: typedef SkSurface_Base INHERITED; }; -SK_DEFINE_INST_COUNT(SkSurface_Gpu) - /////////////////////////////////////////////////////////////////////////////// SkSurface_Gpu::SkSurface_Gpu(GrContext* ctx, const SkImageInfo& info, diff --git a/images/SkDecodingImageGenerator.cpp b/images/SkDecodingImageGenerator.cpp index 65fa6fd5..a833c636 100644 --- a/images/SkDecodingImageGenerator.cpp +++ b/images/SkDecodingImageGenerator.cpp @@ -6,47 +6,203 @@ */ #include "SkDecodingImageGenerator.h" - -#include "SkBitmapFactory.h" #include "SkData.h" -#include "SkDiscardablePixelRef.h" #include "SkImageDecoder.h" +#include "SkImageGenerator.h" +#include "SkImagePriv.h" +#include "SkStream.h" + + +namespace { +/** + * Special allocator used by getPixels(). Uses preallocated memory + * provided. + */ +class TargetAllocator : public SkBitmap::Allocator { +public: + TargetAllocator(void* target, size_t rowBytes, const SkImageInfo& info) + : fTarget(target) + , fRowBytes(rowBytes) + , fInfo(info) { } + + virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) SK_OVERRIDE { + if ((SkImageInfoToBitmapConfig(fInfo) != bm->config()) + || (bm->width() != fInfo.fWidth) + || (bm->height() != fInfo.fHeight)) { + return false; + } + bm->setConfig(bm->config(), bm->width(), bm->height(), + fRowBytes, bm->alphaType()); + bm->setPixels(fTarget, ct); + return true; + } + +private: + void* fTarget; + size_t fRowBytes; + SkImageInfo fInfo; + typedef SkBitmap::Allocator INHERITED; +}; +} // namespace +//////////////////////////////////////////////////////////////////////////////// SkDecodingImageGenerator::SkDecodingImageGenerator(SkData* data) - : fData(data) { + : fData(data) + , fHasInfo(false) + , fDoCopyTo(false) { SkASSERT(fData != NULL); + fStream = SkNEW_ARGS(SkMemoryStream, (fData)); + SkASSERT(fStream != NULL); + SkASSERT(fStream->unique()); fData->ref(); } +SkDecodingImageGenerator::SkDecodingImageGenerator(SkStreamRewindable* stream) + : fData(NULL) + , fStream(stream) + , fHasInfo(false) + , fDoCopyTo(false) { + SkASSERT(fStream != NULL); + SkASSERT(fStream->unique()); +} + SkDecodingImageGenerator::~SkDecodingImageGenerator() { - fData->unref(); + SkSafeUnref(fData); + fStream->unref(); } +// TODO(halcanary): Give this macro a better name and move it into SkTypes.h +#ifdef SK_DEBUG + #define SkCheckResult(expr, value) SkASSERT((value) == (expr)) +#else + #define SkCheckResult(expr, value) (void)(expr) +#endif + SkData* SkDecodingImageGenerator::refEncodedData() { - fData->ref(); - return fData; + // This functionality is used in `gm --serialize` + if (fData != NULL) { + return SkSafeRef(fData); + } + // TODO(halcanary): SkStreamRewindable needs a refData() function + // which returns a cheap copy of the underlying data. + if (!fStream->rewind()) { + return NULL; + } + size_t length = fStream->getLength(); + if (0 == length) { + return NULL; + } + void* buffer = sk_malloc_flags(length, 0); + SkCheckResult(fStream->read(buffer, length), length); + fData = SkData::NewFromMalloc(buffer, length); + return SkSafeRef(fData); } bool SkDecodingImageGenerator::getInfo(SkImageInfo* info) { - SkASSERT(info != NULL); - return SkImageDecoder::DecodeMemoryToTarget(fData->data(), - fData->size(), - info, NULL); + // info can be NULL. If so, will update fInfo, fDoCopyTo, and fHasInfo. + if (fHasInfo) { + if (info != NULL) { + *info = fInfo; + } + return true; + } + SkAssertResult(fStream->rewind()); + SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(fStream)); + if (NULL == decoder.get()) { + return false; + } + SkBitmap bitmap; + if (!decoder->decode(fStream, &bitmap, + SkImageDecoder::kDecodeBounds_Mode)) { + return false; + } + if (bitmap.config() == SkBitmap::kNo_Config) { + return false; + } + if (!bitmap.asImageInfo(&fInfo)) { + // We can't use bitmap.config() as is. + if (!bitmap.canCopyTo(SkBitmap::kARGB_8888_Config)) { + SkDEBUGFAIL("!bitmap->canCopyTo(SkBitmap::kARGB_8888_Config)"); + return false; + } + fDoCopyTo = true; + fInfo.fWidth = bitmap.width(); + fInfo.fHeight = bitmap.height(); + fInfo.fColorType = kPMColor_SkColorType; + fInfo.fAlphaType = bitmap.alphaType(); + } + if (info != NULL) { + *info = fInfo; + } + fHasInfo = true; + return true; } bool SkDecodingImageGenerator::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes) { - SkASSERT(pixels != NULL); - SkBitmapFactory::Target target = {pixels, rowBytes}; - SkImageInfo tmpInfo = info; - return SkImageDecoder::DecodeMemoryToTarget(fData->data(), - fData->size(), - &tmpInfo, &target); -} -bool SkDecodingImageGenerator::Install(SkData* data, SkBitmap* dst) { + if (NULL == pixels) { + return false; + } + if (!this->getInfo(NULL)) { + return false; + } + if (SkImageInfoToBitmapConfig(info) == SkBitmap::kNo_Config) { + return false; // Unsupported SkColorType. + } + SkAssertResult(fStream->rewind()); + SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(fStream)); + if (NULL == decoder.get()) { + return false; + } + if (fInfo != info) { + // The caller has specified a different info. For now, this + // is an error. In the future, we will check to see if we can + // convert. + return false; + } + int bpp = SkBitmap::ComputeBytesPerPixel(SkImageInfoToBitmapConfig(info)); + if (static_cast<size_t>(bpp * info.fWidth) > rowBytes) { + return false; + } + SkBitmap bitmap; + if (!bitmap.setConfig(info, rowBytes)) { + return false; + } + + TargetAllocator allocator(pixels, rowBytes, info); + if (!fDoCopyTo) { + decoder->setAllocator(&allocator); + } + bool success = decoder->decode(fStream, &bitmap, + SkImageDecoder::kDecodePixels_Mode); + decoder->setAllocator(NULL); + if (!success) { + return false; + } + if (fDoCopyTo) { + SkBitmap bm8888; + bitmap.copyTo(&bm8888, SkBitmap::kARGB_8888_Config, &allocator); + } + return true; +} +bool SkDecodingImageGenerator::Install(SkData* data, SkBitmap* dst, + SkDiscardableMemory::Factory* factory) { SkASSERT(data != NULL); SkASSERT(dst != NULL); SkImageGenerator* gen(SkNEW_ARGS(SkDecodingImageGenerator, (data))); - return SkDiscardablePixelRef::Install(gen, dst); + return SkInstallDiscardablePixelRef(gen, dst, factory); +} + +bool SkDecodingImageGenerator::Install(SkStreamRewindable* stream, + SkBitmap* dst, + SkDiscardableMemory::Factory* factory) { + SkASSERT(stream != NULL); + SkASSERT(dst != NULL); + if ((stream == NULL) || !stream->unique()) { + SkSafeUnref(stream); + return false; + } + SkImageGenerator* gen(SkNEW_ARGS(SkDecodingImageGenerator, (stream))); + return SkInstallDiscardablePixelRef(gen, dst, factory); } diff --git a/images/SkDecodingImageGenerator.h b/images/SkDecodingImageGenerator.h index 682aeb61..dba234bc 100644 --- a/images/SkDecodingImageGenerator.h +++ b/images/SkDecodingImageGenerator.h @@ -8,9 +8,12 @@ #ifndef SkDecodingImageGenerator_DEFINED #define SkDecodingImageGenerator_DEFINED +#include "SkDiscardableMemory.h" #include "SkImageGenerator.h" +#include "SkImageInfo.h" class SkBitmap; +class SkStreamRewindable; /** * Calls into SkImageDecoder::DecodeMemoryToTarget to implement a @@ -22,7 +25,32 @@ public: * The constructor will take a reference to the SkData. The * destructor will unref() it. */ - SkDecodingImageGenerator(SkData* data); + explicit SkDecodingImageGenerator(SkData* data); + + /* + * The SkData version of this constructor is preferred. If the + * stream has an underlying SkData (such as a SkMemoryStream) + * pass that in. + * + * This object will unref the stream when done. Since streams + * have internal state (position), the caller should not pass a + * shared stream in. Pass either a new duplicated stream in or + * transfer ownership of the stream. In the latter case, be sure + * that there are no other consumers of the stream who will + * modify the stream's position. This constructor asserts + * stream->unique(). + * + * For example: + * SkStreamRewindable* stream; + * ... + * SkImageGenerator* gen + * = SkNEW_ARGS(SkDecodingImageGenerator, + * (stream->duplicate())); + * ... + * SkDELETE(gen); + */ + explicit SkDecodingImageGenerator(SkStreamRewindable* stream); + virtual ~SkDecodingImageGenerator(); virtual SkData* refEncodedData() SK_OVERRIDE; @@ -36,10 +64,57 @@ public: /** * Install the SkData into the destination bitmap, using a new * SkDiscardablePixelRef and a new SkDecodingImageGenerator. + * + * @param data Contains the encoded image data that will be used + * by the SkDecodingImageGenerator. Will be ref()ed. + * + * @param destination Upon success, this bitmap will be + * configured and have a pixelref installed. + * + * @param factory If not NULL, this object will be used as a + * source of discardable memory when decoding. If NULL, then + * SkDiscardableMemory::Create() will be called. + * + * @return true iff successful. + */ + static bool Install(SkData* data, SkBitmap* destination, + SkDiscardableMemory::Factory* factory = NULL); + /** + * Install the stream into the destination bitmap, using a new + * SkDiscardablePixelRef and a new SkDecodingImageGenerator. + * + * The SkData version of this function is preferred. If the + * stream has an underlying SkData (such as a SkMemoryStream) + * pass that in. + * + * @param stream The source of encoded data that will be passed + * to the decoder. The installed SkDecodingImageGenerator will + * unref the stream when done. If false is returned, this + * function will perform the unref. Since streams have internal + * state (position), the caller should not pass a shared stream + * in. Pass either a new duplicated stream in or transfer + * ownership of the stream. In the latter case, be sure that + * there are no other consumers of the stream who will modify the + * stream's position. This function will fail if + * (!stream->unique()). + * + * @param destination Upon success, this bitmap will be + * configured and have a pixelref installed. + * + * @param factory If not NULL, this object will be used as a + * source of discardable memory when decoding. If NULL, then + * SkDiscardableMemory::Create() will be called. + * + * @return true iff successful. */ - static bool Install(SkData* data, SkBitmap* destination); + static bool Install(SkStreamRewindable* stream, SkBitmap* destination, + SkDiscardableMemory::Factory* factory = NULL); private: - SkData* fData; + SkData* fData; + SkStreamRewindable* fStream; + SkImageInfo fInfo; + bool fHasInfo; + bool fDoCopyTo; }; #endif // SkDecodingImageGenerator_DEFINED diff --git a/images/SkImageDecoder.cpp b/images/SkImageDecoder.cpp index 1ba93652..32cf087e 100644 --- a/images/SkImageDecoder.cpp +++ b/images/SkImageDecoder.cpp @@ -14,10 +14,6 @@ #include "SkTemplates.h" #include "SkCanvas.h" -SK_DEFINE_INST_COUNT(SkImageDecoder::Peeker) -SK_DEFINE_INST_COUNT(SkImageDecoder::Chooser) -SK_DEFINE_INST_COUNT(SkImageDecoderFactory) - static SkBitmap::Config gDeviceConfig = SkBitmap::kNo_Config; SkBitmap::Config SkImageDecoder::GetDeviceConfig() @@ -143,19 +139,6 @@ bool SkImageDecoder::allocPixelRef(SkBitmap* bitmap, /////////////////////////////////////////////////////////////////////////////// -void SkImageDecoder::setPrefConfigTable(const SkBitmap::Config pref[6]) { - if (NULL == pref) { - fUsePrefTable = false; - } else { - fUsePrefTable = true; - fPrefTable.fPrefFor_8Index_NoAlpha_src = pref[0]; - fPrefTable.fPrefFor_8Index_YesAlpha_src = pref[1]; - fPrefTable.fPrefFor_8Gray_src = SkBitmap::kNo_Config; - fPrefTable.fPrefFor_8bpc_NoAlpha_src = pref[4]; - fPrefTable.fPrefFor_8bpc_YesAlpha_src = pref[5]; - } -} - void SkImageDecoder::setPrefConfigTable(const PrefConfigTable& prefTable) { fUsePrefTable = true; fPrefTable = prefTable; @@ -299,153 +282,6 @@ bool SkImageDecoder::DecodeMemory(const void* buffer, size_t size, SkBitmap* bm, return SkImageDecoder::DecodeStream(&stream, bm, pref, mode, format); } -/** - * Special allocator used by DecodeMemoryToTarget. Uses preallocated memory - * provided if the bm is 8888. Otherwise, uses a heap allocator. The same - * allocator will be used again for a copy to 8888, when the preallocated - * memory will be used. - */ -class TargetAllocator : public SkBitmap::HeapAllocator { - -public: - TargetAllocator(void* target) - : fTarget(target) {} - - virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) SK_OVERRIDE { - // If the config is not 8888, allocate a pixelref using the heap. - // fTarget will be used to store the final pixels when copied to - // 8888. - if (bm->config() != SkBitmap::kARGB_8888_Config) { - return INHERITED::allocPixelRef(bm, ct); - } - // In kARGB_8888_Config, there is no colortable. - SkASSERT(NULL == ct); - bm->setPixels(fTarget); - return true; - } - -private: - void* fTarget; - typedef SkBitmap::HeapAllocator INHERITED; -}; - -/** - * Helper function for DecodeMemoryToTarget. DecodeMemoryToTarget wants - * 8888, so set the config to it. All parameters must not be null. - * @param decoder Decoder appropriate for this stream. - * @param stream Rewound stream to the encoded data. - * @param bitmap On success, will have its bounds set to the bounds of the - * encoded data, and its config set to 8888. - * @return True if the bounds were decoded and the bitmap is 8888 or can be - * copied to 8888. - */ -static bool decode_bounds_to_8888(SkImageDecoder* decoder, SkStream* stream, - SkBitmap* bitmap) { - SkASSERT(decoder != NULL); - SkASSERT(stream != NULL); - SkASSERT(bitmap != NULL); - - if (!decoder->decode(stream, bitmap, SkImageDecoder::kDecodeBounds_Mode)) { - return false; - } - - if (bitmap->config() == SkBitmap::kARGB_8888_Config) { - return true; - } - - if (!bitmap->canCopyTo(SkBitmap::kARGB_8888_Config)) { - return false; - } - - bitmap->setConfig(SkBitmap::kARGB_8888_Config, bitmap->width(), bitmap->height()); - return true; -} - -/** - * Helper function for DecodeMemoryToTarget. Decodes the stream into bitmap, and if - * the bitmap is not 8888, then it is copied to 8888. Either way, the end result has - * its pixels stored in target. All parameters must not be null. - * @param decoder Decoder appropriate for this stream. - * @param stream Rewound stream to the encoded data. - * @param bitmap On success, will contain the decoded image, with its pixels stored - * at target. - * @param target Preallocated memory for storing pixels. - * @return bool Whether the decode (and copy, if necessary) succeeded. - */ -static bool decode_pixels_to_8888(SkImageDecoder* decoder, SkStream* stream, - SkBitmap* bitmap, void* target) { - SkASSERT(decoder != NULL); - SkASSERT(stream != NULL); - SkASSERT(bitmap != NULL); - SkASSERT(target != NULL); - - TargetAllocator allocator(target); - decoder->setAllocator(&allocator); - - bool success = decoder->decode(stream, bitmap, SkImageDecoder::kDecodePixels_Mode); - decoder->setAllocator(NULL); - - if (!success) { - return false; - } - - if (bitmap->config() == SkBitmap::kARGB_8888_Config) { - return true; - } - - SkBitmap bm8888; - if (!bitmap->copyTo(&bm8888, SkBitmap::kARGB_8888_Config, &allocator)) { - return false; - } - - bitmap->swap(bm8888); - return true; -} - -bool SkImageDecoder::DecodeMemoryToTarget(const void* buffer, size_t size, - SkImageInfo* info, - const SkBitmapFactory::Target* target) { - // FIXME: Just to get this working, implement in terms of existing - // ImageDecoder calls. - SkBitmap bm; - SkMemoryStream stream(buffer, size); - SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream)); - if (NULL == decoder.get()) { - return false; - } - - if (!decode_bounds_to_8888(decoder.get(), &stream, &bm)) { - return false; - } - - SkASSERT(bm.config() == SkBitmap::kARGB_8888_Config); - - // Now set info properly. - // Since Config is SkBitmap::kARGB_8888_Config, SkBitmapToImageInfo - // will always succeed. - if (info) { - SkAssertResult(SkBitmapToImageInfo(bm, info)); - } - - if (NULL == target) { - return true; - } - - if (target->fRowBytes != SkToU32(bm.rowBytes())) { - size_t minRB = SkBitmap::ComputeRowBytes(bm.config(), bm.width()); - if (target->fRowBytes < minRB) { - SkDEBUGFAIL("Desired row bytes is too small"); - return false; - } - bm.setConfig(bm.config(), bm.width(), bm.height(), target->fRowBytes); - } - - // SkMemoryStream.rewind() will always return true. - SkAssertResult(stream.rewind()); - return decode_pixels_to_8888(decoder.get(), &stream, &bm, target->fAddr); -} - - bool SkImageDecoder::DecodeStream(SkStreamRewindable* stream, SkBitmap* bm, SkBitmap::Config pref, Mode mode, Format* format) { diff --git a/images/SkMovie.cpp b/images/SkMovie.cpp index d2439eb6..9a2a71cd 100644 --- a/images/SkMovie.cpp +++ b/images/SkMovie.cpp @@ -9,8 +9,6 @@ #include "SkCanvas.h" #include "SkPaint.h" -SK_DEFINE_INST_COUNT(SkMovie) - // We should never see this in normal operation since our time values are // 0-based. So we use it as a sentinal. #define UNINITIALIZED_MSEC ((SkMSec)-1) diff --git a/lazy/SkBitmapFactory.cpp b/lazy/SkBitmapFactory.cpp deleted file mode 100644 index 1bbb6f0d..00000000 --- a/lazy/SkBitmapFactory.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkBitmapFactory.h" - -#include "SkBitmap.h" -#include "SkData.h" -#include "SkImageCache.h" -#include "SkImagePriv.h" -#include "SkLazyPixelRef.h" - -SK_DEFINE_INST_COUNT(SkBitmapFactory::CacheSelector) - -SkBitmapFactory::SkBitmapFactory(SkBitmapFactory::DecodeProc proc) - : fDecodeProc(proc) - , fImageCache(NULL) - , fCacheSelector(NULL) { - SkASSERT(fDecodeProc != NULL); -} - -SkBitmapFactory::~SkBitmapFactory() { - SkSafeUnref(fImageCache); - SkSafeUnref(fCacheSelector); -} - -void SkBitmapFactory::setImageCache(SkImageCache *cache) { - SkRefCnt_SafeAssign(fImageCache, cache); - if (cache != NULL) { - SkSafeUnref(fCacheSelector); - fCacheSelector = NULL; - } -} - -void SkBitmapFactory::setCacheSelector(CacheSelector* selector) { - SkRefCnt_SafeAssign(fCacheSelector, selector); - if (selector != NULL) { - SkSafeUnref(fImageCache); - fImageCache = NULL; - } -} - -bool SkBitmapFactory::installPixelRef(SkData* data, SkBitmap* dst) { - if (NULL == data || 0 == data->size() || dst == NULL) { - return false; - } - - SkImageInfo info; - if (!fDecodeProc(data->data(), data->size(), &info, NULL)) { - return false; - } - - SkBitmap::Config config = SkImageInfoToBitmapConfig(info); - - Target target; - // FIMXE: There will be a problem if this rowbytes is calculated differently from - // in SkLazyPixelRef. - target.fRowBytes = SkImageMinRowBytes(info); - dst->setConfig(config, info.fWidth, info.fHeight, target.fRowBytes, info.fAlphaType); - - // fImageCache and fCacheSelector are mutually exclusive. - SkASSERT(NULL == fImageCache || NULL == fCacheSelector); - - SkImageCache* cache = NULL == fCacheSelector ? fImageCache : fCacheSelector->selectCache(info); - - if (cache != NULL) { - // Now set a new LazyPixelRef on dst. - SkAutoTUnref<SkLazyPixelRef> lazyRef(SkNEW_ARGS(SkLazyPixelRef, - (data, fDecodeProc, cache))); - dst->setPixelRef(lazyRef); - return true; - } else { - dst->allocPixels(); - target.fAddr = dst->getPixels(); - return fDecodeProc(data->data(), data->size(), &info, &target); - } -} diff --git a/lazy/SkCachingPixelRef.cpp b/lazy/SkCachingPixelRef.cpp index d12b7cf8..667a9493 100644 --- a/lazy/SkCachingPixelRef.cpp +++ b/lazy/SkCachingPixelRef.cpp @@ -8,66 +8,71 @@ #include "SkCachingPixelRef.h" #include "SkScaledImageCache.h" -SkCachingPixelRef::SkCachingPixelRef() - : fErrorInDecoding(false) - , fScaledCacheId(NULL) { - memset(&fInfo, 0xFF, sizeof(fInfo)); -} -SkCachingPixelRef::~SkCachingPixelRef() { - SkASSERT(NULL == fScaledCacheId); - // Assert always unlock before unref. -} -bool SkCachingPixelRef::getInfo(SkImageInfo* info) { - SkASSERT(info != NULL); - if (fErrorInDecoding) { - return false; // Don't try again. - } - if (fInfo.fWidth < 0) { - SkImageInfo tmp; - if (!this->onDecodeInfo(&tmp)) { - fErrorInDecoding = true; - return false; - } - SkASSERT(tmp.fWidth >= 0); - fInfo = tmp; +bool SkCachingPixelRef::Install(SkImageGenerator* generator, + SkBitmap* dst) { + SkImageInfo info; + SkASSERT(generator != NULL); + SkASSERT(dst != NULL); + if ((NULL == generator) + || !(generator->getInfo(&info)) + || !dst->setConfig(info, 0)) { + SkDELETE(generator); + return false; } - *info = fInfo; + SkAutoTUnref<SkCachingPixelRef> ref(SkNEW_ARGS(SkCachingPixelRef, + (generator, + info, + dst->rowBytes()))); + dst->setPixelRef(ref); return true; } -bool SkCachingPixelRef::configure(SkBitmap* bitmap) { - SkASSERT(bitmap != NULL); - SkImageInfo info; - if (!this->getInfo(&info)) { - return false; - } - return bitmap->setConfig(info, 0); +SkCachingPixelRef::SkCachingPixelRef(SkImageGenerator* generator, + const SkImageInfo& info, + size_t rowBytes) + : fImageGenerator(generator) + , fErrorInDecoding(false) + , fScaledCacheId(NULL) + , fInfo(info) + , fRowBytes(rowBytes) { + SkASSERT(fImageGenerator != NULL); +} +SkCachingPixelRef::~SkCachingPixelRef() { + SkDELETE(fImageGenerator); + SkASSERT(NULL == fScaledCacheId); + // Assert always unlock before unref. } void* SkCachingPixelRef::onLockPixels(SkColorTable** colorTable) { (void)colorTable; - SkImageInfo info; - if (!this->getInfo(&info)) { - return NULL; + if (fErrorInDecoding) { + return NULL; // don't try again. } SkBitmap bitmap; - + SkASSERT(NULL == fScaledCacheId); fScaledCacheId = SkScaledImageCache::FindAndLock(this->getGenerationID(), - info.fWidth, - info.fHeight, + fInfo.fWidth, + fInfo.fHeight, &bitmap); if (NULL == fScaledCacheId) { // Cache has been purged, must re-decode. - if (!this->onDecodeInto(0, &bitmap)) { + if ((!bitmap.setConfig(fInfo, fRowBytes)) || !bitmap.allocPixels()) { + fErrorInDecoding = true; + return NULL; + } + SkAutoLockPixels autoLockPixels(bitmap); + if (!fImageGenerator->getPixels(fInfo, bitmap.getPixels(), fRowBytes)) { + fErrorInDecoding = true; return NULL; } fScaledCacheId = SkScaledImageCache::AddAndLock(this->getGenerationID(), - info.fWidth, - info.fHeight, + fInfo.fWidth, + fInfo.fHeight, bitmap); SkASSERT(fScaledCacheId != NULL); } + // Now bitmap should contain a concrete PixelRef of the decoded // image. SkAutoLockPixels autoLockPixels(bitmap); @@ -91,21 +96,3 @@ void SkCachingPixelRef::onUnlockPixels() { fScaledCacheId = NULL; } } - -bool SkCachingPixelRef::onDecodeInto(int pow2, SkBitmap* bitmap) { - SkASSERT(bitmap != NULL); - SkBitmap tmp; - SkImageInfo info; - // TODO(halcanary) - Enable SkCachingPixelRef to use a custom - // allocator. `tmp.allocPixels(fAllocator, NULL)` - if (!(this->configure(&tmp) && tmp.allocPixels())) { - return false; - } - SkAssertResult(this->getInfo(&info)); // since configure() succeeded. - if (!this->onDecodePixels(info, tmp.getPixels(), tmp.rowBytes())) { - fErrorInDecoding = true; - return false; - } - *bitmap = tmp; - return true; -} diff --git a/lazy/SkCachingPixelRef.h b/lazy/SkCachingPixelRef.h index db968dfc..4a0387dd 100644 --- a/lazy/SkCachingPixelRef.h +++ b/lazy/SkCachingPixelRef.h @@ -8,7 +8,8 @@ #ifndef SkCachingPixelRef_DEFINED #define SkCachingPixelRef_DEFINED -#include "SkImage.h" +#include "SkImageInfo.h" +#include "SkImageGenerator.h" #include "SkPixelRef.h" class SkColorTable; @@ -20,60 +21,49 @@ class SkColorTable; * or be destroyed before the next lock. If so, onLockPixels will * attempt to re-decode. * - * Decoding is handled by the pure-virtual functions onDecodeInfo() - * and onDecodePixels(). Subclasses of this class need only provide - * those two functions. + * Decoding is handled by the SkImageGenerator */ class SkCachingPixelRef : public SkPixelRef { public: - SkCachingPixelRef(); - virtual ~SkCachingPixelRef(); + /** + * Takes ownership of SkImageGenerator. If this method fails for + * whatever reason, it will return false and immediatetely delete + * the generator. If it succeeds, it will modify destination + * bitmap. + * + * If Install fails or when the SkCachingPixelRef that is + * installed into destination is destroyed, it will call + * SkDELETE() on the generator. Therefore, generator should be + * allocated with SkNEW() or SkNEW_ARGS(). + */ + static bool Install(SkImageGenerator* gen, SkBitmap* dst); protected: + virtual ~SkCachingPixelRef(); virtual void* onLockPixels(SkColorTable** colorTable) SK_OVERRIDE; virtual void onUnlockPixels() SK_OVERRIDE; virtual bool onLockPixelsAreWritable() const SK_OVERRIDE { return false; } - virtual bool onImplementsDecodeInto() SK_OVERRIDE { return true; } - virtual bool onDecodeInto(int pow2, SkBitmap*) SK_OVERRIDE; - - /** - * Configure the supplied bitmap for this pixelRef, based on - * information provided by onDecodeInfo(). Does not set the - * bitmap's pixelRef. */ - bool configure(SkBitmap* bitmap); - - /** - * Cache info from onDecodeInfo(). Returns false on failure. - */ - bool getInfo(SkImageInfo* info); - /** - * Return some information about the pixels, allowing this class - * to allocate pixels. @return false if anything goes wrong. - */ - virtual bool onDecodeInfo(SkImageInfo* info) = 0; - /** - * Decode into the given pixels, a block of memory of size - * (info.fHeight - 1) * rowBytes + (info.fWidth * bytesPerPixel) - * - * @param info Should be identical to the info returned by - * onDecodeInfo so that the implementation can confirm - * that the caller knows what it is asking for (config, - * size). Thiscontract also allows the caller to specify - * different output-configs, which the implementation can - * decide to support or not. - * - * @return false if anything goes wrong. - */ - virtual bool onDecodePixels(const SkImageInfo& info, - void* pixels, - size_t rowBytes) = 0; + virtual SkData* onRefEncodedData() SK_OVERRIDE { + return fImageGenerator->refEncodedData(); + } + // No need to flatten this object. When flattening an SkBitmap, + // SkOrderedWriteBuffer will check the encoded data and write that + // instead. + // Future implementations of SkFlattenableWriteBuffer will need to + // special case for onRefEncodedData as well. + SK_DECLARE_UNFLATTENABLE_OBJECT() private: - bool fErrorInDecoding; - void* fScaledCacheId; - SkImageInfo fInfo; + SkImageGenerator* const fImageGenerator; + bool fErrorInDecoding; + void* fScaledCacheId; + const SkImageInfo fInfo; + const size_t fRowBytes; + SkCachingPixelRef(SkImageGenerator* imageGenerator, + const SkImageInfo& info, + size_t rowBytes); typedef SkPixelRef INHERITED; }; diff --git a/lazy/SkDiscardableMemoryPool.cpp b/lazy/SkDiscardableMemoryPool.cpp new file mode 100644 index 00000000..a1b2438a --- /dev/null +++ b/lazy/SkDiscardableMemoryPool.cpp @@ -0,0 +1,203 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkDiscardableMemoryPool.h" +#include "SkOnce.h" + +// Note: +// A PoolDiscardableMemory is memory that is counted in a pool. +// A DiscardableMemoryPool is a pool of PoolDiscardableMemorys. + +/** + * A SkPoolDiscardableMemory is a SkDiscardableMemory that relies on + * a SkDiscardableMemoryPool object to manage the memory. + */ +class SkPoolDiscardableMemory : public SkDiscardableMemory { +public: + SkPoolDiscardableMemory(SkDiscardableMemoryPool* pool, + void* pointer, size_t bytes); + virtual ~SkPoolDiscardableMemory(); + virtual bool lock() SK_OVERRIDE; + virtual void* data() SK_OVERRIDE; + virtual void unlock() SK_OVERRIDE; + friend class SkDiscardableMemoryPool; +private: + SK_DECLARE_INTERNAL_LLIST_INTERFACE(SkPoolDiscardableMemory); + SkDiscardableMemoryPool* const fPool; + bool fLocked; + void* fPointer; + const size_t fBytes; +}; + +SkPoolDiscardableMemory::SkPoolDiscardableMemory(SkDiscardableMemoryPool* pool, + void* pointer, + size_t bytes) + : fPool(pool) + , fLocked(true) + , fPointer(pointer) + , fBytes(bytes) { + SkASSERT(fPool != NULL); + SkASSERT(fPointer != NULL); + SkASSERT(fBytes > 0); + fPool->ref(); +} + +SkPoolDiscardableMemory::~SkPoolDiscardableMemory() { + fPool->free(this); + fPool->unref(); +} + +bool SkPoolDiscardableMemory::lock() { + return fPool->lock(this); +} + +void* SkPoolDiscardableMemory::data() { + return fLocked ? fPointer : NULL; +} + +void SkPoolDiscardableMemory::unlock() { + fPool->unlock(this); +} + +//////////////////////////////////////////////////////////////////////////////// + +SkDiscardableMemoryPool::SkDiscardableMemoryPool(size_t budget, + SkBaseMutex* mutex) + : fMutex(mutex) + , fBudget(budget) + , fUsed(0) { + #if LAZY_CACHE_STATS + fCacheHits = 0; + fCacheMisses = 0; + #endif // LAZY_CACHE_STATS +} +SkDiscardableMemoryPool::~SkDiscardableMemoryPool() { + // SkPoolDiscardableMemory objects that belong to this pool are + // always deleted before deleting this pool since each one has a + // ref to the pool. + SkASSERT(fList.isEmpty()); +} + +void SkDiscardableMemoryPool::dumpDownTo(size_t budget) { + // assert((NULL = fMutex) || fMutex->isLocked()); + // TODO(halcanary) implement bool fMutex::isLocked(). + // WARNING: only call this function after aquiring lock. + if (fUsed <= budget) { + return; + } + typedef SkTInternalLList<SkPoolDiscardableMemory>::Iter Iter; + Iter iter; + SkPoolDiscardableMemory* cur = iter.init(fList, Iter::kTail_IterStart); + while ((fUsed > budget) && (NULL != cur)) { + if (!cur->fLocked) { + SkPoolDiscardableMemory* dm = cur; + SkASSERT(dm->fPointer != NULL); + sk_free(dm->fPointer); + dm->fPointer = NULL; + SkASSERT(fUsed >= dm->fBytes); + fUsed -= dm->fBytes; + cur = iter.prev(); + // Purged DMs are taken out of the list. This saves times + // looking them up. Purged DMs are NOT deleted. + fList.remove(dm); + } else { + cur = iter.prev(); + } + } +} + +SkDiscardableMemory* SkDiscardableMemoryPool::create(size_t bytes) { + void* addr = sk_malloc_flags(bytes, 0); + if (NULL == addr) { + return NULL; + } + SkPoolDiscardableMemory* dm = SkNEW_ARGS(SkPoolDiscardableMemory, + (this, addr, bytes)); + SkAutoMutexAcquire autoMutexAcquire(fMutex); + fList.addToHead(dm); + fUsed += bytes; + this->dumpDownTo(fBudget); + return dm; +} + +void SkDiscardableMemoryPool::free(SkPoolDiscardableMemory* dm) { + // This is called by dm's destructor. + if (dm->fPointer != NULL) { + SkAutoMutexAcquire autoMutexAcquire(fMutex); + sk_free(dm->fPointer); + dm->fPointer = NULL; + SkASSERT(fUsed >= dm->fBytes); + fUsed -= dm->fBytes; + fList.remove(dm); + } else { + SkASSERT(!fList.isInList(dm)); + } +} + +bool SkDiscardableMemoryPool::lock(SkPoolDiscardableMemory* dm) { + SkASSERT(dm != NULL); + if (NULL == dm->fPointer) { + #if LAZY_CACHE_STATS + SkAutoMutexAcquire autoMutexAcquire(fMutex); + ++fCacheMisses; + #endif // LAZY_CACHE_STATS + return false; + } + SkAutoMutexAcquire autoMutexAcquire(fMutex); + if (NULL == dm->fPointer) { + // May have been purged while waiting for lock. + #if LAZY_CACHE_STATS + ++fCacheMisses; + #endif // LAZY_CACHE_STATS + return false; + } + dm->fLocked = true; + fList.remove(dm); + fList.addToHead(dm); + #if LAZY_CACHE_STATS + ++fCacheHits; + #endif // LAZY_CACHE_STATS + return true; +} + +void SkDiscardableMemoryPool::unlock(SkPoolDiscardableMemory* dm) { + SkASSERT(dm != NULL); + SkAutoMutexAcquire autoMutexAcquire(fMutex); + dm->fLocked = false; + this->dumpDownTo(fBudget); +} + +size_t SkDiscardableMemoryPool::getRAMUsed() { + return fUsed; +} +void SkDiscardableMemoryPool::setRAMBudget(size_t budget) { + SkAutoMutexAcquire autoMutexAcquire(fMutex); + fBudget = budget; + this->dumpDownTo(fBudget); +} +void SkDiscardableMemoryPool::dumpPool() { + SkAutoMutexAcquire autoMutexAcquire(fMutex); + this->dumpDownTo(0); +} + +//////////////////////////////////////////////////////////////////////////////// +SK_DECLARE_STATIC_MUTEX(gMutex); +static void create_pool(SkDiscardableMemoryPool** pool) { + SkASSERT(NULL == *pool); + *pool = SkNEW_ARGS(SkDiscardableMemoryPool, + (SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE, + &gMutex)); +} +SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() { + static SkDiscardableMemoryPool* gPool(NULL); + SK_DECLARE_STATIC_ONCE(create_pool_once); + SkOnce(&create_pool_once, create_pool, &gPool); + SkASSERT(NULL != gPool); + return gPool; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/lazy/SkDiscardableMemoryPool.h b/lazy/SkDiscardableMemoryPool.h new file mode 100644 index 00000000..e9f3b04f --- /dev/null +++ b/lazy/SkDiscardableMemoryPool.h @@ -0,0 +1,70 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkDiscardableMemoryPool_DEFINED +#define SkDiscardableMemoryPool_DEFINED + +#include "SkDiscardableMemory.h" +#include "SkTInternalLList.h" +#include "SkThread.h" + +#ifdef SK_DEBUG + #define LAZY_CACHE_STATS 1 +#elif !defined(LAZY_CACHE_STATS) + #define LAZY_CACHE_STATS 0 +#endif + +/** + * This non-global pool can be used for unit tests to verify that the + * pool works. + */ +class SkPoolDiscardableMemory; +class SkDiscardableMemoryPool : public SkDiscardableMemory::Factory { +public: + /** + * Without mutex, will be not be thread safe. + */ + SkDiscardableMemoryPool(size_t budget, SkBaseMutex* mutex = NULL); + ~SkDiscardableMemoryPool(); + SkDiscardableMemory* create(size_t bytes) SK_OVERRIDE; + size_t getRAMUsed(); + void setRAMBudget(size_t budget); + /** purges all unlocked DMs */ + void dumpPool(); + friend class SkPoolDiscardableMemory; + #if LAZY_CACHE_STATS + int fCacheHits; + int fCacheMisses; + #endif // LAZY_CACHE_STATS + +private: + SkBaseMutex* fMutex; + size_t fBudget; + size_t fUsed; + SkTInternalLList<SkPoolDiscardableMemory> fList; + /** Function called to free memory if needed */ + void dumpDownTo(size_t budget); + /** called by SkDiscardableMemoryPool upon destruction */ + void free(SkPoolDiscardableMemory* dm); + /** called by SkDiscardableMemoryPool::lock() */ + bool lock(SkPoolDiscardableMemory* dm); + /** called by SkDiscardableMemoryPool::unlock() */ + void unlock(SkPoolDiscardableMemory* dm); + typedef SkDiscardableMemory::Factory INHERITED; +}; + +/** + * Returns (and creates if needed) a threadsafe global + * SkDiscardableMemoryPool. + */ +SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool(); + +#if !defined(SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE) +#define SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE (128 * 1024 * 1024) +#endif + +#endif // SkDiscardableMemoryPool_DEFINED diff --git a/lazy/SkDiscardablePixelRef.cpp b/lazy/SkDiscardablePixelRef.cpp index b6e1b100..6a9507c8 100644 --- a/lazy/SkDiscardablePixelRef.cpp +++ b/lazy/SkDiscardablePixelRef.cpp @@ -7,12 +7,15 @@ #include "SkDiscardablePixelRef.h" #include "SkDiscardableMemory.h" +#include "SkImageGenerator.h" SkDiscardablePixelRef::SkDiscardablePixelRef(SkImageGenerator* generator, const SkImageInfo& info, size_t size, - size_t rowBytes) + size_t rowBytes, + SkDiscardableMemory::Factory* fact) : fGenerator(generator) + , fDMFactory(fact) , fInfo(info) , fSize(size) , fRowBytes(rowBytes) @@ -20,9 +23,15 @@ SkDiscardablePixelRef::SkDiscardablePixelRef(SkImageGenerator* generator, SkASSERT(fGenerator != NULL); SkASSERT(fSize > 0); SkASSERT(fRowBytes > 0); + // The SkImageGenerator contract requires fGenerator to always + // decode the same image on each call to getPixels(). + this->setImmutable(); + SkSafeRef(fDMFactory); } SkDiscardablePixelRef::~SkDiscardablePixelRef() { + SkDELETE(fDiscardableMemory); + SkSafeUnref(fDMFactory); SkDELETE(fGenerator); } @@ -31,9 +40,14 @@ void* SkDiscardablePixelRef::onLockPixels(SkColorTable**) { if (fDiscardableMemory->lock()) { return fDiscardableMemory->data(); } + SkDELETE(fDiscardableMemory); fDiscardableMemory = NULL; } - fDiscardableMemory = SkDiscardableMemory::Create(fSize); + if (fDMFactory != NULL) { + fDiscardableMemory = fDMFactory->create(fSize); + } else { + fDiscardableMemory = SkDiscardableMemory::Create(fSize); + } if (NULL == fDiscardableMemory) { return NULL; // Memory allocation failed. } @@ -49,8 +63,9 @@ void SkDiscardablePixelRef::onUnlockPixels() { } } -bool SkDiscardablePixelRef::Install(SkImageGenerator* generator, - SkBitmap* dst) { +bool SkInstallDiscardablePixelRef(SkImageGenerator* generator, + SkBitmap* dst, + SkDiscardableMemory::Factory* factory) { SkImageInfo info; SkASSERT(generator != NULL); if ((NULL == generator) @@ -59,10 +74,16 @@ bool SkDiscardablePixelRef::Install(SkImageGenerator* generator, SkDELETE(generator); return false; } + SkASSERT(dst->config() != SkBitmap::kNo_Config); + if (dst->empty()) { // Use a normal pixelref. + SkDELETE(generator); // Do not need this anymore. + return dst->allocPixels(NULL, NULL); + } SkAutoTUnref<SkDiscardablePixelRef> ref(SkNEW_ARGS(SkDiscardablePixelRef, (generator, info, dst->getSize(), - dst->rowBytes()))); + dst->rowBytes(), + factory))); dst->setPixelRef(ref); return true; } diff --git a/lazy/SkDiscardablePixelRef.h b/lazy/SkDiscardablePixelRef.h index bbe19b8b..44c6df96 100644 --- a/lazy/SkDiscardablePixelRef.h +++ b/lazy/SkDiscardablePixelRef.h @@ -8,31 +8,13 @@ #ifndef SkDiscardablePixelRef_DEFINED #define SkDiscardablePixelRef_DEFINED -#include "SkPixelRef.h" +#include "SkDiscardableMemory.h" #include "SkImageGenerator.h" #include "SkImageInfo.h" - -class SkDiscardableMemory; - -/** - * An interface that allows a purgable PixelRef to re-decode an image. - */ +#include "SkPixelRef.h" class SkDiscardablePixelRef : public SkPixelRef { public: - /** - * Takes ownership of SkImageGenerator. If this method fails for - * whatever reason, it will return false and immediatetely delete - * the generator. If it succeeds, it will modify destination - * bitmap. - * - * If Install fails or when the SkDiscardablePixelRef that is - * installed into destination is destroyed, it will call - * SkDELETE() on the generator. Therefore, generator should be - * allocated with SkNEW() or SkNEW_ARGS(). - */ - static bool Install(SkImageGenerator* generator, SkBitmap* destination); - SK_DECLARE_UNFLATTENABLE_OBJECT() protected: @@ -47,6 +29,7 @@ protected: private: SkImageGenerator* const fGenerator; + SkDiscardableMemory::Factory* const fDMFactory; const SkImageInfo fInfo; const size_t fSize; // size of memory to be allocated const size_t fRowBytes; @@ -59,6 +42,11 @@ private: SkDiscardablePixelRef(SkImageGenerator* generator, const SkImageInfo& info, size_t size, - size_t rowBytes); + size_t rowBytes, + SkDiscardableMemory::Factory* factory); + friend bool SkInstallDiscardablePixelRef(SkImageGenerator*, + SkBitmap*, + SkDiscardableMemory::Factory*); + typedef SkPixelRef INHERITED; }; #endif // SkDiscardablePixelRef_DEFINED diff --git a/lazy/SkLazyCachingPixelRef.cpp b/lazy/SkLazyCachingPixelRef.cpp deleted file mode 100644 index 730b5f7d..00000000 --- a/lazy/SkLazyCachingPixelRef.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "Sk64.h" -#include "SkColorTable.h" -#include "SkData.h" -#include "SkImageDecoder.h" -#include "SkImagePriv.h" -#include "SkLazyCachingPixelRef.h" -#include "SkPostConfig.h" - -SkLazyCachingPixelRef::SkLazyCachingPixelRef(SkData* data, - SkBitmapFactory::DecodeProc proc) - : fDecodeProc(proc) { - if (NULL == data) { - fData = SkData::NewEmpty(); - } else { - fData = data; - fData->ref(); - } - if (NULL == fDecodeProc) { // use a reasonable default. - fDecodeProc = SkImageDecoder::DecodeMemoryToTarget; - } - this->setImmutable(); -} - -SkLazyCachingPixelRef::~SkLazyCachingPixelRef() { - SkASSERT(fData != NULL); - fData->unref(); -} - -bool SkLazyCachingPixelRef::onDecodeInfo(SkImageInfo* info) { - SkASSERT(info); - return fDecodeProc(fData->data(), fData->size(), info, NULL); -} - -bool SkLazyCachingPixelRef::onDecodePixels(const SkImageInfo& passedInfo, - void* pixels, size_t rowBytes) { - SkASSERT(pixels); - SkImageInfo info; - if (!this->getInfo(&info)) { - return false; - } - if (passedInfo != info) { - return false; // This implementation can not handle this case. - } - SkBitmapFactory::Target target = {pixels, rowBytes}; - return fDecodeProc(fData->data(), fData->size(), &info, &target); -} - -bool SkLazyCachingPixelRef::Install(SkBitmapFactory::DecodeProc proc, - SkData* data, - SkBitmap* destination) { - SkAutoTUnref<SkLazyCachingPixelRef> ref( - SkNEW_ARGS(SkLazyCachingPixelRef, (data, proc))); - return ref->configure(destination) && destination->setPixelRef(ref); -} diff --git a/lazy/SkLazyCachingPixelRef.h b/lazy/SkLazyCachingPixelRef.h deleted file mode 100644 index a9d2dad9..00000000 --- a/lazy/SkLazyCachingPixelRef.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkLazyCachingPixelRef_DEFINED -#define SkLazyCachingPixelRef_DEFINED - -#include "SkBitmapFactory.h" -#include "SkCachingPixelRef.h" - -class SkData; - -/** - * PixelRef which defers decoding until SkBitmap::lockPixels() is - * called. Makes use of a supplied decode procedure. Will decode at - * the procedure's preferred size. - */ -class SkLazyCachingPixelRef : public SkCachingPixelRef { -public: - /** - * @param data Encoded data representing the pixels. NULL is - * equivalent to an empty data, and will be passed to - * DecodeProc with length zero. - * - * @param procedure Called to decode the pixels when - * needed. If NULL, use SkImageDecoder::DecodeMemoryToTarget. - */ - SkLazyCachingPixelRef(SkData* data, - SkBitmapFactory::DecodeProc procedure); - - virtual ~SkLazyCachingPixelRef(); - - virtual SkData* onRefEncodedData() SK_OVERRIDE { return SkSafeRef(fData); } - - /** - * A simplified version of SkBitmapFactory. Installs a new - * SkLazyCachingPixelRef into the provided bitmap. Will - * immediately call onDecodeInfo() to configure the bitmap, but - * will defer decoding until the first time the bitmap's pixels - * are locked. - * - * @param data Encoded data representing the pixels. NULL is - * equivalent to an empty data, and will be passed to - * DecodeProc with length zero. - * - * @param procedure Called to decode the pixels when - * needed. If NULL, use SkImageDecoder::DecodeMemoryToTarget. - * - * @param destination Bitmap that will be modified on success. - * - * @returns true on success. - */ - static bool Install(SkBitmapFactory::DecodeProc procedure, - SkData* data, - SkBitmap* destination); - - // No need to flatten this object. When flattening an SkBitmap, - // SkOrderedWriteBuffer will check the encoded data and write that - // instead. - // Future implementations of SkFlattenableWriteBuffer will need to - // special case for onRefEncodedData as well. - SK_DECLARE_UNFLATTENABLE_OBJECT() - -protected: - /** - * Return some information about the pixels, allowing this class - * to allocate pixels. @return false if anything goes wrong. - * - * This implementation calls SkBitmapFactory::DecodeProc with a - * NULL target. - */ - virtual bool onDecodeInfo(SkImageInfo* info) SK_OVERRIDE; - /** - * Decode into the given pixels, a block of memory of size - * (info.fHeight * rowBytes) bytes. - * - * @param info Should be identical to the info returned by - * onDecodeInfo so that the implementation can confirm - * that the caller knows what its asking for (config, - * size). - * - * @return false if anything goes wrong. - */ - virtual bool onDecodePixels(const SkImageInfo& info, - void* pixels, - size_t rowBytes) SK_OVERRIDE; - -private: - SkData* fData; - SkBitmapFactory::DecodeProc fDecodeProc; - - typedef SkCachingPixelRef INHERITED; -}; - -#endif // SkLazyCachingPixelRef_DEFINED diff --git a/lazy/SkLazyPixelRef.cpp b/lazy/SkLazyPixelRef.cpp deleted file mode 100644 index c2ca041b..00000000 --- a/lazy/SkLazyPixelRef.cpp +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "Sk64.h" -#include "SkLazyPixelRef.h" -#include "SkColorTable.h" -#include "SkData.h" -#include "SkImageCache.h" -#include "SkImagePriv.h" -#include "SkScaledImageCache.h" - -#if LAZY_CACHE_STATS -#include "SkThread.h" - -int32_t SkLazyPixelRef::gCacheHits; -int32_t SkLazyPixelRef::gCacheMisses; -#endif - -SkLazyPixelRef::SkLazyPixelRef(SkData* data, SkBitmapFactory::DecodeProc proc, SkImageCache* cache) - // Pass NULL for the Mutex so that the default (ring buffer) will be used. - : INHERITED(NULL) - , fErrorInDecoding(false) - , fDecodeProc(proc) - , fImageCache(cache) - , fRowBytes(0) { - SkASSERT(fDecodeProc != NULL); - if (NULL == data) { - fData = SkData::NewEmpty(); - fErrorInDecoding = true; - } else { - fData = data; - fData->ref(); - fErrorInDecoding = data->size() == 0; - } - if (fImageCache != NULL) { - fImageCache->ref(); - fCacheId = SkImageCache::UNINITIALIZED_ID; - } else { - fScaledCacheId = NULL; - } - - // mark as uninitialized -- all fields are -1 - memset(&fLazilyCachedInfo, 0xFF, sizeof(fLazilyCachedInfo)); - - // Since this pixel ref bases its data on encoded data, it should never change. - this->setImmutable(); -} - -SkLazyPixelRef::~SkLazyPixelRef() { - SkASSERT(fData != NULL); - fData->unref(); - if (NULL == fImageCache) { - if (fScaledCacheId != NULL) { - SkScaledImageCache::Unlock(fScaledCacheId); - // TODO(halcanary): SkScaledImageCache needs a - // throwAwayCache(id) method. - } - return; - } - SkASSERT(fImageCache); - if (fCacheId != SkImageCache::UNINITIALIZED_ID) { - fImageCache->throwAwayCache(fCacheId); - } - fImageCache->unref(); -} - -static size_t ComputeMinRowBytesAndSize(const SkImageInfo& info, size_t* rowBytes) { - *rowBytes = SkImageMinRowBytes(info); - - Sk64 safeSize; - safeSize.setZero(); - if (info.fHeight > 0) { - safeSize.setMul(info.fHeight, SkToS32(*rowBytes)); - } - SkASSERT(!safeSize.isNeg()); - return safeSize.is32() ? safeSize.get32() : 0; -} - -const SkImageInfo* SkLazyPixelRef::getCachedInfo() { - if (fLazilyCachedInfo.fWidth < 0) { - SkImageInfo info; - fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, NULL); - if (fErrorInDecoding) { - return NULL; - } - fLazilyCachedInfo = info; - } - return &fLazilyCachedInfo; -} - -/** - Returns bitmap->getPixels() on success; NULL on failure */ -static void* decode_into_bitmap(SkImageInfo* info, - SkBitmapFactory::DecodeProc decodeProc, - size_t* rowBytes, - SkData* data, - SkBitmap* bm) { - SkASSERT(info && decodeProc && rowBytes && data && bm); - if (!(bm->setConfig(SkImageInfoToBitmapConfig(*info), info->fWidth, - info->fHeight, *rowBytes, info->fAlphaType) - && bm->allocPixels(NULL, NULL))) { - // Use the default allocator. It may be necessary for the - // SkLazyPixelRef to have a allocator field which is passed - // into allocPixels(). - return NULL; - } - SkBitmapFactory::Target target; - target.fAddr = bm->getPixels(); - target.fRowBytes = bm->rowBytes(); - *rowBytes = target.fRowBytes; - if (!decodeProc(data->data(), data->size(), info, &target)) { - return NULL; - } - return target.fAddr; -} - -void* SkLazyPixelRef::lockScaledImageCachePixels() { - SkASSERT(!fErrorInDecoding); - SkASSERT(NULL == fImageCache); - SkBitmap bitmap; - const SkImageInfo* info = this->getCachedInfo(); - if (info == NULL) { - return NULL; - } - // If this is the first time though, this is guaranteed to fail. - // Maybe we should have a flag that says "don't even bother looking" - fScaledCacheId = SkScaledImageCache::FindAndLock(this->getGenerationID(), - info->fWidth, - info->fHeight, - &bitmap); - if (fScaledCacheId != NULL) { - SkAutoLockPixels autoLockPixels(bitmap); - void* pixels = bitmap.getPixels(); - SkASSERT(NULL != pixels); - // At this point, the autoLockPixels will unlockPixels() - // to remove bitmap's lock on the pixels. We will then - // destroy bitmap. The *only* guarantee that this pointer - // remains valid is the guarantee made by - // SkScaledImageCache that it will not destroy the *other* - // bitmap (SkScaledImageCache::Rec.fBitmap) that holds a - // reference to the concrete PixelRef while this record is - // locked. - return pixels; - } else { - // Cache has been purged, must re-decode. - void* pixels = decode_into_bitmap(const_cast<SkImageInfo*>(info), - fDecodeProc, &fRowBytes, fData, - &bitmap); - if (NULL == pixels) { - fErrorInDecoding = true; - return NULL; - } - fScaledCacheId = SkScaledImageCache::AddAndLock(this->getGenerationID(), - info->fWidth, - info->fHeight, - bitmap); - SkASSERT(fScaledCacheId != NULL); - return pixels; - } -} - -void* SkLazyPixelRef::onLockPixels(SkColorTable**) { - if (fErrorInDecoding) { - return NULL; - } - if (NULL == fImageCache) { - return this->lockScaledImageCachePixels(); - } else { - return this->lockImageCachePixels(); - } -} - -void* SkLazyPixelRef::lockImageCachePixels() { - SkASSERT(fImageCache != NULL); - SkASSERT(!fErrorInDecoding); - SkBitmapFactory::Target target; - // Check to see if the pixels still exist in the cache. - if (SkImageCache::UNINITIALIZED_ID == fCacheId) { - target.fAddr = NULL; - } else { - SkImageCache::DataStatus status; - target.fAddr = fImageCache->pinCache(fCacheId, &status); - if (target.fAddr == NULL) { - fCacheId = SkImageCache::UNINITIALIZED_ID; - } else { - if (SkImageCache::kRetained_DataStatus == status) { -#if LAZY_CACHE_STATS - sk_atomic_inc(&gCacheHits); -#endif - return target.fAddr; - } - SkASSERT(SkImageCache::kUninitialized_DataStatus == status); - } - // Cache miss. Either pinCache returned NULL or it returned a memory address without the old - // data -#if LAZY_CACHE_STATS - sk_atomic_inc(&gCacheMisses); -#endif - } - - SkASSERT(fData != NULL && fData->size() > 0); - if (NULL == target.fAddr) { - const SkImageInfo* info = this->getCachedInfo(); - if (NULL == info) { - SkASSERT(SkImageCache::UNINITIALIZED_ID == fCacheId); - return NULL; - } - size_t bytes = ComputeMinRowBytesAndSize(*info, &target.fRowBytes); - target.fAddr = fImageCache->allocAndPinCache(bytes, &fCacheId); - if (NULL == target.fAddr) { - // Space could not be allocated. - // Just like the last assert, fCacheId must be UNINITIALIZED_ID. - SkASSERT(SkImageCache::UNINITIALIZED_ID == fCacheId); - return NULL; - } - } else { - // pinCache returned purged memory to which target.fAddr already points. Set - // target.fRowBytes properly. - target.fRowBytes = fRowBytes; - // Assume that the size is correct, since it was determined by this same function - // previously. - } - SkASSERT(target.fAddr != NULL); - SkASSERT(SkImageCache::UNINITIALIZED_ID != fCacheId); - fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), NULL, &target); - if (fErrorInDecoding) { - fImageCache->throwAwayCache(fCacheId); - fCacheId = SkImageCache::UNINITIALIZED_ID; - return NULL; - } - // Upon success, store fRowBytes so it can be used in case pinCache later returns purged memory. - fRowBytes = target.fRowBytes; - return target.fAddr; -} - -void SkLazyPixelRef::onUnlockPixels() { - if (fErrorInDecoding) { - return; - } - if (NULL == fImageCache) { - // onUnlockPixels() should never be called a second time from - // PixelRef::Unlock() without calling onLockPixels() first. - SkASSERT(NULL != fScaledCacheId); - if (NULL != fScaledCacheId) { - SkScaledImageCache::Unlock(fScaledCacheId); - fScaledCacheId = NULL; - } - } else { // use fImageCache - SkASSERT(SkImageCache::UNINITIALIZED_ID != fCacheId); - if (SkImageCache::UNINITIALIZED_ID != fCacheId) { - fImageCache->releaseCache(fCacheId); - } - } -} - -SkData* SkLazyPixelRef::onRefEncodedData() { - fData->ref(); - return fData; -} - -static bool init_from_info(SkBitmap* bm, const SkImageInfo& info, - size_t rowBytes) { - SkBitmap::Config config = SkImageInfoToBitmapConfig(info); - if (SkBitmap::kNo_Config == config) { - return false; - } - - return bm->setConfig(config, info.fWidth, info.fHeight, rowBytes, info.fAlphaType) - && - bm->allocPixels(); -} - -bool SkLazyPixelRef::onImplementsDecodeInto() { - return true; -} - -bool SkLazyPixelRef::onDecodeInto(int pow2, SkBitmap* bitmap) { - SkASSERT(fData != NULL && fData->size() > 0); - if (fErrorInDecoding) { - return false; - } - - SkImageInfo info; - // Determine the size of the image in order to determine how much memory to allocate. - // FIXME: As an optimization, only do this part once. - fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, NULL); - if (fErrorInDecoding) { - return false; - } - - SkBitmapFactory::Target target; - (void)ComputeMinRowBytesAndSize(info, &target.fRowBytes); - - SkBitmap tmp; - if (!init_from_info(&tmp, info, target.fRowBytes)) { - return false; - } - - target.fAddr = tmp.getPixels(); - fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, &target); - if (fErrorInDecoding) { - return false; - } - - *bitmap = tmp; - return true; -} diff --git a/lazy/SkLazyPixelRef.h b/lazy/SkLazyPixelRef.h deleted file mode 100644 index f8a16d16..00000000 --- a/lazy/SkLazyPixelRef.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkLazyPixelRef_DEFINED -#define SkLazyPixelRef_DEFINED - -#include "SkBitmapFactory.h" -#include "SkImage.h" -#include "SkImageCache.h" -#include "SkPixelRef.h" -#include "SkFlattenable.h" -#include "SkScaledImageCache.h" - -class SkColorTable; -class SkData; -class SkImageCache; - -#ifdef SK_DEBUG - #define LAZY_CACHE_STATS 1 -#elif !defined(LAZY_CACHE_STATS) - #define LAZY_CACHE_STATS 0 -#endif - -/** - * PixelRef which defers decoding until SkBitmap::lockPixels() is called. - */ -class SkLazyPixelRef : public SkPixelRef { - -public: - /** - * Create a new SkLazyPixelRef. - * @param SkData Encoded data representing the pixels. - * @param DecodeProc Called to decode the pixels when needed. Must be non-NULL. - * @param SkImageCache Object that handles allocating and freeing - * the pixel memory, as needed. If NULL, use the global - * SkScaledImageCache. - */ - SkLazyPixelRef(SkData*, SkBitmapFactory::DecodeProc, SkImageCache*); - - virtual ~SkLazyPixelRef(); - -#ifdef SK_DEBUG - intptr_t getCacheId() const { return fCacheId; } -#endif - -#if LAZY_CACHE_STATS - static int32_t GetCacheHits() { return gCacheHits; } - static int32_t GetCacheMisses() { return gCacheMisses; } - static void ResetCacheStats() { gCacheHits = gCacheMisses = 0; } -#endif - - // No need to flatten this object. When flattening an SkBitmap, SkOrderedWriteBuffer will check - // the encoded data and write that instead. - // Future implementations of SkFlattenableWriteBuffer will need to special case for - // onRefEncodedData as well. - SK_DECLARE_UNFLATTENABLE_OBJECT() - -protected: - virtual void* onLockPixels(SkColorTable**) SK_OVERRIDE; - virtual void onUnlockPixels() SK_OVERRIDE; - virtual bool onLockPixelsAreWritable() const SK_OVERRIDE { return false; } - virtual SkData* onRefEncodedData() SK_OVERRIDE; - virtual bool onImplementsDecodeInto() SK_OVERRIDE; - virtual bool onDecodeInto(int pow2, SkBitmap*) SK_OVERRIDE; - -private: - bool fErrorInDecoding; - SkData* fData; - SkBitmapFactory::DecodeProc fDecodeProc; - SkImageCache* fImageCache; - union { - SkImageCache::ID fCacheId; - SkScaledImageCache::ID* fScaledCacheId; - }; - size_t fRowBytes; - SkImageInfo fLazilyCachedInfo; - -#if LAZY_CACHE_STATS - static int32_t gCacheHits; - static int32_t gCacheMisses; -#endif - - // lazily initialized our cached info. Returns NULL on failure. - const SkImage::Info* getCachedInfo(); - void* lockScaledImageCachePixels(); - void* lockImageCachePixels(); - - - typedef SkPixelRef INHERITED; -}; - -#endif // SkLazyPixelRef_DEFINED diff --git a/lazy/SkLruImageCache.cpp b/lazy/SkLruImageCache.cpp deleted file mode 100644 index 40cfefa2..00000000 --- a/lazy/SkLruImageCache.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkLruImageCache.h" - -SK_DEFINE_INST_COUNT(SkImageCache) -SK_DEFINE_INST_COUNT(SkLruImageCache) - -static intptr_t NextGenerationID() { - static intptr_t gNextID; - do { - gNextID++; - } while (SkImageCache::UNINITIALIZED_ID == gNextID); - return gNextID; -} - -class CachedPixels : public SkNoncopyable { - -public: - CachedPixels(size_t length) - : fLength(length) - , fID(NextGenerationID()) - , fLocked(false) { - fAddr = sk_malloc_throw(length); - } - - ~CachedPixels() { - sk_free(fAddr); - } - - void* getData() { return fAddr; } - - intptr_t getID() const { return fID; } - - size_t getLength() const { return fLength; } - - void lock() { SkASSERT(!fLocked); fLocked = true; } - - void unlock() { SkASSERT(fLocked); fLocked = false; } - - bool isLocked() const { return fLocked; } - -private: - void* fAddr; - size_t fLength; - const intptr_t fID; - bool fLocked; - SK_DECLARE_INTERNAL_LLIST_INTERFACE(CachedPixels); -}; - -//////////////////////////////////////////////////////////////////////////////////// - -SkLruImageCache::SkLruImageCache(size_t budget) - : fRamBudget(budget) - , fRamUsed(0) {} - -SkLruImageCache::~SkLruImageCache() { - // Don't worry about updating pointers. All will be deleted. - Iter iter; - CachedPixels* pixels = iter.init(fLRU, Iter::kTail_IterStart); - while (pixels != NULL) { - CachedPixels* prev = iter.prev(); - SkASSERT(!pixels->isLocked()); -#ifdef SK_DEBUG - fRamUsed -= pixels->getLength(); -#endif - SkDELETE(pixels); - pixels = prev; - } -#ifdef SK_DEBUG - SkASSERT(fRamUsed == 0); -#endif -} - -#ifdef SK_DEBUG -SkImageCache::MemoryStatus SkLruImageCache::getMemoryStatus(intptr_t ID) const { - if (SkImageCache::UNINITIALIZED_ID == ID) { - return SkImageCache::kFreed_MemoryStatus; - } - SkAutoMutexAcquire ac(&fMutex); - CachedPixels* pixels = this->findByID(ID); - if (NULL == pixels) { - return SkImageCache::kFreed_MemoryStatus; - } - if (pixels->isLocked()) { - return SkImageCache::kPinned_MemoryStatus; - } - return SkImageCache::kUnpinned_MemoryStatus; -} - -void SkLruImageCache::purgeAllUnpinnedCaches() { - SkAutoMutexAcquire ac(&fMutex); - this->purgeTilAtOrBelow(0); -} -#endif - -size_t SkLruImageCache::setImageCacheLimit(size_t newLimit) { - size_t oldLimit = fRamBudget; - SkAutoMutexAcquire ac(&fMutex); - fRamBudget = newLimit; - this->purgeIfNeeded(); - return oldLimit; -} - -void* SkLruImageCache::allocAndPinCache(size_t bytes, intptr_t* ID) { - SkAutoMutexAcquire ac(&fMutex); - CachedPixels* pixels = SkNEW_ARGS(CachedPixels, (bytes)); - if (ID != NULL) { - *ID = pixels->getID(); - } - pixels->lock(); - fRamUsed += bytes; - fLRU.addToHead(pixels); - this->purgeIfNeeded(); - return pixels->getData(); -} - -void* SkLruImageCache::pinCache(intptr_t ID, SkImageCache::DataStatus* status) { - SkASSERT(ID != SkImageCache::UNINITIALIZED_ID); - SkAutoMutexAcquire ac(&fMutex); - CachedPixels* pixels = this->findByID(ID); - if (NULL == pixels) { - return NULL; - } - if (pixels != fLRU.head()) { - fLRU.remove(pixels); - fLRU.addToHead(pixels); - } - SkASSERT(status != NULL); - // This cache will never return pinned memory whose data has been overwritten. - *status = SkImageCache::kRetained_DataStatus; - pixels->lock(); - return pixels->getData(); -} - -void SkLruImageCache::releaseCache(intptr_t ID) { - SkASSERT(ID != SkImageCache::UNINITIALIZED_ID); - SkAutoMutexAcquire ac(&fMutex); - CachedPixels* pixels = this->findByID(ID); - SkASSERT(pixels != NULL); - pixels->unlock(); - this->purgeIfNeeded(); -} - -void SkLruImageCache::throwAwayCache(intptr_t ID) { - SkASSERT(ID != SkImageCache::UNINITIALIZED_ID); - SkAutoMutexAcquire ac(&fMutex); - CachedPixels* pixels = this->findByID(ID); - if (pixels != NULL) { - if (pixels->isLocked()) { - pixels->unlock(); - } - this->removePixels(pixels); - } -} - -void SkLruImageCache::removePixels(CachedPixels* pixels) { - // Mutex is already locked. - SkASSERT(!pixels->isLocked()); - const size_t size = pixels->getLength(); - SkASSERT(size <= fRamUsed); - fLRU.remove(pixels); - SkDELETE(pixels); - fRamUsed -= size; -} - -CachedPixels* SkLruImageCache::findByID(intptr_t ID) const { - // Mutex is already locked. - Iter iter; - // Start from the head, most recently used. - CachedPixels* pixels = iter.init(fLRU, Iter::kHead_IterStart); - while (pixels != NULL) { - if (pixels->getID() == ID) { - return pixels; - } - pixels = iter.next(); - } - return NULL; -} - -void SkLruImageCache::purgeIfNeeded() { - // Mutex is already locked. - if (fRamBudget > 0) { - this->purgeTilAtOrBelow(fRamBudget); - } -} - -void SkLruImageCache::purgeTilAtOrBelow(size_t limit) { - // Mutex is already locked. - if (fRamUsed > limit) { - Iter iter; - // Start from the tail, least recently used. - CachedPixels* pixels = iter.init(fLRU, Iter::kTail_IterStart); - while (pixels != NULL && fRamUsed > limit) { - CachedPixels* prev = iter.prev(); - if (!pixels->isLocked()) { - this->removePixels(pixels); - } - pixels = prev; - } - } -} diff --git a/lazy/SkPurgeableImageCache.cpp b/lazy/SkPurgeableImageCache.cpp deleted file mode 100644 index 0d36e4a9..00000000 --- a/lazy/SkPurgeableImageCache.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkThread.h" -#include "SkPurgeableImageCache.h" -#include "SkPurgeableMemoryBlock.h" - -#ifdef SK_DEBUG - #include "SkTSearch.h" -#endif - -SK_DEFINE_INST_COUNT(SkPurgeableImageCache) -SK_DECLARE_STATIC_MUTEX(gPurgeableImageMutex); - -SkImageCache* SkPurgeableImageCache::Create() { - if (!SkPurgeableMemoryBlock::IsSupported()) { - return NULL; - } - SkAutoMutexAcquire ac(&gPurgeableImageMutex); - static SkPurgeableImageCache gCache; - gCache.ref(); - return &gCache; -} - -SkPurgeableImageCache::SkPurgeableImageCache() {} - -#ifdef SK_DEBUG -SkPurgeableImageCache::~SkPurgeableImageCache() { - SkASSERT(fRecs.count() == 0); -} -#endif - - -void* SkPurgeableImageCache::allocAndPinCache(size_t bytes, intptr_t* ID) { - SkAutoMutexAcquire ac(&gPurgeableImageMutex); - - SkPurgeableMemoryBlock* block = SkPurgeableMemoryBlock::Create(bytes); - if (NULL == block) { - return NULL; - } - - SkPurgeableMemoryBlock::PinResult pinResult; - void* data = block->pin(&pinResult); - if (NULL == data) { - SkDELETE(block); - return NULL; - } - - SkASSERT(ID != NULL); - *ID = reinterpret_cast<intptr_t>(block); -#ifdef SK_DEBUG - // Insert into the array of all recs: - int index = this->findRec(*ID); - SkASSERT(index < 0); - fRecs.insert(~index, 1, ID); -#endif - return data; -} - -void* SkPurgeableImageCache::pinCache(intptr_t ID, SkImageCache::DataStatus* status) { - SkASSERT(ID != SkImageCache::UNINITIALIZED_ID); - SkAutoMutexAcquire ac(&gPurgeableImageMutex); - - SkASSERT(this->findRec(ID) >= 0); - SkPurgeableMemoryBlock* block = reinterpret_cast<SkPurgeableMemoryBlock*>(ID); - SkPurgeableMemoryBlock::PinResult pinResult; - void* data = block->pin(&pinResult); - if (NULL == data) { - this->removeRec(ID); - return NULL; - } - - switch (pinResult) { - case SkPurgeableMemoryBlock::kRetained_PinResult: - *status = SkImageCache::kRetained_DataStatus; - break; - - case SkPurgeableMemoryBlock::kUninitialized_PinResult: - *status = SkImageCache::kUninitialized_DataStatus; - break; - - default: - // Invalid value. Treat as a failure to pin. - SkASSERT(false); - this->removeRec(ID); - return NULL; - } - - return data; -} - -void SkPurgeableImageCache::releaseCache(intptr_t ID) { - SkASSERT(ID != SkImageCache::UNINITIALIZED_ID); - SkAutoMutexAcquire ac(&gPurgeableImageMutex); - - SkASSERT(this->findRec(ID) >= 0); - SkPurgeableMemoryBlock* block = reinterpret_cast<SkPurgeableMemoryBlock*>(ID); - block->unpin(); -} - -void SkPurgeableImageCache::throwAwayCache(intptr_t ID) { - SkASSERT(ID != SkImageCache::UNINITIALIZED_ID); - SkAutoMutexAcquire ac(&gPurgeableImageMutex); - - this->removeRec(ID); -} - -#ifdef SK_DEBUG -SkImageCache::MemoryStatus SkPurgeableImageCache::getMemoryStatus(intptr_t ID) const { - SkAutoMutexAcquire ac(&gPurgeableImageMutex); - if (SkImageCache::UNINITIALIZED_ID == ID || this->findRec(ID) < 0) { - return SkImageCache::kFreed_MemoryStatus; - } - - SkPurgeableMemoryBlock* block = reinterpret_cast<SkPurgeableMemoryBlock*>(ID); - if (block->isPinned()) { - return SkImageCache::kPinned_MemoryStatus; - } - return SkImageCache::kUnpinned_MemoryStatus; -} - -void SkPurgeableImageCache::purgeAllUnpinnedCaches() { - SkAutoMutexAcquire ac(&gPurgeableImageMutex); - if (SkPurgeableMemoryBlock::PlatformSupportsPurgingAllUnpinnedBlocks()) { - SkPurgeableMemoryBlock::PurgeAllUnpinnedBlocks(); - } else { - // Go through the blocks, and purge them individually. - // Rather than deleting the blocks, which would interfere with further calls, purge them - // and keep them around. - for (int i = 0; i < fRecs.count(); i++) { - SkPurgeableMemoryBlock* block = reinterpret_cast<SkPurgeableMemoryBlock*>(fRecs[i]); - if (!block->isPinned()) { - if (!block->purge()) { - // FIXME: This should be more meaningful (which one, etc...) - SkDebugf("Failed to purge\n"); - } - } - } - } -} - -int SkPurgeableImageCache::findRec(intptr_t rec) const { - return SkTSearch(fRecs.begin(), fRecs.count(), rec, sizeof(intptr_t)); -} -#endif - -void SkPurgeableImageCache::removeRec(intptr_t ID) { -#ifdef SK_DEBUG - int index = this->findRec(ID); - SkASSERT(index >= 0); - fRecs.remove(index); -#endif - SkPurgeableMemoryBlock* block = reinterpret_cast<SkPurgeableMemoryBlock*>(ID); - SkASSERT(!block->isPinned()); - SkDELETE(block); -} diff --git a/opts/SkBlitMask_opts_arm.cpp b/opts/SkBlitMask_opts_arm.cpp index 0ad09193..2bf76031 100644 --- a/opts/SkBlitMask_opts_arm.cpp +++ b/opts/SkBlitMask_opts_arm.cpp @@ -1,14 +1,39 @@ +#include "SkColor.h" +#include "SkColorPriv.h" #include "SkBlitMask.h" +#include "SkUtilsArm.h" +#include "SkBlitMask_opts_arm_neon.h" SkBlitMask::ColorProc SkBlitMask::PlatformColorProcs(SkBitmap::Config dstConfig, SkMask::Format maskFormat, SkColor color) { +#if SK_ARM_NEON_IS_NONE + return NULL; +#else +#if SK_ARM_NEON_IS_DYNAMIC + if (!sk_cpu_arm_has_neon()) { + return NULL; + } +#endif + if ((SkBitmap::kARGB_8888_Config == dstConfig) && + (SkMask::kA8_Format == maskFormat)) { + return D32_A8_Factory_neon(color); + } +#endif + + // We don't need to handle the SkMask::kLCD16_Format case as the default + // LCD16 will call us through SkBlitMask::PlatformBlitRowProcs16() + return NULL; } SkBlitMask::BlitLCD16RowProc SkBlitMask::PlatformBlitRowProcs16(bool isOpaque) { - return NULL; + if (isOpaque) { + return SK_ARM_NEON_WRAP(SkBlitLCD16OpaqueRow); + } else { + return SK_ARM_NEON_WRAP(SkBlitLCD16Row); + } } SkBlitMask::RowProc SkBlitMask::PlatformRowProcs(SkBitmap::Config dstConfig, diff --git a/opts/SkBlitMask_opts_arm_neon.cpp b/opts/SkBlitMask_opts_arm_neon.cpp new file mode 100644 index 00000000..84b5c3da --- /dev/null +++ b/opts/SkBlitMask_opts_arm_neon.cpp @@ -0,0 +1,254 @@ + +#include "SkBlitMask.h" +#include "SkColor_opts_neon.h" + +static void D32_A8_Black_neon(void* SK_RESTRICT dst, size_t dstRB, + const void* SK_RESTRICT maskPtr, size_t maskRB, + SkColor, int width, int height) { + SkPMColor* SK_RESTRICT device = (SkPMColor*)dst; + const uint8_t* SK_RESTRICT mask = (const uint8_t*)maskPtr; + + maskRB -= width; + dstRB -= (width << 2); + do { + int w = width; + while (w >= 8) { + uint8x8_t vmask = vld1_u8(mask); + uint16x8_t vscale = vsubw_u8(vdupq_n_u16(256), vmask); + uint8x8x4_t vdevice = vld4_u8((uint8_t*)device); + + vdevice = SkAlphaMulQ_neon8(vdevice, vscale); + vdevice.val[NEON_A] += vmask; + + vst4_u8((uint8_t*)device, vdevice); + + mask += 8; + device += 8; + w -= 8; + } + while (w-- > 0) { + unsigned aa = *mask++; + *device = (aa << SK_A32_SHIFT) + + SkAlphaMulQ(*device, SkAlpha255To256(255 - aa)); + device += 1; + }; + device = (uint32_t*)((char*)device + dstRB); + mask += maskRB; + } while (--height != 0); +} + +template <bool isColor> +static void D32_A8_Opaque_Color_neon(void* SK_RESTRICT dst, size_t dstRB, + const void* SK_RESTRICT maskPtr, size_t maskRB, + SkColor color, int width, int height) { + SkPMColor pmc = SkPreMultiplyColor(color); + SkPMColor* SK_RESTRICT device = (SkPMColor*)dst; + const uint8_t* SK_RESTRICT mask = (const uint8_t*)maskPtr; + uint8x8x4_t vpmc; + + maskRB -= width; + dstRB -= (width << 2); + + if (width >= 8) { + vpmc.val[NEON_A] = vdup_n_u8(SkGetPackedA32(pmc)); + vpmc.val[NEON_R] = vdup_n_u8(SkGetPackedR32(pmc)); + vpmc.val[NEON_G] = vdup_n_u8(SkGetPackedG32(pmc)); + vpmc.val[NEON_B] = vdup_n_u8(SkGetPackedB32(pmc)); + } + do { + int w = width; + while (w >= 8) { + uint8x8_t vmask = vld1_u8(mask); + uint16x8_t vscale, vmask256 = SkAlpha255To256_neon8(vmask); + if (isColor) { + vscale = vsubw_u8(vdupq_n_u16(256), + SkAlphaMul_neon8(vpmc.val[NEON_A], vmask256)); + } else { + vscale = vsubw_u8(vdupq_n_u16(256), vmask); + } + uint8x8x4_t vdev = vld4_u8((uint8_t*)device); + + vdev.val[NEON_A] = SkAlphaMul_neon8(vpmc.val[NEON_A], vmask256) + + SkAlphaMul_neon8(vdev.val[NEON_A], vscale); + vdev.val[NEON_R] = SkAlphaMul_neon8(vpmc.val[NEON_R], vmask256) + + SkAlphaMul_neon8(vdev.val[NEON_R], vscale); + vdev.val[NEON_G] = SkAlphaMul_neon8(vpmc.val[NEON_G], vmask256) + + SkAlphaMul_neon8(vdev.val[NEON_G], vscale); + vdev.val[NEON_B] = SkAlphaMul_neon8(vpmc.val[NEON_B], vmask256) + + SkAlphaMul_neon8(vdev.val[NEON_B], vscale); + + vst4_u8((uint8_t*)device, vdev); + + mask += 8; + device += 8; + w -= 8; + } + + while (w--) { + unsigned aa = *mask++; + if (isColor) { + *device = SkBlendARGB32(pmc, *device, aa); + } else { + *device = SkAlphaMulQ(pmc, SkAlpha255To256(aa)) + + SkAlphaMulQ(*device, SkAlpha255To256(255 - aa)); + } + device += 1; + }; + + device = (uint32_t*)((char*)device + dstRB); + mask += maskRB; + + } while (--height != 0); +} + +static void D32_A8_Opaque_neon(void* SK_RESTRICT dst, size_t dstRB, + const void* SK_RESTRICT maskPtr, size_t maskRB, + SkColor color, int width, int height) { + D32_A8_Opaque_Color_neon<false>(dst, dstRB, maskPtr, maskRB, color, width, height); +} + +static void D32_A8_Color_neon(void* SK_RESTRICT dst, size_t dstRB, + const void* SK_RESTRICT maskPtr, size_t maskRB, + SkColor color, int width, int height) { + D32_A8_Opaque_Color_neon<true>(dst, dstRB, maskPtr, maskRB, color, width, height); +} + +SkBlitMask::ColorProc D32_A8_Factory_neon(SkColor color) { + if (SK_ColorBLACK == color) { + return D32_A8_Black_neon; + } else if (0xFF == SkColorGetA(color)) { + return D32_A8_Opaque_neon; + } else { + return D32_A8_Color_neon; + } +} + +//////////////////////////////////////////////////////////////////////////////// + +void SkBlitLCD16OpaqueRow_neon(SkPMColor dst[], const uint16_t src[], + SkColor color, int width, + SkPMColor opaqueDst) { + int colR = SkColorGetR(color); + int colG = SkColorGetG(color); + int colB = SkColorGetB(color); + + uint8x8_t vcolR, vcolG, vcolB; + uint8x8_t vopqDstA, vopqDstR, vopqDstG, vopqDstB; + + if (width >= 8) { + vcolR = vdup_n_u8(colR); + vcolG = vdup_n_u8(colG); + vcolB = vdup_n_u8(colB); + vopqDstA = vdup_n_u8(SkGetPackedA32(opaqueDst)); + vopqDstR = vdup_n_u8(SkGetPackedR32(opaqueDst)); + vopqDstG = vdup_n_u8(SkGetPackedG32(opaqueDst)); + vopqDstB = vdup_n_u8(SkGetPackedB32(opaqueDst)); + } + + while (width >= 8) { + uint8x8x4_t vdst; + uint16x8_t vmask; + uint16x8_t vmaskR, vmaskG, vmaskB; + uint8x8_t vsel_trans, vsel_opq; + + vdst = vld4_u8((uint8_t*)dst); + vmask = vld1q_u16(src); + + // Prepare compare masks + vsel_trans = vmovn_u16(vceqq_u16(vmask, vdupq_n_u16(0))); + vsel_opq = vmovn_u16(vceqq_u16(vmask, vdupq_n_u16(0xFFFF))); + + // Get all the color masks on 5 bits + vmaskR = vshrq_n_u16(vmask, SK_R16_SHIFT); + vmaskG = vshrq_n_u16(vshlq_n_u16(vmask, SK_R16_BITS), + SK_B16_BITS + SK_R16_BITS + 1); + vmaskB = vmask & vdupq_n_u16(SK_B16_MASK); + + // Upscale to 0..32 + vmaskR = vmaskR + vshrq_n_u16(vmaskR, 4); + vmaskG = vmaskG + vshrq_n_u16(vmaskG, 4); + vmaskB = vmaskB + vshrq_n_u16(vmaskB, 4); + + vdst.val[NEON_A] = vbsl_u8(vsel_trans, vdst.val[NEON_A], vdup_n_u8(0xFF)); + vdst.val[NEON_A] = vbsl_u8(vsel_opq, vopqDstA, vdst.val[NEON_A]); + + vdst.val[NEON_R] = SkBlend32_neon8(vcolR, vdst.val[NEON_R], vmaskR); + vdst.val[NEON_G] = SkBlend32_neon8(vcolG, vdst.val[NEON_G], vmaskG); + vdst.val[NEON_B] = SkBlend32_neon8(vcolB, vdst.val[NEON_B], vmaskB); + + vdst.val[NEON_R] = vbsl_u8(vsel_opq, vopqDstR, vdst.val[NEON_R]); + vdst.val[NEON_G] = vbsl_u8(vsel_opq, vopqDstG, vdst.val[NEON_G]); + vdst.val[NEON_B] = vbsl_u8(vsel_opq, vopqDstB, vdst.val[NEON_B]); + + vst4_u8((uint8_t*)dst, vdst); + + dst += 8; + src += 8; + width -= 8; + } + + // Leftovers + for (int i = 0; i < width; i++) { + dst[i] = SkBlendLCD16Opaque(colR, colG, colB, dst[i], src[i], + opaqueDst); + } +} + +void SkBlitLCD16Row_neon(SkPMColor dst[], const uint16_t src[], + SkColor color, int width, SkPMColor) { + int colA = SkColorGetA(color); + int colR = SkColorGetR(color); + int colG = SkColorGetG(color); + int colB = SkColorGetB(color); + + colA = SkAlpha255To256(colA); + + uint8x8_t vcolR, vcolG, vcolB; + uint16x8_t vcolA; + + if (width >= 8) { + vcolA = vdupq_n_u16(colA); + vcolR = vdup_n_u8(colR); + vcolG = vdup_n_u8(colG); + vcolB = vdup_n_u8(colB); + } + + while (width >= 8) { + uint8x8x4_t vdst; + uint16x8_t vmask; + uint16x8_t vmaskR, vmaskG, vmaskB; + + vdst = vld4_u8((uint8_t*)dst); + vmask = vld1q_u16(src); + + // Get all the color masks on 5 bits + vmaskR = vshrq_n_u16(vmask, SK_R16_SHIFT); + vmaskG = vshrq_n_u16(vshlq_n_u16(vmask, SK_R16_BITS), + SK_B16_BITS + SK_R16_BITS + 1); + vmaskB = vmask & vdupq_n_u16(SK_B16_MASK); + + // Upscale to 0..32 + vmaskR = vmaskR + vshrq_n_u16(vmaskR, 4); + vmaskG = vmaskG + vshrq_n_u16(vmaskG, 4); + vmaskB = vmaskB + vshrq_n_u16(vmaskB, 4); + + vmaskR = vshrq_n_u16(vmaskR * vcolA, 8); + vmaskG = vshrq_n_u16(vmaskG * vcolA, 8); + vmaskB = vshrq_n_u16(vmaskB * vcolA, 8); + + vdst.val[NEON_A] = vdup_n_u8(0xFF); + vdst.val[NEON_R] = SkBlend32_neon8(vcolR, vdst.val[NEON_R], vmaskR); + vdst.val[NEON_G] = SkBlend32_neon8(vcolG, vdst.val[NEON_G], vmaskG); + vdst.val[NEON_B] = SkBlend32_neon8(vcolB, vdst.val[NEON_B], vmaskB); + + vst4_u8((uint8_t*)dst, vdst); + + dst += 8; + src += 8; + width -= 8; + } + + for (int i = 0; i < width; i++) { + dst[i] = SkBlendLCD16(colA, colR, colG, colB, dst[i], src[i]); + } +} diff --git a/opts/SkBlitMask_opts_arm_neon.h b/opts/SkBlitMask_opts_arm_neon.h new file mode 100644 index 00000000..fdbce14f --- /dev/null +++ b/opts/SkBlitMask_opts_arm_neon.h @@ -0,0 +1,16 @@ +#ifndef SkBlitMask_opts_arm_neon_DEFINED +#define SkBlitMask_opts_arm_neon_DEFINED + +#include "SkColor.h" +#include "SkBlitMask.h" + +extern SkBlitMask::ColorProc D32_A8_Factory_neon(SkColor color); + +extern void SkBlitLCD16OpaqueRow_neon(SkPMColor dst[], const uint16_t src[], + SkColor color, int width, + SkPMColor opaqueDst); + +extern void SkBlitLCD16Row_neon(SkPMColor dst[], const uint16_t src[], + SkColor color, int width, SkPMColor); + +#endif // #ifndef SkBlitMask_opts_arm_neon_DEFINED diff --git a/opts/SkBlurImage_opts_neon.cpp b/opts/SkBlurImage_opts_neon.cpp new file mode 100644 index 00000000..4e33d72d --- /dev/null +++ b/opts/SkBlurImage_opts_neon.cpp @@ -0,0 +1,101 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#include "SkBitmap.h" +#include "SkColorPriv.h" +#include "SkBlurImage_opts.h" +#include "SkRect.h" + +#include <arm_neon.h> + +namespace { + +enum BlurDirection { + kX, kY +}; + +/** + * Helper function to spread the components of a 32-bit integer into the + * lower 8 bits of each 16-bit element of a NEON register. + */ + +static inline uint16x4_t expand(uint32_t a) { + // ( ARGB ) -> ( ARGB ARGB ) -> ( A R G B A R G B ) + uint8x8_t v8 = vreinterpret_u8_u32(vdup_n_u32(a)); + // ( A R G B A R G B ) -> ( 0A 0R 0G 0B 0A 0R 0G 0B ) -> ( 0A 0R 0G 0B ) + return vget_low_u16(vmovl_u8(v8)); +} + +template<BlurDirection srcDirection, BlurDirection dstDirection> +void SkBoxBlur_NEON(const SkPMColor* src, int srcStride, SkPMColor* dst, int kernelSize, + int leftOffset, int rightOffset, int width, int height) +{ + const int rightBorder = SkMin32(rightOffset + 1, width); + const int srcStrideX = srcDirection == kX ? 1 : srcStride; + const int dstStrideX = dstDirection == kX ? 1 : height; + const int srcStrideY = srcDirection == kX ? srcStride : 1; + const int dstStrideY = dstDirection == kX ? width : 1; + const uint32x4_t scale = vdupq_n_u32((1 << 24) / kernelSize); + const uint32x4_t half = vdupq_n_u32(1 << 23); + for (int y = 0; y < height; ++y) { + uint32x4_t sum = vdupq_n_u32(0); + const SkPMColor* p = src; + for (int i = 0; i < rightBorder; ++i) { + sum = vaddw_u16(sum, expand(*p)); + p += srcStrideX; + } + + const SkPMColor* sptr = src; + SkPMColor* dptr = dst; + for (int x = 0; x < width; ++x) { + // ( half+sumA*scale half+sumR*scale half+sumG*scale half+sumB*scale ) + uint32x4_t result = vmlaq_u32(half, sum, scale); + + // Saturated conversion to 16-bit. + // ( AAAA RRRR GGGG BBBB ) -> ( 0A 0R 0G 0B ) + uint16x4_t result16 = vqshrn_n_u32(result, 16); + + // Saturated conversion to 8-bit. + // ( 0A 0R 0G 0B ) -> ( 0A 0R 0G 0B 0A 0R 0G 0B ) -> ( A R G B A R G B ) + uint8x8_t result8 = vqshrn_n_u16(vcombine_u16(result16, result16), 8); + + // ( A R G B A R G B ) -> ( ARGB ARGB ) -> ( ARGB ) + // Store low 32 bits to destination. + vst1_lane_u32(dptr, vreinterpret_u32_u8(result8), 0); + + if (x >= leftOffset) { + const SkPMColor* l = sptr - leftOffset * srcStrideX; + sum = vsubw_u16(sum, expand(*l)); + } + if (x + rightOffset + 1 < width) { + const SkPMColor* r = sptr + (rightOffset + 1) * srcStrideX; + sum = vaddw_u16(sum, expand(*r)); + } + sptr += srcStrideX; + if (srcDirection == kY) { + SK_PREFETCH(sptr + (rightOffset + 1) * srcStrideX); + } + dptr += dstStrideX; + } + src += srcStrideY; + dst += dstStrideY; + } +} + +} // namespace + +bool SkBoxBlurGetPlatformProcs_NEON(SkBoxBlurProc* boxBlurX, + SkBoxBlurProc* boxBlurY, + SkBoxBlurProc* boxBlurXY, + SkBoxBlurProc* boxBlurYX) { + *boxBlurX = SkBoxBlur_NEON<kX, kX>; + *boxBlurY = SkBoxBlur_NEON<kY, kY>; + *boxBlurXY = SkBoxBlur_NEON<kX, kY>; + *boxBlurYX = SkBoxBlur_NEON<kY, kX>; + return true; +} diff --git a/opts/SkBlurImage_opts_neon.h b/opts/SkBlurImage_opts_neon.h new file mode 100644 index 00000000..7fbe9633 --- /dev/null +++ b/opts/SkBlurImage_opts_neon.h @@ -0,0 +1,13 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkBlurImage_opts.h" + +bool SkBoxBlurGetPlatformProcs_NEON(SkBoxBlurProc* boxBlurX, + SkBoxBlurProc* boxBlurY, + SkBoxBlurProc* boxBlurXY, + SkBoxBlurProc* boxBlurYX); diff --git a/opts/SkColor_opts_neon.h b/opts/SkColor_opts_neon.h index f8123971..a3430b5c 100644 --- a/opts/SkColor_opts_neon.h +++ b/opts/SkColor_opts_neon.h @@ -2,6 +2,7 @@ #define SkColor_opts_neon_DEFINED #include "SkTypes.h" +#include "SkColorPriv.h" #include <arm_neon.h> @@ -65,4 +66,49 @@ static inline uint16x8_t SkPixel32ToPixel16_neon8(uint8x8x4_t vsrc) { return ret; } +/* This function blends 8 pixels of the same channel in the exact same way as + * SkBlend32. + */ +static inline uint8x8_t SkBlend32_neon8(uint8x8_t src, uint8x8_t dst, uint16x8_t scale) { + int16x8_t src_wide, dst_wide; + + src_wide = vreinterpretq_s16_u16(vmovl_u8(src)); + dst_wide = vreinterpretq_s16_u16(vmovl_u8(dst)); + + src_wide = (src_wide - dst_wide) * vreinterpretq_s16_u16(scale); + + dst_wide += vshrq_n_s16(src_wide, 5); + + return vmovn_u16(vreinterpretq_u16_s16(dst_wide)); +} + +static inline SkPMColor SkFourByteInterp256_neon(SkPMColor src, SkPMColor dst, + unsigned srcScale) { + SkASSERT(srcScale <= 256); + int16x8_t vscale = vdupq_n_s16(srcScale); + int16x8_t vsrc_wide, vdst_wide, vdiff; + uint8x8_t res; + + vsrc_wide = vreinterpretq_s16_u16(vmovl_u8(vreinterpret_u8_u32(vdup_n_u32(src)))); + vdst_wide = vreinterpretq_s16_u16(vmovl_u8(vreinterpret_u8_u32(vdup_n_u32(dst)))); + + vdiff = vsrc_wide - vdst_wide; + vdiff *= vscale; + + vdiff = vshrq_n_s16(vdiff, 8); + + vdst_wide += vdiff; + + res = vmovn_u16(vreinterpretq_u16_s16(vdst_wide)); + + return vget_lane_u32(vreinterpret_u32_u8(res), 0); +} + +static inline SkPMColor SkFourByteInterp_neon(SkPMColor src, SkPMColor dst, + U8CPU srcWeight) { + SkASSERT(srcWeight <= 255); + unsigned scale = SkAlpha255To256(srcWeight); + return SkFourByteInterp256_neon(src, dst, scale); +} + #endif /* #ifndef SkColor_opts_neon_DEFINED */ diff --git a/opts/SkXfermode_opts_arm.cpp b/opts/SkXfermode_opts_arm.cpp index eb3b3016..2a796d6f 100644 --- a/opts/SkXfermode_opts_arm.cpp +++ b/opts/SkXfermode_opts_arm.cpp @@ -5,12 +5,22 @@ extern SkProcCoeffXfermode* SkPlatformXfermodeFactory_impl_neon(const ProcCoeff& rec, SkXfermode::Mode mode); +extern SkXfermodeProc SkPlatformXfermodeProcFactory_impl_neon(SkXfermode::Mode mode); + SkProcCoeffXfermode* SkPlatformXfermodeFactory_impl(const ProcCoeff& rec, SkXfermode::Mode mode) { return NULL; } +SkXfermodeProc SkPlatformXfermodeProcFactory_impl(SkXfermode::Mode mode) { + return NULL; +} + SkProcCoeffXfermode* SkPlatformXfermodeFactory(const ProcCoeff& rec, SkXfermode::Mode mode) { return SK_ARM_NEON_WRAP(SkPlatformXfermodeFactory_impl)(rec, mode); } + +SkXfermodeProc SkPlatformXfermodeProcFactory(SkXfermode::Mode mode) { + return SK_ARM_NEON_WRAP(SkPlatformXfermodeProcFactory_impl)(mode); +} diff --git a/opts/SkXfermode_opts_arm_neon.cpp b/opts/SkXfermode_opts_arm_neon.cpp index b8d8ef52..6a79b737 100644 --- a/opts/SkXfermode_opts_arm_neon.cpp +++ b/opts/SkXfermode_opts_arm_neon.cpp @@ -93,6 +93,133 @@ static inline uint8x8_t clamp_div255round_simd8_32(int32x4_t val1, int32x4_t val } //////////////////////////////////////////////////////////////////////////////// +// 1 pixel modeprocs +//////////////////////////////////////////////////////////////////////////////// + +// kSrcATop_Mode, //!< [Da, Sc * Da + (1 - Sa) * Dc] +SkPMColor srcatop_modeproc_neon(SkPMColor src, SkPMColor dst) { + unsigned sa = SkGetPackedA32(src); + unsigned da = SkGetPackedA32(dst); + unsigned isa = 255 - sa; + + uint8x8_t vda, visa, vsrc, vdst; + + vda = vdup_n_u8(da); + visa = vdup_n_u8(isa); + + uint16x8_t vsrc_wide, vdst_wide; + vsrc_wide = vmull_u8(vda, vreinterpret_u8_u32(vdup_n_u32(src))); + vdst_wide = vmull_u8(visa, vreinterpret_u8_u32(vdup_n_u32(dst))); + + vsrc_wide += vdupq_n_u16(128); + vsrc_wide += vshrq_n_u16(vsrc_wide, 8); + + vdst_wide += vdupq_n_u16(128); + vdst_wide += vshrq_n_u16(vdst_wide, 8); + + vsrc = vshrn_n_u16(vsrc_wide, 8); + vdst = vshrn_n_u16(vdst_wide, 8); + + vsrc += vdst; + vsrc = vset_lane_u8(da, vsrc, 3); + + return vget_lane_u32(vreinterpret_u32_u8(vsrc), 0); +} + +// kDstATop_Mode, //!< [Sa, Sa * Dc + Sc * (1 - Da)] +SkPMColor dstatop_modeproc_neon(SkPMColor src, SkPMColor dst) { + unsigned sa = SkGetPackedA32(src); + unsigned da = SkGetPackedA32(dst); + unsigned ida = 255 - da; + + uint8x8_t vsa, vida, vsrc, vdst; + + vsa = vdup_n_u8(sa); + vida = vdup_n_u8(ida); + + uint16x8_t vsrc_wide, vdst_wide; + vsrc_wide = vmull_u8(vida, vreinterpret_u8_u32(vdup_n_u32(src))); + vdst_wide = vmull_u8(vsa, vreinterpret_u8_u32(vdup_n_u32(dst))); + + vsrc_wide += vdupq_n_u16(128); + vsrc_wide += vshrq_n_u16(vsrc_wide, 8); + + vdst_wide += vdupq_n_u16(128); + vdst_wide += vshrq_n_u16(vdst_wide, 8); + + vsrc = vshrn_n_u16(vsrc_wide, 8); + vdst = vshrn_n_u16(vdst_wide, 8); + + vsrc += vdst; + vsrc = vset_lane_u8(sa, vsrc, 3); + + return vget_lane_u32(vreinterpret_u32_u8(vsrc), 0); +} + +// kXor_Mode [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc] +SkPMColor xor_modeproc_neon(SkPMColor src, SkPMColor dst) { + unsigned sa = SkGetPackedA32(src); + unsigned da = SkGetPackedA32(dst); + unsigned ret_alpha = sa + da - (SkAlphaMulAlpha(sa, da) << 1); + unsigned isa = 255 - sa; + unsigned ida = 255 - da; + + uint8x8_t vsrc, vdst, visa, vida; + uint16x8_t vsrc_wide, vdst_wide; + + visa = vdup_n_u8(isa); + vida = vdup_n_u8(ida); + vsrc = vreinterpret_u8_u32(vdup_n_u32(src)); + vdst = vreinterpret_u8_u32(vdup_n_u32(dst)); + + vsrc_wide = vmull_u8(vsrc, vida); + vdst_wide = vmull_u8(vdst, visa); + + vsrc_wide += vdupq_n_u16(128); + vsrc_wide += vshrq_n_u16(vsrc_wide, 8); + + vdst_wide += vdupq_n_u16(128); + vdst_wide += vshrq_n_u16(vdst_wide, 8); + + vsrc = vshrn_n_u16(vsrc_wide, 8); + vdst = vshrn_n_u16(vdst_wide, 8); + + vsrc += vdst; + + vsrc = vset_lane_u8(ret_alpha, vsrc, 3); + + return vget_lane_u32(vreinterpret_u32_u8(vsrc), 0); +} + +// kPlus_Mode +SkPMColor plus_modeproc_neon(SkPMColor src, SkPMColor dst) { + uint8x8_t vsrc, vdst; + vsrc = vreinterpret_u8_u32(vdup_n_u32(src)); + vdst = vreinterpret_u8_u32(vdup_n_u32(dst)); + vsrc = vqadd_u8(vsrc, vdst); + + return vget_lane_u32(vreinterpret_u32_u8(vsrc), 0); +} + +// kModulate_Mode +SkPMColor modulate_modeproc_neon(SkPMColor src, SkPMColor dst) { + uint8x8_t vsrc, vdst, vres; + uint16x8_t vres_wide; + + vsrc = vreinterpret_u8_u32(vdup_n_u32(src)); + vdst = vreinterpret_u8_u32(vdup_n_u32(dst)); + + vres_wide = vmull_u8(vsrc, vdst); + + vres_wide += vdupq_n_u16(128); + vres_wide += vshrq_n_u16(vres_wide, 8); + + vres = vshrn_n_u16(vres_wide, 8); + + return vget_lane_u32(vreinterpret_u32_u8(vres), 0); +} + +//////////////////////////////////////////////////////////////////////////////// // 8 pixels modeprocs //////////////////////////////////////////////////////////////////////////////// @@ -632,7 +759,7 @@ void SkNEONProcCoeffXfermode::xfer32(SkPMColor dst[], const SkPMColor src[], SkPMColor dstC = dst[i]; SkPMColor C = proc(src[i], dstC); if (a != 0xFF) { - C = SkFourByteInterp(C, dstC, a); + C = SkFourByteInterp_neon(C, dstC, a); } dst[i] = C; } @@ -700,7 +827,7 @@ void SkNEONProcCoeffXfermode::xfer16(uint16_t* SK_RESTRICT dst, SkPMColor dstC = SkPixel16ToPixel32(dst[i]); SkPMColor C = proc(src[i], dstC); if (0xFF != a) { - C = SkFourByteInterp(C, dstC, a); + C = SkFourByteInterp_neon(C, dstC, a); } dst[i] = SkPixel32ToPixel16_ToU16(C); } @@ -755,6 +882,45 @@ SK_COMPILE_ASSERT( mode_count_arm ); +SkXfermodeProc gNEONXfermodeProcs1[] = { + NULL, // kClear_Mode + NULL, // kSrc_Mode + NULL, // kDst_Mode + NULL, // kSrcOver_Mode + NULL, // kDstOver_Mode + NULL, // kSrcIn_Mode + NULL, // kDstIn_Mode + NULL, // kSrcOut_Mode + NULL, // kDstOut_Mode + srcatop_modeproc_neon, + dstatop_modeproc_neon, + xor_modeproc_neon, + plus_modeproc_neon, + modulate_modeproc_neon, + NULL, // kScreen_Mode + + NULL, // kOverlay_Mode + NULL, // kDarken_Mode + NULL, // kLighten_Mode + NULL, // kColorDodge_Mode + NULL, // kColorBurn_Mode + NULL, // kHardLight_Mode + NULL, // kSoftLight_Mode + NULL, // kDifference_Mode + NULL, // kExclusion_Mode + NULL, // kMultiply_Mode + + NULL, // kHue_Mode + NULL, // kSaturation_Mode + NULL, // kColor_Mode + NULL, // kLuminosity_Mode +}; + +SK_COMPILE_ASSERT( + SK_ARRAY_COUNT(gNEONXfermodeProcs1) == SkXfermode::kLastMode + 1, + mode1_count_arm +); + SkProcCoeffXfermode* SkPlatformXfermodeFactory_impl_neon(const ProcCoeff& rec, SkXfermode::Mode mode) { @@ -765,3 +931,7 @@ SkProcCoeffXfermode* SkPlatformXfermodeFactory_impl_neon(const ProcCoeff& rec, } return NULL; } + +SkXfermodeProc SkPlatformXfermodeProcFactory_impl_neon(SkXfermode::Mode mode) { + return gNEONXfermodeProcs1[mode]; +} diff --git a/opts/SkXfermode_opts_arm_neon.h b/opts/SkXfermode_opts_arm_neon.h index 4c88fc7a..a8d43819 100644 --- a/opts/SkXfermode_opts_arm_neon.h +++ b/opts/SkXfermode_opts_arm_neon.h @@ -26,4 +26,10 @@ private: typedef SkProcCoeffXfermode INHERITED; }; +extern SkPMColor srcatop_modeproc_neon(SkPMColor src, SkPMColor dst); +extern SkPMColor dstatop_modeproc_neon(SkPMColor src, SkPMColor dst); +extern SkPMColor xor_modeproc_neon(SkPMColor src, SkPMColor dst); +extern SkPMColor plus_modeproc_neon(SkPMColor src, SkPMColor dst); +extern SkPMColor modulate_modeproc_neon(SkPMColor src, SkPMColor dst); + #endif //#ifdef SkXfermode_opts_arm_neon_DEFINED diff --git a/opts/SkXfermode_opts_none.cpp b/opts/SkXfermode_opts_none.cpp index ca53fa0d..7c46fdd9 100644 --- a/opts/SkXfermode_opts_none.cpp +++ b/opts/SkXfermode_opts_none.cpp @@ -1,11 +1,17 @@ #include "SkXfermode.h" #include "SkXfermode_proccoeff.h" -// The prototype below is for Clang +// The prototypes below are for Clang extern SkProcCoeffXfermode* SkPlatformXfermodeFactory(const ProcCoeff& rec, SkXfermode::Mode mode); +extern SkXfermodeProc SkPlatformXfermodeProcFactory(SkXfermode::Mode mode); + SkProcCoeffXfermode* SkPlatformXfermodeFactory(const ProcCoeff& rec, SkXfermode::Mode mode) { return NULL; } + +SkXfermodeProc SkPlatformXfermodeProcFactory(SkXfermode::Mode mode) { + return NULL; +} diff --git a/opts/opts_check_arm.cpp b/opts/opts_check_arm.cpp index a9afa75b..3a322aa0 100644 --- a/opts/opts_check_arm.cpp +++ b/opts/opts_check_arm.cpp @@ -19,6 +19,7 @@ #include "SkUtilsArm.h" #include "SkMorphology_opts.h" #include "SkMorphology_opts_neon.h" +#include "SkBlurImage_opts_neon.h" #if defined(SK_CPU_LENDIAN) && !SK_ARM_NEON_IS_NONE extern "C" void memset16_neon(uint16_t dst[], uint16_t value, int count); @@ -91,3 +92,19 @@ SkMorphologyProc SkMorphologyGetPlatformProc(SkMorphologyProcType type) { } #endif } + +bool SkBoxBlurGetPlatformProcs(SkBoxBlurProc* boxBlurX, + SkBoxBlurProc* boxBlurY, + SkBoxBlurProc* boxBlurXY, + SkBoxBlurProc* boxBlurYX) { +#if SK_ARM_NEON_IS_NONE + return false; +#else +#if SK_ARM_NEON_IS_DYNAMIC + if (!sk_cpu_arm_has_neon()) { + return false; + } +#endif + return SkBoxBlurGetPlatformProcs_NEON(boxBlurX, boxBlurY, boxBlurXY, boxBlurYX); +#endif +} diff --git a/pdf/SkPDFDevice.cpp b/pdf/SkPDFDevice.cpp index bdabc53f..cdfec330 100644 --- a/pdf/SkPDFDevice.cpp +++ b/pdf/SkPDFDevice.cpp @@ -1875,11 +1875,12 @@ ContentEntry* SkPDFDevice::setUpContentEntry(const SkClipStack* clipStack, return entry; } -void SkPDFDevice::finishContentEntry(const SkXfermode::Mode xfermode, +void SkPDFDevice::finishContentEntry(SkXfermode::Mode xfermode, SkPDFFormXObject* dst, SkPath* shape) { if (xfermode != SkXfermode::kClear_Mode && xfermode != SkXfermode::kSrc_Mode && + xfermode != SkXfermode::kDstOver_Mode && xfermode != SkXfermode::kSrcIn_Mode && xfermode != SkXfermode::kDstIn_Mode && xfermode != SkXfermode::kSrcOut_Mode && @@ -1890,6 +1891,18 @@ void SkPDFDevice::finishContentEntry(const SkXfermode::Mode xfermode, SkASSERT(!dst); return; } + if (xfermode == SkXfermode::kDstOver_Mode) { + SkASSERT(!dst); + ContentEntry* firstContentEntry = getContentEntries()->get(); + if (firstContentEntry->fContent.getOffset() == 0) { + // For DstOver, an empty content entry was inserted before the rest + // of the content entries. If nothing was drawn, it needs to be + // removed. + SkAutoTDelete<ContentEntry>* contentEntries = getContentEntries(); + contentEntries->reset(firstContentEntry->fNext.detach()); + } + return; + } if (!dst) { SkASSERT(xfermode == SkXfermode::kSrc_Mode || xfermode == SkXfermode::kSrcOut_Mode); @@ -1904,17 +1917,32 @@ void SkPDFDevice::finishContentEntry(const SkXfermode::Mode xfermode, SkClipStack clipStack = contentEntries->fState.fClipStack; SkRegion clipRegion = contentEntries->fState.fClipRegion; + SkMatrix identity; + identity.reset(); + SkPaint stockPaint; + SkAutoTUnref<SkPDFFormXObject> srcFormXObject; if (isContentEmpty()) { - SkASSERT(xfermode == SkXfermode::kClear_Mode); + // If nothing was drawn and there's no shape, then the draw was a + // no-op, but dst needs to be restored for that to be true. + // If there is shape, then an empty source with Src, SrcIn, SrcOut, + // DstIn, DstAtop or Modulate reduces to Clear and DstOut or SrcAtop + // reduces to Dst. + if (shape == NULL || xfermode == SkXfermode::kDstOut_Mode || + xfermode == SkXfermode::kSrcATop_Mode) { + ScopedContentEntry content(this, &clipStack, clipRegion, identity, + stockPaint); + SkPDFUtils::DrawFormXObject(this->addXObjectResource(dst), + &content.entry()->fContent); + return; + } else { + xfermode = SkXfermode::kClear_Mode; + } } else { SkASSERT(!fContentEntries->fNext.get()); srcFormXObject.reset(createFormXObjectFromDevice()); } - SkMatrix identity; - identity.reset(); - // TODO(vandebo) srcFormXObject may contain alpha, but here we want it // without alpha. if (xfermode == SkXfermode::kSrcATop_Mode) { @@ -1946,16 +1974,7 @@ void SkPDFDevice::finishContentEntry(const SkXfermode::Mode xfermode, clipRegion, SkXfermode::kSrcOver_Mode, true); } - SkPaint stockPaint; - - if (xfermode == SkXfermode::kSrcATop_Mode) { - ScopedContentEntry content(this, &clipStack, clipRegion, identity, - stockPaint); - if (content.entry()) { - SkPDFUtils::DrawFormXObject(this->addXObjectResource(dst), - &content.entry()->fContent); - } - } else if (xfermode == SkXfermode::kClear_Mode || !srcFormXObject.get()) { + if (xfermode == SkXfermode::kClear_Mode) { return; } else if (xfermode == SkXfermode::kSrc_Mode || xfermode == SkXfermode::kDstATop_Mode) { @@ -1969,6 +1988,13 @@ void SkPDFDevice::finishContentEntry(const SkXfermode::Mode xfermode, if (xfermode == SkXfermode::kSrc_Mode) { return; } + } else if (xfermode == SkXfermode::kSrcATop_Mode) { + ScopedContentEntry content(this, &clipStack, clipRegion, identity, + stockPaint); + if (content.entry()) { + SkPDFUtils::DrawFormXObject(this->addXObjectResource(dst), + &content.entry()->fContent); + } } SkASSERT(xfermode == SkXfermode::kSrcIn_Mode || diff --git a/pdf/SkPDFFont.cpp b/pdf/SkPDFFont.cpp index b824506b..1641a891 100644 --- a/pdf/SkPDFFont.cpp +++ b/pdf/SkPDFFont.cpp @@ -875,9 +875,8 @@ SkPDFFont::SkPDFFont(SkAdvancedTypefaceMetrics* info, SkTypeface* typeface, fTypeface(ref_or_default(typeface)), fFirstGlyphID(1), fLastGlyphID(info ? info->fLastGlyphID : 0), - fFontInfo(info), - fDescriptor(relatedFontDescriptor) { - SkSafeRef(info); + fFontInfo(SkSafeRef(info)), + fDescriptor(SkSafeRef(relatedFontDescriptor)) { if (info == NULL) { fFontType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font; } else if (info->fMultiMaster) { diff --git a/pdf/SkPDFFont.h b/pdf/SkPDFFont.h index f5d358f3..694c69ac 100644 --- a/pdf/SkPDFFont.h +++ b/pdf/SkPDFFont.h @@ -78,6 +78,7 @@ private: reference to each instantiated class. */ class SkPDFFont : public SkPDFDict { + SK_DECLARE_INST_COUNT(SkPDFFont) public: virtual ~SkPDFFont(); @@ -198,6 +199,7 @@ private: // This should be made a hash table if performance is a problem. static SkTDArray<FontRec>& CanonicalFonts(); static SkBaseMutex& CanonicalFontsMutex(); + typedef SkPDFDict INHERITED; }; #endif diff --git a/pdf/SkPDFGraphicState.h b/pdf/SkPDFGraphicState.h index 84c42910..9182efef 100644 --- a/pdf/SkPDFGraphicState.h +++ b/pdf/SkPDFGraphicState.h @@ -27,6 +27,7 @@ class SkPDFFormXObject; */ class SkPDFGraphicState : public SkPDFDict { + SK_DECLARE_INST_COUNT(SkPDFGraphicState) public: enum SkPDFSMaskMode { kAlpha_SMaskMode, @@ -104,6 +105,7 @@ private: static SkPDFObject* GetInvertFunction(); static int Find(const SkPaint& paint); + typedef SkPDFDict INHERITED; }; #endif diff --git a/pdf/SkPDFImage.cpp b/pdf/SkPDFImage.cpp index a99c9fe3..81adcc20 100644 --- a/pdf/SkPDFImage.cpp +++ b/pdf/SkPDFImage.cpp @@ -36,7 +36,6 @@ static size_t get_uncompressed_size(const SkBitmap& bitmap, return srcRect.width() * 3 * srcRect.height(); case SkBitmap::kARGB_8888_Config: return srcRect.width() * 3 * srcRect.height(); - case SkBitmap::kA1_Config: case SkBitmap::kA8_Config: return 1; default: @@ -166,48 +165,6 @@ static SkStream* extract_argb8888_data(const SkBitmap& bitmap, return stream; } -static SkStream* extract_a1_alpha(const SkBitmap& bitmap, - const SkIRect& srcRect, - bool* isOpaque, - bool* isTransparent) { - const int alphaRowBytes = (srcRect.width() + 7) / 8; - SkStream* stream = SkNEW_ARGS(SkMemoryStream, - (alphaRowBytes * srcRect.height())); - uint8_t* alphaDst = (uint8_t*)stream->getMemoryBase(); - - int offset1 = srcRect.fLeft % 8; - int offset2 = 8 - offset1; - - for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { - uint8_t* src = bitmap.getAddr1(0, y); - // This may read up to one byte after src, but the - // potentially invalid bits are never used for computation. - for (int x = srcRect.fLeft; x < srcRect.fRight; x += 8) { - if (offset1) { - alphaDst[0] = src[x / 8] << offset1 | - src[x / 8 + 1] >> offset2; - } else { - alphaDst[0] = src[x / 8]; - } - if (x + 7 < srcRect.fRight) { - *isOpaque &= alphaDst[0] == SK_AlphaOPAQUE; - *isTransparent &= alphaDst[0] == SK_AlphaTRANSPARENT; - } - alphaDst++; - } - // Calculate the mask of bits we're interested in within the - // last byte of alphaDst. - // width mod 8 == 1 -> 0x80 ... width mod 8 == 7 -> 0xFE - uint8_t mask = ~((1 << (8 - (srcRect.width() % 8))) - 1); - if (srcRect.width() % 8) { - *isOpaque &= (alphaDst[-1] & mask) == (SK_AlphaOPAQUE & mask); - *isTransparent &= - (alphaDst[-1] & mask) == (SK_AlphaTRANSPARENT & mask); - } - } - return stream; -} - static SkStream* extract_a8_alpha(const SkBitmap& bitmap, const SkIRect& srcRect, bool* isOpaque, @@ -283,14 +240,6 @@ static SkStream* extract_image_data(const SkBitmap& bitmap, stream = extract_argb8888_data(bitmap, srcRect, extractAlpha, &isOpaque, &transparent); break; - case SkBitmap::kA1_Config: - if (!extractAlpha) { - stream = create_black_image(); - } else { - stream = extract_a1_alpha(bitmap, srcRect, - &isOpaque, &transparent); - } - break; case SkBitmap::kA8_Config: if (!extractAlpha) { stream = create_black_image(); @@ -574,8 +523,7 @@ SkPDFImage::SkPDFImage(SkStream* stream, insertName("Type", "XObject"); insertName("Subtype", "Image"); - bool alphaOnly = (config == SkBitmap::kA1_Config || - config == SkBitmap::kA8_Config); + bool alphaOnly = (config == SkBitmap::kA8_Config); if (!isAlpha && alphaOnly) { // For alpha only images, we stretch a single pixel of black for @@ -601,8 +549,6 @@ SkPDFImage::SkPDFImage(SkStream* stream, int bitsPerComp = 8; if (config == SkBitmap::kARGB_4444_Config) { bitsPerComp = 4; - } else if (isAlpha && config == SkBitmap::kA1_Config) { - bitsPerComp = 1; } insertInt("BitsPerComponent", bitsPerComp); diff --git a/pdf/SkPDFPage.h b/pdf/SkPDFPage.h index 2ce773c0..47573c74 100644 --- a/pdf/SkPDFPage.h +++ b/pdf/SkPDFPage.h @@ -25,6 +25,7 @@ class SkWStream; tree and points to the content of the page. */ class SkPDFPage : public SkPDFDict { + SK_DECLARE_INST_COUNT(SkPDFPage) public: /** Create a PDF page with the passed PDF device. The device need not * have content on it yet. @@ -102,6 +103,7 @@ private: // Once the content is finalized, put it into a stream for output. SkAutoTUnref<SkPDFStream> fContentStream; + typedef SkPDFDict INHERITED; }; #endif diff --git a/pdf/SkPDFResourceDict.cpp b/pdf/SkPDFResourceDict.cpp index 6f5d1679..8a0208d6 100644 --- a/pdf/SkPDFResourceDict.cpp +++ b/pdf/SkPDFResourceDict.cpp @@ -8,8 +8,6 @@ #include "SkPDFResourceDict.h" #include "SkPostConfig.h" -SK_DEFINE_INST_COUNT(SkPDFResourceDict) - // Sanity check that the values of enum SkPDFResourceType correspond to the // expected values as defined in the arrays below. // If these are failing, you may need to update the resource_type_prefixes diff --git a/pdf/SkPDFResourceDict.h b/pdf/SkPDFResourceDict.h index ab25b4a4..17ea338f 100644 --- a/pdf/SkPDFResourceDict.h +++ b/pdf/SkPDFResourceDict.h @@ -95,6 +95,7 @@ private: SkTSet<SkPDFObject*> fResources; SkTDArray<SkPDFDict*> fTypes; + typedef SkPDFDict INHERITED; }; #endif diff --git a/pdf/SkPDFShader.cpp b/pdf/SkPDFShader.cpp index 60992c3b..94bdf2d7 100644 --- a/pdf/SkPDFShader.cpp +++ b/pdf/SkPDFShader.cpp @@ -499,6 +499,7 @@ private: }; class SkPDFFunctionShader : public SkPDFDict, public SkPDFShader { + SK_DECLARE_INST_COUNT(SkPDFFunctionShader) public: explicit SkPDFFunctionShader(SkPDFShader::State* state); virtual ~SkPDFFunctionShader() { @@ -524,6 +525,7 @@ private: SkAutoTDelete<const SkPDFShader::State> fState; SkPDFStream* makePSFunction(const SkString& psCode, SkPDFArray* domain); + typedef SkPDFDict INHERITED; }; /** diff --git a/pdf/SkPDFStream.h b/pdf/SkPDFStream.h index d7ff115b..6371bc18 100644 --- a/pdf/SkPDFStream.h +++ b/pdf/SkPDFStream.h @@ -25,6 +25,7 @@ class SkPDFCatalog; is feasible. */ class SkPDFStream : public SkPDFDict { + SK_DECLARE_INST_COUNT(SkPDFStream) public: /** Create a PDF stream. A Length entry is automatically added to the * stream dictionary. The stream may be retained (stream->ref() may be diff --git a/pdf/SkPDFTypes.cpp b/pdf/SkPDFTypes.cpp index ed02d2bd..55871c50 100644 --- a/pdf/SkPDFTypes.cpp +++ b/pdf/SkPDFTypes.cpp @@ -17,16 +17,6 @@ #define SNPRINTF snprintf #endif -SK_DEFINE_INST_COUNT(SkPDFArray) -SK_DEFINE_INST_COUNT(SkPDFBool) -SK_DEFINE_INST_COUNT(SkPDFDict) -SK_DEFINE_INST_COUNT(SkPDFInt) -SK_DEFINE_INST_COUNT(SkPDFName) -SK_DEFINE_INST_COUNT(SkPDFObject) -SK_DEFINE_INST_COUNT(SkPDFObjRef) -SK_DEFINE_INST_COUNT(SkPDFScalar) -SK_DEFINE_INST_COUNT(SkPDFString) - /////////////////////////////////////////////////////////////////////////////// void SkPDFObject::emit(SkWStream* stream, SkPDFCatalog* catalog, diff --git a/ports/SkDebug_win.cpp b/ports/SkDebug_win.cpp index 599b133e..fe28ee27 100644 --- a/ports/SkDebug_win.cpp +++ b/ports/SkDebug_win.cpp @@ -14,7 +14,7 @@ static const size_t kBufferSize = 2048; #include <stdarg.h> #include <stdio.h> -#include <Windows.h> +#include <windows.h> void SkDebugf(const char format[], ...) { char buffer[kBufferSize + 1]; diff --git a/ports/SkDiscardableMemory_ashmem.cpp b/ports/SkDiscardableMemory_ashmem.cpp new file mode 100644 index 00000000..6f8684e3 --- /dev/null +++ b/ports/SkDiscardableMemory_ashmem.cpp @@ -0,0 +1,114 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <unistd.h> +#include <sys/mman.h> +#include "SkDiscardableMemory.h" +#include "SkTypes.h" +#include "android/ashmem.h" + +//////////////////////////////////////////////////////////////////////////////// +namespace { +/** + * DiscardableMemory implementation that uses the Android kernel's + * ashmem (Android shared memory). + */ +class SkAshmemDiscardableMemory : public SkDiscardableMemory { +public: + SkAshmemDiscardableMemory(int fd, void* address, size_t size); + virtual ~SkAshmemDiscardableMemory(); + virtual bool lock() SK_OVERRIDE; + virtual void* data() SK_OVERRIDE; + virtual void unlock() SK_OVERRIDE; +private: + bool fLocked; + int fFd; + void* fMemory; + const size_t fSize; +}; + +SkAshmemDiscardableMemory::SkAshmemDiscardableMemory(int fd, + void* address, + size_t size) + : fLocked(true) // Ashmem pages are pinned by default. + , fFd(fd) + , fMemory(address) + , fSize(size) { + SkASSERT(fFd >= 0); + SkASSERT(fMemory != NULL); + SkASSERT(fSize > 0); +} + +SkAshmemDiscardableMemory::~SkAshmemDiscardableMemory() { + SkASSERT(!fLocked); + if (NULL != fMemory) { + munmap(fMemory, fSize); + } + if (fFd != -1) { + close(fFd); + } +} + +bool SkAshmemDiscardableMemory::lock() { + SkASSERT(!fLocked); + if (-1 == fFd) { + fLocked = false; + return false; + } + SkASSERT(fMemory != NULL); + if (fLocked || (ASHMEM_NOT_PURGED == ashmem_pin_region(fFd, 0, 0))) { + fLocked = true; + return true; + } else { + munmap(fMemory, fSize); + fMemory = NULL; + + close(fFd); + fFd = -1; + fLocked = false; + return false; + } +} + +void* SkAshmemDiscardableMemory::data() { + SkASSERT(fLocked); + return fLocked ? fMemory : NULL; +} + +void SkAshmemDiscardableMemory::unlock() { + SkASSERT(fLocked); + if (fLocked && (fFd != -1)) { + ashmem_unpin_region(fFd, 0, 0); + } + fLocked = false; +} +} // namespace +//////////////////////////////////////////////////////////////////////////////// + +SkDiscardableMemory* SkDiscardableMemory::Create(size_t bytes) { + // ashmem likes lengths on page boundaries. + const size_t mask = getpagesize() - 1; + size_t size = (bytes + mask) & ~mask; + + static const char name[] = "Skia_Ashmem_Discardable_Memory"; + int fd = ashmem_create_region(name, size); + if (fd < 0) { + return NULL; + } + if (0 != ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE)) { + close(fd); + return NULL; + } + void* addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + if ((MAP_FAILED == addr) || (NULL == addr)) { + close(fd); + return NULL; + } + + return SkNEW_ARGS(SkAshmemDiscardableMemory, (fd, addr, size)); +} + diff --git a/ports/SkDiscardableMemory_none.cpp b/ports/SkDiscardableMemory_none.cpp index 700713ba..51c3164d 100644 --- a/ports/SkDiscardableMemory_none.cpp +++ b/ports/SkDiscardableMemory_none.cpp @@ -5,57 +5,9 @@ * found in the LICENSE file. */ -#include "SkDiscardableMemory.h" +#include "SkDiscardableMemoryPool.h" #include "SkTypes.h" -namespace { -//////////////////////////////////////////////////////////////////////////////// -/** - * Always successful, never purges. Useful for testing. - */ -class SkMockDiscardableMemory : public SkDiscardableMemory { -public: - SkMockDiscardableMemory(void*); - virtual ~SkMockDiscardableMemory(); - virtual bool lock() SK_OVERRIDE; - virtual void* data() SK_OVERRIDE; - virtual void unlock() SK_OVERRIDE; -private: - bool fLocked; - void* fPointer; -}; - -//////////////////////////////////////////////////////////////////////////////// - -SkMockDiscardableMemory::SkMockDiscardableMemory(void* ptr) - : fLocked(true) - , fPointer(ptr) { // Takes ownership of ptr. - SkASSERT(fPointer != NULL); -} - -SkMockDiscardableMemory::~SkMockDiscardableMemory() { - SkASSERT(!fLocked); - sk_free(fPointer); -} - -bool SkMockDiscardableMemory::lock() { - SkASSERT(!fLocked); - return fLocked = true; -} - -void* SkMockDiscardableMemory::data() { - SkASSERT(fLocked); - return fLocked ? fPointer : NULL; -} - -void SkMockDiscardableMemory::unlock() { - SkASSERT(fLocked); - fLocked = false; -} -//////////////////////////////////////////////////////////////////////////////// -} // namespace - SkDiscardableMemory* SkDiscardableMemory::Create(size_t bytes) { - void* ptr = sk_malloc_throw(bytes); - return (ptr != NULL) ? SkNEW_ARGS(SkMockDiscardableMemory, (ptr)) : NULL; + return SkGetGlobalDiscardableMemoryPool()->create(bytes); } diff --git a/ports/SkFontConfigInterface_direct.cpp b/ports/SkFontConfigInterface_direct.cpp index f1ac7342..13993f10 100644 --- a/ports/SkFontConfigInterface_direct.cpp +++ b/ports/SkFontConfigInterface_direct.cpp @@ -330,11 +330,13 @@ bool IsFallbackFontAllowed(const std::string& family) { } static bool valid_pattern(FcPattern* pattern) { +#ifdef SK_FONT_CONFIG_ONLY_ALLOW_SCALABLE_FONTS FcBool is_scalable; if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != FcResultMatch || !is_scalable) { return false; } +#endif // fontconfig can also return fonts which are unreadable const char* c_filename = get_name(pattern, FC_FILE); diff --git a/ports/SkFontHost_FreeType.cpp b/ports/SkFontHost_FreeType.cpp index f4ec4205..745a58a9 100644 --- a/ports/SkFontHost_FreeType.cpp +++ b/ports/SkFontHost_FreeType.cpp @@ -58,6 +58,21 @@ #include <freetype/ftsynth.h> #endif +// FT_LOAD_COLOR and the corresponding FT_Pixel_Mode::FT_PIXEL_MODE_BGRA +// were introduced in FreeType 2.5.0. +// The following may be removed once FreeType 2.5.0 is required to build. +#ifndef FT_LOAD_COLOR +# define FT_LOAD_COLOR ( 1L << 20 ) +# define FT_PIXEL_MODE_BGRA 7 +#endif + +// FT_HAS_COLOR and the corresponding FT_FACE_FLAG_COLOR +// were introduced in FreeType 2.5.1 +// The following may be removed once FreeType 2.5.1 is required to build. +#ifndef FT_HAS_COLOR +# define FT_HAS_COLOR(face) false +#endif + //#define ENABLE_GLYPH_SPEW // for tracing calls //#define DUMP_STRIKE_CREATION @@ -184,6 +199,7 @@ private: SkFaceRec* fFaceRec; FT_Face fFace; // reference to shared face in gFaceRecHead FT_Size fFTSize; // our own copy + FT_Int fStrikeIndex; SkFixed fScaleX, fScaleY; FT_Matrix fMatrix22; uint32_t fLoadGlyphFlags; @@ -516,6 +532,8 @@ SkAdvancedTypefaceMetrics* SkTypeface_FreeType::onGetAdvancedTypefaceMetrics( ft_sfnt_head)) != NULL) { info->fEmSize = ttHeader->Units_Per_EM; } + } else { + info->fType = SkAdvancedTypefaceMetrics::kOther_Font; } info->fStyle = 0; @@ -749,6 +767,50 @@ bool SkTypeface_FreeType::onGetKerningPairAdjustments(const uint16_t glyphs[], return true; } +static FT_Int chooseBitmapStrike(FT_Face face, SkFixed scaleY) { + // early out if face is bad + if (face == NULL) { + SkDEBUGF(("chooseBitmapStrike aborted due to NULL face\n")); + return -1; + } + // determine target ppem + FT_Pos targetPPEM = SkFixedToFDot6(scaleY); + // find a bitmap strike equal to or just larger than the requested size + FT_Int chosenStrikeIndex = -1; + FT_Pos chosenPPEM = 0; + for (FT_Int strikeIndex = 0; strikeIndex < face->num_fixed_sizes; ++strikeIndex) { + FT_Pos thisPPEM = face->available_sizes[strikeIndex].y_ppem; + if (thisPPEM == targetPPEM) { + // exact match - our search stops here + chosenPPEM = thisPPEM; + chosenStrikeIndex = strikeIndex; + break; + } else if (chosenPPEM < targetPPEM) { + // attempt to increase chosenPPEM + if (thisPPEM > chosenPPEM) { + chosenPPEM = thisPPEM; + chosenStrikeIndex = strikeIndex; + } + } else { + // attempt to decrease chosenPPEM, but not below targetPPEM + if (thisPPEM < chosenPPEM && thisPPEM > targetPPEM) { + chosenPPEM = thisPPEM; + chosenStrikeIndex = strikeIndex; + } + } + } + if (chosenStrikeIndex != -1) { + // use the chosen strike + FT_Error err = FT_Select_Size(face, chosenStrikeIndex); + if (err != 0) { + SkDEBUGF(("FT_Select_Size(%s, %d) returned 0x%x\n", face->family_name, + chosenStrikeIndex, err)); + chosenStrikeIndex = -1; + } + } + return chosenStrikeIndex; +} + SkScalerContext_FreeType::SkScalerContext_FreeType(SkTypeface* typeface, const SkDescriptor* desc) : SkScalerContext_FreeType_Base(typeface, desc) { @@ -762,6 +824,7 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(SkTypeface* typeface, ++gFTCount; // load the font file + fStrikeIndex = -1; fFTSize = NULL; fFace = NULL; fFaceRec = ref_ft_face(typeface); @@ -821,9 +884,9 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(SkTypeface* typeface, fLCDIsVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag); // compute the flags we send to Load_Glyph + bool linearMetrics = SkToBool(fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag); { FT_Int32 loadFlags = FT_LOAD_DEFAULT; - bool linearMetrics = SkToBool(fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag); if (SkMask::kBW_Format == fRec.fMaskFormat) { // See http://code.google.com/p/chromium/issues/detail?id=43252#c24 @@ -881,42 +944,57 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(SkTypeface* typeface, loadFlags |= FT_LOAD_VERTICAL_LAYOUT; } + loadFlags |= FT_LOAD_COLOR; + fLoadGlyphFlags = loadFlags; - fDoLinearMetrics = linearMetrics; } - // now create the FT_Size - - { - FT_Error err; + FT_Error err = FT_New_Size(fFace, &fFTSize); + if (err != 0) { + SkDEBUGF(("FT_New_Size returned %x for face %s\n", err, fFace->family_name)); + fFace = NULL; + return; + } + err = FT_Activate_Size(fFTSize); + if (err != 0) { + SkDEBUGF(("FT_Activate_Size(%08x, 0x%x, 0x%x) returned 0x%x\n", fFace, fScaleX, fScaleY, + err)); + fFTSize = NULL; + return; + } - err = FT_New_Size(fFace, &fFTSize); + if (FT_IS_SCALABLE(fFace)) { + err = FT_Set_Char_Size(fFace, SkFixedToFDot6(fScaleX), SkFixedToFDot6(fScaleY), 72, 72); if (err != 0) { - SkDEBUGF(("SkScalerContext_FreeType::FT_New_Size(%x): FT_Set_Char_Size(0x%x, 0x%x) returned 0x%x\n", - fFaceRec->fFontID, fScaleX, fScaleY, err)); + SkDEBUGF(("FT_Set_CharSize(%08x, 0x%x, 0x%x) returned 0x%x\n", + fFace, fScaleX, fScaleY, err)); fFace = NULL; return; } - - err = FT_Activate_Size(fFTSize); - if (err != 0) { - SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n", - fFaceRec->fFontID, fScaleX, fScaleY, err)); - fFTSize = NULL; - } - - err = FT_Set_Char_Size( fFace, - SkFixedToFDot6(fScaleX), SkFixedToFDot6(fScaleY), - 72, 72); - if (err != 0) { - SkDEBUGF(("SkScalerContext_FreeType::FT_Set_Char_Size(%x, 0x%x, 0x%x) returned 0x%x\n", - fFaceRec->fFontID, fScaleX, fScaleY, err)); - fFace = NULL; - return; + FT_Set_Transform(fFace, &fMatrix22, NULL); + } else if (FT_HAS_FIXED_SIZES(fFace)) { + fStrikeIndex = chooseBitmapStrike(fFace, fScaleY); + if (fStrikeIndex == -1) { + SkDEBUGF(("no glyphs for font \"%s\" size %f?\n", + fFace->family_name, SkFixedToScalar(fScaleY))); + } else { + // FreeType does no provide linear metrics for bitmap fonts. + linearMetrics = false; + + // FreeType documentation says: + // FT_LOAD_NO_BITMAP -- Ignore bitmap strikes when loading. + // Bitmap-only fonts ignore this flag. + // + // However, in FreeType 2.5.1 color bitmap only fonts do not ignore this flag. + // Force this flag off for bitmap only fonts. + fLoadGlyphFlags &= ~FT_LOAD_NO_BITMAP; } - - FT_Set_Transform( fFace, &fMatrix22, NULL); + } else { + SkDEBUGF(("unknown kind of font \"%s\" size %f?\n", + fFace->family_name, SkFixedToScalar(fScaleY))); } + + fDoLinearMetrics = linearMetrics; } SkScalerContext_FreeType::~SkScalerContext_FreeType() { @@ -930,7 +1008,6 @@ SkScalerContext_FreeType::~SkScalerContext_FreeType() { unref_ft_face(fFace); } if (--gFTCount == 0) { -// SkDEBUGF(("FT_Done_FreeType\n")); FT_Done_FreeType(gFTLibrary); SkDEBUGCODE(gFTLibrary = NULL;) } @@ -940,18 +1017,18 @@ SkScalerContext_FreeType::~SkScalerContext_FreeType() { this face with other context (at different sizes). */ FT_Error SkScalerContext_FreeType::setupSize() { - FT_Error err = FT_Activate_Size(fFTSize); - + FT_Error err = FT_Activate_Size(fFTSize); if (err != 0) { SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n", - fFaceRec->fFontID, fScaleX, fScaleY, err)); + fFaceRec->fFontID, fScaleX, fScaleY, err)); fFTSize = NULL; - } else { - // seems we need to reset this every time (not sure why, but without it - // I get random italics from some other fFTSize) - FT_Set_Transform( fFace, &fMatrix22, NULL); + return err; } - return err; + + // seems we need to reset this every time (not sure why, but without it + // I get random italics from some other fFTSize) + FT_Set_Transform(fFace, &fMatrix22, NULL); + return 0; } unsigned SkScalerContext_FreeType::generateGlyphCount() { @@ -1061,6 +1138,17 @@ void SkScalerContext_FreeType::updateGlyphIfLCD(SkGlyph* glyph) { } } +inline void scaleGlyphMetrics(SkGlyph& glyph, SkScalar scale) { + glyph.fWidth *= scale; + glyph.fHeight *= scale; + glyph.fTop *= scale; + glyph.fLeft *= scale; + + SkFixed fixedScale = SkScalarToFixed(scale); + glyph.fAdvanceX = SkFixedMul(glyph.fAdvanceX, fixedScale); + glyph.fAdvanceY = SkFixedMul(glyph.fAdvanceY, fixedScale); +} + void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { SkAutoMutexAcquire ac(gFTMutex); @@ -1085,32 +1173,28 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { } switch ( fFace->glyph->format ) { - case FT_GLYPH_FORMAT_OUTLINE: { - FT_BBox bbox; - + case FT_GLYPH_FORMAT_OUTLINE: if (0 == fFace->glyph->outline.n_contours) { glyph->fWidth = 0; glyph->fHeight = 0; glyph->fTop = 0; glyph->fLeft = 0; - break; - } - - if (fRec.fFlags & kEmbolden_Flag) { - emboldenOutline(fFace, &fFace->glyph->outline); - } - - getBBoxForCurrentGlyph(glyph, &bbox, true); + } else { + if (fRec.fFlags & kEmbolden_Flag && !(fFace->style_flags & FT_STYLE_FLAG_BOLD)) { + emboldenOutline(fFace, &fFace->glyph->outline); + } - glyph->fWidth = SkToU16(SkFDot6Floor(bbox.xMax - bbox.xMin)); - glyph->fHeight = SkToU16(SkFDot6Floor(bbox.yMax - bbox.yMin)); - glyph->fTop = -SkToS16(SkFDot6Floor(bbox.yMax)); - glyph->fLeft = SkToS16(SkFDot6Floor(bbox.xMin)); + FT_BBox bbox; + getBBoxForCurrentGlyph(glyph, &bbox, true); - updateGlyphIfLCD(glyph); + glyph->fWidth = SkToU16(SkFDot6Floor(bbox.xMax - bbox.xMin)); + glyph->fHeight = SkToU16(SkFDot6Floor(bbox.yMax - bbox.yMin)); + glyph->fTop = -SkToS16(SkFDot6Floor(bbox.yMax)); + glyph->fLeft = SkToS16(SkFDot6Floor(bbox.xMin)); + updateGlyphIfLCD(glyph); + } break; - } case FT_GLYPH_FORMAT_BITMAP: if (fRec.fFlags & kEmbolden_Flag) { @@ -1127,6 +1211,10 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { fFace->glyph->bitmap_top += SkFDot6Floor(vector.y); } + if (fFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) { + glyph->fMaskFormat = SkMask::kARGB32_Format; + } + glyph->fWidth = SkToU16(fFace->glyph->bitmap.width); glyph->fHeight = SkToU16(fFace->glyph->bitmap.rows); glyph->fTop = -SkToS16(fFace->glyph->bitmap_top); @@ -1161,6 +1249,11 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { } } + if (fFace->glyph->format == FT_GLYPH_FORMAT_BITMAP && fScaleY && fFace->size->metrics.y_ppem) { + // NOTE: both dimensions are scaled by y_ppem. this is WAI. + scaleGlyphMetrics(*glyph, SkScalarDiv(SkFixedToScalar(fScaleY), + SkIntToScalar(fFace->size->metrics.y_ppem))); + } #ifdef ENABLE_GLYPH_SPEW SkDEBUGF(("FT_Set_Char_Size(this:%p sx:%x sy:%x ", this, fScaleX, fScaleY)); @@ -1248,77 +1341,104 @@ void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* mx, } FT_Face face = fFace; - int upem = face->units_per_EM; - if (upem <= 0) { - goto ERROR; - } - - SkPoint pts[6]; - SkFixed ys[6]; + SkScalar scaleX = fScale.x(); SkScalar scaleY = fScale.y(); - SkScalar mxy = fMatrix22Scalar.getSkewX(); - SkScalar myy = fMatrix22Scalar.getScaleY(); - SkScalar xmin = SkIntToScalar(face->bbox.xMin) / upem; - SkScalar xmax = SkIntToScalar(face->bbox.xMax) / upem; - - int leading = face->height - (face->ascender + -face->descender); - if (leading < 0) { - leading = 0; + SkScalar mxy = fMatrix22Scalar.getSkewX() * scaleY; + SkScalar myy = fMatrix22Scalar.getScaleY() * scaleY; + + // fetch units/EM from "head" table if needed (ie for bitmap fonts) + SkScalar upem = SkIntToScalar(face->units_per_EM); + if (!upem) { + TT_Header* ttHeader = (TT_Header*)FT_Get_Sfnt_Table(face, ft_sfnt_head); + if (ttHeader) { + upem = SkIntToScalar(ttHeader->Units_Per_EM); + } } - // Try to get the OS/2 table from the font. This contains the specific - // average font width metrics which Windows uses. + // use the os/2 table as a source of reasonable defaults. + SkScalar x_height = 0.0f; + SkScalar avgCharWidth = 0.0f; TT_OS2* os2 = (TT_OS2*) FT_Get_Sfnt_Table(face, ft_sfnt_os2); - - ys[0] = -face->bbox.yMax; - ys[1] = -face->ascender; - ys[2] = -face->descender; - ys[3] = -face->bbox.yMin; - ys[4] = leading; - ys[5] = os2 ? os2->xAvgCharWidth : 0; - - SkScalar x_height; - if (os2 && os2->sxHeight) { - x_height = fScale.x() * os2->sxHeight / upem; - } else { - const FT_UInt x_glyph = FT_Get_Char_Index(fFace, 'x'); - if (x_glyph) { - FT_BBox bbox; - FT_Load_Glyph(fFace, x_glyph, fLoadGlyphFlags); - if (fRec.fFlags & kEmbolden_Flag) { - emboldenOutline(fFace, &fFace->glyph->outline); + if (os2) { + x_height = scaleX * SkIntToScalar(os2->sxHeight) / upem; + avgCharWidth = SkIntToScalar(os2->xAvgCharWidth) / upem; + } + + // pull from format-specific metrics as needed + SkScalar ascent, descent, leading, xmin, xmax, ymin, ymax; + if (face->face_flags & FT_FACE_FLAG_SCALABLE) { // scalable outline font + ascent = -SkIntToScalar(face->ascender) / upem; + descent = -SkIntToScalar(face->descender) / upem; + leading = SkIntToScalar(face->height + (face->descender - face->ascender)) / upem; + xmin = SkIntToScalar(face->bbox.xMin) / upem; + xmax = SkIntToScalar(face->bbox.xMax) / upem; + ymin = -SkIntToScalar(face->bbox.yMin) / upem; + ymax = -SkIntToScalar(face->bbox.yMax) / upem; + // we may be able to synthesize x_height from outline + if (!x_height) { + const FT_UInt x_glyph = FT_Get_Char_Index(fFace, 'x'); + if (x_glyph) { + FT_BBox bbox; + FT_Load_Glyph(fFace, x_glyph, fLoadGlyphFlags); + if ((fRec.fFlags & kEmbolden_Flag) && !(fFace->style_flags & FT_STYLE_FLAG_BOLD)) { + emboldenOutline(fFace, &fFace->glyph->outline); + } + FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox); + x_height = SkIntToScalar(bbox.yMax) / 64.0f; } - FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox); - x_height = bbox.yMax / 64.0f; - } else { - x_height = 0; } + } else if (fStrikeIndex != -1) { // bitmap strike metrics + SkScalar xppem = SkIntToScalar(face->size->metrics.x_ppem); + SkScalar yppem = SkIntToScalar(face->size->metrics.y_ppem); + ascent = -SkIntToScalar(face->size->metrics.ascender) / (yppem * 64.0f); + descent = -SkIntToScalar(face->size->metrics.descender) / (yppem * 64.0f); + leading = (SkIntToScalar(face->size->metrics.height) / (yppem * 64.0f)) + + ascent - descent; + xmin = 0.0f; + xmax = SkIntToScalar(face->available_sizes[fStrikeIndex].width) / xppem; + ymin = descent + leading; + ymax = ascent - descent; + if (!x_height) { + x_height = -ascent; + } + if (!avgCharWidth) { + avgCharWidth = xmax - xmin; + } + } else { + goto ERROR; + } + + // synthesize elements that were not provided by the os/2 table or format-specific metrics + if (!x_height) { + x_height = -ascent; + } + if (!avgCharWidth) { + avgCharWidth = xmax - xmin; } - // convert upem-y values into scalar points - for (int i = 0; i < 6; i++) { - SkScalar y = scaleY * ys[i] / upem; - pts[i].set(y * mxy, y * myy); + // disallow negative linespacing + if (leading < 0.0f) { + leading = 0.0f; } if (mx) { - mx->fTop = pts[0].fX; - mx->fAscent = pts[1].fX; - mx->fDescent = pts[2].fX; - mx->fBottom = pts[3].fX; - mx->fLeading = pts[4].fX; - mx->fAvgCharWidth = pts[5].fX; + mx->fTop = ymax * mxy; + mx->fAscent = ascent * mxy; + mx->fDescent = descent * mxy; + mx->fBottom = ymin * mxy; + mx->fLeading = leading * mxy; + mx->fAvgCharWidth = avgCharWidth * mxy; mx->fXMin = xmin; mx->fXMax = xmax; mx->fXHeight = x_height; } if (my) { - my->fTop = pts[0].fY; - my->fAscent = pts[1].fY; - my->fDescent = pts[2].fY; - my->fBottom = pts[3].fY; - my->fLeading = pts[4].fY; - my->fAvgCharWidth = pts[5].fY; + my->fTop = ymax * myy; + my->fAscent = ascent * myy; + my->fDescent = descent * myy; + my->fBottom = ymin * myy; + my->fLeading = leading * myy; + my->fAvgCharWidth = avgCharWidth * myy; my->fXMin = xmin; my->fXMax = xmax; my->fXHeight = x_height; diff --git a/ports/SkFontHost_FreeType_common.cpp b/ports/SkFontHost_FreeType_common.cpp index 2c486847..065a83a4 100644 --- a/ports/SkFontHost_FreeType_common.cpp +++ b/ports/SkFontHost_FreeType_common.cpp @@ -6,17 +6,32 @@ * found in the LICENSE file. */ +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkColor.h" #include "SkColorPriv.h" #include "SkFDot6.h" #include "SkFontHost_FreeType_common.h" #include "SkPath.h" #include <ft2build.h> -#include FT_OUTLINE_H +#include FT_FREETYPE_H #include FT_BITMAP_H +#include FT_IMAGE_H +#include FT_OUTLINE_H // In the past, FT_GlyphSlot_Own_Bitmap was defined in this header file. #include FT_SYNTHESIS_H +// FT_LOAD_COLOR and the corresponding FT_Pixel_Mode::FT_PIXEL_MODE_BGRA +// were introduced in FreeType 2.5.0. +// The following may be removed once FreeType 2.5.0 is required to build. +#ifndef FT_LOAD_COLOR +# define FT_LOAD_COLOR ( 1L << 20 ) +# define FT_PIXEL_MODE_BGRA 7 +#endif + +//#define SK_SHOW_TEXT_BLIT_COVERAGE + static FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) { switch (format) { case SkMask::kBW_Format: @@ -29,13 +44,20 @@ static FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) { /////////////////////////////////////////////////////////////////////////////// -static uint16_t packTriple(unsigned r, unsigned g, unsigned b) { - return SkPackRGB16(r >> 3, g >> 2, b >> 3); +static uint16_t packTriple(U8CPU r, U8CPU g, U8CPU b) { +#ifdef SK_SHOW_TEXT_BLIT_COVERAGE + r = SkTMax(r, (U8CPU)0x40); + g = SkTMax(g, (U8CPU)0x40); + b = SkTMax(b, (U8CPU)0x40); +#endif + return SkPack888ToRGB16(r, g, b); } static uint16_t grayToRGB16(U8CPU gray) { - SkASSERT(gray <= 255); - return SkPackRGB16(gray >> 3, gray >> 2, gray >> 3); +#ifdef SK_SHOW_TEXT_BLIT_COVERAGE + gray = SkTMax(gray, (U8CPU)0x40); +#endif + return SkPack888ToRGB16(gray, gray, gray); } static int bittst(const uint8_t data[], int bitOffset) { @@ -44,78 +66,271 @@ static int bittst(const uint8_t data[], int bitOffset) { return lowBit & 1; } +/** + * Copies a FT_Bitmap into an SkMask with the same dimensions. + * + * FT_PIXEL_MODE_MONO + * FT_PIXEL_MODE_GRAY + * FT_PIXEL_MODE_LCD + * FT_PIXEL_MODE_LCD_V + */ template<bool APPLY_PREBLEND> -static void copyFT2LCD16(const SkGlyph& glyph, const FT_Bitmap& bitmap, - int lcdIsBGR, bool lcdIsVert, const uint8_t* tableR, - const uint8_t* tableG, const uint8_t* tableB) { - if (lcdIsVert) { - SkASSERT(3 * glyph.fHeight == bitmap.rows); - } else { - SkASSERT(glyph.fHeight == bitmap.rows); +static void copyFT2LCD16(const FT_Bitmap& bitmap, const SkMask& mask, int lcdIsBGR, + const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) +{ + SkASSERT(SkMask::kLCD16_Format == mask.fFormat); + if (FT_PIXEL_MODE_LCD != bitmap.pixel_mode) { + SkASSERT(mask.fBounds.width() == bitmap.width); + } + if (FT_PIXEL_MODE_LCD_V != bitmap.pixel_mode) { + SkASSERT(mask.fBounds.height() == bitmap.rows); } - uint16_t* dst = reinterpret_cast<uint16_t*>(glyph.fImage); - const size_t dstRB = glyph.rowBytes(); - const int width = glyph.fWidth; const uint8_t* src = bitmap.buffer; + uint16_t* dst = reinterpret_cast<uint16_t*>(mask.fImage); + const size_t dstRB = mask.fRowBytes; + + const int width = mask.fBounds.width(); + const int height = mask.fBounds.height(); switch (bitmap.pixel_mode) { - case FT_PIXEL_MODE_MONO: { - for (int y = 0; y < glyph.fHeight; ++y) { + case FT_PIXEL_MODE_MONO: + for (int y = height; y --> 0;) { for (int x = 0; x < width; ++x) { dst[x] = -bittst(src, x); } dst = (uint16_t*)((char*)dst + dstRB); src += bitmap.pitch; } - } break; - case FT_PIXEL_MODE_GRAY: { - for (int y = 0; y < glyph.fHeight; ++y) { + break; + case FT_PIXEL_MODE_GRAY: + for (int y = height; y --> 0;) { for (int x = 0; x < width; ++x) { dst[x] = grayToRGB16(src[x]); } dst = (uint16_t*)((char*)dst + dstRB); src += bitmap.pitch; } - } break; - default: { - SkASSERT(lcdIsVert || (glyph.fWidth * 3 == bitmap.width)); - for (int y = 0; y < glyph.fHeight; y++) { - if (lcdIsVert) { // vertical stripes - const uint8_t* srcR = src; - const uint8_t* srcG = srcR + bitmap.pitch; - const uint8_t* srcB = srcG + bitmap.pitch; - if (lcdIsBGR) { - SkTSwap(srcR, srcB); - } + break; + case FT_PIXEL_MODE_LCD: + SkASSERT(3 * mask.fBounds.width() == bitmap.width); + for (int y = height; y --> 0;) { + const uint8_t* triple = src; + if (lcdIsBGR) { for (int x = 0; x < width; x++) { - dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(*srcR++, tableR), - sk_apply_lut_if<APPLY_PREBLEND>(*srcG++, tableG), - sk_apply_lut_if<APPLY_PREBLEND>(*srcB++, tableB)); + dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableR), + sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG), + sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableB)); + triple += 3; } - src += 3 * bitmap.pitch; - } else { // horizontal stripes - const uint8_t* triple = src; - if (lcdIsBGR) { - for (int x = 0; x < width; x++) { - dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableR), - sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG), - sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableB)); - triple += 3; - } - } else { - for (int x = 0; x < width; x++) { - dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableR), - sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG), - sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableB)); - triple += 3; - } + } else { + for (int x = 0; x < width; x++) { + dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableR), + sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG), + sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableB)); + triple += 3; } - src += bitmap.pitch; } + src += bitmap.pitch; dst = (uint16_t*)((char*)dst + dstRB); } - } break; + break; + case FT_PIXEL_MODE_LCD_V: + SkASSERT(3 * mask.fBounds.height() == bitmap.rows); + for (int y = height; y --> 0;) { + const uint8_t* srcR = src; + const uint8_t* srcG = srcR + bitmap.pitch; + const uint8_t* srcB = srcG + bitmap.pitch; + if (lcdIsBGR) { + SkTSwap(srcR, srcB); + } + for (int x = 0; x < width; x++) { + dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(*srcR++, tableR), + sk_apply_lut_if<APPLY_PREBLEND>(*srcG++, tableG), + sk_apply_lut_if<APPLY_PREBLEND>(*srcB++, tableB)); + } + src += 3 * bitmap.pitch; + dst = (uint16_t*)((char*)dst + dstRB); + } + break; + default: + SkDEBUGF(("FT_Pixel_Mode %d", bitmap.pixel_mode)); + SkDEBUGFAIL("unsupported FT_Pixel_Mode for LCD16"); + break; + } +} + +/** + * Copies a FT_Bitmap into an SkMask with the same dimensions. + * + * Yes, No, Never Requested, Never Produced + * + * kBW kA8 k3D kARGB32 kLCD16 kLCD32 + * FT_PIXEL_MODE_MONO Y Y NR N Y NR + * FT_PIXEL_MODE_GRAY N Y NR N Y NR + * FT_PIXEL_MODE_GRAY2 NP NP NR NP NP NR + * FT_PIXEL_MODE_GRAY4 NP NP NR NP NP NR + * FT_PIXEL_MODE_LCD NP NP NR NP NP NR + * FT_PIXEL_MODE_LCD_V NP NP NR NP NP NR + * FT_PIXEL_MODE_BGRA N N NR Y N NR + * + * TODO: All of these N need to be Y or otherwise ruled out. + */ +static void copyFTBitmap(const FT_Bitmap& srcFTBitmap, SkMask& dstMask) { + SkASSERT(dstMask.fBounds.width() == srcFTBitmap.width); + SkASSERT(dstMask.fBounds.height() == srcFTBitmap.rows); + + const uint8_t* src = reinterpret_cast<const uint8_t*>(srcFTBitmap.buffer); + const FT_Pixel_Mode srcFormat = static_cast<FT_Pixel_Mode>(srcFTBitmap.pixel_mode); + // FT_Bitmap::pitch is an int and allowed to be negative. + const int srcPitch = srcFTBitmap.pitch; + const size_t srcRowBytes = SkTAbs(srcPitch); + + uint8_t* dst = dstMask.fImage; + const SkMask::Format dstFormat = static_cast<SkMask::Format>(dstMask.fFormat); + const size_t dstRowBytes = dstMask.fRowBytes; + + const size_t width = srcFTBitmap.width; + const size_t height = srcFTBitmap.rows; + + if (SkMask::kLCD16_Format == dstFormat) { + copyFT2LCD16<false>(srcFTBitmap, dstMask, false, NULL, NULL, NULL); + return; + } + + if ((FT_PIXEL_MODE_MONO == srcFormat && SkMask::kBW_Format == dstFormat) || + (FT_PIXEL_MODE_GRAY == srcFormat && SkMask::kA8_Format == dstFormat)) + { + size_t commonRowBytes = SkTMin(srcRowBytes, dstRowBytes); + for (size_t y = height; y --> 0;) { + memcpy(dst, src, commonRowBytes); + src += srcPitch; + dst += dstRowBytes; + } + } else if (FT_PIXEL_MODE_MONO == srcFormat && SkMask::kA8_Format == dstFormat) { + for (size_t y = height; y --> 0;) { + uint8_t byte = 0; + int bits = 0; + const uint8_t* src_row = src; + uint8_t* dst_row = dst; + for (size_t x = width; x --> 0;) { + if (0 == bits) { + byte = *src_row++; + bits = 8; + } + *dst_row++ = byte & 0x80 ? 0xff : 0x00; + bits--; + byte <<= 1; + } + src += srcPitch; + dst += dstRowBytes; + } + } else if (FT_PIXEL_MODE_BGRA == srcFormat && SkMask::kARGB32_Format == dstFormat) { + // FT_PIXEL_MODE_BGRA is pre-multiplied. + for (size_t y = height; y --> 0;) { + const uint8_t* src_row = src; + SkPMColor* dst_row = reinterpret_cast<SkPMColor*>(dst); + for (size_t x = 0; x < width; ++x) { + uint8_t b = *src_row++; + uint8_t g = *src_row++; + uint8_t r = *src_row++; + uint8_t a = *src_row++; + *dst_row++ = SkPackARGB32(a, r, g, b); +#ifdef SK_SHOW_TEXT_BLIT_COVERAGE + *(dst_row-1) = SkFourByteInterp256(*(dst_row-1), SK_ColorWHITE, 0x40); +#endif + } + src += srcPitch; + dst += dstRowBytes; + } + } else { + SkDEBUGF(("FT_Pixel_Mode %d, SkMask::Format %d\n", srcFormat, dstFormat)); + SkDEBUGFAIL("unsupported combination of FT_Pixel_Mode and SkMask::Format"); + } +} + +static inline int convert_8_to_1(unsigned byte) { + SkASSERT(byte <= 0xFF); + // Arbitrary decision that making the cutoff at 1/4 instead of 1/2 in general looks better. + return (byte >> 6) != 0; +} + +static uint8_t pack_8_to_1(const uint8_t alpha[8]) { + unsigned bits = 0; + for (int i = 0; i < 8; ++i) { + bits <<= 1; + bits |= convert_8_to_1(alpha[i]); + } + return SkToU8(bits); +} + +static void packA8ToA1(const SkMask& mask, const uint8_t* src, size_t srcRB) { + const int height = mask.fBounds.height(); + const int width = mask.fBounds.width(); + const int octs = width >> 3; + const int leftOverBits = width & 7; + + uint8_t* dst = mask.fImage; + const int dstPad = mask.fRowBytes - SkAlign8(width)/8; + SkASSERT(dstPad >= 0); + + const int srcPad = srcRB - width; + SkASSERT(srcPad >= 0); + + for (int y = 0; y < height; ++y) { + for (int i = 0; i < octs; ++i) { + *dst++ = pack_8_to_1(src); + src += 8; + } + if (leftOverBits > 0) { + unsigned bits = 0; + int shift = 7; + for (int i = 0; i < leftOverBits; ++i, --shift) { + bits |= convert_8_to_1(*src++) << shift; + } + *dst++ = bits; + } + src += srcPad; + dst += dstPad; + } +} + +inline SkMask::Format SkMaskFormat_for_SkBitmapConfig(SkBitmap::Config config) { + switch (config) { + case SkBitmap::kA8_Config: + return SkMask::kA8_Format; + case SkBitmap::kARGB_8888_Config: + return SkMask::kARGB32_Format; + default: + SkDEBUGFAIL("unsupported SkBitmap::Config"); + return SkMask::kA8_Format; + } +} + +inline SkBitmap::Config SkBitmapConfig_for_FTPixelMode(FT_Pixel_Mode pixel_mode) { + switch (pixel_mode) { + case FT_PIXEL_MODE_MONO: + case FT_PIXEL_MODE_GRAY: + return SkBitmap::kA8_Config; + case FT_PIXEL_MODE_BGRA: + return SkBitmap::kARGB_8888_Config; + default: + SkDEBUGFAIL("unsupported FT_PIXEL_MODE"); + return SkBitmap::kA8_Config; + } +} + +inline SkBitmap::Config SkBitmapConfig_for_SkMaskFormat(SkMask::Format format) { + switch (format) { + case SkMask::kBW_Format: + case SkMask::kA8_Format: + case SkMask::kLCD16_Format: + return SkBitmap::kA8_Config; + case SkMask::kARGB32_Format: + return SkBitmap::kARGB_8888_Config; + default: + SkDEBUGFAIL("unsupported destination SkBitmap::Config"); + return SkBitmap::kA8_Config; } } @@ -129,7 +344,8 @@ void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGly FT_BBox bbox; FT_Bitmap target; - if (fRec.fFlags & SkScalerContext::kEmbolden_Flag) { + if (fRec.fFlags & SkScalerContext::kEmbolden_Flag && + !(face->style_flags & FT_STYLE_FLAG_BOLD)) { emboldenOutline(face, outline); } @@ -154,11 +370,13 @@ void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGly if (SkMask::kLCD16_Format == glyph.fMaskFormat) { FT_Render_Glyph(face->glyph, doVert ? FT_RENDER_MODE_LCD_V : FT_RENDER_MODE_LCD); + SkMask mask; + glyph.toMask(&mask); if (fPreBlend.isApplicable()) { - copyFT2LCD16<true>(glyph, face->glyph->bitmap, doBGR, doVert, + copyFT2LCD16<true>(face->glyph->bitmap, mask, doBGR, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } else { - copyFT2LCD16<false>(glyph, face->glyph->bitmap, doBGR, doVert, + copyFT2LCD16<false>(face->glyph->bitmap, mask, doBGR, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } } else { @@ -166,8 +384,7 @@ void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGly target.rows = glyph.fHeight; target.pitch = glyph.rowBytes(); target.buffer = reinterpret_cast<uint8_t*>(glyph.fImage); - target.pixel_mode = compute_pixel_mode( - (SkMask::Format)fRec.fMaskFormat); + target.pixel_mode = compute_pixel_mode( (SkMask::Format)fRec.fMaskFormat); target.num_grays = 256; memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); @@ -176,71 +393,106 @@ void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGly } break; case FT_GLYPH_FORMAT_BITMAP: { - if (fRec.fFlags & SkScalerContext::kEmbolden_Flag) { + FT_Pixel_Mode pixel_mode = static_cast<FT_Pixel_Mode>(face->glyph->bitmap.pixel_mode); + SkMask::Format maskFormat = static_cast<SkMask::Format>(glyph.fMaskFormat); + + // Assume that the other formats do not exist. + SkASSERT(FT_PIXEL_MODE_MONO == pixel_mode || + FT_PIXEL_MODE_GRAY == pixel_mode || + FT_PIXEL_MODE_BGRA == pixel_mode); + + // These are the only formats this ScalerContext should request. + SkASSERT(SkMask::kBW_Format == maskFormat || + SkMask::kA8_Format == maskFormat || + SkMask::kARGB32_Format == maskFormat || + SkMask::kLCD16_Format == maskFormat); + + if (fRec.fFlags & SkScalerContext::kEmbolden_Flag && + !(face->style_flags & FT_STYLE_FLAG_BOLD)) + { FT_GlyphSlot_Own_Bitmap(face->glyph); - FT_Bitmap_Embolden(face->glyph->library, &face->glyph->bitmap, kBitmapEmboldenStrength, 0); + FT_Bitmap_Embolden(face->glyph->library, &face->glyph->bitmap, + kBitmapEmboldenStrength, 0); } - SkASSERT_CONTINUE(glyph.fWidth == face->glyph->bitmap.width); - SkASSERT_CONTINUE(glyph.fHeight == face->glyph->bitmap.rows); - SkASSERT_CONTINUE(glyph.fTop == -face->glyph->bitmap_top); - SkASSERT_CONTINUE(glyph.fLeft == face->glyph->bitmap_left); - - const uint8_t* src = (const uint8_t*)face->glyph->bitmap.buffer; - uint8_t* dst = (uint8_t*)glyph.fImage; - - if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY || - (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO && - glyph.fMaskFormat == SkMask::kBW_Format)) { - unsigned srcRowBytes = face->glyph->bitmap.pitch; - unsigned dstRowBytes = glyph.rowBytes(); - unsigned minRowBytes = SkMin32(srcRowBytes, dstRowBytes); - unsigned extraRowBytes = dstRowBytes - minRowBytes; - - for (int y = face->glyph->bitmap.rows - 1; y >= 0; --y) { - memcpy(dst, src, minRowBytes); - memset(dst + minRowBytes, 0, extraRowBytes); - src += srcRowBytes; - dst += dstRowBytes; - } - } else if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO && - glyph.fMaskFormat == SkMask::kA8_Format) { - for (int y = 0; y < face->glyph->bitmap.rows; ++y) { - uint8_t byte = 0; - int bits = 0; - const uint8_t* src_row = src; - uint8_t* dst_row = dst; - - for (int x = 0; x < face->glyph->bitmap.width; ++x) { - if (!bits) { - byte = *src_row++; - bits = 8; - } - - *dst_row++ = byte & 0x80 ? 0xff : 0; - bits--; - byte <<= 1; - } - src += face->glyph->bitmap.pitch; - dst += glyph.rowBytes(); - } - } else if (SkMask::kLCD16_Format == glyph.fMaskFormat) { - if (fPreBlend.isApplicable()) { - copyFT2LCD16<true>(glyph, face->glyph->bitmap, doBGR, doVert, - fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); - } else { - copyFT2LCD16<false>(glyph, face->glyph->bitmap, doBGR, doVert, - fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); - } + // If no scaling needed, directly copy glyph bitmap. + if (glyph.fWidth == face->glyph->bitmap.width && + glyph.fHeight == face->glyph->bitmap.rows && + glyph.fTop == -face->glyph->bitmap_top && + glyph.fLeft == face->glyph->bitmap_left) + { + SkMask dstMask; + glyph.toMask(&dstMask); + copyFTBitmap(face->glyph->bitmap, dstMask); + break; + } + + // Otherwise, scale the bitmap. + + // Copy the FT_Bitmap into an SkBitmap (either A8 or ARGB) + SkBitmap unscaledBitmap; + unscaledBitmap.setConfig(SkBitmapConfig_for_FTPixelMode(pixel_mode), + face->glyph->bitmap.width, face->glyph->bitmap.rows); + unscaledBitmap.allocPixels(); + + SkMask unscaledBitmapAlias; + unscaledBitmapAlias.fImage = reinterpret_cast<uint8_t*>(unscaledBitmap.getPixels()); + unscaledBitmapAlias.fBounds.set(0, 0, unscaledBitmap.width(), unscaledBitmap.height()); + unscaledBitmapAlias.fRowBytes = unscaledBitmap.rowBytes(); + unscaledBitmapAlias.fFormat = SkMaskFormat_for_SkBitmapConfig(unscaledBitmap.config()); + copyFTBitmap(face->glyph->bitmap, unscaledBitmapAlias); + + // Wrap the glyph's mask in a bitmap, unless the glyph's mask is BW or LCD. + // BW requires an A8 target for resizing, which can then be down sampled. + // LCD should use a 4x A8 target, which will then be down sampled. + // For simplicity, LCD uses A8 and is replicated. + int bitmapRowBytes = 0; + if (SkMask::kBW_Format != maskFormat && SkMask::kLCD16_Format != maskFormat) { + bitmapRowBytes = glyph.rowBytes(); + } + SkBitmap dstBitmap; + dstBitmap.setConfig(SkBitmapConfig_for_SkMaskFormat(maskFormat), + glyph.fWidth, glyph.fHeight, bitmapRowBytes); + if (SkMask::kBW_Format == maskFormat || SkMask::kLCD16_Format == maskFormat) { + dstBitmap.allocPixels(); } else { - SkDEBUGFAIL("unknown glyph bitmap transform needed"); + dstBitmap.setPixels(glyph.fImage); } + + // Scale unscaledBitmap into dstBitmap. + SkCanvas canvas(dstBitmap); + canvas.clear(SK_ColorTRANSPARENT); + canvas.scale(SkIntToScalar(glyph.fWidth) / SkIntToScalar(face->glyph->bitmap.width), + SkIntToScalar(glyph.fHeight) / SkIntToScalar(face->glyph->bitmap.rows)); + SkPaint paint; + paint.setFilterLevel(SkPaint::kLow_FilterLevel); + canvas.drawBitmap(unscaledBitmap, 0, 0, &paint); + + // If the destination is BW or LCD, convert from A8. + if (SkMask::kBW_Format == maskFormat) { + // Copy the A8 dstBitmap into the A1 glyph.fImage. + SkMask dstMask; + glyph.toMask(&dstMask); + packA8ToA1(dstMask, dstBitmap.getAddr8(0, 0), dstBitmap.rowBytes()); + } else if (SkMask::kLCD16_Format == maskFormat) { + // Copy the A8 dstBitmap into the LCD16 glyph.fImage. + uint8_t* src = dstBitmap.getAddr8(0, 0); + uint16_t* dst = reinterpret_cast<uint16_t*>(glyph.fImage); + for (int y = dstBitmap.height(); y --> 0;) { + for (int x = 0; x < dstBitmap.width(); ++x) { + dst[x] = grayToRGB16(src[x]); + } + dst = (uint16_t*)((char*)dst + glyph.rowBytes()); + src += dstBitmap.rowBytes(); + } + } + } break; - default: - SkDEBUGFAIL("unknown glyph format"); - memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); - return; + default: + SkDEBUGFAIL("unknown glyph format"); + memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); + return; } // We used to always do this pre-USE_COLOR_LUMINANCE, but with colorlum, diff --git a/ports/SkFontHost_mac.cpp b/ports/SkFontHost_mac.cpp index 3c1a2748..fe3fd069 100755 --- a/ports/SkFontHost_mac.cpp +++ b/ports/SkFontHost_mac.cpp @@ -2107,7 +2107,13 @@ static SkTypeface* createFromDesc(CFStringRef cfFamilyName, return face; } - AutoCFRelease<CTFontRef> ctNamed(CTFontCreateWithName(cfFamilyName, 1, NULL)); + AutoCFRelease<CFDictionaryRef> fontFamilyNameDictionary( + CFDictionaryCreate(kCFAllocatorDefault, + (const void**)&kCTFontFamilyNameAttribute, (const void**)&cfFamilyName, + 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + AutoCFRelease<CTFontDescriptorRef> fontDescriptor( + CTFontDescriptorCreateWithAttributes(fontFamilyNameDictionary)); + AutoCFRelease<CTFontRef> ctNamed(CTFontCreateWithFontDescriptor(fontDescriptor, 0, NULL)); CTFontRef ctFont = CTFontCreateCopyWithAttributes(ctNamed, 1, NULL, desc); if (NULL == ctFont) { return NULL; diff --git a/ports/SkFontHost_win_dw.cpp b/ports/SkFontHost_win_dw.cpp index 89e85b73..00623623 100644 --- a/ports/SkFontHost_win_dw.cpp +++ b/ports/SkFontHost_win_dw.cpp @@ -21,6 +21,7 @@ #include "SkGlyph.h" #include "SkHRESULT.h" #include "SkMaskGamma.h" +#include "SkOnce.h" #include "SkOTTable_head.h" #include "SkOTTable_hhea.h" #include "SkOTTable_OS_2.h" @@ -44,6 +45,7 @@ static bool isLCD(const SkScalerContext::Rec& rec) { /** Prefer to use this type to prevent template proliferation. */ typedef SkAutoSTMalloc<16, WCHAR> SkSMallocWCHAR; +/** Converts a utf8 string to a WCHAR string. */ static HRESULT cstring_to_wchar(const char* skname, SkSMallocWCHAR* name) { int wlen = MultiByteToWideChar(CP_UTF8, 0, skname, -1, NULL, 0); if (0 == wlen) { @@ -58,6 +60,7 @@ static HRESULT cstring_to_wchar(const char* skname, SkSMallocWCHAR* name) { return S_OK; } +/** Converts a WCHAR string to a utf8 string. */ static HRESULT wchar_to_skstring(WCHAR* name, SkString* skname) { int len = WideCharToMultiByte(CP_UTF8, 0, name, -1, NULL, 0, NULL, NULL); if (0 == len) { @@ -74,6 +77,35 @@ static HRESULT wchar_to_skstring(WCHAR* name, SkString* skname) { /////////////////////////////////////////////////////////////////////////////// +static void create_dwrite_factory(IDWriteFactory** factory) { + typedef decltype(DWriteCreateFactory)* DWriteCreateFactoryProc; + DWriteCreateFactoryProc dWriteCreateFactoryProc = reinterpret_cast<DWriteCreateFactoryProc>( + GetProcAddress(LoadLibraryW(L"dwrite.dll"), "DWriteCreateFactory")); + + if (!dWriteCreateFactoryProc) { + HRESULT hr = HRESULT_FROM_WIN32(GetLastError()); + if (!IS_ERROR(hr)) { + hr = ERROR_PROC_NOT_FOUND; + } + HRVM(hr, "Could not get DWriteCreateFactory proc."); + } + + HRVM(dWriteCreateFactoryProc(DWRITE_FACTORY_TYPE_SHARED, + __uuidof(IDWriteFactory), + reinterpret_cast<IUnknown**>(factory)), + "Could not create DirectWrite factory."); +} + +static IDWriteFactory* get_dwrite_factory() { + static IDWriteFactory* gDWriteFactory = NULL; + SK_DECLARE_STATIC_ONCE(once); + SkOnce(&once, create_dwrite_factory, &gDWriteFactory); + + return gDWriteFactory; +} + +/////////////////////////////////////////////////////////////////////////////// + class StreamFontFileLoader; class SkFontMgr_DirectWrite : public SkFontMgr { @@ -173,40 +205,9 @@ private: SkTDArray<uint8_t> fBits; }; -static HRESULT get_dwrite_factory(IDWriteFactory** factory) { - static IDWriteFactory* gDWriteFactory = NULL; - - if (gDWriteFactory != NULL) { - *factory = gDWriteFactory; - return S_OK; - } - - typedef decltype(DWriteCreateFactory)* DWriteCreateFactoryProc; - DWriteCreateFactoryProc dWriteCreateFactoryProc = - reinterpret_cast<DWriteCreateFactoryProc>( - GetProcAddress(LoadLibraryW(L"dwrite.dll"), "DWriteCreateFactory") - ) - ; - if (!dWriteCreateFactoryProc) { - HRESULT hr = HRESULT_FROM_WIN32(GetLastError()); - if (!IS_ERROR(hr)) { - hr = ERROR_PROC_NOT_FOUND; - } - return hr; - } - - HRM(dWriteCreateFactoryProc(DWRITE_FACTORY_TYPE_SHARED, - __uuidof(IDWriteFactory), - reinterpret_cast<IUnknown**>(&gDWriteFactory)), - "Could not create DirectWrite factory."); - - *factory = gDWriteFactory; - return S_OK; -} - const void* DWriteOffscreen::draw(const SkGlyph& glyph, bool isBW) { - IDWriteFactory* factory; - HRNM(get_dwrite_factory(&factory), "Could not get factory."); + IDWriteFactory* factory = get_dwrite_factory(); + SkASSERT(factory != NULL); if (fWidth < glyph.fWidth || fHeight < glyph.fHeight) { fWidth = SkMax32(fWidth, glyph.fWidth); @@ -553,8 +554,8 @@ public: ~DWriteFontTypeface() { if (fDWriteFontCollectionLoader.get() == NULL) return; - IDWriteFactory* factory; - HRVM(get_dwrite_factory(&factory), "Could not get factory."); + IDWriteFactory* factory = get_dwrite_factory(); + SkASSERT(factory != NULL); HRV(factory->UnregisterFontCollectionLoader(fDWriteFontCollectionLoader.get())); HRV(factory->UnregisterFontFileLoader(fDWriteFontFileLoader.get())); } @@ -806,8 +807,8 @@ void SkScalerContext_DW::generateMetrics(SkGlyph* glyph) { run.isSideways = FALSE; run.glyphOffsets = &offset; - IDWriteFactory* factory; - HRVM(get_dwrite_factory(&factory), "Could not get factory."); + IDWriteFactory* factory = get_dwrite_factory(); + SkASSERT(factory != NULL); const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat; DWRITE_RENDERING_MODE renderingMode; @@ -1272,8 +1273,10 @@ private: }; static SkTypeface* create_from_stream(SkStream* stream, int ttcIndex) { - IDWriteFactory* factory; - HRN(get_dwrite_factory(&factory)); + IDWriteFactory* factory = get_dwrite_factory(); + if (NULL == factory) { + return NULL; + } SkTScopedComPtr<StreamFontFileLoader> fontFileLoader; HRN(StreamFontFileLoader::Create(stream, &fontFileLoader)); @@ -1373,8 +1376,8 @@ void DWriteFontTypeface::onFilterRec(SkScalerContext::Rec* rec) const { rec->setHinting(h); #if SK_FONT_HOST_USE_SYSTEM_SETTINGS - IDWriteFactory* factory; - if (SUCCEEDED(get_dwrite_factory(&factory))) { + IDWriteFactory* factory = get_dwrite_factory(); + if (factory != NULL) { SkTScopedComPtr<IDWriteRenderingParams> defaultRenderingParams; if (SUCCEEDED(factory->CreateRenderingParams(&defaultRenderingParams))) { float gamma = defaultRenderingParams->GetGamma(); @@ -1880,8 +1883,10 @@ static HRESULT GetGetUserDefaultLocaleNameProc(GetUserDefaultLocaleNameProc* pro } SkFontMgr* SkFontMgr_New_DirectWrite() { - IDWriteFactory* factory; - HRNM(get_dwrite_factory(&factory), "Could not get factory."); + IDWriteFactory* factory = get_dwrite_factory(); + if (NULL == factory) { + return NULL; + } SkTScopedComPtr<IDWriteFontCollection> sysFontCollection; HRNM(factory->GetSystemFontCollection(&sysFontCollection, FALSE), diff --git a/ports/SkImageDecoder_empty.cpp b/ports/SkImageDecoder_empty.cpp index 8a45dfac..ae0fc363 100644 --- a/ports/SkImageDecoder_empty.cpp +++ b/ports/SkImageDecoder_empty.cpp @@ -7,7 +7,6 @@ */ #include "SkBitmap.h" -#include "SkBitmapFactory.h" #include "SkImage.h" #include "SkImageDecoder.h" #include "SkImageEncoder.h" @@ -84,11 +83,6 @@ SkBitmap::Allocator* SkImageDecoder::setAllocator(SkBitmap::Allocator*) { void SkImageDecoder::setSampleSize(int) {} -bool SkImageDecoder::DecodeMemoryToTarget(const void*, size_t, SkImageInfo*, - const SkBitmapFactory::Target*) { - return false; -} - SkBitmap::Config SkImageDecoder::GetDeviceConfig() { return SkBitmap::kNo_Config; } diff --git a/text/SkTextLayout.cpp b/text/SkTextLayout.cpp index 4e531cf2..6f8bca56 100644 --- a/text/SkTextLayout.cpp +++ b/text/SkTextLayout.cpp @@ -7,8 +7,6 @@ */ #include "SkTextLayout.h" -SK_DEFINE_INST_COUNT(SkTextStyle) - SkTextStyle::SkTextStyle() { fPaint.setAntiAlias(true); } diff --git a/utils/SkBoundaryPatch.cpp b/utils/SkBoundaryPatch.cpp index fd1545d2..e5fcc405 100644 --- a/utils/SkBoundaryPatch.cpp +++ b/utils/SkBoundaryPatch.cpp @@ -7,8 +7,6 @@ */ #include "SkBoundaryPatch.h" -SK_DEFINE_INST_COUNT(SkBoundary) - SkBoundaryPatch::SkBoundaryPatch() : fBoundary(NULL) {} SkBoundaryPatch::~SkBoundaryPatch() { diff --git a/utils/SkCanvasStack.cpp b/utils/SkCanvasStack.cpp index db5a8b27..8951149b 100644 --- a/utils/SkCanvasStack.cpp +++ b/utils/SkCanvasStack.cpp @@ -35,7 +35,7 @@ void SkCanvasStack::pushCanvas(SkCanvas* canvas, const SkIPoint& origin) { localBounds.offset(origin - fCanvasData[i-1].origin); fCanvasData[i-1].requiredClip.op(localBounds, SkRegion::kDifference_Op); - fList[i-i]->clipRegion(fCanvasData[i-1].requiredClip); + fList[i-1]->clipRegion(fCanvasData[i-1].requiredClip); } } SkASSERT(fList.count() == fCanvasData.count()); diff --git a/utils/SkDumpCanvas.cpp b/utils/SkDumpCanvas.cpp index 0e1a2321..dabf0d79 100644 --- a/utils/SkDumpCanvas.cpp +++ b/utils/SkDumpCanvas.cpp @@ -24,8 +24,6 @@ #include "SkPathEffect.h" #include "SkMaskFilter.h" -SK_DEFINE_INST_COUNT(SkDumpCanvas::Dumper) - static void toString(const SkRect& r, SkString* str) { str->appendf("[%g,%g %g:%g]", SkScalarToFloat(r.fLeft), SkScalarToFloat(r.fTop), diff --git a/utils/SkLayer.cpp b/utils/SkLayer.cpp index 126dd921..525917ff 100644 --- a/utils/SkLayer.cpp +++ b/utils/SkLayer.cpp @@ -15,8 +15,6 @@ static int gLayerAllocCount; #endif -SK_DEFINE_INST_COUNT(SkLayer) - /////////////////////////////////////////////////////////////////////////////// SkLayer::SkLayer() { diff --git a/utils/SkUnitMappers.cpp b/utils/SkUnitMappers.cpp index ceff9ca0..5976e9de 100644 --- a/utils/SkUnitMappers.cpp +++ b/utils/SkUnitMappers.cpp @@ -8,8 +8,6 @@ #include "SkUnitMappers.h" #include "SkFlattenableBuffers.h" -SK_DEFINE_INST_COUNT(SkUnitMapper) - SkDiscreteMapper::SkDiscreteMapper(int segments) { if (segments < 2) { fSegments = 0; diff --git a/utils/android/ashmem.h b/utils/android/ashmem.h index 278192b4..94ffe1a3 100644 --- a/utils/android/ashmem.h +++ b/utils/android/ashmem.h @@ -16,6 +16,12 @@ extern "C" { int ashmem_create_region(const char *name, size_t size); int ashmem_set_prot_region(int fd, int prot); + +/** + * @return ASHMEM_NOT_PURGED if the memory was not purged. + * ASHMEM_WAS_PURGED if the memory was purged. + * -1 on error. + */ int ashmem_pin_region(int fd, size_t offset, size_t len); int ashmem_unpin_region(int fd, size_t offset, size_t len); int ashmem_get_size_region(int fd); diff --git a/utils/debugger/SkDebugCanvas.cpp b/utils/debugger/SkDebugCanvas.cpp index d1a9f0c9..e491f3b8 100644 --- a/utils/debugger/SkDebugCanvas.cpp +++ b/utils/debugger/SkDebugCanvas.cpp @@ -22,18 +22,15 @@ static SkBitmap make_noconfig_bm(int width, int height) { SkDebugCanvas::SkDebugCanvas(int width, int height) : INHERITED(make_noconfig_bm(width, height)) + , fWidth(width) + , fHeight(height) + , fFilter(false) + , fIndex(0) , fOverdrawViz(false) , fOverdrawFilter(NULL) , fOverrideTexFiltering(false) , fTexOverrideFilter(NULL) , fOutstandingSaveCount(0) { - // TODO(chudy): Free up memory from all draw commands in destructor. - fWidth = width; - fHeight = height; - // do we need fBm anywhere? - fBm.setConfig(SkBitmap::kNo_Config, fWidth, fHeight); - fFilter = false; - fIndex = 0; fUserMatrix.reset(); // SkPicturePlayback uses the base-class' quickReject calls to cull clipped @@ -58,6 +55,7 @@ SkDebugCanvas::SkDebugCanvas(int width, int height) SkDebugCanvas::~SkDebugCanvas() { fCommandVector.deleteAll(); SkSafeUnref(fOverdrawFilter); + SkSafeUnref(fTexOverrideFilter); } void SkDebugCanvas::addDrawCommand(SkDrawCommand* command) { @@ -65,14 +63,9 @@ void SkDebugCanvas::addDrawCommand(SkDrawCommand* command) { } void SkDebugCanvas::draw(SkCanvas* canvas) { - if(!fCommandVector.isEmpty()) { - for (int i = 0; i < fCommandVector.count(); i++) { - if (fCommandVector[i]->isVisible()) { - fCommandVector[i]->execute(canvas); - } - } + if (!fCommandVector.isEmpty()) { + drawTo(canvas, fCommandVector.count() - 1); } - fIndex = fCommandVector.count() - 1; } void SkDebugCanvas::applyUserTransform(SkCanvas* canvas) { @@ -177,18 +170,19 @@ private: void SkDebugCanvas::drawTo(SkCanvas* canvas, int index) { SkASSERT(!fCommandVector.isEmpty()); SkASSERT(index < fCommandVector.count()); - int i; + int i = 0; // This only works assuming the canvas and device are the same ones that // were previously drawn into because they need to preserve all saves // and restores. - if (fIndex < index) { + // The visibility filter also requires a full re-draw - otherwise we can + // end up drawing the filter repeatedly. + if (fIndex < index && !fFilter) { i = fIndex + 1; } else { for (int j = 0; j < fOutstandingSaveCount; j++) { canvas->restore(); } - i = 0; canvas->clear(SK_ColorTRANSPARENT); canvas->resetMatrix(); SkRect rect = SkRect::MakeWH(SkIntToScalar(fWidth), diff --git a/utils/debugger/SkDebugCanvas.h b/utils/debugger/SkDebugCanvas.h index 3df31513..262619e7 100644 --- a/utils/debugger/SkDebugCanvas.h +++ b/utils/debugger/SkDebugCanvas.h @@ -42,15 +42,6 @@ public: void draw(SkCanvas* canvas); /** - Executes the draw calls in the specified range. - @param canvas The canvas being drawn to - @param i The beginning of the range - @param j The end of the range - TODO(chudy): Implement - */ - void drawRange(SkCanvas* canvas, int i, int j); - - /** Executes the draw calls up to the specified index. @param canvas The canvas being drawn to @param index The index of the final command being executed @@ -244,9 +235,8 @@ public: private: SkTDArray<SkDrawCommand*> fCommandVector; - int fHeight; int fWidth; - SkBitmap fBm; + int fHeight; bool fFilter; int fIndex; SkMatrix fUserMatrix; diff --git a/utils/debugger/SkObjectParser.cpp b/utils/debugger/SkObjectParser.cpp index 54ae0773..ebbd4001 100644 --- a/utils/debugger/SkObjectParser.cpp +++ b/utils/debugger/SkObjectParser.cpp @@ -26,9 +26,9 @@ SkString* SkObjectParser::BitmapToString(const SkBitmap& bitmap) { mBitmap->appendS32(bitmap.height()); const char* gConfigStrings[] = { - "None", "A1", "A8", "Index8", "RGB565", "ARGB4444", "ARGB8888" + "None", "A8", "Index8", "RGB565", "ARGB4444", "ARGB8888" }; - SkASSERT(SkBitmap::kConfigCount == 7); + SkASSERT(SkBitmap::kConfigCount == SK_ARRAY_COUNT(gConfigStrings)); mBitmap->append(" Config: "); mBitmap->append(gConfigStrings[bitmap.config()]); diff --git a/utils/win/SkAutoCoInitialize.cpp b/utils/win/SkAutoCoInitialize.cpp index dd6e9368..89115bfe 100644 --- a/utils/win/SkAutoCoInitialize.cpp +++ b/utils/win/SkAutoCoInitialize.cpp @@ -8,7 +8,7 @@ #define WIN32_LEAN_AND_MEAN -#include <Windows.h> +#include <windows.h> #include <ole2.h> #include "SkAutoCoInitialize.h" diff --git a/utils/win/SkIStream.cpp b/utils/win/SkIStream.cpp index 0386ea78..74d814cc 100644 --- a/utils/win/SkIStream.cpp +++ b/utils/win/SkIStream.cpp @@ -8,7 +8,7 @@ #define WIN32_LEAN_AND_MEAN -#include <Windows.h> +#include <windows.h> #include <ole2.h> #include "SkIStream.h" #include "SkStream.h" diff --git a/views/SkEventSink.cpp b/views/SkEventSink.cpp index b6a3a6ee..7763adef 100644 --- a/views/SkEventSink.cpp +++ b/views/SkEventSink.cpp @@ -14,8 +14,6 @@ #include "SkThread.h" #include "SkTime.h" -SK_DEFINE_INST_COUNT(SkEventSink) - class SkEventSink_Globals { public: SkEventSink_Globals() { diff --git a/views/SkView.cpp b/views/SkView.cpp index e3e05f91..f211ad96 100644 --- a/views/SkView.cpp +++ b/views/SkView.cpp @@ -8,9 +8,6 @@ #include "SkView.h" #include "SkCanvas.h" -SK_DEFINE_INST_COUNT(SkView::Artist) -SK_DEFINE_INST_COUNT(SkView::Layout) - //////////////////////////////////////////////////////////////////////// SkView::SkView(uint32_t flags) : fFlags(SkToU8(flags)) diff --git a/views/animated/SkWidgetViews.cpp b/views/animated/SkWidgetViews.cpp index 45e98688..8e7f5175 100644 --- a/views/animated/SkWidgetViews.cpp +++ b/views/animated/SkWidgetViews.cpp @@ -24,8 +24,6 @@ enum SkinEnum { }; */ -SK_DEFINE_INST_COUNT(SkListSource) - const char* get_skin_enum_path(SkinEnum se) { SkASSERT((unsigned)se < kSkinEnumCount); diff --git a/views/win/skia_win.cpp b/views/win/skia_win.cpp index 2d66bd82..72ff54fe 100644 --- a/views/win/skia_win.cpp +++ b/views/win/skia_win.cpp @@ -5,7 +5,7 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ -#include <Windows.h> +#include <windows.h> #include <tchar.h> #include "SkApplication.h" |