SkPixelRef.cpp revision 96fcdcc219d2a0d3579719b84b28bede76efba64
1/* 2 * Copyright 2011 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "SkBitmapCache.h" 9#include "SkMutex.h" 10#include "SkPixelRef.h" 11#include "SkTraceEvent.h" 12 13//#define SK_SUPPORT_LEGACY_UNBALANCED_PIXELREF_LOCKCOUNT 14//#define SK_TRACE_PIXELREF_LIFETIME 15 16#include "SkNextID.h" 17 18uint32_t SkNextID::ImageID() { 19 static uint32_t gID = 0; 20 uint32_t id; 21 // Loop in case our global wraps around, as we never want to return a 0. 22 do { 23 id = sk_atomic_fetch_add(&gID, 2u) + 2; // Never set the low bit. 24 } while (0 == id); 25 return id; 26} 27 28/////////////////////////////////////////////////////////////////////////////// 29 30// just need a > 0 value, so pick a funny one to aid in debugging 31#define SKPIXELREF_PRELOCKED_LOCKCOUNT 123456789 32 33static SkImageInfo validate_info(const SkImageInfo& info) { 34 SkAlphaType newAlphaType = info.alphaType(); 35 SkAssertResult(SkColorTypeValidateAlphaType(info.colorType(), info.alphaType(), &newAlphaType)); 36 return info.makeAlphaType(newAlphaType); 37} 38 39#ifdef SK_TRACE_PIXELREF_LIFETIME 40 static int32_t gInstCounter; 41#endif 42 43SkPixelRef::SkPixelRef(const SkImageInfo& info) 44 : fInfo(validate_info(info)) 45#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK 46 , fStableID(SkNextID::ImageID()) 47#endif 48 49{ 50#ifdef SK_TRACE_PIXELREF_LIFETIME 51 SkDebugf(" pixelref %d\n", sk_atomic_inc(&gInstCounter)); 52#endif 53 fRec.zero(); 54 fLockCount = 0; 55 this->needsNewGenID(); 56 fMutability = kMutable; 57 fPreLocked = false; 58 fAddedToCache.store(false); 59} 60 61SkPixelRef::~SkPixelRef() { 62#ifndef SK_SUPPORT_LEGACY_UNBALANCED_PIXELREF_LOCKCOUNT 63 SkASSERT(SKPIXELREF_PRELOCKED_LOCKCOUNT == fLockCount || 0 == fLockCount); 64#endif 65 66#ifdef SK_TRACE_PIXELREF_LIFETIME 67 SkDebugf("~pixelref %d\n", sk_atomic_dec(&gInstCounter) - 1); 68#endif 69 this->callGenIDChangeListeners(); 70} 71 72void SkPixelRef::needsNewGenID() { 73 fTaggedGenID.store(0); 74 SkASSERT(!this->genIDIsUnique()); // This method isn't threadsafe, so the assert should be fine. 75} 76 77void SkPixelRef::cloneGenID(const SkPixelRef& that) { 78 // This is subtle. We must call that.getGenerationID() to make sure its genID isn't 0. 79 uint32_t genID = that.getGenerationID(); 80 81 // Neither ID is unique any more. 82 // (These & ~1u are actually redundant. that.getGenerationID() just did it for us.) 83 this->fTaggedGenID.store(genID & ~1u); 84 that. fTaggedGenID.store(genID & ~1u); 85 86 // This method isn't threadsafe, so these asserts should be fine. 87 SkASSERT(!this->genIDIsUnique()); 88 SkASSERT(!that. genIDIsUnique()); 89} 90 91static void validate_pixels_ctable(const SkImageInfo& info, const SkColorTable* ctable) { 92 if (info.isEmpty()) { 93 return; // can't require ctable if the dimensions are empty 94 } 95 if (kIndex_8_SkColorType == info.colorType()) { 96 SkASSERT(ctable); 97 } else { 98 SkASSERT(nullptr == ctable); 99 } 100} 101 102void SkPixelRef::setPreLocked(void* pixels, size_t rowBytes, SkColorTable* ctable) { 103 SkASSERT(pixels); 104 validate_pixels_ctable(fInfo, ctable); 105 // only call me in your constructor, otherwise fLockCount tracking can get 106 // out of sync. 107 fRec.fPixels = pixels; 108 fRec.fColorTable = ctable; 109 fRec.fRowBytes = rowBytes; 110 fLockCount = SKPIXELREF_PRELOCKED_LOCKCOUNT; 111 fPreLocked = true; 112} 113 114// Increments fLockCount only on success 115bool SkPixelRef::lockPixelsInsideMutex() { 116 fMutex.assertHeld(); 117 118 if (1 == ++fLockCount) { 119 SkASSERT(fRec.isZero()); 120 if (!this->onNewLockPixels(&fRec)) { 121 fRec.zero(); 122 fLockCount -= 1; // we return fLockCount unchanged if we fail. 123 return false; 124 } 125 } 126 if (fRec.fPixels) { 127 validate_pixels_ctable(fInfo, fRec.fColorTable); 128 return true; 129 } 130 // no pixels, so we failed (somehow) 131 --fLockCount; 132 return false; 133} 134 135// For historical reasons, we always inc fLockCount, even if we return false. 136// It would be nice to change this (it seems), and only inc if we actually succeed... 137bool SkPixelRef::lockPixels() { 138 SkASSERT(!fPreLocked || SKPIXELREF_PRELOCKED_LOCKCOUNT == fLockCount); 139 140 if (!fPreLocked) { 141 TRACE_EVENT_BEGIN0("skia", "SkPixelRef::lockPixelsMutex"); 142 SkAutoMutexAcquire ac(fMutex); 143 TRACE_EVENT_END0("skia", "SkPixelRef::lockPixelsMutex"); 144 SkDEBUGCODE(int oldCount = fLockCount;) 145 bool success = this->lockPixelsInsideMutex(); 146 // lockPixelsInsideMutex only increments the count if it succeeds. 147 SkASSERT(oldCount + (int)success == fLockCount); 148 149 if (!success) { 150 // For compatibility with SkBitmap calling lockPixels, we still want to increment 151 // fLockCount even if we failed. If we updated SkBitmap we could remove this oddity. 152 fLockCount += 1; 153 return false; 154 } 155 } 156 if (fRec.fPixels) { 157 validate_pixels_ctable(fInfo, fRec.fColorTable); 158 return true; 159 } 160 return false; 161} 162 163bool SkPixelRef::lockPixels(LockRec* rec) { 164 if (this->lockPixels()) { 165 *rec = fRec; 166 return true; 167 } 168 return false; 169} 170 171void SkPixelRef::unlockPixels() { 172 SkASSERT(!fPreLocked || SKPIXELREF_PRELOCKED_LOCKCOUNT == fLockCount); 173 174 if (!fPreLocked) { 175 SkAutoMutexAcquire ac(fMutex); 176 177 SkASSERT(fLockCount > 0); 178 if (0 == --fLockCount) { 179 // don't call onUnlockPixels unless onLockPixels succeeded 180 if (fRec.fPixels) { 181 this->onUnlockPixels(); 182 fRec.zero(); 183 } else { 184 SkASSERT(fRec.isZero()); 185 } 186 } 187 } 188} 189 190bool SkPixelRef::requestLock(const LockRequest& request, LockResult* result) { 191 SkASSERT(result); 192 if (request.fSize.isEmpty()) { 193 return false; 194 } 195 // until we support subsets, we have to check this... 196 if (request.fSize.width() != fInfo.width() || request.fSize.height() != fInfo.height()) { 197 return false; 198 } 199 200 if (fPreLocked) { 201 result->fUnlockProc = nullptr; 202 result->fUnlockContext = nullptr; 203 result->fCTable = fRec.fColorTable; 204 result->fPixels = fRec.fPixels; 205 result->fRowBytes = fRec.fRowBytes; 206 result->fSize.set(fInfo.width(), fInfo.height()); 207 } else { 208 SkAutoMutexAcquire ac(fMutex); 209 if (!this->onRequestLock(request, result)) { 210 return false; 211 } 212 } 213 if (result->fPixels) { 214 validate_pixels_ctable(fInfo, result->fCTable); 215 return true; 216 } 217 return false; 218} 219 220bool SkPixelRef::lockPixelsAreWritable() const { 221 return this->onLockPixelsAreWritable(); 222} 223 224bool SkPixelRef::onLockPixelsAreWritable() const { 225 return true; 226} 227 228uint32_t SkPixelRef::getGenerationID() const { 229 uint32_t id = fTaggedGenID.load(); 230 if (0 == id) { 231 uint32_t next = SkNextID::ImageID() | 1u; 232 if (fTaggedGenID.compare_exchange(&id, next)) { 233 id = next; // There was no race or we won the race. fTaggedGenID is next now. 234 } else { 235 // We lost a race to set fTaggedGenID. compare_exchange() filled id with the winner. 236 } 237 // We can't quite SkASSERT(this->genIDIsUnique()). It could be non-unique 238 // if we got here via the else path (pretty unlikely, but possible). 239 } 240 return id & ~1u; // Mask off bottom unique bit. 241} 242 243void SkPixelRef::addGenIDChangeListener(GenIDChangeListener* listener) { 244 if (nullptr == listener || !this->genIDIsUnique()) { 245 // No point in tracking this if we're not going to call it. 246 delete listener; 247 return; 248 } 249 *fGenIDChangeListeners.append() = listener; 250} 251 252// we need to be called *before* the genID gets changed or zerod 253void SkPixelRef::callGenIDChangeListeners() { 254 // We don't invalidate ourselves if we think another SkPixelRef is sharing our genID. 255 if (this->genIDIsUnique()) { 256 for (int i = 0; i < fGenIDChangeListeners.count(); i++) { 257 fGenIDChangeListeners[i]->onChange(); 258 } 259 260 // TODO: SkAtomic could add "old_value = atomic.xchg(new_value)" to make this clearer. 261 if (fAddedToCache.load()) { 262 SkNotifyBitmapGenIDIsStale(this->getGenerationID()); 263 fAddedToCache.store(false); 264 } 265 } 266 // Listeners get at most one shot, so whether these triggered or not, blow them away. 267 fGenIDChangeListeners.deleteAll(); 268} 269 270void SkPixelRef::notifyPixelsChanged() { 271#ifdef SK_DEBUG 272 if (this->isImmutable()) { 273 SkDebugf("========== notifyPixelsChanged called on immutable pixelref"); 274 } 275#endif 276 this->callGenIDChangeListeners(); 277 this->needsNewGenID(); 278 this->onNotifyPixelsChanged(); 279} 280 281void SkPixelRef::changeAlphaType(SkAlphaType at) { 282 *const_cast<SkImageInfo*>(&fInfo) = fInfo.makeAlphaType(at); 283} 284 285void SkPixelRef::setImmutable() { 286 fMutability = kImmutable; 287} 288 289void SkPixelRef::setImmutableWithID(uint32_t genID) { 290 /* 291 * We are forcing the genID to match an external value. The caller must ensure that this 292 * value does not conflict with other content. 293 * 294 * One use is to force this pixelref's id to match an SkImage's id 295 */ 296 fMutability = kImmutable; 297 fTaggedGenID.store(genID); 298} 299 300void SkPixelRef::setTemporarilyImmutable() { 301 SkASSERT(fMutability != kImmutable); 302 fMutability = kTemporarilyImmutable; 303} 304 305void SkPixelRef::restoreMutability() { 306 SkASSERT(fMutability != kImmutable); 307 fMutability = kMutable; 308} 309 310bool SkPixelRef::readPixels(SkBitmap* dst, const SkIRect* subset) { 311 return this->onReadPixels(dst, subset); 312} 313 314/////////////////////////////////////////////////////////////////////////////////////////////////// 315 316bool SkPixelRef::onReadPixels(SkBitmap* dst, const SkIRect* subset) { 317 return false; 318} 319 320void SkPixelRef::onNotifyPixelsChanged() { } 321 322SkData* SkPixelRef::onRefEncodedData() { 323 return nullptr; 324} 325 326bool SkPixelRef::onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3], 327 SkYUVColorSpace* colorSpace) { 328 return false; 329} 330 331size_t SkPixelRef::getAllocatedSizeInBytes() const { 332 return 0; 333} 334 335static void unlock_legacy_result(void* ctx) { 336 SkPixelRef* pr = (SkPixelRef*)ctx; 337 pr->unlockPixels(); 338 pr->unref(); // balancing the Ref in onRequestLoc 339} 340 341bool SkPixelRef::onRequestLock(const LockRequest& request, LockResult* result) { 342 if (!this->lockPixelsInsideMutex()) { 343 return false; 344 } 345 346 result->fUnlockProc = unlock_legacy_result; 347 result->fUnlockContext = SkRef(this); // this is balanced in our fUnlockProc 348 result->fCTable = fRec.fColorTable; 349 result->fPixels = fRec.fPixels; 350 result->fRowBytes = fRec.fRowBytes; 351 result->fSize.set(fInfo.width(), fInfo.height()); 352 return true; 353} 354