michael@0: michael@0: /* michael@0: * Copyright 2011 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: #include "SkPixelRef.h" michael@0: #include "SkReadBuffer.h" michael@0: #include "SkWriteBuffer.h" michael@0: #include "SkThread.h" michael@0: michael@0: #ifdef SK_USE_POSIX_THREADS michael@0: michael@0: static SkBaseMutex gPixelRefMutexRing[] = { michael@0: { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, michael@0: { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, michael@0: { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, michael@0: { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, michael@0: michael@0: { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, michael@0: { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, michael@0: { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, michael@0: { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, michael@0: michael@0: { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, michael@0: { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, michael@0: { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, michael@0: { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, michael@0: michael@0: { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, michael@0: { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, michael@0: { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, michael@0: { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, michael@0: }; michael@0: michael@0: // must be a power-of-2. undef to just use 1 mutex michael@0: #define PIXELREF_MUTEX_RING_COUNT SK_ARRAY_COUNT(gPixelRefMutexRing) michael@0: michael@0: #else // not pthreads michael@0: michael@0: // must be a power-of-2. undef to just use 1 mutex michael@0: #define PIXELREF_MUTEX_RING_COUNT 32 michael@0: static SkBaseMutex gPixelRefMutexRing[PIXELREF_MUTEX_RING_COUNT]; michael@0: michael@0: #endif michael@0: michael@0: static SkBaseMutex* get_default_mutex() { michael@0: static int32_t gPixelRefMutexRingIndex; michael@0: michael@0: SkASSERT(SkIsPow2(PIXELREF_MUTEX_RING_COUNT)); michael@0: michael@0: // atomic_inc might be overkill here. It may be fine if once in a while michael@0: // we hit a race-condition and two subsequent calls get the same index... michael@0: int index = sk_atomic_inc(&gPixelRefMutexRingIndex); michael@0: return &gPixelRefMutexRing[index & (PIXELREF_MUTEX_RING_COUNT - 1)]; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: int32_t SkNextPixelRefGenerationID(); michael@0: michael@0: int32_t SkNextPixelRefGenerationID() { michael@0: static int32_t gPixelRefGenerationID; michael@0: // do a loop in case our global wraps around, as we never want to michael@0: // return a 0 michael@0: int32_t genID; michael@0: do { michael@0: genID = sk_atomic_inc(&gPixelRefGenerationID) + 1; michael@0: } while (0 == genID); michael@0: return genID; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: void SkPixelRef::setMutex(SkBaseMutex* mutex) { michael@0: if (NULL == mutex) { michael@0: mutex = get_default_mutex(); michael@0: } michael@0: fMutex = mutex; michael@0: } michael@0: michael@0: // just need a > 0 value, so pick a funny one to aid in debugging michael@0: #define SKPIXELREF_PRELOCKED_LOCKCOUNT 123456789 michael@0: michael@0: SkPixelRef::SkPixelRef(const SkImageInfo& info) : fInfo(info) { michael@0: this->setMutex(NULL); michael@0: fRec.zero(); michael@0: fLockCount = 0; michael@0: this->needsNewGenID(); michael@0: fIsImmutable = false; michael@0: fPreLocked = false; michael@0: } michael@0: michael@0: michael@0: SkPixelRef::SkPixelRef(const SkImageInfo& info, SkBaseMutex* mutex) : fInfo(info) { michael@0: this->setMutex(mutex); michael@0: fRec.zero(); michael@0: fLockCount = 0; michael@0: this->needsNewGenID(); michael@0: fIsImmutable = false; michael@0: fPreLocked = false; michael@0: } michael@0: michael@0: static SkImageInfo read_info(SkReadBuffer& buffer) { michael@0: SkImageInfo info; michael@0: info.unflatten(buffer); michael@0: return info; michael@0: } michael@0: michael@0: SkPixelRef::SkPixelRef(SkReadBuffer& buffer, SkBaseMutex* mutex) michael@0: : INHERITED(buffer) michael@0: , fInfo(read_info(buffer)) michael@0: { michael@0: this->setMutex(mutex); michael@0: fRec.zero(); michael@0: fLockCount = 0; michael@0: fIsImmutable = buffer.readBool(); michael@0: fGenerationID = buffer.readUInt(); michael@0: fUniqueGenerationID = false; // Conservatively assuming the original still exists. michael@0: fPreLocked = false; michael@0: } michael@0: michael@0: SkPixelRef::~SkPixelRef() { michael@0: this->callGenIDChangeListeners(); michael@0: } michael@0: michael@0: void SkPixelRef::needsNewGenID() { michael@0: fGenerationID = 0; michael@0: fUniqueGenerationID = false; michael@0: } michael@0: michael@0: void SkPixelRef::cloneGenID(const SkPixelRef& that) { michael@0: // This is subtle. We must call that.getGenerationID() to make sure its genID isn't 0. michael@0: this->fGenerationID = that.getGenerationID(); michael@0: this->fUniqueGenerationID = false; michael@0: that.fUniqueGenerationID = false; michael@0: } michael@0: michael@0: void SkPixelRef::setPreLocked(void* pixels, size_t rowBytes, SkColorTable* ctable) { michael@0: #ifndef SK_IGNORE_PIXELREF_SETPRELOCKED michael@0: // only call me in your constructor, otherwise fLockCount tracking can get michael@0: // out of sync. michael@0: fRec.fPixels = pixels; michael@0: fRec.fColorTable = ctable; michael@0: fRec.fRowBytes = rowBytes; michael@0: fLockCount = SKPIXELREF_PRELOCKED_LOCKCOUNT; michael@0: fPreLocked = true; michael@0: #endif michael@0: } michael@0: michael@0: void SkPixelRef::flatten(SkWriteBuffer& buffer) const { michael@0: this->INHERITED::flatten(buffer); michael@0: fInfo.flatten(buffer); michael@0: buffer.writeBool(fIsImmutable); michael@0: // We write the gen ID into the picture for within-process recording. This michael@0: // is safe since the same genID will never refer to two different sets of michael@0: // pixels (barring overflow). However, each process has its own "namespace" michael@0: // of genIDs. So for cross-process recording we write a zero which will michael@0: // trigger assignment of a new genID in playback. michael@0: if (buffer.isCrossProcess()) { michael@0: buffer.writeUInt(0); michael@0: } else { michael@0: buffer.writeUInt(fGenerationID); michael@0: fUniqueGenerationID = false; // Conservative, a copy is probably about to exist. michael@0: } michael@0: } michael@0: michael@0: bool SkPixelRef::lockPixels(LockRec* rec) { michael@0: SkASSERT(!fPreLocked || SKPIXELREF_PRELOCKED_LOCKCOUNT == fLockCount); michael@0: michael@0: if (!fPreLocked) { michael@0: SkAutoMutexAcquire ac(*fMutex); michael@0: michael@0: if (1 == ++fLockCount) { michael@0: SkASSERT(fRec.isZero()); michael@0: michael@0: LockRec rec; michael@0: if (!this->onNewLockPixels(&rec)) { michael@0: return false; michael@0: } michael@0: SkASSERT(!rec.isZero()); // else why did onNewLock return true? michael@0: fRec = rec; michael@0: } michael@0: } michael@0: *rec = fRec; michael@0: return true; michael@0: } michael@0: michael@0: bool SkPixelRef::lockPixels() { michael@0: LockRec rec; michael@0: return this->lockPixels(&rec); michael@0: } michael@0: michael@0: void SkPixelRef::unlockPixels() { michael@0: SkASSERT(!fPreLocked || SKPIXELREF_PRELOCKED_LOCKCOUNT == fLockCount); michael@0: michael@0: if (!fPreLocked) { michael@0: SkAutoMutexAcquire ac(*fMutex); michael@0: michael@0: SkASSERT(fLockCount > 0); michael@0: if (0 == --fLockCount) { michael@0: // don't call onUnlockPixels unless onLockPixels succeeded michael@0: if (fRec.fPixels) { michael@0: this->onUnlockPixels(); michael@0: fRec.zero(); michael@0: } else { michael@0: SkASSERT(fRec.isZero()); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool SkPixelRef::lockPixelsAreWritable() const { michael@0: return this->onLockPixelsAreWritable(); michael@0: } michael@0: michael@0: bool SkPixelRef::onLockPixelsAreWritable() const { michael@0: return true; michael@0: } michael@0: michael@0: bool SkPixelRef::onImplementsDecodeInto() { michael@0: return false; michael@0: } michael@0: michael@0: bool SkPixelRef::onDecodeInto(int pow2, SkBitmap* bitmap) { michael@0: return false; michael@0: } michael@0: michael@0: uint32_t SkPixelRef::getGenerationID() const { michael@0: if (0 == fGenerationID) { michael@0: fGenerationID = SkNextPixelRefGenerationID(); michael@0: fUniqueGenerationID = true; // The only time we can be sure of this! michael@0: } michael@0: return fGenerationID; michael@0: } michael@0: michael@0: void SkPixelRef::addGenIDChangeListener(GenIDChangeListener* listener) { michael@0: if (NULL == listener || !fUniqueGenerationID) { michael@0: // No point in tracking this if we're not going to call it. michael@0: SkDELETE(listener); michael@0: return; michael@0: } michael@0: *fGenIDChangeListeners.append() = listener; michael@0: } michael@0: michael@0: void SkPixelRef::callGenIDChangeListeners() { michael@0: // We don't invalidate ourselves if we think another SkPixelRef is sharing our genID. michael@0: if (fUniqueGenerationID) { michael@0: for (int i = 0; i < fGenIDChangeListeners.count(); i++) { michael@0: fGenIDChangeListeners[i]->onChange(); michael@0: } michael@0: } michael@0: // Listeners get at most one shot, so whether these triggered or not, blow them away. michael@0: fGenIDChangeListeners.deleteAll(); michael@0: } michael@0: michael@0: void SkPixelRef::notifyPixelsChanged() { michael@0: #ifdef SK_DEBUG michael@0: if (fIsImmutable) { michael@0: SkDebugf("========== notifyPixelsChanged called on immutable pixelref"); michael@0: } michael@0: #endif michael@0: this->callGenIDChangeListeners(); michael@0: this->needsNewGenID(); michael@0: } michael@0: michael@0: void SkPixelRef::changeAlphaType(SkAlphaType at) { michael@0: *const_cast(&fInfo.fAlphaType) = at; michael@0: } michael@0: michael@0: void SkPixelRef::setImmutable() { michael@0: fIsImmutable = true; michael@0: } michael@0: michael@0: bool SkPixelRef::readPixels(SkBitmap* dst, const SkIRect* subset) { michael@0: return this->onReadPixels(dst, subset); michael@0: } michael@0: michael@0: bool SkPixelRef::onReadPixels(SkBitmap* dst, const SkIRect* subset) { michael@0: return false; michael@0: } michael@0: michael@0: SkData* SkPixelRef::onRefEncodedData() { michael@0: return NULL; michael@0: } michael@0: michael@0: size_t SkPixelRef::getAllocatedSizeInBytes() const { michael@0: return 0; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: #ifdef SK_BUILD_FOR_ANDROID michael@0: void SkPixelRef::globalRef(void* data) { michael@0: this->ref(); michael@0: } michael@0: michael@0: void SkPixelRef::globalUnref() { michael@0: this->unref(); michael@0: } michael@0: #endif