blob: d7117617f62e4243c6b36935393df128b277b293 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
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 */
reed3b72f792014-07-14 10:13:57 -07007
reed7eeba252015-02-24 13:54:23 -08008#include "SkBitmapCache.h"
mtklein1b249332015-07-07 12:21:21 -07009#include "SkMutex.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkPixelRef.h"
enne54b666b2015-04-17 12:42:49 -070011#include "SkTraceEvent.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012
reedab865772015-05-21 06:29:05 -070013//#define SK_SUPPORT_LEGACY_UNBALANCED_PIXELREF_LOCKCOUNT
reed67ca2a92015-05-20 13:22:58 -070014//#define SK_TRACE_PIXELREF_LIFETIME
15
reed80c772b2015-07-30 18:58:23 -070016#include "SkNextID.h"
bsalomon@google.com586f48c2011-04-14 15:07:22 +000017
reed80c772b2015-07-30 18:58:23 -070018uint32_t SkNextID::ImageID() {
19 static uint32_t gID = 0;
20 uint32_t id;
mtklein63d00242015-02-25 09:10:57 -080021 // Loop in case our global wraps around, as we never want to return a 0.
bsalomon@google.com586f48c2011-04-14 15:07:22 +000022 do {
reed80c772b2015-07-30 18:58:23 -070023 id = sk_atomic_fetch_add(&gID, 2u) + 2; // Never set the low bit.
24 } while (0 == id);
25 return id;
bsalomon@google.com586f48c2011-04-14 15:07:22 +000026}
27
reed@google.comff0da4f2012-05-17 13:14:52 +000028///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000029
reed@google.comff0da4f2012-05-17 13:14:52 +000030// just need a > 0 value, so pick a funny one to aid in debugging
31#define SKPIXELREF_PRELOCKED_LOCKCOUNT 123456789
32
reede5ea5002014-09-03 11:54:58 -070033static 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}
scroggo2fd0d142014-07-01 07:08:19 -070038
reed67ca2a92015-05-20 13:22:58 -070039#ifdef SK_TRACE_PIXELREF_LIFETIME
40 static int32_t gInstCounter;
41#endif
42
scroggof3ca41c2014-11-25 13:42:12 -080043SkPixelRef::SkPixelRef(const SkImageInfo& info)
44 : fInfo(validate_info(info))
45#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
reed685f2772015-07-31 11:46:12 -070046 , fStableID(SkNextID::ImageID())
scroggof3ca41c2014-11-25 13:42:12 -080047#endif
48
49{
reed67ca2a92015-05-20 13:22:58 -070050#ifdef SK_TRACE_PIXELREF_LIFETIME
51 SkDebugf(" pixelref %d\n", sk_atomic_inc(&gInstCounter));
52#endif
reed@google.comd0419b12014-01-06 17:08:27 +000053 fRec.zero();
reed@google.com398337b2013-12-11 21:22:39 +000054 fLockCount = 0;
55 this->needsNewGenID();
reed26e0e582015-07-29 11:44:52 -070056 fMutability = kMutable;
reed@google.com398337b2013-12-11 21:22:39 +000057 fPreLocked = false;
mtklein61010772015-02-25 08:27:41 -080058 fAddedToCache.store(false);
reed@google.com398337b2013-12-11 21:22:39 +000059}
reed@google.com398337b2013-12-11 21:22:39 +000060
commit-bot@chromium.org50a30432013-10-24 17:44:27 +000061SkPixelRef::~SkPixelRef() {
reedab865772015-05-21 06:29:05 -070062#ifndef SK_SUPPORT_LEGACY_UNBALANCED_PIXELREF_LOCKCOUNT
63 SkASSERT(SKPIXELREF_PRELOCKED_LOCKCOUNT == fLockCount || 0 == fLockCount);
64#endif
65
reed67ca2a92015-05-20 13:22:58 -070066#ifdef SK_TRACE_PIXELREF_LIFETIME
67 SkDebugf("~pixelref %d\n", sk_atomic_dec(&gInstCounter) - 1);
68#endif
commit-bot@chromium.org50a30432013-10-24 17:44:27 +000069 this->callGenIDChangeListeners();
70}
71
72void SkPixelRef::needsNewGenID() {
mtklein63d00242015-02-25 09:10:57 -080073 fTaggedGenID.store(0);
74 SkASSERT(!this->genIDIsUnique()); // This method isn't threadsafe, so the assert should be fine.
commit-bot@chromium.org50a30432013-10-24 17:44:27 +000075}
76
77void SkPixelRef::cloneGenID(const SkPixelRef& that) {
78 // This is subtle. We must call that.getGenerationID() to make sure its genID isn't 0.
mtklein63d00242015-02-25 09:10:57 -080079 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());
commit-bot@chromium.org50a30432013-10-24 17:44:27 +000089}
90
reedbca14002015-07-27 12:19:16 -070091static void validate_pixels_ctable(const SkImageInfo& info, const SkColorTable* ctable) {
reeddb74f622015-05-30 13:41:15 -070092 if (info.isEmpty()) {
reedbca14002015-07-27 12:19:16 -070093 return; // can't require ctable if the dimensions are empty
reeddb74f622015-05-30 13:41:15 -070094 }
reeddb74f622015-05-30 13:41:15 -070095 if (kIndex_8_SkColorType == info.colorType()) {
96 SkASSERT(ctable);
97 } else {
halcanary96fcdcc2015-08-27 07:41:13 -070098 SkASSERT(nullptr == ctable);
reeddb74f622015-05-30 13:41:15 -070099 }
100}
101
reed@google.com7627c652014-01-06 15:01:48 +0000102void SkPixelRef::setPreLocked(void* pixels, size_t rowBytes, SkColorTable* ctable) {
reedbca14002015-07-27 12:19:16 -0700103 SkASSERT(pixels);
104 validate_pixels_ctable(fInfo, ctable);
reed@google.comff0da4f2012-05-17 13:14:52 +0000105 // only call me in your constructor, otherwise fLockCount tracking can get
106 // out of sync.
reed@google.comd0419b12014-01-06 17:08:27 +0000107 fRec.fPixels = pixels;
108 fRec.fColorTable = ctable;
109 fRec.fRowBytes = rowBytes;
reed@google.comff0da4f2012-05-17 13:14:52 +0000110 fLockCount = SKPIXELREF_PRELOCKED_LOCKCOUNT;
111 fPreLocked = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000112}
113
reeddb74f622015-05-30 13:41:15 -0700114// Increments fLockCount only on success
115bool SkPixelRef::lockPixelsInsideMutex() {
mtklein7e6d9c02015-08-13 14:02:06 -0700116 fMutex.assertHeld();
skia.committer@gmail.com98272d92014-01-04 07:01:40 +0000117
reed92fc2ae2015-05-22 08:06:21 -0700118 if (1 == ++fLockCount) {
119 SkASSERT(fRec.isZero());
reeddb74f622015-05-30 13:41:15 -0700120 if (!this->onNewLockPixels(&fRec)) {
121 fRec.zero();
reed92fc2ae2015-05-22 08:06:21 -0700122 fLockCount -= 1; // we return fLockCount unchanged if we fail.
123 return false;
reed@google.comff0da4f2012-05-17 13:14:52 +0000124 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 }
reedbca14002015-07-27 12:19:16 -0700126 if (fRec.fPixels) {
127 validate_pixels_ctable(fInfo, fRec.fColorTable);
128 return true;
129 }
reed7cdfa8a2015-08-11 12:23:45 -0700130 // no pixels, so we failed (somehow)
131 --fLockCount;
reedbca14002015-07-27 12:19:16 -0700132 return false;
reed@google.comd0419b12014-01-06 17:08:27 +0000133}
134
reeddb74f622015-05-30 13:41:15 -0700135// 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() {
reed92fc2ae2015-05-22 08:06:21 -0700138 SkASSERT(!fPreLocked || SKPIXELREF_PRELOCKED_LOCKCOUNT == fLockCount);
139
reeddb74f622015-05-30 13:41:15 -0700140 if (!fPreLocked) {
reed92fc2ae2015-05-22 08:06:21 -0700141 TRACE_EVENT_BEGIN0("skia", "SkPixelRef::lockPixelsMutex");
mtklein7e6d9c02015-08-13 14:02:06 -0700142 SkAutoMutexAcquire ac(fMutex);
reed92fc2ae2015-05-22 08:06:21 -0700143 TRACE_EVENT_END0("skia", "SkPixelRef::lockPixelsMutex");
144 SkDEBUGCODE(int oldCount = fLockCount;)
reeddb74f622015-05-30 13:41:15 -0700145 bool success = this->lockPixelsInsideMutex();
reed92fc2ae2015-05-22 08:06:21 -0700146 // 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;
reeddb74f622015-05-30 13:41:15 -0700153 return false;
reed92fc2ae2015-05-22 08:06:21 -0700154 }
reed92fc2ae2015-05-22 08:06:21 -0700155 }
reedbca14002015-07-27 12:19:16 -0700156 if (fRec.fPixels) {
157 validate_pixels_ctable(fInfo, fRec.fColorTable);
158 return true;
159 }
160 return false;
reed92fc2ae2015-05-22 08:06:21 -0700161}
162
reeddb74f622015-05-30 13:41:15 -0700163bool SkPixelRef::lockPixels(LockRec* rec) {
164 if (this->lockPixels()) {
165 *rec = fRec;
166 return true;
167 }
168 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000169}
170
171void SkPixelRef::unlockPixels() {
reed@google.comff0da4f2012-05-17 13:14:52 +0000172 SkASSERT(!fPreLocked || SKPIXELREF_PRELOCKED_LOCKCOUNT == fLockCount);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000173
reed@google.comff0da4f2012-05-17 13:14:52 +0000174 if (!fPreLocked) {
mtklein7e6d9c02015-08-13 14:02:06 -0700175 SkAutoMutexAcquire ac(fMutex);
reed@google.com93c5f9e2011-02-24 18:09:46 +0000176
reed@google.comff0da4f2012-05-17 13:14:52 +0000177 SkASSERT(fLockCount > 0);
178 if (0 == --fLockCount) {
reed@google.comc83a91f2013-12-13 13:41:14 +0000179 // don't call onUnlockPixels unless onLockPixels succeeded
reed@google.comd0419b12014-01-06 17:08:27 +0000180 if (fRec.fPixels) {
reed@google.comc83a91f2013-12-13 13:41:14 +0000181 this->onUnlockPixels();
reed@google.comd0419b12014-01-06 17:08:27 +0000182 fRec.zero();
reed@google.comc83a91f2013-12-13 13:41:14 +0000183 } else {
reed@google.comd0419b12014-01-06 17:08:27 +0000184 SkASSERT(fRec.isZero());
reed@google.comc83a91f2013-12-13 13:41:14 +0000185 }
reed@google.comff0da4f2012-05-17 13:14:52 +0000186 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187 }
188}
189
reed92fc2ae2015-05-22 08:06:21 -0700190bool SkPixelRef::requestLock(const LockRequest& request, LockResult* result) {
191 SkASSERT(result);
192 if (request.fSize.isEmpty()) {
193 return false;
194 }
reed871872f2015-06-22 12:48:26 -0700195 // 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 }
reed92fc2ae2015-05-22 08:06:21 -0700199
200 if (fPreLocked) {
halcanary96fcdcc2015-08-27 07:41:13 -0700201 result->fUnlockProc = nullptr;
202 result->fUnlockContext = nullptr;
reed92fc2ae2015-05-22 08:06:21 -0700203 result->fCTable = fRec.fColorTable;
204 result->fPixels = fRec.fPixels;
205 result->fRowBytes = fRec.fRowBytes;
206 result->fSize.set(fInfo.width(), fInfo.height());
reed92fc2ae2015-05-22 08:06:21 -0700207 } else {
mtklein7e6d9c02015-08-13 14:02:06 -0700208 SkAutoMutexAcquire ac(fMutex);
reeddb74f622015-05-30 13:41:15 -0700209 if (!this->onRequestLock(request, result)) {
210 return false;
211 }
reed92fc2ae2015-05-22 08:06:21 -0700212 }
reedbca14002015-07-27 12:19:16 -0700213 if (result->fPixels) {
214 validate_pixels_ctable(fInfo, result->fCTable);
215 return true;
216 }
217 return false;
reed92fc2ae2015-05-22 08:06:21 -0700218}
219
reed@google.com9c49bc32011-07-07 13:42:37 +0000220bool SkPixelRef::lockPixelsAreWritable() const {
221 return this->onLockPixelsAreWritable();
222}
223
224bool SkPixelRef::onLockPixelsAreWritable() const {
225 return true;
226}
227
reed@android.com8a1c16f2008-12-17 15:59:43 +0000228uint32_t SkPixelRef::getGenerationID() const {
mtklein63d00242015-02-25 09:10:57 -0800229 uint32_t id = fTaggedGenID.load();
mtklein86821b52015-02-24 14:38:12 -0800230 if (0 == id) {
reed80c772b2015-07-30 18:58:23 -0700231 uint32_t next = SkNextID::ImageID() | 1u;
mtklein59c92032015-02-25 12:51:55 -0800232 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).
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239 }
mtklein63d00242015-02-25 09:10:57 -0800240 return id & ~1u; // Mask off bottom unique bit.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241}
242
commit-bot@chromium.org50a30432013-10-24 17:44:27 +0000243void SkPixelRef::addGenIDChangeListener(GenIDChangeListener* listener) {
halcanary96fcdcc2015-08-27 07:41:13 -0700244 if (nullptr == listener || !this->genIDIsUnique()) {
commit-bot@chromium.org50a30432013-10-24 17:44:27 +0000245 // No point in tracking this if we're not going to call it.
halcanary385fe4d2015-08-26 13:07:48 -0700246 delete listener;
commit-bot@chromium.org50a30432013-10-24 17:44:27 +0000247 return;
248 }
249 *fGenIDChangeListeners.append() = listener;
250}
251
reed7eeba252015-02-24 13:54:23 -0800252// we need to be called *before* the genID gets changed or zerod
commit-bot@chromium.org50a30432013-10-24 17:44:27 +0000253void SkPixelRef::callGenIDChangeListeners() {
254 // We don't invalidate ourselves if we think another SkPixelRef is sharing our genID.
mtklein63d00242015-02-25 09:10:57 -0800255 if (this->genIDIsUnique()) {
commit-bot@chromium.org50a30432013-10-24 17:44:27 +0000256 for (int i = 0; i < fGenIDChangeListeners.count(); i++) {
257 fGenIDChangeListeners[i]->onChange();
258 }
reed7eeba252015-02-24 13:54:23 -0800259
mtklein61010772015-02-25 08:27:41 -0800260 // TODO: SkAtomic could add "old_value = atomic.xchg(new_value)" to make this clearer.
261 if (fAddedToCache.load()) {
reed83787d02015-02-25 07:17:11 -0800262 SkNotifyBitmapGenIDIsStale(this->getGenerationID());
mtklein61010772015-02-25 08:27:41 -0800263 fAddedToCache.store(false);
reed83787d02015-02-25 07:17:11 -0800264 }
commit-bot@chromium.org50a30432013-10-24 17:44:27 +0000265 }
266 // Listeners get at most one shot, so whether these triggered or not, blow them away.
267 fGenIDChangeListeners.deleteAll();
268}
269
reed@google.comc1587f92014-01-28 16:05:39 +0000270void SkPixelRef::notifyPixelsChanged() {
reed@android.com3eab80c2009-03-24 18:47:35 +0000271#ifdef SK_DEBUG
reed26e0e582015-07-29 11:44:52 -0700272 if (this->isImmutable()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273 SkDebugf("========== notifyPixelsChanged called on immutable pixelref");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274 }
reed@android.com3eab80c2009-03-24 18:47:35 +0000275#endif
commit-bot@chromium.org50a30432013-10-24 17:44:27 +0000276 this->callGenIDChangeListeners();
277 this->needsNewGenID();
junovda5469d2015-06-15 09:48:15 -0700278 this->onNotifyPixelsChanged();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279}
280
reed@google.comc1587f92014-01-28 16:05:39 +0000281void SkPixelRef::changeAlphaType(SkAlphaType at) {
reede5ea5002014-09-03 11:54:58 -0700282 *const_cast<SkImageInfo*>(&fInfo) = fInfo.makeAlphaType(at);
reed@google.comc1587f92014-01-28 16:05:39 +0000283}
284
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285void SkPixelRef::setImmutable() {
reed26e0e582015-07-29 11:44:52 -0700286 fMutability = kImmutable;
287}
reed6f1216a2015-08-04 08:10:13 -0700288
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
reed26e0e582015-07-29 11:44:52 -0700300void SkPixelRef::setTemporarilyImmutable() {
301 SkASSERT(fMutability != kImmutable);
302 fMutability = kTemporarilyImmutable;
303}
304
305void SkPixelRef::restoreMutability() {
306 SkASSERT(fMutability != kImmutable);
307 fMutability = kMutable;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308}
309
reed@google.com50dfa012011-04-01 19:05:36 +0000310bool SkPixelRef::readPixels(SkBitmap* dst, const SkIRect* subset) {
311 return this->onReadPixels(dst, subset);
312}
313
reed92fc2ae2015-05-22 08:06:21 -0700314///////////////////////////////////////////////////////////////////////////////////////////////////
315
reed@google.com50dfa012011-04-01 19:05:36 +0000316bool SkPixelRef::onReadPixels(SkBitmap* dst, const SkIRect* subset) {
317 return false;
318}
319
junovda5469d2015-06-15 09:48:15 -0700320void SkPixelRef::onNotifyPixelsChanged() { }
321
reed@google.comeb776122012-12-06 14:26:02 +0000322SkData* SkPixelRef::onRefEncodedData() {
halcanary96fcdcc2015-08-27 07:41:13 -0700323 return nullptr;
reed@google.comeb776122012-12-06 14:26:02 +0000324}
325
rileyaabaef862014-09-12 17:45:58 -0700326bool SkPixelRef::onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3],
327 SkYUVColorSpace* colorSpace) {
sugoi518d83d2014-07-21 11:37:39 -0700328 return false;
329}
330
commit-bot@chromium.orgcd3b15c2013-12-04 17:06:49 +0000331size_t SkPixelRef::getAllocatedSizeInBytes() const {
332 return 0;
333}
334
reed92fc2ae2015-05-22 08:06:21 -0700335static 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) {
reeddb74f622015-05-30 13:41:15 -0700342 if (!this->lockPixelsInsideMutex()) {
reed92fc2ae2015-05-22 08:06:21 -0700343 return false;
344 }
345
346 result->fUnlockProc = unlock_legacy_result;
347 result->fUnlockContext = SkRef(this); // this is balanced in our fUnlockProc
reeddb74f622015-05-30 13:41:15 -0700348 result->fCTable = fRec.fColorTable;
349 result->fPixels = fRec.fPixels;
350 result->fRowBytes = fRec.fRowBytes;
reed92fc2ae2015-05-22 08:06:21 -0700351 result->fSize.set(fInfo.width(), fInfo.height());
352 return true;
353}