diff options
author | Android Chromium Automerger <chromium-automerger@android> | 2013-12-16 13:12:17 +0000 |
---|---|---|
committer | Android Chromium Automerger <chromium-automerger@android> | 2013-12-16 13:12:17 +0000 |
commit | f210270120ee1da24674ae29ce081a95ce9b36e3 (patch) | |
tree | 6d6891571b5b8503b79c3f2594be27787f72c965 | |
parent | e69bc09ae4a12f72e1918c0cb1729da7aac1a79e (diff) | |
parent | c2062d0e1bd6b258229669463ab5ce7020780c8a (diff) | |
download | src-f210270120ee1da24674ae29ce081a95ce9b36e3.tar.gz |
Merge third_party/skia/src from https://chromium.googlesource.com/external/skia/src.git at c2062d0e1bd6b258229669463ab5ce7020780c8a
This commit was generated by merge_from_chromium.py.
Change-Id: I8ed55c7efa600f30df090e824e3770368eb28e72
31 files changed, 689 insertions, 274 deletions
diff --git a/core/SkBitmap.cpp b/core/SkBitmap.cpp index 25a6b1db..b387795e 100644 --- a/core/SkBitmap.cpp +++ b/core/SkBitmap.cpp @@ -298,7 +298,7 @@ bool SkBitmap::setConfig(Config config, int width, int height, size_t rowBytes, } if (rowBytes == 0) { rowBytes = SkBitmap::ComputeRowBytes(config, width); - if (0 == rowBytes && kNo_Config != config) { + if (0 == rowBytes && kNo_Config != config && width > 0) { goto BAD_CONFIG; } } @@ -453,10 +453,20 @@ void SkBitmap::setPixels(void* p, SkColorTable* ctable) { return; } - Sk64 size = this->getSize64(); - SkASSERT(!size.isNeg() && size.is32()); + SkImageInfo info; + if (!this->asImageInfo(&info)) { + this->setPixelRef(NULL, 0); + return; + } + + SkPixelRef* pr = SkMallocPixelRef::NewDirect(info, p, fRowBytes, ctable); + if (NULL == pr) { + this->setPixelRef(NULL, 0); + return; + } + + this->setPixelRef(pr)->unref(); - this->setPixelRef(new SkMallocPixelRef(p, size.get32(), ctable, false))->unref(); // since we're already allocated, we lockPixels right away this->lockPixels(); SkDEBUGCODE(this->validate();) @@ -521,17 +531,19 @@ GrTexture* SkBitmap::getTexture() const { */ bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst, SkColorTable* ctable) { - Sk64 size = dst->getSize64(); - if (size.isNeg() || !size.is32()) { + SkImageInfo info; + if (!dst->asImageInfo(&info)) { +// SkDebugf("unsupported config for info %d\n", dst->config()); return false; } - - void* addr = sk_malloc_flags(size.get32(), 0); // returns NULL on failure - if (NULL == addr) { + + SkPixelRef* pr = SkMallocPixelRef::NewAllocate(info, dst->rowBytes(), + ctable); + if (NULL == pr) { return false; } - dst->setPixelRef(new SkMallocPixelRef(addr, size.get32(), ctable))->unref(); + dst->setPixelRef(pr, 0)->unref(); // since we're already allocated, we lockPixels right away dst->lockPixels(); return true; diff --git a/core/SkBitmapProcState.cpp b/core/SkBitmapProcState.cpp index 39f6a780..cdc582bf 100644 --- a/core/SkBitmapProcState.cpp +++ b/core/SkBitmapProcState.cpp @@ -106,11 +106,33 @@ static SkScalar effective_matrix_scale_sqrd(const SkMatrix& mat) { return SkMaxScalar(v1.lengthSqd(), v2.lengthSqd()); } +class AutoScaledCacheUnlocker { +public: + AutoScaledCacheUnlocker(SkScaledImageCache::ID** idPtr) : fIDPtr(idPtr) {} + ~AutoScaledCacheUnlocker() { + if (fIDPtr && *fIDPtr) { + SkScaledImageCache::Unlock(*fIDPtr); + *fIDPtr = NULL; + } + } + + // forgets the ID, so it won't call Unlock + void release() { + fIDPtr = NULL; + } + +private: + SkScaledImageCache::ID** fIDPtr; +}; +#define AutoScaledCacheUnlocker(...) SK_REQUIRE_LOCAL_VAR(AutoScaledCacheUnlocker) + // TODO -- we may want to pass the clip into this function so we only scale // the portion of the image that we're going to need. This will complicate // the interface to the cache, but might be well worth it. bool SkBitmapProcState::possiblyScaleImage() { + AutoScaledCacheUnlocker unlocker(&fScaledCacheID); + SkASSERT(NULL == fBitmap); SkASSERT(NULL == fScaledCacheID); @@ -132,6 +154,17 @@ bool SkBitmapProcState::possiblyScaleImage() { fScaledCacheID = SkScaledImageCache::FindAndLock(fOrigBitmap, invScaleX, invScaleY, &fScaledBitmap); + if (fScaledCacheID) { + fScaledBitmap.lockPixels(); + if (!fScaledBitmap.getPixels()) { + fScaledBitmap.unlockPixels(); + // found a purged entry (discardablememory?), release it + SkScaledImageCache::Unlock(fScaledCacheID); + fScaledCacheID = NULL; + // fall through to rebuild + } + } + if (NULL == fScaledCacheID) { int dest_width = SkScalarCeilToInt(fOrigBitmap.width() / invScaleX); int dest_height = SkScalarCeilToInt(fOrigBitmap.height() / invScaleY); @@ -154,18 +187,19 @@ bool SkBitmapProcState::possiblyScaleImage() { return false; } + SkASSERT(NULL != fScaledBitmap.getPixels()); fScaledCacheID = SkScaledImageCache::AddAndLock(fOrigBitmap, invScaleX, invScaleY, fScaledBitmap); - } - fScaledBitmap.lockPixels(); // wonder if Resize() should have locked this - if (!fScaledBitmap.getPixels()) { - // TODO: find out how this can happen, and add a unittest to exercise - // inspired by BUG=chromium:295895 - return false; + if (!fScaledCacheID) { + fScaledBitmap.reset(); + return false; + } + SkASSERT(NULL != fScaledBitmap.getPixels()); } + SkASSERT(NULL != fScaledBitmap.getPixels()); fBitmap = &fScaledBitmap; // set the inv matrix type to translate-only; @@ -174,6 +208,7 @@ bool SkBitmapProcState::possiblyScaleImage() { // no need for any further filtering; we just did it! fFilterLevel = SkPaint::kNone_FilterLevel; + unlocker.release(); return true; } @@ -248,6 +283,7 @@ bool SkBitmapProcState::possiblyScaleImage() { fScaledBitmap.setPixels(level.fPixels); fBitmap = &fScaledBitmap; fFilterLevel = SkPaint::kLow_FilterLevel; + unlocker.release(); return true; } } @@ -273,15 +309,34 @@ static bool get_locked_pixels(const SkBitmap& src, int pow2, SkBitmap* dst) { } bool SkBitmapProcState::lockBaseBitmap() { + AutoScaledCacheUnlocker unlocker(&fScaledCacheID); + SkPixelRef* pr = fOrigBitmap.pixelRef(); + SkASSERT(NULL == fScaledCacheID); + if (pr->isLocked() || !pr->implementsDecodeInto()) { // fast-case, no need to look in our cache fScaledBitmap = fOrigBitmap; + fScaledBitmap.lockPixels(); + if (NULL == fScaledBitmap.getPixels()) { + return false; + } } else { fScaledCacheID = SkScaledImageCache::FindAndLock(fOrigBitmap, SK_Scalar1, SK_Scalar1, &fScaledBitmap); + if (fScaledCacheID) { + fScaledBitmap.lockPixels(); + if (!fScaledBitmap.getPixels()) { + fScaledBitmap.unlockPixels(); + // found a purged entry (discardablememory?), release it + SkScaledImageCache::Unlock(fScaledCacheID); + fScaledCacheID = NULL; + // fall through to rebuild + } + } + if (NULL == fScaledCacheID) { if (!get_locked_pixels(fOrigBitmap, 0, &fScaledBitmap)) { return false; @@ -299,13 +354,8 @@ bool SkBitmapProcState::lockBaseBitmap() { } } } - fScaledBitmap.lockPixels(); // just 'cause the cache made a copy :( - if (!fScaledBitmap.getPixels()) { - // TODO: find out how this can happen, and add a unittest to exercise - // inspired by BUG=chromium:295895 - return false; - } fBitmap = &fScaledBitmap; + unlocker.release(); return true; } @@ -334,6 +384,8 @@ bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) { fInvMatrix = inv; fFilterLevel = paint.getFilterLevel(); + SkASSERT(NULL == fScaledCacheID); + // possiblyScaleImage will look to see if it can rescale the image as a // preprocess; either by scaling up to the target size, or by selecting // a nearby mipmap level. If it does, it will adjust the working diff --git a/core/SkBitmapScaler.cpp b/core/SkBitmapScaler.cpp index c28d4779..67a9508e 100644 --- a/core/SkBitmapScaler.cpp +++ b/core/SkBitmapScaler.cpp @@ -301,6 +301,8 @@ bool SkBitmapScaler::Resize(SkBitmap* resultPtr, convolveProcs, true); *resultPtr = result; + resultPtr->lockPixels(); + SkASSERT(NULL != resultPtr->getPixels()); return true; } diff --git a/core/SkImageFilterUtils.cpp b/core/SkImageFilterUtils.cpp index 8385fb44..a59cf7bb 100644 --- a/core/SkImageFilterUtils.cpp +++ b/core/SkImageFilterUtils.cpp @@ -15,8 +15,14 @@ #include "SkGr.h" bool SkImageFilterUtils::WrapTexture(GrTexture* texture, int width, int height, SkBitmap* result) { - result->setConfig(SkBitmap::kARGB_8888_Config, width, height); - result->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (texture)))->unref(); + SkImageInfo info = { + width, + height, + kPMColor_SkColorType, + kPremul_SkAlphaType, + }; + result->setConfig(info); + result->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (info, texture)))->unref(); return true; } @@ -36,8 +42,12 @@ bool SkImageFilterUtils::GetInputResultGPU(SkImageFilter* filter, SkImageFilter: } else { if (filter->filterImage(proxy, src, ctm, result, offset)) { if (!result->getTexture()) { + SkImageInfo info; + if (!result->asImageInfo(&info)) { + return false; + } GrTexture* resultTex = GrLockAndRefCachedBitmapTexture(context, *result, NULL); - result->setPixelRef(new SkGrPixelRef(resultTex))->unref(); + result->setPixelRef(new SkGrPixelRef(info, resultTex))->unref(); GrUnlockAndUnrefCachedBitmapTexture(resultTex); } return true; diff --git a/core/SkMallocPixelRef.cpp b/core/SkMallocPixelRef.cpp index f229e9de..25337e7b 100644 --- a/core/SkMallocPixelRef.cpp +++ b/core/SkMallocPixelRef.cpp @@ -1,26 +1,104 @@ - /* * Copyright 2011 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ + #include "SkMallocPixelRef.h" #include "SkBitmap.h" #include "SkFlattenableBuffers.h" -SkMallocPixelRef::SkMallocPixelRef(void* storage, size_t size, - SkColorTable* ctable, bool ownPixels) { - if (NULL == storage) { - SkASSERT(ownPixels); - storage = sk_malloc_throw(size); +static bool is_valid(const SkImageInfo& info, SkColorTable* ctable) { + if (info.fWidth < 0 || + info.fHeight < 0 || + (unsigned)info.fColorType > (unsigned)kLastEnum_SkColorType || + (unsigned)info.fAlphaType > (unsigned)kLastEnum_SkAlphaType) + { + return false; + } + + // these seem like good checks, but currently we have (at least) tests + // that expect the pixelref to succeed even when there is a mismatch + // with colortables. fix? +#if 0 + if (kIndex8_SkColorType == info.fColorType && NULL == ctable) { + return false; + } + if (kIndex8_SkColorType != info.fColorType && NULL != ctable) { + return false; + } +#endif + return true; +} + +SkMallocPixelRef* SkMallocPixelRef::NewDirect(const SkImageInfo& info, + void* addr, + size_t rowBytes, + SkColorTable* ctable) { + if (!is_valid(info, ctable)) { + return NULL; + } + return SkNEW_ARGS(SkMallocPixelRef, (info, addr, rowBytes, ctable, false)); +} + +SkMallocPixelRef* SkMallocPixelRef::NewAllocate(const SkImageInfo& info, + size_t requestedRowBytes, + SkColorTable* ctable) { + if (!is_valid(info, ctable)) { + return NULL; + } + + int32_t minRB = info.minRowBytes(); + if (minRB < 0) { + return NULL; // allocation will be too large + } + if (requestedRowBytes > 0 && (int32_t)requestedRowBytes < minRB) { + return NULL; // cannot meet requested rowbytes + } + + int32_t rowBytes; + if (requestedRowBytes) { + rowBytes = requestedRowBytes; + } else { + rowBytes = minRB; } + + Sk64 bigSize; + bigSize.setMul(info.fHeight, rowBytes); + if (!bigSize.is32()) { + return NULL; + } + + size_t size = bigSize.get32(); + void* addr = sk_malloc_flags(size, 0); + if (NULL == addr) { + return NULL; + } + + return SkNEW_ARGS(SkMallocPixelRef, (info, addr, rowBytes, ctable, true)); +} + +/////////////////////////////////////////////////////////////////////////////// + +SkMallocPixelRef::SkMallocPixelRef(const SkImageInfo& info, void* storage, + size_t rowBytes, SkColorTable* ctable, + bool ownsPixels) + : INHERITED(info) + , fOwnPixels(ownsPixels) +{ + SkASSERT(is_valid(info, ctable)); + SkASSERT(rowBytes >= info.minRowBytes()); + + if (kIndex_8_SkColorType != info.fColorType) { + ctable = NULL; + } + fStorage = storage; - fSize = size; fCTable = ctable; + fRB = rowBytes; SkSafeRef(ctable); - fOwnPixels = ownPixels; - + this->setPreLocked(fStorage, fCTable); } @@ -31,8 +109,8 @@ SkMallocPixelRef::~SkMallocPixelRef() { } } -void* SkMallocPixelRef::onLockPixels(SkColorTable** ct) { - *ct = fCTable; +void* SkMallocPixelRef::onLockPixels(SkColorTable** ctable) { + *ctable = fCTable; return fStorage; } @@ -40,10 +118,19 @@ void SkMallocPixelRef::onUnlockPixels() { // nothing to do } +size_t SkMallocPixelRef::getAllocatedSizeInBytes() const { + return this->info().getSafeSize(fRB); +} + void SkMallocPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const { this->INHERITED::flatten(buffer); - buffer.writeByteArray(fStorage, fSize); + buffer.write32(fRB); + + // TODO: replace this bulk write with a chunky one that can trim off any + // trailing bytes on each scanline (in case rowbytes > width*size) + size_t size = this->info().getSafeSize(fRB); + buffer.writeByteArray(fStorage, size); buffer.writeBool(fCTable != NULL); if (fCTable) { fCTable->writeToBuffer(buffer); @@ -51,16 +138,18 @@ void SkMallocPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const { } SkMallocPixelRef::SkMallocPixelRef(SkFlattenableReadBuffer& buffer) - : INHERITED(buffer, NULL) { - fSize = buffer.getArrayCount(); - fStorage = sk_malloc_throw(fSize); - buffer.readByteArray(fStorage, fSize); + : INHERITED(buffer, NULL) + , fOwnPixels(true) +{ + fRB = buffer.read32(); + size_t size = this->info().getSafeSize(fRB); + fStorage = sk_malloc_throw(size); + buffer.readByteArray(fStorage, size); if (buffer.readBool()) { fCTable = SkNEW_ARGS(SkColorTable, (buffer)); } else { fCTable = NULL; } - fOwnPixels = true; this->setPreLocked(fStorage, fCTable); } diff --git a/core/SkMaskFilter.cpp b/core/SkMaskFilter.cpp index f062f135..1bc17bb4 100644 --- a/core/SkMaskFilter.cpp +++ b/core/SkMaskFilter.cpp @@ -349,10 +349,14 @@ bool SkMaskFilter::filterMaskGPU(GrContext* context, if (!result) { return false; } + SkAutoUnref aur(dst); + SkImageInfo info; resultBM->setConfig(srcBM.config(), dst->width(), dst->height()); - resultBM->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (dst)))->unref(); - dst->unref(); + if (!resultBM->asImageInfo(&info)) { + return false; + } + resultBM->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (info, dst)))->unref(); return true; } diff --git a/core/SkPath.cpp b/core/SkPath.cpp index eaa6c93d..af8b1aa5 100644 --- a/core/SkPath.cpp +++ b/core/SkPath.cpp @@ -15,11 +15,6 @@ #include "SkRRect.h" #include "SkThread.h" -// 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... -#define MIN_COUNT_FOR_MEMSET_TO_BE_FAST 16 - //////////////////////////////////////////////////////////////////////////// /** @@ -143,7 +138,6 @@ void SkPath::resetFields() { //fPathRef is assumed to have been emptied by the caller. fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE; fFillType = kWinding_FillType; - fSegmentMask = 0; fConvexity = kUnknown_Convexity; fDirection = kUnknown_Direction; @@ -182,7 +176,6 @@ void SkPath::copyFields(const SkPath& that) { //fPathRef is assumed to have been set by the caller. fLastMoveToIndex = that.fLastMoveToIndex; fFillType = that.fFillType; - fSegmentMask = that.fSegmentMask; fConvexity = that.fConvexity; fDirection = that.fDirection; } @@ -190,14 +183,8 @@ void SkPath::copyFields(const SkPath& that) { bool operator==(const SkPath& a, const SkPath& b) { // note: don't need to look at isConvex or bounds, since just comparing the // raw data is sufficient. - - // We explicitly check fSegmentMask as a quick-reject. We could skip it, - // since it is only a cache of info in the fVerbs, but its a fast way to - // notice a difference - return &a == &b || - (a.fFillType == b.fFillType && a.fSegmentMask == b.fSegmentMask && - *a.fPathRef.get() == *b.fPathRef.get()); + (a.fFillType == b.fFillType && *a.fPathRef.get() == *b.fPathRef.get()); } void SkPath::swap(SkPath& that) { @@ -207,7 +194,6 @@ void SkPath::swap(SkPath& that) { fPathRef.swap(&that.fPathRef); SkTSwap<int>(fLastMoveToIndex, that.fLastMoveToIndex); SkTSwap<uint8_t>(fFillType, that.fFillType); - SkTSwap<uint8_t>(fSegmentMask, that.fSegmentMask); SkTSwap<uint8_t>(fConvexity, that.fConvexity); SkTSwap<uint8_t>(fDirection, that.fDirection); #ifdef SK_BUILD_FOR_ANDROID @@ -674,7 +660,6 @@ void SkPath::lineTo(SkScalar x, SkScalar y) { SkPathRef::Editor ed(&fPathRef); ed.growForVerb(kLine_Verb)->set(x, y); - fSegmentMask |= kLine_SegmentMask; DIRTY_AFTER_EDIT; } @@ -695,7 +680,6 @@ void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) { SkPoint* pts = ed.growForVerb(kQuad_Verb); pts[0].set(x1, y1); pts[1].set(x2, y2); - fSegmentMask |= kQuad_SegmentMask; DIRTY_AFTER_EDIT; } @@ -723,10 +707,9 @@ void SkPath::conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, this->injectMoveToIfNeeded(); SkPathRef::Editor ed(&fPathRef); - SkPoint* pts = ed.growForConic(w); + SkPoint* pts = ed.growForVerb(kConic_Verb, w); pts[0].set(x1, y1); pts[1].set(x2, y2); - fSegmentMask |= kConic_SegmentMask; DIRTY_AFTER_EDIT; } @@ -751,7 +734,6 @@ void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, pts[0].set(x1, y1); pts[1].set(x2, y2); pts[2].set(x3, y3); - fSegmentMask |= kCubic_SegmentMask; DIRTY_AFTER_EDIT; } @@ -838,29 +820,19 @@ void SkPath::addPoly(const SkPoint pts[], int count, bool close) { return; } - SkPathRef::Editor ed(&fPathRef); - fLastMoveToIndex = ed.pathRef()->countPoints(); - uint8_t* vb; - SkPoint* p; + fLastMoveToIndex = fPathRef->countPoints(); + // +close makes room for the extra kClose_Verb - ed.grow(count + close, count, &vb, &p); + SkPathRef::Editor ed(&fPathRef, count+close, count); - memcpy(p, pts, count * sizeof(SkPoint)); - vb[~0] = kMove_Verb; + ed.growForVerb(kMove_Verb)->set(pts[0].fX, pts[0].fY); if (count > 1) { - // cast to unsigned, so if MIN_COUNT_FOR_MEMSET_TO_BE_FAST is defined to - // be 0, the compiler will remove the test/branch entirely. - if ((unsigned)count >= MIN_COUNT_FOR_MEMSET_TO_BE_FAST) { - memset(vb - count, kLine_Verb, count - 1); - } else { - for (int i = 1; i < count; ++i) { - vb[~i] = kLine_Verb; - } - } - fSegmentMask |= kLine_SegmentMask; + SkPoint* p = ed.growForRepeatedVerb(kLine_Verb, count - 1); + memcpy(p, &pts[1], (count-1) * sizeof(SkPoint)); } + if (close) { - vb[~count] = kClose_Verb; + ed.growForVerb(kClose_Verb); } DIRTY_AFTER_EDIT; @@ -1343,11 +1315,21 @@ void SkPath::addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle SkPoint pts[kSkBuildQuadArcStorage]; int count = build_arc_points(oval, startAngle, sweepAngle, pts); - this->incReserve(count); - this->moveTo(pts[0]); - for (int i = 1; i < count; i += 2) { - this->quadTo(pts[i], pts[i+1]); + SkDEBUGCODE(this->validate();) + SkASSERT(count & 1); + + fLastMoveToIndex = fPathRef->countPoints(); + + SkPathRef::Editor ed(&fPathRef, 1+(count-1)/2, count); + + ed.growForVerb(kMove_Verb)->set(pts[0].fX, pts[0].fY); + if (count > 1) { + SkPoint* p = ed.growForRepeatedVerb(kQuad_Verb, (count-1)/2); + memcpy(p, &pts[1], (count-1) * sizeof(SkPoint)); } + + DIRTY_AFTER_EDIT; + SkDEBUGCODE(this->validate();) } /* @@ -1671,7 +1653,6 @@ void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const { if (this != dst) { dst->fFillType = fFillType; - dst->fSegmentMask = fSegmentMask; dst->fConvexity = fConvexity; } @@ -2045,7 +2026,6 @@ size_t SkPath::writeToMemory(void* storage) const { int32_t packed = (fConvexity << kConvexity_SerializationShift) | (fFillType << kFillType_SerializationShift) | - (fSegmentMask << kSegmentMask_SerializationShift) | (fDirection << kDirection_SerializationShift) #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V16_AND_ALL_OTHER_INSTANCES_TOO | (0x1 << kNewFormat_SerializationShift) @@ -2070,7 +2050,6 @@ size_t SkPath::readFromMemory(const void* storage, size_t length) { 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_V16_AND_ALL_OTHER_INSTANCES_TOO bool newFormat = (packed >> kNewFormat_SerializationShift) & 1; @@ -2201,34 +2180,6 @@ void SkPath::validate() const { } } } - - uint32_t mask = 0; - const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbs(); - for (int i = 0; i < fPathRef->countVerbs(); i++) { - switch (verbs[~i]) { - case kLine_Verb: - mask |= kLine_SegmentMask; - break; - case kQuad_Verb: - mask |= kQuad_SegmentMask; - break; - case kConic_Verb: - mask |= kConic_SegmentMask; - break; - case kCubic_Verb: - mask |= kCubic_SegmentMask; - case kMove_Verb: // these verbs aren't included in the segment mask. - case kClose_Verb: - break; - case kDone_Verb: - SkDEBUGFAIL("Done verb shouldn't be recorded."); - break; - default: - SkDEBUGFAIL("Unknown Verb"); - break; - } - } - SkASSERT(mask == fSegmentMask); #endif // SK_DEBUG_PATH } #endif // SK_DEBUG diff --git a/core/SkPathRef.cpp b/core/SkPathRef.cpp index a02df302..0711e3f0 100644 --- a/core/SkPathRef.cpp +++ b/core/SkPathRef.cpp @@ -24,17 +24,9 @@ SkPathRef::Editor::Editor(SkAutoTUnref<SkPathRef>* pathRef, } fPathRef = *pathRef; fPathRef->fGenerationID = 0; - fPathRef->fIsOval = false; SkDEBUGCODE(sk_atomic_inc(&fPathRef->fEditorsAttached);) } -SkPoint* SkPathRef::Editor::growForConic(SkScalar w) { - SkDEBUGCODE(fPathRef->validate();) - SkPoint* pts = fPathRef->growForVerb(SkPath::kConic_Verb); - *fPathRef->fConicWeights.append() = w; - return pts; -} - ////////////////////////////////////////////////////////////////////////////// void SkPathRef::CreateEmptyImpl(SkPathRef** empty) { *empty = SkNEW(SkPathRef); @@ -105,6 +97,8 @@ void SkPathRef::CreateTransformedCopy(SkAutoTUnref<SkPathRef>* dst, (*dst)->fBoundsIsDirty = true; } + (*dst)->fSegmentMask = src.fSegmentMask; + // It's an oval only if it stays a rect. (*dst)->fIsOval = src.fIsOval && matrix.rectStaysRect(); @@ -118,6 +112,7 @@ SkPathRef* SkPathRef::CreateFromBuffer(SkRBuffer* buffer ) { SkPathRef* ref = SkNEW(SkPathRef); bool isOval; + uint8_t segmentMask; int32_t packed; if (!buffer->readS32(&packed)) { @@ -130,9 +125,11 @@ SkPathRef* SkPathRef::CreateFromBuffer(SkRBuffer* buffer #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V16_AND_ALL_OTHER_INSTANCES_TOO if (newFormat) { #endif + segmentMask = (packed >> kSegmentMask_SerializationShift) & 0xF; isOval = (packed >> kIsOval_SerializationShift) & 1; #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V16_AND_ALL_OTHER_INSTANCES_TOO } else { + segmentMask = (oldPacked >> SkPath::kOldSegmentMask_SerializationShift) & 0xF; isOval = (oldPacked >> SkPath::kOldIsOval_SerializationShift) & 1; } #endif @@ -159,6 +156,9 @@ SkPathRef* SkPathRef::CreateFromBuffer(SkRBuffer* buffer return NULL; } ref->fBoundsIsDirty = false; + + // resetToSize clears fSegmentMask and fIsOval + ref->fSegmentMask = segmentMask; ref->fIsOval = isOval; return ref; } @@ -172,6 +172,7 @@ void SkPathRef::Rewind(SkAutoTUnref<SkPathRef>* pathRef) { (*pathRef)->fFreeSpace = (*pathRef)->currSize(); (*pathRef)->fGenerationID = 0; (*pathRef)->fConicWeights.rewind(); + (*pathRef)->fSegmentMask = 0; (*pathRef)->fIsOval = false; SkDEBUGCODE((*pathRef)->validate();) } else { @@ -185,6 +186,14 @@ void SkPathRef::Rewind(SkAutoTUnref<SkPathRef>* pathRef) { bool SkPathRef::operator== (const SkPathRef& ref) const { SkDEBUGCODE(this->validate();) SkDEBUGCODE(ref.validate();) + + // We explicitly check fSegmentMask as a quick-reject. We could skip it, + // since it is only a cache of info in the fVerbs, but its a fast way to + // notice a difference + if (fSegmentMask != ref.fSegmentMask) { + return false; + } + bool genIDMatch = fGenerationID && fGenerationID == ref.fGenerationID; #ifdef SK_RELEASE if (genIDMatch) { @@ -222,7 +231,7 @@ bool SkPathRef::operator== (const SkPathRef& ref) const { return true; } -void SkPathRef::writeToBuffer(SkWBuffer* buffer) { +void SkPathRef::writeToBuffer(SkWBuffer* buffer) const { SkDEBUGCODE(this->validate();) SkDEBUGCODE(size_t beforePos = buffer->pos();) @@ -231,7 +240,8 @@ void SkPathRef::writeToBuffer(SkWBuffer* buffer) { const SkRect& bounds = this->getBounds(); int32_t packed = ((fIsFinite & 1) << kIsFinite_SerializationShift) | - ((fIsOval & 1) << kIsOval_SerializationShift); + ((fIsOval & 1) << kIsOval_SerializationShift) | + (fSegmentMask << kSegmentMask_SerializationShift); buffer->write32(packed); // TODO: write gen ID here. Problem: We don't know if we're cross process or not from @@ -248,7 +258,7 @@ void SkPathRef::writeToBuffer(SkWBuffer* buffer) { SkASSERT(buffer->pos() - beforePos == (size_t) this->writeSize()); } -uint32_t SkPathRef::writeSize() { +uint32_t SkPathRef::writeSize() const { return uint32_t(5 * sizeof(uint32_t) + fVerbCnt * sizeof(uint8_t) + fPointCnt * sizeof(SkPoint) + @@ -273,11 +283,91 @@ void SkPathRef::copy(const SkPathRef& ref, fBounds = ref.fBounds; fIsFinite = ref.fIsFinite; } + fSegmentMask = ref.fSegmentMask; fIsOval = ref.fIsOval; SkDEBUGCODE(this->validate();) } -SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb) { +SkPoint* SkPathRef::growForRepeatedVerb(int /*SkPath::Verb*/ verb, + int numVbs, + SkScalar** weights) { + // 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 will appear to have been a fluke... + static const unsigned int kMIN_COUNT_FOR_MEMSET_TO_BE_FAST = 16; + + SkDEBUGCODE(this->validate();) + int pCnt; + bool dirtyAfterEdit = true; + switch (verb) { + case SkPath::kMove_Verb: + pCnt = numVbs; + dirtyAfterEdit = false; + break; + case SkPath::kLine_Verb: + fSegmentMask |= SkPath::kLine_SegmentMask; + pCnt = numVbs; + break; + case SkPath::kQuad_Verb: + fSegmentMask |= SkPath::kQuad_SegmentMask; + pCnt = 2 * numVbs; + break; + case SkPath::kConic_Verb: + fSegmentMask |= SkPath::kConic_SegmentMask; + pCnt = 2 * numVbs; + break; + case SkPath::kCubic_Verb: + fSegmentMask |= SkPath::kCubic_SegmentMask; + pCnt = 3 * numVbs; + break; + case SkPath::kClose_Verb: + SkDEBUGFAIL("growForRepeatedVerb called for kClose_Verb"); + pCnt = 0; + dirtyAfterEdit = false; + break; + case SkPath::kDone_Verb: + SkDEBUGFAIL("growForRepeatedVerb called for kDone"); + // fall through + default: + SkDEBUGFAIL("default should not be reached"); + pCnt = 0; + dirtyAfterEdit = false; + } + + size_t space = numVbs * sizeof(uint8_t) + pCnt * sizeof (SkPoint); + this->makeSpace(space); + + SkPoint* ret = fPoints + fPointCnt; + uint8_t* vb = fVerbs - fVerbCnt; + + // cast to unsigned, so if kMIN_COUNT_FOR_MEMSET_TO_BE_FAST is defined to + // be 0, the compiler will remove the test/branch entirely. + if ((unsigned)numVbs >= kMIN_COUNT_FOR_MEMSET_TO_BE_FAST) { + memset(vb - numVbs, verb, numVbs); + } else { + for (int i = 0; i < numVbs; ++i) { + vb[~i] = verb; + } + } + + fVerbCnt += numVbs; + fPointCnt += pCnt; + fFreeSpace -= space; + fBoundsIsDirty = true; // this also invalidates fIsFinite + if (dirtyAfterEdit) { + fIsOval = false; + } + + if (SkPath::kConic_Verb == verb) { + SkASSERT(NULL != weights); + *weights = fConicWeights.append(numVbs); + } + + SkDEBUGCODE(this->validate();) + return ret; +} + +SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb, SkScalar weight) { SkDEBUGCODE(this->validate();) int pCnt; bool dirtyAfterEdit = true; @@ -287,14 +377,19 @@ SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb) { dirtyAfterEdit = false; break; case SkPath::kLine_Verb: + fSegmentMask |= SkPath::kLine_SegmentMask; pCnt = 1; break; case SkPath::kQuad_Verb: - // fall through + fSegmentMask |= SkPath::kQuad_SegmentMask; + pCnt = 2; + break; case SkPath::kConic_Verb: + fSegmentMask |= SkPath::kConic_SegmentMask; pCnt = 2; break; case SkPath::kCubic_Verb: + fSegmentMask |= SkPath::kCubic_SegmentMask; pCnt = 3; break; case SkPath::kClose_Verb: @@ -320,6 +415,11 @@ SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb) { if (dirtyAfterEdit) { fIsOval = false; } + + if (SkPath::kConic_Verb == verb) { + *fConicWeights.append() = weight; + } + SkDEBUGCODE(this->validate();) return ret; } @@ -369,5 +469,36 @@ void SkPathRef::validate() const { } SkASSERT(SkToBool(fIsFinite) == isFinite); } + +#ifdef SK_DEBUG_PATH + uint32_t mask = 0; + for (int i = 0; i < fVerbCnt; ++i) { + switch (fVerbs[~i]) { + case SkPath::kMove_Verb: + break; + case SkPath::kLine_Verb: + mask |= SkPath::kLine_SegmentMask; + break; + case SkPath::kQuad_Verb: + mask |= SkPath::kQuad_SegmentMask; + break; + case SkPath::kConic_Verb: + mask |= SkPath::kConic_SegmentMask; + break; + case SkPath::kCubic_Verb: + mask |= SkPath::kCubic_SegmentMask; + break; + case SkPath::kClose_Verb: + break; + case SkPath::kDone_Verb: + SkDEBUGFAIL("Done verb shouldn't be recorded."); + break; + default: + SkDEBUGFAIL("Unknown Verb"); + break; + } + } + SkASSERT(mask == fSegmentMask); +#endif // SK_DEBUG_PATH } #endif diff --git a/core/SkPixelRef.cpp b/core/SkPixelRef.cpp index e93882a4..6cc67d89 100644 --- a/core/SkPixelRef.cpp +++ b/core/SkPixelRef.cpp @@ -122,6 +122,7 @@ SkPixelRef::SkPixelRef(SkBaseMutex* mutex) { SkPixelRef::SkPixelRef(SkFlattenableReadBuffer& buffer, SkBaseMutex* mutex) : INHERITED(buffer) { this->setMutex(mutex); + fInfo.unflatten(buffer); fPixels = NULL; fColorTable = NULL; // we do not track ownership of this fLockCount = 0; @@ -160,6 +161,7 @@ void SkPixelRef::setPreLocked(void* pixels, SkColorTable* ctable) { void SkPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const { this->INHERITED::flatten(buffer); + fInfo.flatten(buffer); buffer.writeBool(fIsImmutable); // We write the gen ID into the picture for within-process recording. This // is safe since the same genID will never refer to two different sets of @@ -182,6 +184,10 @@ void SkPixelRef::lockPixels() { if (1 == ++fLockCount) { fPixels = this->onLockPixels(&fColorTable); + // If onLockPixels failed, it will return NULL + if (NULL == fPixels) { + fColorTable = NULL; + } } } } @@ -194,9 +200,14 @@ void SkPixelRef::unlockPixels() { SkASSERT(fLockCount > 0); if (0 == --fLockCount) { - this->onUnlockPixels(); - fPixels = NULL; - fColorTable = NULL; + // don't call onUnlockPixels unless onLockPixels succeeded + if (fPixels) { + this->onUnlockPixels(); + fPixels = NULL; + fColorTable = NULL; + } else { + SkASSERT(NULL == fColorTable); + } } } } diff --git a/core/SkScaledImageCache.cpp b/core/SkScaledImageCache.cpp index b3956f4a..2529b5f8 100644 --- a/core/SkScaledImageCache.cpp +++ b/core/SkScaledImageCache.cpp @@ -209,7 +209,6 @@ private: SkDiscardableMemory* fDM; size_t fRB; bool fFirstTime; - bool fIsLocked; typedef SkPixelRef INHERITED; }; @@ -225,7 +224,6 @@ SkOneShotDiscardablePixelRef::SkOneShotDiscardablePixelRef(const SkImageInfo& in SkASSERT(dm->data()); fFirstTime = true; - fIsLocked = false; } SkOneShotDiscardablePixelRef::~SkOneShotDiscardablePixelRef() { @@ -235,21 +233,28 @@ SkOneShotDiscardablePixelRef::~SkOneShotDiscardablePixelRef() { void* SkOneShotDiscardablePixelRef::onLockPixels(SkColorTable** ctable) { if (fFirstTime) { // we're already locked + SkASSERT(fDM->data()); fFirstTime = false; return fDM->data(); } - SkASSERT(!fIsLocked); - fIsLocked = fDM->lock(); - return fIsLocked ? fDM->data() : NULL; + // A previous call to onUnlock may have deleted our DM, so check for that + if (NULL == fDM) { + return NULL; + } + + if (!fDM->lock()) { + // since it failed, we delete it now, to free-up the resource + delete fDM; + fDM = NULL; + return NULL; + } + return fDM->data(); } void SkOneShotDiscardablePixelRef::onUnlockPixels() { SkASSERT(!fFirstTime); - if (fIsLocked) { - fIsLocked = false; - fDM->unlock(); - } + fDM->unlock(); } size_t SkOneShotDiscardablePixelRef::getAllocatedSizeInBytes() const { @@ -613,6 +618,8 @@ void SkScaledImageCache::addToHead(Rec* rec) { this->validate(); } +/////////////////////////////////////////////////////////////////////////////// + #ifdef SK_DEBUG void SkScaledImageCache::validate() const { if (NULL == fHead) { @@ -658,6 +665,21 @@ void SkScaledImageCache::validate() const { } #endif +void SkScaledImageCache::dump() const { + this->validate(); + + const Rec* rec = fHead; + int locked = 0; + while (rec) { + locked += rec->fLockCount > 0; + rec = rec->fNext; + } + + SkDebugf("SkScaledImageCache: count=%d bytes=%d locked=%d %s\n", + fCount, fBytesUsed, locked, + fDiscardableFactory ? "discardable" : "malloc"); +} + /////////////////////////////////////////////////////////////////////////////// #include "SkThread.h" @@ -730,7 +752,9 @@ SkScaledImageCache::ID* SkScaledImageCache::AddAndLockMip(const SkBitmap& orig, void SkScaledImageCache::Unlock(SkScaledImageCache::ID* id) { SkAutoMutexAcquire am(gMutex); - return get_cache()->unlock(id); + get_cache()->unlock(id); + +// get_cache()->dump(); } size_t SkScaledImageCache::GetBytesUsed() { @@ -753,6 +777,11 @@ SkBitmap::Allocator* SkScaledImageCache::GetAllocator() { return get_cache()->allocator(); } +void SkScaledImageCache::Dump() { + SkAutoMutexAcquire am(gMutex); + get_cache()->dump(); +} + /////////////////////////////////////////////////////////////////////////////// #include "SkGraphics.h" diff --git a/core/SkScaledImageCache.h b/core/SkScaledImageCache.h index 311db325..fe072306 100644 --- a/core/SkScaledImageCache.h +++ b/core/SkScaledImageCache.h @@ -66,6 +66,11 @@ public: static SkBitmap::Allocator* GetAllocator(); + /** + * Call SkDebugf() with diagnostic information about the state of the cache + */ + static void Dump(); + /////////////////////////////////////////////////////////////////////////// /** @@ -151,6 +156,11 @@ public: SkBitmap::Allocator* allocator() const { return fAllocator; }; + /** + * Call SkDebugf() with diagnostic information about the state of the cache + */ + void dump() const; + public: struct Rec; struct Key; @@ -174,6 +184,7 @@ private: Rec* findAndLock(const Key& key); ID* addAndLock(Rec* rec); + void purgeRec(Rec*); void purgeAsNeeded(); // linklist management diff --git a/effects/SkDisplacementMapEffect.cpp b/effects/SkDisplacementMapEffect.cpp index 6e5c910e..f43287c4 100644 --- a/effects/SkDisplacementMapEffect.cpp +++ b/effects/SkDisplacementMapEffect.cpp @@ -205,6 +205,14 @@ bool SkDisplacementMapEffect::onFilterImage(Proxy* proxy, if (!this->applyCropRect(&bounds, ctm)) { return false; } + SkIRect displBounds; + displ.getBounds(&displBounds); + if (!this->applyCropRect(&displBounds, ctm)) { + return false; + } + if (!bounds.intersect(displBounds)) { + return false; + } dst->setConfig(color.config(), bounds.width(), bounds.height()); dst->allocPixels(); @@ -338,6 +346,14 @@ bool SkDisplacementMapEffect::filterImageGPU(Proxy* proxy, const SkBitmap& src, if (!this->applyCropRect(&bounds, ctm)) { return false; } + SkIRect displBounds; + displacementBM.getBounds(&displBounds); + if (!this->applyCropRect(&displBounds, ctm)) { + return false; + } + if (!bounds.intersect(displBounds)) { + return false; + } SkRect srcRect = SkRect::Make(bounds); SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); context->drawRectToRect(paint, dstRect, srcRect); diff --git a/effects/SkPictureImageFilter.cpp b/effects/SkPictureImageFilter.cpp new file mode 100644 index 00000000..5b4af654 --- /dev/null +++ b/effects/SkPictureImageFilter.cpp @@ -0,0 +1,77 @@ +/* + * 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 "SkPictureImageFilter.h" +#include "SkDevice.h" +#include "SkCanvas.h" +#include "SkFlattenableBuffers.h" +#include "SkValidationUtils.h" + +SkPictureImageFilter::SkPictureImageFilter(SkPicture* picture) + : INHERITED(0, 0), + fPicture(picture), + fRect(SkRect::MakeWH(picture ? SkIntToScalar(picture->width()) : 0, + picture ? SkIntToScalar(picture->height()) : 0)) { + SkSafeRef(fPicture); +} + +SkPictureImageFilter::SkPictureImageFilter(SkPicture* picture, const SkRect& rect) + : INHERITED(0, 0), + fPicture(picture), + fRect(rect) { + SkSafeRef(fPicture); +} + +SkPictureImageFilter::~SkPictureImageFilter() { + SkSafeUnref(fPicture); +} + +SkPictureImageFilter::SkPictureImageFilter(SkFlattenableReadBuffer& buffer) + : INHERITED(0, buffer), + fPicture(NULL) { + // FIXME: unflatten picture here. + buffer.readRect(&fRect); +} + +void SkPictureImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const { + this->INHERITED::flatten(buffer); + // FIXME: flatten picture here. + buffer.writeRect(fRect); +} + +bool SkPictureImageFilter::onFilterImage(Proxy* proxy, const SkBitmap&, const SkMatrix& matrix, + SkBitmap* result, SkIPoint* offset) { + if (!fPicture) { + return true; + } + + SkRect floatBounds; + SkIRect bounds; + matrix.mapRect(&floatBounds, fRect); + floatBounds.roundOut(&bounds); + + if (bounds.isEmpty()) { + return true; + } + + SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height())); + if (NULL == device.get()) { + return false; + } + + SkCanvas canvas(device.get()); + SkPaint paint; + + canvas.translate(-SkIntToScalar(bounds.fLeft), -SkIntToScalar(bounds.fTop)); + canvas.concat(matrix); + canvas.drawPicture(*fPicture); + + *result = device.get()->accessBitmap(false); + offset->fX += bounds.fLeft; + offset->fY += bounds.fTop; + return true; +} diff --git a/effects/gradients/SkGradientShader.cpp b/effects/gradients/SkGradientShader.cpp index 27761993..5d200d18 100644 --- a/effects/gradients/SkGradientShader.cpp +++ b/effects/gradients/SkGradientShader.cpp @@ -513,13 +513,14 @@ const uint16_t* SkGradientShaderBase::getCache16() const { const SkPMColor* SkGradientShaderBase::getCache32() const { if (fCache32 == NULL) { - // double the count for dither entries - const int entryCount = kCache32Count * 4; - const size_t allocSize = sizeof(SkPMColor) * entryCount; + SkImageInfo info; + info.fWidth = kCache32Count; + info.fHeight = 4; // for our 4 dither rows + info.fAlphaType = kPremul_SkAlphaType; + info.fColorType = kPMColor_SkColorType; if (NULL == fCache32PixelRef) { - fCache32PixelRef = SkNEW_ARGS(SkMallocPixelRef, - (NULL, allocSize, NULL)); + fCache32PixelRef = SkMallocPixelRef::NewAllocate(info, 0, NULL); } fCache32 = (SkPMColor*)fCache32PixelRef->getAddr(); if (fColorCount == 2) { @@ -541,8 +542,7 @@ const SkPMColor* SkGradientShaderBase::getCache32() const { } if (fMapper) { - SkMallocPixelRef* newPR = SkNEW_ARGS(SkMallocPixelRef, - (NULL, allocSize, NULL)); + SkMallocPixelRef* newPR = SkMallocPixelRef::NewAllocate(info, 0, NULL); SkPMColor* linear = fCache32; // just computed linear data SkPMColor* mapped = (SkPMColor*)newPR->getAddr(); // storage for mapped data SkUnitMapper* map = fMapper; diff --git a/gpu/GrSurface.cpp b/gpu/GrSurface.cpp index fed95f23..1fcc4ff1 100644 --- a/gpu/GrSurface.cpp +++ b/gpu/GrSurface.cpp @@ -8,9 +8,19 @@ #include "GrSurface.h" #include "SkBitmap.h" +#include "SkGr.h" #include "SkImageEncoder.h" #include <stdio.h> +void GrSurface::asImageInfo(SkImageInfo* info) const { + if (!GrPixelConfig2ColorType(this->config(), &info->fColorType)) { + sk_throw(); + } + info->fWidth = this->width(); + info->fHeight = this->height(); + info->fAlphaType = kPremul_SkAlphaType; +} + bool GrSurface::savePixels(const char* filename) { SkBitmap bm; bm.setConfig(SkBitmap::kARGB_8888_Config, this->width(), this->height()); diff --git a/gpu/SkGpuDevice.cpp b/gpu/SkGpuDevice.cpp index ce02f2c5..a413d042 100644 --- a/gpu/SkGpuDevice.cpp +++ b/gpu/SkGpuDevice.cpp @@ -214,7 +214,10 @@ void SkGpuDevice::initFromRenderTarget(GrContext* context, if (NULL == surface) { surface = fRenderTarget; } - SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (surface, cached)); + + SkImageInfo info; + surface->asImageInfo(&info); + SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (info, surface, cached)); this->setPixelRef(pr, 0)->unref(); } @@ -224,8 +227,8 @@ SkGpuDevice::SkGpuDevice(GrContext* context, int width, int height, int sampleCount) - : SkBitmapDevice(make_bitmap(config, width, height, false /*isOpaque*/)) { - + : SkBitmapDevice(make_bitmap(config, width, height, false /*isOpaque*/)) +{ fDrawProcs = NULL; fContext = context; @@ -245,6 +248,14 @@ SkGpuDevice::SkGpuDevice(GrContext* context, desc.fConfig = SkBitmapConfig2GrPixelConfig(config); desc.fSampleCnt = sampleCount; + SkImageInfo info; + if (!GrPixelConfig2ColorType(desc.fConfig, &info.fColorType)) { + sk_throw(); + } + info.fWidth = width; + info.fHeight = height; + info.fAlphaType = kPremul_SkAlphaType; + SkAutoTUnref<GrTexture> texture(fContext->createUncachedTexture(desc, NULL, 0)); if (NULL != texture) { @@ -254,7 +265,7 @@ SkGpuDevice::SkGpuDevice(GrContext* context, SkASSERT(NULL != fRenderTarget); // wrap the bitmap with a pixelref to expose our texture - SkGrPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (texture)); + SkGrPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (info, texture)); this->setPixelRef(pr, 0)->unref(); } else { GrPrintf("--- failed to create gpu-offscreen [%d %d]\n", @@ -840,11 +851,12 @@ bool create_mask_GPU(GrContext* context, } SkBitmap wrap_texture(GrTexture* texture) { + SkImageInfo info; + texture->asImageInfo(&info); + SkBitmap result; - bool dummy; - SkBitmap::Config config = grConfig2skConfig(texture->config(), &dummy); - result.setConfig(config, texture->width(), texture->height()); - result.setPixelRef(SkNEW_ARGS(SkGrPixelRef, (texture)))->unref(); + result.setConfig(info); + result.setPixelRef(SkNEW_ARGS(SkGrPixelRef, (info, texture)))->unref(); return result; } diff --git a/gpu/SkGr.cpp b/gpu/SkGr.cpp index c7ae0c8f..a3f0eefa 100644 --- a/gpu/SkGr.cpp +++ b/gpu/SkGr.cpp @@ -258,3 +258,33 @@ GrPixelConfig SkBitmapConfig2GrPixelConfig(SkBitmap::Config config) { return kUnknown_GrPixelConfig; } } + +bool GrPixelConfig2ColorType(GrPixelConfig config, SkColorType* ctOut) { + SkColorType ct; + switch (config) { + case kAlpha_8_GrPixelConfig: + ct = kAlpha_8_SkColorType; + break; + case kIndex_8_GrPixelConfig: + ct = kIndex_8_SkColorType; + break; + case kRGB_565_GrPixelConfig: + ct = kRGB_565_SkColorType; + break; + case kRGBA_4444_GrPixelConfig: + ct = kARGB_4444_SkColorType; + break; + case kRGBA_8888_GrPixelConfig: + ct = kRGBA_8888_SkColorType; + break; + case kBGRA_8888_GrPixelConfig: + ct = kBGRA_8888_SkColorType; + break; + default: + return false; + } + if (ctOut) { + *ctOut = ct; + } + return true; +} diff --git a/gpu/SkGrPixelRef.cpp b/gpu/SkGrPixelRef.cpp index 01294266..ddd60ff8 100644 --- a/gpu/SkGrPixelRef.cpp +++ b/gpu/SkGrPixelRef.cpp @@ -18,11 +18,10 @@ // to avoid deadlock with the default one provided by SkPixelRef. SK_DECLARE_STATIC_MUTEX(gROLockPixelsPixelRefMutex); -SkROLockPixelsPixelRef::SkROLockPixelsPixelRef() : INHERITED(&gROLockPixelsPixelRefMutex) { -} +SkROLockPixelsPixelRef::SkROLockPixelsPixelRef(const SkImageInfo& info) + : INHERITED(info, &gROLockPixelsPixelRefMutex) {} -SkROLockPixelsPixelRef::~SkROLockPixelsPixelRef() { -} +SkROLockPixelsPixelRef::~SkROLockPixelsPixelRef() {} void* SkROLockPixelsPixelRef::onLockPixels(SkColorTable** ctable) { if (ctable) { @@ -76,6 +75,14 @@ static SkGrPixelRef* copyToTexturePixelRef(GrTexture* texture, SkBitmap::Config desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit; desc.fConfig = SkBitmapConfig2GrPixelConfig(dstConfig); + SkImageInfo info; + if (!GrPixelConfig2ColorType(desc.fConfig, &info.fColorType)) { + return NULL; + } + info.fWidth = desc.fWidth; + info.fHeight = desc.fHeight; + info.fAlphaType = kPremul_SkAlphaType; + GrTexture* dst = context->createUncachedTexture(desc, NULL, 0); if (NULL == dst) { return NULL; @@ -93,31 +100,15 @@ static SkGrPixelRef* copyToTexturePixelRef(GrTexture* texture, SkBitmap::Config dst->releaseRenderTarget(); #endif - SkGrPixelRef* pixelRef = SkNEW_ARGS(SkGrPixelRef, (dst)); + SkGrPixelRef* pixelRef = SkNEW_ARGS(SkGrPixelRef, (info, dst)); SkSafeUnref(dst); return pixelRef; } /////////////////////////////////////////////////////////////////////////////// -SkGrPixelRef::SkGrPixelRef(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(const SkImageInfo&, GrSurface* surface, bool transferCacheLock) { +SkGrPixelRef::SkGrPixelRef(const SkImageInfo& info, GrSurface* surface, + bool transferCacheLock) : INHERITED(info) { // 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. diff --git a/image/SkDataPixelRef.cpp b/image/SkDataPixelRef.cpp index 7897bf93..c8bfe808 100644 --- a/image/SkDataPixelRef.cpp +++ b/image/SkDataPixelRef.cpp @@ -9,7 +9,10 @@ #include "SkData.h" #include "SkFlattenableBuffers.h" -SkDataPixelRef::SkDataPixelRef(SkData* data) : fData(data) { +SkDataPixelRef::SkDataPixelRef(const SkImageInfo& info, SkData* data) + : INHERITED(info) + , fData(data) +{ fData->ref(); this->setPreLocked(const_cast<void*>(fData->data()), NULL); } diff --git a/image/SkDataPixelRef.h b/image/SkDataPixelRef.h index 50c88571..0f8269c8 100644 --- a/image/SkDataPixelRef.h +++ b/image/SkDataPixelRef.h @@ -14,7 +14,7 @@ class SkData; class SkDataPixelRef : public SkPixelRef { public: - SkDataPixelRef(SkData* data); + SkDataPixelRef(const SkImageInfo&, SkData* data); virtual ~SkDataPixelRef(); SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDataPixelRef) diff --git a/image/SkImage_Raster.cpp b/image/SkImage_Raster.cpp index a872ae36..807c19e7 100644 --- a/image/SkImage_Raster.cpp +++ b/image/SkImage_Raster.cpp @@ -84,10 +84,8 @@ SkImage* SkImage_Raster::NewEmpty() { SkImage_Raster::SkImage_Raster(const Info& info, SkData* data, size_t rowBytes) : INHERITED(info.fWidth, info.fHeight) { - SkBitmap::Config config = SkImageInfoToBitmapConfig(info); - - fBitmap.setConfig(config, info.fWidth, info.fHeight, rowBytes, info.fAlphaType); - fBitmap.setPixelRef(SkNEW_ARGS(SkDataPixelRef, (data)))->unref(); + fBitmap.setConfig(info, rowBytes); + fBitmap.setPixelRef(SkNEW_ARGS(SkDataPixelRef, (info, data)))->unref(); fBitmap.setImmutable(); } diff --git a/image/SkSurface_Raster.cpp b/image/SkSurface_Raster.cpp index 27db504d..61ade6f4 100644 --- a/image/SkSurface_Raster.cpp +++ b/image/SkSurface_Raster.cpp @@ -155,19 +155,9 @@ SkSurface* SkSurface::NewRaster(const SkImageInfo& info) { return NULL; } - static const size_t kMaxTotalSize = SK_MaxS32; - size_t rowBytes = SkImageMinRowBytes(info); - uint64_t size64 = (uint64_t)info.fHeight * rowBytes; - if (size64 > kMaxTotalSize) { - return NULL; - } - - size_t size = (size_t)size64; - void* pixels = sk_malloc_throw(size); - if (NULL == pixels) { + SkAutoTUnref<SkPixelRef> pr(SkMallocPixelRef::NewAllocate(info, 0, NULL)); + if (NULL == pr.get()) { return NULL; } - - SkAutoTUnref<SkPixelRef> pr(SkNEW_ARGS(SkMallocPixelRef, (pixels, size, NULL, true))); - return SkNEW_ARGS(SkSurface_Raster, (info, pr, rowBytes)); + return SkNEW_ARGS(SkSurface_Raster, (info, pr, info.minRowBytes())); } diff --git a/images/SkImageRef.cpp b/images/SkImageRef.cpp index 1a8284bd..716519f0 100644 --- a/images/SkImageRef.cpp +++ b/images/SkImageRef.cpp @@ -18,13 +18,12 @@ /////////////////////////////////////////////////////////////////////////////// -SkImageRef::SkImageRef(SkStreamRewindable* stream, SkBitmap::Config config, +SkImageRef::SkImageRef(const SkImageInfo& info, SkStreamRewindable* stream, int sampleSize, SkBaseMutex* mutex) - : SkPixelRef(mutex), fErrorInDecoding(false) { + : SkPixelRef(info, mutex), fErrorInDecoding(false) { SkASSERT(stream); stream->ref(); fStream = stream; - fConfig = config; fSampleSize = sampleSize; fDoDither = true; fPrev = fNext = NULL; @@ -32,7 +31,7 @@ SkImageRef::SkImageRef(SkStreamRewindable* stream, SkBitmap::Config config, #ifdef DUMP_IMAGEREF_LIFECYCLE SkDebugf("add ImageRef %p [%d] data=%d\n", - this, config, (int)stream->getLength()); + this, this->info().fColorType, (int)stream->getLength()); #endif } @@ -92,14 +91,6 @@ bool SkImageRef::prepareBitmap(SkImageDecoder::Mode mode) { return false; } - /* As soon as we really know our config, we record it, so that on - subsequent calls to the codec, we are sure we will always get the same - result. - */ - if (SkBitmap::kNo_Config != fBitmap.config()) { - fConfig = fBitmap.config(); - } - if (NULL != fBitmap.getPixels() || (SkBitmap::kNo_Config != fBitmap.config() && SkImageDecoder::kDecodeBounds_Mode == mode)) { @@ -125,7 +116,7 @@ bool SkImageRef::prepareBitmap(SkImageDecoder::Mode mode) { codec->setSampleSize(fSampleSize); codec->setDitherImage(fDoDither); - if (this->onDecode(codec, fStream, &fBitmap, fConfig, mode)) { + if (this->onDecode(codec, fStream, &fBitmap, fBitmap.config(), mode)) { return true; } } @@ -170,7 +161,6 @@ size_t SkImageRef::ramUsed() const { SkImageRef::SkImageRef(SkFlattenableReadBuffer& buffer, SkBaseMutex* mutex) : INHERITED(buffer, mutex), fErrorInDecoding(false) { - fConfig = (SkBitmap::Config)buffer.readUInt(); fSampleSize = buffer.readInt(); fDoDither = buffer.readBool(); @@ -185,7 +175,6 @@ SkImageRef::SkImageRef(SkFlattenableReadBuffer& buffer, SkBaseMutex* mutex) void SkImageRef::flatten(SkFlattenableWriteBuffer& buffer) const { this->INHERITED::flatten(buffer); - buffer.writeUInt(fConfig); buffer.writeInt(fSampleSize); buffer.writeBool(fDoDither); // FIXME: Consider moving this logic should go into writeStream itself. diff --git a/images/SkImageRef_GlobalPool.cpp b/images/SkImageRef_GlobalPool.cpp index 352dd42d..f91cebab 100644 --- a/images/SkImageRef_GlobalPool.cpp +++ b/images/SkImageRef_GlobalPool.cpp @@ -24,10 +24,10 @@ static SkImageRefPool* GetGlobalPool() { return gPool; } -SkImageRef_GlobalPool::SkImageRef_GlobalPool(SkStreamRewindable* stream, - SkBitmap::Config config, +SkImageRef_GlobalPool::SkImageRef_GlobalPool(const SkImageInfo& info, + SkStreamRewindable* stream, int sampleSize) - : SkImageRef(stream, config, sampleSize, &gGlobalPoolMutex) { + : SkImageRef(info, stream, sampleSize, &gGlobalPoolMutex) { SkASSERT(&gGlobalPoolMutex == this->mutex()); SkAutoMutexAcquire ac(gGlobalPoolMutex); GetGlobalPool()->addToHead(this); diff --git a/images/SkImageRef_ashmem.cpp b/images/SkImageRef_ashmem.cpp index 0dba1d11..269199fa 100644 --- a/images/SkImageRef_ashmem.cpp +++ b/images/SkImageRef_ashmem.cpp @@ -1,10 +1,10 @@ - /* * Copyright 2011 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ + #include "SkImageRef_ashmem.h" #include "SkImageDecoder.h" #include "SkFlattenableBuffers.h" @@ -31,11 +31,11 @@ static size_t roundToPageSize(size_t size) { return newsize; } -SkImageRef_ashmem::SkImageRef_ashmem(SkStreamRewindable* stream, - SkBitmap::Config config, - int sampleSize) - : SkImageRef(stream, config, sampleSize) { - +SkImageRef_ashmem::SkImageRef_ashmem(const SkImageInfo& info, + SkStreamRewindable* stream, + int sampleSize) + : SkImageRef(info, stream, sampleSize) +{ fRec.fFD = -1; fRec.fAddr = NULL; fRec.fSize = 0; diff --git a/images/SkImageRef_ashmem.h b/images/SkImageRef_ashmem.h index efee5e75..a2652fbc 100644 --- a/images/SkImageRef_ashmem.h +++ b/images/SkImageRef_ashmem.h @@ -19,7 +19,7 @@ struct SkAshmemRec { class SkImageRef_ashmem : public SkImageRef { public: - SkImageRef_ashmem(SkStreamRewindable*, SkBitmap::Config, int sampleSize = 1); + SkImageRef_ashmem(const SkImageInfo&, SkStreamRewindable*, int sampleSize = 1); virtual ~SkImageRef_ashmem(); SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkImageRef_ashmem) diff --git a/lazy/SkCachingPixelRef.cpp b/lazy/SkCachingPixelRef.cpp index 667a9493..668f57ef 100644 --- a/lazy/SkCachingPixelRef.cpp +++ b/lazy/SkCachingPixelRef.cpp @@ -21,20 +21,18 @@ bool SkCachingPixelRef::Install(SkImageGenerator* generator, return false; } SkAutoTUnref<SkCachingPixelRef> ref(SkNEW_ARGS(SkCachingPixelRef, - (generator, - info, - dst->rowBytes()))); + (info, generator, dst->rowBytes()))); dst->setPixelRef(ref); return true; } -SkCachingPixelRef::SkCachingPixelRef(SkImageGenerator* generator, - const SkImageInfo& info, +SkCachingPixelRef::SkCachingPixelRef(const SkImageInfo& info, + SkImageGenerator* generator, size_t rowBytes) - : fImageGenerator(generator) + : INHERITED(info) + , fImageGenerator(generator) , fErrorInDecoding(false) , fScaledCacheId(NULL) - , fInfo(info) , fRowBytes(rowBytes) { SkASSERT(fImageGenerator != NULL); } @@ -44,31 +42,32 @@ SkCachingPixelRef::~SkCachingPixelRef() { // Assert always unlock before unref. } -void* SkCachingPixelRef::onLockPixels(SkColorTable** colorTable) { - (void)colorTable; +void* SkCachingPixelRef::onLockPixels(SkColorTable**) { + const SkImageInfo& info = this->info(); + if (fErrorInDecoding) { return NULL; // don't try again. } SkBitmap bitmap; SkASSERT(NULL == fScaledCacheId); fScaledCacheId = SkScaledImageCache::FindAndLock(this->getGenerationID(), - fInfo.fWidth, - fInfo.fHeight, + info.fWidth, + info.fHeight, &bitmap); if (NULL == fScaledCacheId) { // Cache has been purged, must re-decode. - if ((!bitmap.setConfig(fInfo, fRowBytes)) || !bitmap.allocPixels()) { + if ((!bitmap.setConfig(info, fRowBytes)) || !bitmap.allocPixels()) { fErrorInDecoding = true; return NULL; } SkAutoLockPixels autoLockPixels(bitmap); - if (!fImageGenerator->getPixels(fInfo, bitmap.getPixels(), fRowBytes)) { + if (!fImageGenerator->getPixels(info, bitmap.getPixels(), fRowBytes)) { fErrorInDecoding = true; return NULL; } fScaledCacheId = SkScaledImageCache::AddAndLock(this->getGenerationID(), - fInfo.fWidth, - fInfo.fHeight, + info.fWidth, + info.fHeight, bitmap); SkASSERT(fScaledCacheId != NULL); } @@ -90,9 +89,7 @@ void* SkCachingPixelRef::onLockPixels(SkColorTable** colorTable) { } void SkCachingPixelRef::onUnlockPixels() { - if (fScaledCacheId != NULL) { - SkScaledImageCache::Unlock( - static_cast<SkScaledImageCache::ID*>(fScaledCacheId)); - fScaledCacheId = NULL; - } + SkASSERT(fScaledCacheId != NULL); + SkScaledImageCache::Unlock( static_cast<SkScaledImageCache::ID*>(fScaledCacheId)); + fScaledCacheId = NULL; } diff --git a/lazy/SkCachingPixelRef.h b/lazy/SkCachingPixelRef.h index 4a0387dd..b1f2fcd6 100644 --- a/lazy/SkCachingPixelRef.h +++ b/lazy/SkCachingPixelRef.h @@ -58,12 +58,10 @@ private: SkImageGenerator* const fImageGenerator; bool fErrorInDecoding; void* fScaledCacheId; - const SkImageInfo fInfo; const size_t fRowBytes; - SkCachingPixelRef(SkImageGenerator* imageGenerator, - const SkImageInfo& info, - size_t rowBytes); + SkCachingPixelRef(const SkImageInfo&, SkImageGenerator*, size_t rowBytes); + typedef SkPixelRef INHERITED; }; diff --git a/lazy/SkDiscardableMemoryPool.cpp b/lazy/SkDiscardableMemoryPool.cpp index a1b2438a..47097098 100644 --- a/lazy/SkDiscardableMemoryPool.cpp +++ b/lazy/SkDiscardableMemoryPool.cpp @@ -47,19 +47,23 @@ SkPoolDiscardableMemory::SkPoolDiscardableMemory(SkDiscardableMemoryPool* pool, } SkPoolDiscardableMemory::~SkPoolDiscardableMemory() { + SkASSERT(!fLocked); // contract for SkDiscardableMemory fPool->free(this); fPool->unref(); } bool SkPoolDiscardableMemory::lock() { + SkASSERT(!fLocked); // contract for SkDiscardableMemory return fPool->lock(this); } void* SkPoolDiscardableMemory::data() { - return fLocked ? fPointer : NULL; + SkASSERT(fLocked); // contract for SkDiscardableMemory + return fPointer; } void SkPoolDiscardableMemory::unlock() { + SkASSERT(fLocked); // contract for SkDiscardableMemory fPool->unlock(this); } diff --git a/lazy/SkDiscardablePixelRef.cpp b/lazy/SkDiscardablePixelRef.cpp index 6a9507c8..160ca5b4 100644 --- a/lazy/SkDiscardablePixelRef.cpp +++ b/lazy/SkDiscardablePixelRef.cpp @@ -9,19 +9,17 @@ #include "SkDiscardableMemory.h" #include "SkImageGenerator.h" -SkDiscardablePixelRef::SkDiscardablePixelRef(SkImageGenerator* generator, - const SkImageInfo& info, - size_t size, +SkDiscardablePixelRef::SkDiscardablePixelRef(const SkImageInfo& info, + SkImageGenerator* generator, size_t rowBytes, SkDiscardableMemory::Factory* fact) - : fGenerator(generator) + : INHERITED(info) + , fGenerator(generator) , fDMFactory(fact) - , fInfo(info) - , fSize(size) , fRowBytes(rowBytes) - , fDiscardableMemory(NULL) { + , fDiscardableMemory(NULL) +{ 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(). @@ -30,6 +28,9 @@ SkDiscardablePixelRef::SkDiscardablePixelRef(SkImageGenerator* generator, } SkDiscardablePixelRef::~SkDiscardablePixelRef() { + if (this->isLocked()) { + fDiscardableMemory->unlock(); + } SkDELETE(fDiscardableMemory); SkSafeUnref(fDMFactory); SkDELETE(fGenerator); @@ -43,24 +44,28 @@ void* SkDiscardablePixelRef::onLockPixels(SkColorTable**) { SkDELETE(fDiscardableMemory); fDiscardableMemory = NULL; } + + const size_t size = this->info().getSafeSize(fRowBytes); + if (fDMFactory != NULL) { - fDiscardableMemory = fDMFactory->create(fSize); + fDiscardableMemory = fDMFactory->create(size); } else { - fDiscardableMemory = SkDiscardableMemory::Create(fSize); + fDiscardableMemory = SkDiscardableMemory::Create(size); } if (NULL == fDiscardableMemory) { return NULL; // Memory allocation failed. } void* pixels = fDiscardableMemory->data(); - if (!fGenerator->getPixels(fInfo, pixels, fRowBytes)) { - return NULL; // TODO(halcanary) Find out correct thing to do. + if (!fGenerator->getPixels(this->info(), pixels, fRowBytes)) { + fDiscardableMemory->unlock(); + SkDELETE(fDiscardableMemory); + fDiscardableMemory = NULL; + return NULL; } return pixels; } void SkDiscardablePixelRef::onUnlockPixels() { - if (fDiscardableMemory != NULL) { - fDiscardableMemory->unlock(); - } + fDiscardableMemory->unlock(); } bool SkInstallDiscardablePixelRef(SkImageGenerator* generator, @@ -80,10 +85,7 @@ bool SkInstallDiscardablePixelRef(SkImageGenerator* generator, return dst->allocPixels(NULL, NULL); } SkAutoTUnref<SkDiscardablePixelRef> ref(SkNEW_ARGS(SkDiscardablePixelRef, - (generator, info, - dst->getSize(), - dst->rowBytes(), - factory))); + (info, generator, dst->rowBytes(), factory))); dst->setPixelRef(ref); return true; } diff --git a/lazy/SkDiscardablePixelRef.h b/lazy/SkDiscardablePixelRef.h index 44c6df96..4b669383 100644 --- a/lazy/SkDiscardablePixelRef.h +++ b/lazy/SkDiscardablePixelRef.h @@ -30,8 +30,6 @@ 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; // These const members should not change over the life of the // PixelRef, since the SkBitmap doesn't expect them to change. @@ -39,9 +37,7 @@ private: SkDiscardableMemory* fDiscardableMemory; /* Takes ownership of SkImageGenerator. */ - SkDiscardablePixelRef(SkImageGenerator* generator, - const SkImageInfo& info, - size_t size, + SkDiscardablePixelRef(const SkImageInfo&, SkImageGenerator*, size_t rowBytes, SkDiscardableMemory::Factory* factory); friend bool SkInstallDiscardablePixelRef(SkImageGenerator*, |