blob: a7231135bd599be1b09ef5b4859f539c144276d5 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com8a1c16f2008-12-17 15:59:43 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkBitmap.h"
11#include "SkColorPriv.h"
12#include "SkDither.h"
13#include "SkFlattenable.h"
14#include "SkMallocPixelRef.h"
15#include "SkMask.h"
16#include "SkPixelRef.h"
17#include "SkThread.h"
vandebo@chromium.org112706d2011-02-24 22:50:55 +000018#include "SkUnPreMultiply.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#include "SkUtils.h"
20#include "SkPackBits.h"
21#include <new>
22
bsalomon@google.com586f48c2011-04-14 15:07:22 +000023extern int32_t SkNextPixelRefGenerationID();
24
reed@android.com149e2f62009-05-22 14:39:03 +000025static bool isPos32Bits(const Sk64& value) {
26 return !value.isNeg() && value.is32();
27}
28
reed@android.com8a1c16f2008-12-17 15:59:43 +000029struct MipLevel {
30 void* fPixels;
31 uint32_t fRowBytes;
reed@android.comf459a492009-03-27 12:33:50 +000032 uint32_t fWidth, fHeight;
reed@android.com8a1c16f2008-12-17 15:59:43 +000033};
34
35struct SkBitmap::MipMap : SkNoncopyable {
36 int32_t fRefCnt;
37 int fLevelCount;
38// MipLevel fLevel[fLevelCount];
39// Pixels[]
weita@google.comf9ab99a2009-05-03 18:23:30 +000040
reed@android.com8a1c16f2008-12-17 15:59:43 +000041 static MipMap* Alloc(int levelCount, size_t pixelSize) {
reed@android.com149e2f62009-05-22 14:39:03 +000042 if (levelCount < 0) {
43 return NULL;
44 }
45 Sk64 size;
46 size.setMul(levelCount + 1, sizeof(MipLevel));
47 size.add(sizeof(MipMap));
48 size.add(pixelSize);
49 if (!isPos32Bits(size)) {
50 return NULL;
51 }
52 MipMap* mm = (MipMap*)sk_malloc_throw(size.get32());
reed@android.com8a1c16f2008-12-17 15:59:43 +000053 mm->fRefCnt = 1;
54 mm->fLevelCount = levelCount;
55 return mm;
56 }
57
58 const MipLevel* levels() const { return (const MipLevel*)(this + 1); }
59 MipLevel* levels() { return (MipLevel*)(this + 1); }
60
61 const void* pixels() const { return levels() + fLevelCount; }
62 void* pixels() { return levels() + fLevelCount; }
weita@google.comf9ab99a2009-05-03 18:23:30 +000063
reed@android.com149e2f62009-05-22 14:39:03 +000064 void ref() {
65 if (SK_MaxS32 == sk_atomic_inc(&fRefCnt)) {
66 sk_throw();
reed@android.com8a1c16f2008-12-17 15:59:43 +000067 }
68 }
reed@android.com149e2f62009-05-22 14:39:03 +000069 void unref() {
70 SkASSERT(fRefCnt > 0);
71 if (sk_atomic_dec(&fRefCnt) == 1) {
72 sk_free(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +000073 }
74 }
75};
reed@android.com8a1c16f2008-12-17 15:59:43 +000076
77///////////////////////////////////////////////////////////////////////////////
78///////////////////////////////////////////////////////////////////////////////
79
80SkBitmap::SkBitmap() {
reed@android.com4516f472009-06-29 16:25:36 +000081 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000082}
83
84SkBitmap::SkBitmap(const SkBitmap& src) {
85 SkDEBUGCODE(src.validate();)
reed@android.com4516f472009-06-29 16:25:36 +000086 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000087 *this = src;
88 SkDEBUGCODE(this->validate();)
89}
90
91SkBitmap::~SkBitmap() {
92 SkDEBUGCODE(this->validate();)
93 this->freePixels();
94}
95
96SkBitmap& SkBitmap::operator=(const SkBitmap& src) {
97 if (this != &src) {
98 this->freePixels();
99 memcpy(this, &src, sizeof(src));
100
101 // inc src reference counts
reed@android.com83f7bc32009-07-17 02:42:41 +0000102 SkSafeRef(src.fPixelRef);
reed@android.com149e2f62009-05-22 14:39:03 +0000103 SkSafeRef(src.fMipMap);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104
105 // we reset our locks if we get blown away
106 fPixelLockCount = 0;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000107
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108 /* The src could be in 3 states
109 1. no pixelref, in which case we just copy/ref the pixels/ctable
110 2. unlocked pixelref, pixels/ctable should be null
111 3. locked pixelref, we should lock the ref again ourselves
112 */
113 if (NULL == fPixelRef) {
114 // leave fPixels as it is
reed@google.com82065d62011-02-07 15:30:46 +0000115 SkSafeRef(fColorTable); // ref the user's ctable if present
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116 } else { // we have a pixelref, so pixels/ctable reflect it
117 // ignore the values from the memcpy
118 fPixels = NULL;
119 fColorTable = NULL;
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000120 // Note that what to for genID is somewhat arbitrary. We have no
121 // way to track changes to raw pixels across multiple SkBitmaps.
122 // Would benefit from an SkRawPixelRef type created by
123 // setPixels.
124 // Just leave the memcpy'ed one but they'll get out of sync
125 // as soon either is modified.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126 }
127 }
128
129 SkDEBUGCODE(this->validate();)
130 return *this;
131}
132
133void SkBitmap::swap(SkBitmap& other) {
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000134 SkTSwap(fColorTable, other.fColorTable);
135 SkTSwap(fPixelRef, other.fPixelRef);
136 SkTSwap(fPixelRefOffset, other.fPixelRefOffset);
137 SkTSwap(fPixelLockCount, other.fPixelLockCount);
138 SkTSwap(fMipMap, other.fMipMap);
139 SkTSwap(fPixels, other.fPixels);
140 SkTSwap(fRawPixelGenerationID, other.fRawPixelGenerationID);
141 SkTSwap(fRowBytes, other.fRowBytes);
142 SkTSwap(fWidth, other.fWidth);
143 SkTSwap(fHeight, other.fHeight);
144 SkTSwap(fConfig, other.fConfig);
145 SkTSwap(fFlags, other.fFlags);
146 SkTSwap(fBytesPerPixel, other.fBytesPerPixel);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147
148 SkDEBUGCODE(this->validate();)
149}
150
151void SkBitmap::reset() {
152 this->freePixels();
reed@android.com4516f472009-06-29 16:25:36 +0000153 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000154}
155
156int SkBitmap::ComputeBytesPerPixel(SkBitmap::Config config) {
157 int bpp;
158 switch (config) {
159 case kNo_Config:
160 case kA1_Config:
161 bpp = 0; // not applicable
162 break;
163 case kRLE_Index8_Config:
164 case kA8_Config:
165 case kIndex8_Config:
166 bpp = 1;
167 break;
168 case kRGB_565_Config:
169 case kARGB_4444_Config:
170 bpp = 2;
171 break;
172 case kARGB_8888_Config:
173 bpp = 4;
174 break;
175 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000176 SkDEBUGFAIL("unknown config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177 bpp = 0; // error
178 break;
179 }
180 return bpp;
181}
182
183int SkBitmap::ComputeRowBytes(Config c, int width) {
reed@android.com149e2f62009-05-22 14:39:03 +0000184 if (width < 0) {
185 return 0;
186 }
187
188 Sk64 rowBytes;
189 rowBytes.setZero();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000190
191 switch (c) {
192 case kNo_Config:
193 case kRLE_Index8_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194 break;
195 case kA1_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000196 rowBytes.set(width);
197 rowBytes.add(7);
198 rowBytes.shiftRight(3);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 break;
200 case kA8_Config:
201 case kIndex8_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000202 rowBytes.set(width);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203 break;
204 case kRGB_565_Config:
205 case kARGB_4444_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000206 rowBytes.set(width);
207 rowBytes.shiftLeft(1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208 break;
209 case kARGB_8888_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000210 rowBytes.set(width);
211 rowBytes.shiftLeft(2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212 break;
213 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000214 SkDEBUGFAIL("unknown config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215 break;
216 }
reed@android.com149e2f62009-05-22 14:39:03 +0000217 return isPos32Bits(rowBytes) ? rowBytes.get32() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218}
219
220Sk64 SkBitmap::ComputeSize64(Config c, int width, int height) {
221 Sk64 size;
222 size.setMul(SkBitmap::ComputeRowBytes(c, width), height);
223 return size;
224}
225
226size_t SkBitmap::ComputeSize(Config c, int width, int height) {
227 Sk64 size = SkBitmap::ComputeSize64(c, width, height);
reed@android.com149e2f62009-05-22 14:39:03 +0000228 return isPos32Bits(size) ? size.get32() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229}
230
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000231Sk64 SkBitmap::ComputeSafeSize64(Config config,
232 uint32_t width,
233 uint32_t height,
234 uint32_t rowBytes) {
235 Sk64 safeSize;
236 safeSize.setZero();
237 if (height > 0) {
238 safeSize.set(ComputeRowBytes(config, width));
239 Sk64 sizeAllButLastRow;
240 sizeAllButLastRow.setMul(height - 1, rowBytes);
241 safeSize.add(sizeAllButLastRow);
242 }
243 SkASSERT(!safeSize.isNeg());
244 return safeSize;
245}
246
247size_t SkBitmap::ComputeSafeSize(Config config,
248 uint32_t width,
249 uint32_t height,
250 uint32_t rowBytes) {
251 Sk64 safeSize = ComputeSafeSize64(config, width, height, rowBytes);
252 return (safeSize.is32() ? safeSize.get32() : 0);
253}
254
reed@google.com86b2e432012-03-15 21:17:03 +0000255void SkBitmap::getBounds(SkRect* bounds) const {
256 SkASSERT(bounds);
257 bounds->set(0, 0,
258 SkIntToScalar(fWidth), SkIntToScalar(fHeight));
259}
260
reed@google.com80e14592012-03-16 14:58:07 +0000261void SkBitmap::getBounds(SkIRect* bounds) const {
262 SkASSERT(bounds);
263 bounds->set(0, 0, fWidth, fHeight);
264}
265
reed@google.com86b2e432012-03-15 21:17:03 +0000266///////////////////////////////////////////////////////////////////////////////
267
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268void SkBitmap::setConfig(Config c, int width, int height, int rowBytes) {
269 this->freePixels();
270
reed@android.com149e2f62009-05-22 14:39:03 +0000271 if ((width | height | rowBytes) < 0) {
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000272 goto err;
reed@android.com149e2f62009-05-22 14:39:03 +0000273 }
274
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275 if (rowBytes == 0) {
276 rowBytes = SkBitmap::ComputeRowBytes(c, width);
reed@android.com149e2f62009-05-22 14:39:03 +0000277 if (0 == rowBytes && kNo_Config != c) {
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000278 goto err;
reed@android.com149e2f62009-05-22 14:39:03 +0000279 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280 }
reed@android.com89bb83a2009-05-29 21:30:42 +0000281
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 fConfig = SkToU8(c);
reed@android.comf459a492009-03-27 12:33:50 +0000283 fWidth = width;
284 fHeight = height;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 fRowBytes = rowBytes;
286
287 fBytesPerPixel = (uint8_t)ComputeBytesPerPixel(c);
288
289 SkDEBUGCODE(this->validate();)
reed@android.com9e0c2fc2009-06-02 18:54:28 +0000290 return;
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000291
reed@android.com9e0c2fc2009-06-02 18:54:28 +0000292 // if we got here, we had an error, so we reset the bitmap to empty
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000293err:
294 this->reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295}
296
297void SkBitmap::updatePixelsFromRef() const {
298 if (NULL != fPixelRef) {
299 if (fPixelLockCount > 0) {
reed@google.comff0da4f2012-05-17 13:14:52 +0000300 SkASSERT(fPixelRef->isLocked());
weita@google.comf9ab99a2009-05-03 18:23:30 +0000301
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302 void* p = fPixelRef->pixels();
303 if (NULL != p) {
304 p = (char*)p + fPixelRefOffset;
305 }
306 fPixels = p;
307 SkRefCnt_SafeAssign(fColorTable, fPixelRef->colorTable());
308 } else {
309 SkASSERT(0 == fPixelLockCount);
310 fPixels = NULL;
reed@android.com149e2f62009-05-22 14:39:03 +0000311 if (fColorTable) {
312 fColorTable->unref();
313 fColorTable = NULL;
314 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315 }
316 }
317}
318
319SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, size_t offset) {
320 // do this first, we that we never have a non-zero offset with a null ref
321 if (NULL == pr) {
322 offset = 0;
323 }
324
325 if (fPixelRef != pr || fPixelRefOffset != offset) {
326 if (fPixelRef != pr) {
327 this->freePixels();
328 SkASSERT(NULL == fPixelRef);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000329
reed@google.com82065d62011-02-07 15:30:46 +0000330 SkSafeRef(pr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000331 fPixelRef = pr;
332 }
333 fPixelRefOffset = offset;
334 this->updatePixelsFromRef();
335 }
336
337 SkDEBUGCODE(this->validate();)
338 return pr;
339}
340
341void SkBitmap::lockPixels() const {
342 if (NULL != fPixelRef && 1 == ++fPixelLockCount) {
343 fPixelRef->lockPixels();
344 this->updatePixelsFromRef();
345 }
346 SkDEBUGCODE(this->validate();)
347}
348
349void SkBitmap::unlockPixels() const {
350 SkASSERT(NULL == fPixelRef || fPixelLockCount > 0);
351
352 if (NULL != fPixelRef && 0 == --fPixelLockCount) {
353 fPixelRef->unlockPixels();
354 this->updatePixelsFromRef();
355 }
356 SkDEBUGCODE(this->validate();)
357}
358
reed@google.com9c49bc32011-07-07 13:42:37 +0000359bool SkBitmap::lockPixelsAreWritable() const {
360 if (fPixelRef) {
361 return fPixelRef->lockPixelsAreWritable();
362 } else {
363 return fPixels != NULL;
364 }
365}
366
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
368 this->freePixels();
369 fPixels = p;
370 SkRefCnt_SafeAssign(fColorTable, ctable);
371
372 SkDEBUGCODE(this->validate();)
373}
374
375bool SkBitmap::allocPixels(Allocator* allocator, SkColorTable* ctable) {
376 HeapAllocator stdalloc;
377
378 if (NULL == allocator) {
379 allocator = &stdalloc;
380 }
381 return allocator->allocPixelRef(this, ctable);
382}
383
384void SkBitmap::freePixels() {
385 // if we're gonna free the pixels, we certainly need to free the mipmap
386 this->freeMipMap();
387
reed@android.com149e2f62009-05-22 14:39:03 +0000388 if (fColorTable) {
389 fColorTable->unref();
390 fColorTable = NULL;
391 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000392
393 if (NULL != fPixelRef) {
394 if (fPixelLockCount > 0) {
395 fPixelRef->unlockPixels();
396 }
397 fPixelRef->unref();
398 fPixelRef = NULL;
399 fPixelRefOffset = 0;
400 }
401 fPixelLockCount = 0;
402 fPixels = NULL;
403}
404
405void SkBitmap::freeMipMap() {
reed@android.com149e2f62009-05-22 14:39:03 +0000406 if (fMipMap) {
407 fMipMap->unref();
408 fMipMap = NULL;
409 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000410}
411
412uint32_t SkBitmap::getGenerationID() const {
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000413 if (fPixelRef) {
414 return fPixelRef->getGenerationID();
415 } else {
416 SkASSERT(fPixels || !fRawPixelGenerationID);
417 if (fPixels && !fRawPixelGenerationID) {
418 fRawPixelGenerationID = SkNextPixelRefGenerationID();
419 }
420 return fRawPixelGenerationID;
421 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000422}
423
424void SkBitmap::notifyPixelsChanged() const {
junov@chromium.orgb0521292011-12-15 20:14:06 +0000425 SkASSERT(!this->isImmutable());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000426 if (fPixelRef) {
427 fPixelRef->notifyPixelsChanged();
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000428 } else {
429 fRawPixelGenerationID = 0; // will grab next ID in getGenerationID
reed@android.com8a1c16f2008-12-17 15:59:43 +0000430 }
431}
432
reed@android.comce4e53a2010-09-09 16:01:26 +0000433SkGpuTexture* SkBitmap::getTexture() const {
434 return fPixelRef ? fPixelRef->getTexture() : NULL;
435}
436
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437///////////////////////////////////////////////////////////////////////////////
438
reed@android.com8a1c16f2008-12-17 15:59:43 +0000439/** We explicitly use the same allocator for our pixels that SkMask does,
440 so that we can freely assign memory allocated by one class to the other.
441 */
442bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
443 SkColorTable* ctable) {
444 Sk64 size = dst->getSize64();
445 if (size.isNeg() || !size.is32()) {
446 return false;
447 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000448
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449 void* addr = sk_malloc_flags(size.get32(), 0); // returns NULL on failure
450 if (NULL == addr) {
451 return false;
452 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000453
reed@android.com8a1c16f2008-12-17 15:59:43 +0000454 dst->setPixelRef(new SkMallocPixelRef(addr, size.get32(), ctable))->unref();
455 // since we're already allocated, we lockPixels right away
456 dst->lockPixels();
457 return true;
458}
459
460///////////////////////////////////////////////////////////////////////////////
461
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000462size_t SkBitmap::getSafeSize() const {
463 // This is intended to be a size_t version of ComputeSafeSize64(), just
464 // faster. The computation is meant to be identical.
465 return (fHeight ? ((fHeight - 1) * fRowBytes) +
466 ComputeRowBytes(getConfig(), fWidth): 0);
467}
468
469Sk64 SkBitmap::getSafeSize64() const {
470 return ComputeSafeSize64(getConfig(), fWidth, fHeight, fRowBytes);
471}
472
bsalomon@google.comc6980972011-11-02 19:57:21 +0000473bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize,
474 int dstRowBytes, bool preserveDstPad) const {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000475
476 if (dstRowBytes == -1)
477 dstRowBytes = fRowBytes;
478 SkASSERT(dstRowBytes >= 0);
479
480 if (getConfig() == kRLE_Index8_Config ||
481 dstRowBytes < ComputeRowBytes(getConfig(), fWidth) ||
482 dst == NULL || (getPixels() == NULL && pixelRef() == NULL))
483 return false;
484
bsalomon@google.comc6980972011-11-02 19:57:21 +0000485 if (!preserveDstPad && static_cast<uint32_t>(dstRowBytes) == fRowBytes) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000486 size_t safeSize = getSafeSize();
487 if (safeSize > dstSize || safeSize == 0)
488 return false;
489 else {
490 SkAutoLockPixels lock(*this);
491 // This implementation will write bytes beyond the end of each row,
492 // excluding the last row, if the bitmap's stride is greater than
493 // strictly required by the current config.
494 memcpy(dst, getPixels(), safeSize);
495
496 return true;
497 }
498 } else {
499 // If destination has different stride than us, then copy line by line.
500 if (ComputeSafeSize(getConfig(), fWidth, fHeight, dstRowBytes) >
501 dstSize)
502 return false;
503 else {
504 // Just copy what we need on each line.
505 uint32_t rowBytes = ComputeRowBytes(getConfig(), fWidth);
506 SkAutoLockPixels lock(*this);
507 const uint8_t* srcP = reinterpret_cast<const uint8_t*>(getPixels());
508 uint8_t* dstP = reinterpret_cast<uint8_t*>(dst);
509 for (uint32_t row = 0; row < fHeight;
510 row++, srcP += fRowBytes, dstP += dstRowBytes) {
511 memcpy(dstP, srcP, rowBytes);
512 }
513
514 return true;
515 }
516 }
517}
518
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000519///////////////////////////////////////////////////////////////////////////////
520
junov@chromium.orgb0521292011-12-15 20:14:06 +0000521bool SkBitmap::isImmutable() const {
522 return fPixelRef ? fPixelRef->isImmutable() :
523 fFlags & kImageIsImmutable_Flag;
524}
525
526void SkBitmap::setImmutable() {
527 if (fPixelRef) {
528 fPixelRef->setImmutable();
529 } else {
530 fFlags |= kImageIsImmutable_Flag;
531 }
532}
533
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534bool SkBitmap::isOpaque() const {
535 switch (fConfig) {
536 case kNo_Config:
537 return true;
538
539 case kA1_Config:
540 case kA8_Config:
541 case kARGB_4444_Config:
542 case kARGB_8888_Config:
543 return (fFlags & kImageIsOpaque_Flag) != 0;
544
545 case kIndex8_Config:
546 case kRLE_Index8_Config: {
547 uint32_t flags = 0;
548
549 this->lockPixels();
550 // if lockPixels failed, we may not have a ctable ptr
551 if (fColorTable) {
552 flags = fColorTable->getFlags();
553 }
554 this->unlockPixels();
555
556 return (flags & SkColorTable::kColorsAreOpaque_Flag) != 0;
557 }
558
559 case kRGB_565_Config:
560 return true;
561
562 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000563 SkDEBUGFAIL("unknown bitmap config pased to isOpaque");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564 return false;
565 }
566}
567
568void SkBitmap::setIsOpaque(bool isOpaque) {
569 /* we record this regardless of fConfig, though it is ignored in
570 isOpaque() for configs that can't support per-pixel alpha.
571 */
572 if (isOpaque) {
573 fFlags |= kImageIsOpaque_Flag;
574 } else {
575 fFlags &= ~kImageIsOpaque_Flag;
576 }
577}
578
junov@google.com4ee7ae52011-06-30 17:30:49 +0000579bool SkBitmap::isVolatile() const {
580 return (fFlags & kImageIsVolatile_Flag) != 0;
581}
582
583void SkBitmap::setIsVolatile(bool isVolatile) {
584 if (isVolatile) {
585 fFlags |= kImageIsVolatile_Flag;
586 } else {
587 fFlags &= ~kImageIsVolatile_Flag;
588 }
589}
590
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591void* SkBitmap::getAddr(int x, int y) const {
592 SkASSERT((unsigned)x < (unsigned)this->width());
593 SkASSERT((unsigned)y < (unsigned)this->height());
594
595 char* base = (char*)this->getPixels();
596 if (base) {
597 base += y * this->rowBytes();
598 switch (this->config()) {
599 case SkBitmap::kARGB_8888_Config:
600 base += x << 2;
601 break;
602 case SkBitmap::kARGB_4444_Config:
603 case SkBitmap::kRGB_565_Config:
604 base += x << 1;
605 break;
606 case SkBitmap::kA8_Config:
607 case SkBitmap::kIndex8_Config:
608 base += x;
609 break;
610 case SkBitmap::kA1_Config:
611 base += x >> 3;
612 break;
613 case kRLE_Index8_Config:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000614 SkDEBUGFAIL("Can't return addr for kRLE_Index8_Config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000615 base = NULL;
616 break;
617 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000618 SkDEBUGFAIL("Can't return addr for config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000619 base = NULL;
620 break;
621 }
622 }
623 return base;
624}
625
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000626SkColor SkBitmap::getColor(int x, int y) const {
627 SkASSERT((unsigned)x < (unsigned)this->width());
628 SkASSERT((unsigned)y < (unsigned)this->height());
629
630 switch (this->config()) {
631 case SkBitmap::kA1_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000632 uint8_t* addr = this->getAddr1(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000633 uint8_t mask = 1 << (7 - (x % 8));
634 if (addr[0] & mask) {
635 return SK_ColorBLACK;
636 } else {
637 return 0;
638 }
639 }
640 case SkBitmap::kA8_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000641 uint8_t* addr = this->getAddr8(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000642 return SkColorSetA(0, addr[0]);
643 }
644 case SkBitmap::kIndex8_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000645 SkPMColor c = this->getIndex8Color(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000646 return SkUnPreMultiply::PMColorToColor(c);
647 }
648 case SkBitmap::kRGB_565_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000649 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000650 return SkPixel16ToColor(addr[0]);
651 }
652 case SkBitmap::kARGB_4444_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000653 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000654 SkPMColor c = SkPixel4444ToPixel32(addr[0]);
655 return SkUnPreMultiply::PMColorToColor(c);
656 }
657 case SkBitmap::kARGB_8888_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000658 uint32_t* addr = this->getAddr32(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000659 return SkUnPreMultiply::PMColorToColor(addr[0]);
660 }
661 case kRLE_Index8_Config: {
662 uint8_t dst;
663 const SkBitmap::RLEPixels* rle =
reed@google.com3b521d02011-04-29 11:53:41 +0000664 (const SkBitmap::RLEPixels*)this->getPixels();
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000665 SkPackBits::Unpack8(&dst, x, 1, rle->packedAtY(y));
666 return SkUnPreMultiply::PMColorToColor((*fColorTable)[dst]);
667 }
668 case kNo_Config:
669 case kConfigCount:
670 SkASSERT(false);
671 return 0;
672 }
673 SkASSERT(false); // Not reached.
674 return 0;
675}
676
reed@android.com8a1c16f2008-12-17 15:59:43 +0000677///////////////////////////////////////////////////////////////////////////////
678///////////////////////////////////////////////////////////////////////////////
679
680void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
681 SkDEBUGCODE(this->validate();)
682
683 if (0 == fWidth || 0 == fHeight ||
684 kNo_Config == fConfig || kIndex8_Config == fConfig) {
685 return;
686 }
687
688 SkAutoLockPixels alp(*this);
689 // perform this check after the lock call
690 if (!this->readyToDraw()) {
691 return;
692 }
693
694 int height = fHeight;
695 const int width = fWidth;
696 const int rowBytes = fRowBytes;
697
698 // make rgb premultiplied
699 if (255 != a) {
700 r = SkAlphaMul(r, a);
701 g = SkAlphaMul(g, a);
702 b = SkAlphaMul(b, a);
703 }
704
705 switch (fConfig) {
706 case kA1_Config: {
707 uint8_t* p = (uint8_t*)fPixels;
708 const int count = (width + 7) >> 3;
709 a = (a >> 7) ? 0xFF : 0;
710 SkASSERT(count <= rowBytes);
711 while (--height >= 0) {
712 memset(p, a, count);
713 p += rowBytes;
714 }
715 break;
716 }
717 case kA8_Config: {
718 uint8_t* p = (uint8_t*)fPixels;
719 while (--height >= 0) {
720 memset(p, a, width);
721 p += rowBytes;
722 }
723 break;
724 }
725 case kARGB_4444_Config:
726 case kRGB_565_Config: {
727 uint16_t* p = (uint16_t*)fPixels;
728 uint16_t v;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000729
reed@android.com8a1c16f2008-12-17 15:59:43 +0000730 if (kARGB_4444_Config == fConfig) {
731 v = SkPackARGB4444(a >> 4, r >> 4, g >> 4, b >> 4);
732 } else { // kRGB_565_Config
733 v = SkPackRGB16(r >> (8 - SK_R16_BITS), g >> (8 - SK_G16_BITS),
734 b >> (8 - SK_B16_BITS));
735 }
736 while (--height >= 0) {
737 sk_memset16(p, v, width);
738 p = (uint16_t*)((char*)p + rowBytes);
739 }
740 break;
741 }
742 case kARGB_8888_Config: {
743 uint32_t* p = (uint32_t*)fPixels;
744 uint32_t v = SkPackARGB32(a, r, g, b);
745
746 while (--height >= 0) {
747 sk_memset32(p, v, width);
748 p = (uint32_t*)((char*)p + rowBytes);
749 }
750 break;
751 }
752 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000753
reed@android.com8a1c16f2008-12-17 15:59:43 +0000754 this->notifyPixelsChanged();
755}
756
757//////////////////////////////////////////////////////////////////////////////////////
758//////////////////////////////////////////////////////////////////////////////////////
759
760#define SUB_OFFSET_FAILURE ((size_t)-1)
761
762static size_t getSubOffset(const SkBitmap& bm, int x, int y) {
763 SkASSERT((unsigned)x < (unsigned)bm.width());
764 SkASSERT((unsigned)y < (unsigned)bm.height());
weita@google.comf9ab99a2009-05-03 18:23:30 +0000765
reed@android.com8a1c16f2008-12-17 15:59:43 +0000766 switch (bm.getConfig()) {
767 case SkBitmap::kA8_Config:
768 case SkBitmap:: kIndex8_Config:
769 // x is fine as is for the calculation
770 break;
771
772 case SkBitmap::kRGB_565_Config:
773 case SkBitmap::kARGB_4444_Config:
774 x <<= 1;
775 break;
776
777 case SkBitmap::kARGB_8888_Config:
778 x <<= 2;
779 break;
780
781 case SkBitmap::kNo_Config:
782 case SkBitmap::kA1_Config:
783 default:
784 return SUB_OFFSET_FAILURE;
785 }
786 return y * bm.rowBytes() + x;
787}
788
789bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
790 SkDEBUGCODE(this->validate();)
791
792 if (NULL == result || (NULL == fPixelRef && NULL == fPixels)) {
793 return false; // no src pixels
794 }
795
796 SkIRect srcRect, r;
797 srcRect.set(0, 0, this->width(), this->height());
798 if (!r.intersect(srcRect, subset)) {
799 return false; // r is empty (i.e. no intersection)
800 }
801
802 if (kRLE_Index8_Config == fConfig) {
803 SkAutoLockPixels alp(*this);
804 // don't call readyToDraw(), since we can operate w/o a colortable
805 // at this stage
806 if (this->getPixels() == NULL) {
807 return false;
808 }
809 SkBitmap bm;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000810
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811 bm.setConfig(kIndex8_Config, r.width(), r.height());
812 bm.allocPixels(this->getColorTable());
813 if (NULL == bm.getPixels()) {
814 return false;
815 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000816
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817 const RLEPixels* rle = (const RLEPixels*)this->getPixels();
818 uint8_t* dst = bm.getAddr8(0, 0);
819 const int width = bm.width();
820 const int rowBytes = bm.rowBytes();
weita@google.comf9ab99a2009-05-03 18:23:30 +0000821
reed@android.com8a1c16f2008-12-17 15:59:43 +0000822 for (int y = r.fTop; y < r.fBottom; y++) {
823 SkPackBits::Unpack8(dst, r.fLeft, width, rle->packedAtY(y));
824 dst += rowBytes;
825 }
826 result->swap(bm);
827 return true;
828 }
829
830 size_t offset = getSubOffset(*this, r.fLeft, r.fTop);
831 if (SUB_OFFSET_FAILURE == offset) {
832 return false; // config not supported
833 }
834
835 SkBitmap dst;
836 dst.setConfig(this->config(), r.width(), r.height(), this->rowBytes());
skyostil@google.com0eb75762012-01-16 10:45:53 +0000837 dst.setIsVolatile(this->isVolatile());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000838
839 if (fPixelRef) {
840 // share the pixelref with a custom offset
841 dst.setPixelRef(fPixelRef, fPixelRefOffset + offset);
842 } else {
843 // share the pixels (owned by the caller)
844 dst.setPixels((char*)fPixels + offset, this->getColorTable());
845 }
846 SkDEBUGCODE(dst.validate();)
847
848 // we know we're good, so commit to result
849 result->swap(dst);
850 return true;
851}
852
853///////////////////////////////////////////////////////////////////////////////
854
855#include "SkCanvas.h"
856#include "SkPaint.h"
857
reed@android.comfbaa88d2009-05-06 17:44:34 +0000858bool SkBitmap::canCopyTo(Config dstConfig) const {
859 if (this->getConfig() == kNo_Config) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000860 return false;
861 }
862
reed@android.comfbaa88d2009-05-06 17:44:34 +0000863 bool sameConfigs = (this->config() == dstConfig);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000864 switch (dstConfig) {
865 case kA8_Config:
866 case kARGB_4444_Config:
867 case kRGB_565_Config:
868 case kARGB_8888_Config:
869 break;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000870 case kA1_Config:
871 case kIndex8_Config:
872 if (!sameConfigs) {
873 return false;
874 }
875 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000876 default:
877 return false;
878 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000879
880 // do not copy src if srcConfig == kA1_Config while dstConfig != kA1_Config
881 if (this->getConfig() == kA1_Config && !sameConfigs) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000882 return false;
883 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000884
reed@android.comfbaa88d2009-05-06 17:44:34 +0000885 return true;
886}
887
888bool SkBitmap::copyTo(SkBitmap* dst, Config dstConfig, Allocator* alloc) const {
889 if (!this->canCopyTo(dstConfig)) {
890 return false;
891 }
892
reed@google.com50dfa012011-04-01 19:05:36 +0000893 // if we have a texture, first get those pixels
894 SkBitmap tmpSrc;
895 const SkBitmap* src = this;
896
bsalomon@google.com669fdc42011-04-05 17:08:27 +0000897 if (fPixelRef && fPixelRef->readPixels(&tmpSrc)) {
reed@google.com50dfa012011-04-01 19:05:36 +0000898 SkASSERT(tmpSrc.width() == this->width());
899 SkASSERT(tmpSrc.height() == this->height());
900
901 // did we get lucky and we can just return tmpSrc?
902 if (tmpSrc.config() == dstConfig && NULL == alloc) {
903 dst->swap(tmpSrc);
904 return true;
905 }
906
907 // fall through to the raster case
908 src = &tmpSrc;
reed@android.comfbaa88d2009-05-06 17:44:34 +0000909 }
reed@android.com311c82d2009-05-05 23:13:23 +0000910
reed@google.com50dfa012011-04-01 19:05:36 +0000911 // we lock this now, since we may need its colortable
912 SkAutoLockPixels srclock(*src);
913 if (!src->readyToDraw()) {
914 return false;
915 }
916
917 SkBitmap tmpDst;
918 tmpDst.setConfig(dstConfig, src->width(), src->height());
919
weita@google.comf9ab99a2009-05-03 18:23:30 +0000920 // allocate colortable if srcConfig == kIndex8_Config
921 SkColorTable* ctable = (dstConfig == kIndex8_Config) ?
reed@google.com50dfa012011-04-01 19:05:36 +0000922 new SkColorTable(*src->getColorTable()) : NULL;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000923 SkAutoUnref au(ctable);
reed@google.com50dfa012011-04-01 19:05:36 +0000924 if (!tmpDst.allocPixels(alloc, ctable)) {
weita@google.comf9ab99a2009-05-03 18:23:30 +0000925 return false;
926 }
reed@google.com50dfa012011-04-01 19:05:36 +0000927
reed@google.com50dfa012011-04-01 19:05:36 +0000928 if (!tmpDst.readyToDraw()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000929 // allocator/lock failed
930 return false;
931 }
reed@google.com50dfa012011-04-01 19:05:36 +0000932
reed@android.comfbaa88d2009-05-06 17:44:34 +0000933 /* do memcpy for the same configs cases, else use drawing
weita@google.comf9ab99a2009-05-03 18:23:30 +0000934 */
reed@google.com50dfa012011-04-01 19:05:36 +0000935 if (src->config() == dstConfig) {
936 if (tmpDst.getSize() == src->getSize()) {
937 memcpy(tmpDst.getPixels(), src->getPixels(), src->getSafeSize());
reed@android.com311c82d2009-05-05 23:13:23 +0000938 } else {
reed@google.com50dfa012011-04-01 19:05:36 +0000939 const char* srcP = reinterpret_cast<const char*>(src->getPixels());
940 char* dstP = reinterpret_cast<char*>(tmpDst.getPixels());
reed@android.com311c82d2009-05-05 23:13:23 +0000941 // to be sure we don't read too much, only copy our logical pixels
reed@google.com50dfa012011-04-01 19:05:36 +0000942 size_t bytesToCopy = tmpDst.width() * tmpDst.bytesPerPixel();
943 for (int y = 0; y < tmpDst.height(); y++) {
reed@android.com311c82d2009-05-05 23:13:23 +0000944 memcpy(dstP, srcP, bytesToCopy);
reed@google.com50dfa012011-04-01 19:05:36 +0000945 srcP += src->rowBytes();
946 dstP += tmpDst.rowBytes();
reed@android.com311c82d2009-05-05 23:13:23 +0000947 }
948 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000949 } else {
950 // if the src has alpha, we have to clear the dst first
reed@google.com50dfa012011-04-01 19:05:36 +0000951 if (!src->isOpaque()) {
952 tmpDst.eraseColor(0);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000953 }
954
reed@google.com50dfa012011-04-01 19:05:36 +0000955 SkCanvas canvas(tmpDst);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000956 SkPaint paint;
957
958 paint.setDither(true);
reed@google.com50dfa012011-04-01 19:05:36 +0000959 canvas.drawBitmap(*src, 0, 0, &paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000960 }
961
reed@google.com50dfa012011-04-01 19:05:36 +0000962 tmpDst.setIsOpaque(src->isOpaque());
reed@android.comcafc9f92009-08-22 03:44:57 +0000963
reed@google.com50dfa012011-04-01 19:05:36 +0000964 dst->swap(tmpDst);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000965 return true;
966}
967
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +0000968bool SkBitmap::deepCopyTo(SkBitmap* dst, Config dstConfig) const {
969 if (!this->canCopyTo(dstConfig)) {
970 return false;
971 }
972
973 // If we have a PixelRef, and it supports deep copy, use it.
974 // Currently supported only by texture-backed bitmaps.
975 if (fPixelRef) {
976 SkPixelRef* pixelRef = fPixelRef->deepCopy(dstConfig);
977 if (pixelRef) {
978 dst->setConfig(dstConfig, fWidth, fHeight);
979 dst->setPixelRef(pixelRef)->unref();
980 return true;
981 }
982 }
983
984 if (this->getTexture()) {
985 return false;
986 } else {
987 return this->copyTo(dst, dstConfig, NULL);
988 }
989}
990
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991///////////////////////////////////////////////////////////////////////////////
992///////////////////////////////////////////////////////////////////////////////
993
994static void downsampleby2_proc32(SkBitmap* dst, int x, int y,
995 const SkBitmap& src) {
996 x <<= 1;
997 y <<= 1;
998 const SkPMColor* p = src.getAddr32(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +0000999 const SkPMColor* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001000 SkPMColor c, ag, rb;
1001
1002 c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF;
1003 if (x < src.width() - 1) {
1004 p += 1;
1005 }
1006 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1007
reed@android.com829c83c2009-06-08 12:05:31 +00001008 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001009 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +00001010 p += src.rowBytes() >> 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001011 }
1012 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1013 if (x < src.width() - 1) {
1014 p += 1;
1015 }
1016 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1017
1018 *dst->getAddr32(x >> 1, y >> 1) =
1019 ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00);
1020}
1021
1022static inline uint32_t expand16(U16CPU c) {
1023 return (c & ~SK_G16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16);
1024}
1025
1026// returns dirt in the top 16bits, but we don't care, since we only
1027// store the low 16bits.
1028static inline U16CPU pack16(uint32_t c) {
1029 return (c & ~SK_G16_MASK_IN_PLACE) | ((c >> 16) & SK_G16_MASK_IN_PLACE);
1030}
1031
1032static void downsampleby2_proc16(SkBitmap* dst, int x, int y,
1033 const SkBitmap& src) {
1034 x <<= 1;
1035 y <<= 1;
1036 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001037 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001038 SkPMColor c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001039
reed@android.com8a1c16f2008-12-17 15:59:43 +00001040 c = expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001041 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001042 p += 1;
1043 }
1044 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001045
reed@android.com829c83c2009-06-08 12:05:31 +00001046 p = baseP;
1047 if (y < src.height() - 1) {
1048 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001049 }
1050 c += expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001051 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001052 p += 1;
1053 }
1054 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001055
reed@android.com8a1c16f2008-12-17 15:59:43 +00001056 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)pack16(c >> 2);
1057}
1058
1059static uint32_t expand4444(U16CPU c) {
1060 return (c & 0xF0F) | ((c & ~0xF0F) << 12);
1061}
1062
1063static U16CPU collaps4444(uint32_t c) {
1064 return (c & 0xF0F) | ((c >> 12) & ~0xF0F);
1065}
1066
1067static void downsampleby2_proc4444(SkBitmap* dst, int x, int y,
1068 const SkBitmap& src) {
1069 x <<= 1;
1070 y <<= 1;
1071 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001072 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001073 uint32_t c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001074
reed@android.com8a1c16f2008-12-17 15:59:43 +00001075 c = expand4444(*p);
1076 if (x < src.width() - 1) {
1077 p += 1;
1078 }
1079 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001080
reed@android.com829c83c2009-06-08 12:05:31 +00001081 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001082 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +00001083 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001084 }
1085 c += expand4444(*p);
1086 if (x < src.width() - 1) {
1087 p += 1;
1088 }
1089 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001090
reed@android.com8a1c16f2008-12-17 15:59:43 +00001091 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)collaps4444(c >> 2);
1092}
1093
1094void SkBitmap::buildMipMap(bool forceRebuild) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001095 if (forceRebuild)
1096 this->freeMipMap();
1097 else if (fMipMap)
1098 return; // we're already built
1099
1100 SkASSERT(NULL == fMipMap);
1101
1102 void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src);
1103
1104 const SkBitmap::Config config = this->getConfig();
1105
1106 switch (config) {
1107 case kARGB_8888_Config:
1108 proc = downsampleby2_proc32;
1109 break;
1110 case kRGB_565_Config:
1111 proc = downsampleby2_proc16;
1112 break;
1113 case kARGB_4444_Config:
1114 proc = downsampleby2_proc4444;
1115 break;
1116 case kIndex8_Config:
1117 case kA8_Config:
1118 default:
1119 return; // don't build mipmaps for these configs
1120 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001121
reed@android.com149e2f62009-05-22 14:39:03 +00001122 SkAutoLockPixels alp(*this);
1123 if (!this->readyToDraw()) {
1124 return;
1125 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001126
1127 // whip through our loop to compute the exact size needed
1128 size_t size = 0;
1129 int maxLevels = 0;
1130 {
reed@android.com149e2f62009-05-22 14:39:03 +00001131 int width = this->width();
1132 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133 for (;;) {
1134 width >>= 1;
1135 height >>= 1;
1136 if (0 == width || 0 == height) {
1137 break;
1138 }
1139 size += ComputeRowBytes(config, width) * height;
1140 maxLevels += 1;
1141 }
1142 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001143
reed@android.com149e2f62009-05-22 14:39:03 +00001144 // nothing to build
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145 if (0 == maxLevels) {
1146 return;
1147 }
1148
reed@android.com149e2f62009-05-22 14:39:03 +00001149 SkBitmap srcBM(*this);
1150 srcBM.lockPixels();
1151 if (!srcBM.readyToDraw()) {
1152 return;
1153 }
1154
1155 MipMap* mm = MipMap::Alloc(maxLevels, size);
1156 if (NULL == mm) {
1157 return;
1158 }
1159
reed@android.com8a1c16f2008-12-17 15:59:43 +00001160 MipLevel* level = mm->levels();
1161 uint8_t* addr = (uint8_t*)mm->pixels();
reed@android.com149e2f62009-05-22 14:39:03 +00001162 int width = this->width();
1163 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001164 unsigned rowBytes = this->rowBytes();
reed@android.com149e2f62009-05-22 14:39:03 +00001165 SkBitmap dstBM;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001166
1167 for (int i = 0; i < maxLevels; i++) {
1168 width >>= 1;
1169 height >>= 1;
1170 rowBytes = ComputeRowBytes(config, width);
1171
1172 level[i].fPixels = addr;
reed@android.comf459a492009-03-27 12:33:50 +00001173 level[i].fWidth = width;
1174 level[i].fHeight = height;
reed@android.com49f0ff22009-03-19 21:52:42 +00001175 level[i].fRowBytes = rowBytes;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001176
1177 dstBM.setConfig(config, width, height, rowBytes);
1178 dstBM.setPixels(addr);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001179
reed@android.com149e2f62009-05-22 14:39:03 +00001180 for (int y = 0; y < height; y++) {
1181 for (int x = 0; x < width; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001182 proc(&dstBM, x, y, srcBM);
1183 }
1184 }
1185
1186 srcBM = dstBM;
1187 addr += height * rowBytes;
1188 }
1189 SkASSERT(addr == (uint8_t*)mm->pixels() + size);
1190 fMipMap = mm;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001191}
1192
1193bool SkBitmap::hasMipMap() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001194 return fMipMap != NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001195}
1196
1197int SkBitmap::extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy) {
reed@android.com83f7bc32009-07-17 02:42:41 +00001198 if (NULL == fMipMap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001199 return 0;
reed@android.com83f7bc32009-07-17 02:42:41 +00001200 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001201
reed@android.com8a1c16f2008-12-17 15:59:43 +00001202 int level = ComputeMipLevel(sx, sy) >> 16;
1203 SkASSERT(level >= 0);
1204 if (level <= 0) {
1205 return 0;
1206 }
1207
1208 if (level >= fMipMap->fLevelCount) {
1209 level = fMipMap->fLevelCount - 1;
1210 }
1211 if (dst) {
1212 const MipLevel& mip = fMipMap->levels()[level - 1];
1213 dst->setConfig((SkBitmap::Config)this->config(),
1214 mip.fWidth, mip.fHeight, mip.fRowBytes);
1215 dst->setPixels(mip.fPixels);
1216 }
1217 return level;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001218}
1219
1220SkFixed SkBitmap::ComputeMipLevel(SkFixed sx, SkFixed sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001221 sx = SkAbs32(sx);
1222 sy = SkAbs32(sy);
1223 if (sx < sy) {
1224 sx = sy;
1225 }
1226 if (sx < SK_Fixed1) {
1227 return 0;
1228 }
1229 int clz = SkCLZ(sx);
1230 SkASSERT(clz >= 1 && clz <= 15);
1231 return SkIntToFixed(15 - clz) + ((unsigned)(sx << (clz + 1)) >> 16);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001232}
1233
1234///////////////////////////////////////////////////////////////////////////////
1235
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +00001236static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001237 int alphaRowBytes) {
1238 SkASSERT(alpha != NULL);
1239 SkASSERT(alphaRowBytes >= src.width());
1240
1241 SkBitmap::Config config = src.getConfig();
1242 int w = src.width();
1243 int h = src.height();
1244 int rb = src.rowBytes();
1245
reed@android.com1cdcb512009-08-24 19:11:00 +00001246 SkAutoLockPixels alp(src);
1247 if (!src.readyToDraw()) {
1248 // zero out the alpha buffer and return
1249 while (--h >= 0) {
1250 memset(alpha, 0, w);
1251 alpha += alphaRowBytes;
1252 }
1253 return false;
1254 }
reed@google.com82065d62011-02-07 15:30:46 +00001255
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256 if (SkBitmap::kA8_Config == config && !src.isOpaque()) {
1257 const uint8_t* s = src.getAddr8(0, 0);
1258 while (--h >= 0) {
1259 memcpy(alpha, s, w);
1260 s += rb;
1261 alpha += alphaRowBytes;
1262 }
1263 } else if (SkBitmap::kARGB_8888_Config == config && !src.isOpaque()) {
1264 const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0);
1265 while (--h >= 0) {
1266 for (int x = 0; x < w; x++) {
1267 alpha[x] = SkGetPackedA32(s[x]);
1268 }
1269 s = (const SkPMColor*)((const char*)s + rb);
1270 alpha += alphaRowBytes;
1271 }
1272 } else if (SkBitmap::kARGB_4444_Config == config && !src.isOpaque()) {
1273 const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0);
1274 while (--h >= 0) {
1275 for (int x = 0; x < w; x++) {
1276 alpha[x] = SkPacked4444ToA32(s[x]);
1277 }
1278 s = (const SkPMColor16*)((const char*)s + rb);
1279 alpha += alphaRowBytes;
1280 }
1281 } else if (SkBitmap::kIndex8_Config == config && !src.isOpaque()) {
1282 SkColorTable* ct = src.getColorTable();
1283 if (ct) {
1284 const SkPMColor* SK_RESTRICT table = ct->lockColors();
1285 const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0);
1286 while (--h >= 0) {
1287 for (int x = 0; x < w; x++) {
1288 alpha[x] = SkGetPackedA32(table[s[x]]);
1289 }
1290 s += rb;
1291 alpha += alphaRowBytes;
1292 }
1293 ct->unlockColors(false);
1294 }
1295 } else { // src is opaque, so just fill alpha[] with 0xFF
1296 memset(alpha, 0xFF, h * alphaRowBytes);
1297 }
reed@android.com1cdcb512009-08-24 19:11:00 +00001298 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001299}
1300
1301#include "SkPaint.h"
1302#include "SkMaskFilter.h"
1303#include "SkMatrix.h"
1304
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001305bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
djsollen@google.com57f49692011-02-23 20:46:31 +00001306 Allocator *allocator, SkIPoint* offset) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001307 SkDEBUGCODE(this->validate();)
1308
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001309 SkBitmap tmpBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001310 SkMatrix identity;
1311 SkMask srcM, dstM;
1312
1313 srcM.fBounds.set(0, 0, this->width(), this->height());
1314 srcM.fRowBytes = SkAlign4(this->width());
1315 srcM.fFormat = SkMask::kA8_Format;
1316
1317 SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL;
1318
1319 // compute our (larger?) dst bounds if we have a filter
1320 if (NULL != filter) {
1321 identity.reset();
1322 srcM.fImage = NULL;
1323 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1324 goto NO_FILTER_CASE;
1325 }
1326 dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
1327 } else {
1328 NO_FILTER_CASE:
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001329 tmpBitmap.setConfig(SkBitmap::kA8_Config, this->width(), this->height(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330 srcM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001331 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1332 // Allocation of pixels for alpha bitmap failed.
1333 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1334 tmpBitmap.width(), tmpBitmap.height());
1335 return false;
1336 }
1337 GetBitmapAlpha(*this, tmpBitmap.getAddr8(0, 0), srcM.fRowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338 if (offset) {
1339 offset->set(0, 0);
1340 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001341 tmpBitmap.swap(*dst);
1342 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001344 srcM.fImage = SkMask::AllocImage(srcM.computeImageSize());
1345 SkAutoMaskFreeImage srcCleanup(srcM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001346
1347 GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
1348 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1349 goto NO_FILTER_CASE;
1350 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001351 SkAutoMaskFreeImage dstCleanup(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001352
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001353 tmpBitmap.setConfig(SkBitmap::kA8_Config, dstM.fBounds.width(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001354 dstM.fBounds.height(), dstM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001355 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1356 // Allocation of pixels for alpha bitmap failed.
1357 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1358 tmpBitmap.width(), tmpBitmap.height());
1359 return false;
1360 }
1361 memcpy(tmpBitmap.getPixels(), dstM.fImage, dstM.computeImageSize());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001362 if (offset) {
1363 offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
1364 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001365 SkDEBUGCODE(tmpBitmap.validate();)
1366
1367 tmpBitmap.swap(*dst);
1368 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001369}
1370
1371///////////////////////////////////////////////////////////////////////////////
1372
1373enum {
1374 SERIALIZE_PIXELTYPE_NONE,
1375 SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE,
1376 SERIALIZE_PIXELTYPE_RAW_NO_CTABLE,
1377 SERIALIZE_PIXELTYPE_REF_DATA,
tomhudson@google.com1f902872012-06-01 13:15:47 +00001378 SERIALIZE_PIXELTYPE_REF_PTR
reed@android.com8a1c16f2008-12-17 15:59:43 +00001379};
1380
reed@android.com8a1c16f2008-12-17 15:59:43 +00001381/*
1382 It is tricky to know how much to flatten. If we don't have a pixelref (i.e.
1383 we just have pixels, then we can only flatten the pixels, or write out an
1384 empty bitmap.
weita@google.comf9ab99a2009-05-03 18:23:30 +00001385
reed@android.com8a1c16f2008-12-17 15:59:43 +00001386 With a pixelref, we still have the question of recognizing when two sitings
1387 of the same pixelref are the same, and when they are different. Perhaps we
1388 should look at the generationID and keep a record of that in some dictionary
1389 associated with the buffer. SkGLTextureCache does this sort of thing to know
1390 when to create a new texture.
1391*/
1392void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
1393 buffer.write32(fWidth);
1394 buffer.write32(fHeight);
1395 buffer.write32(fRowBytes);
1396 buffer.write8(fConfig);
1397 buffer.writeBool(this->isOpaque());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001398
reed@android.com8a1c16f2008-12-17 15:59:43 +00001399 /* If we are called in this mode, then it is up to the caller to manage
1400 the owner-counts on the pixelref, as we just record the ptr itself.
1401 */
1402 if (!buffer.persistBitmapPixels()) {
1403 if (fPixelRef) {
1404 buffer.write8(SERIALIZE_PIXELTYPE_REF_PTR);
1405 buffer.write32(fPixelRefOffset);
1406 buffer.writeRefCnt(fPixelRef);
1407 return;
1408 } else {
1409 // we ignore the non-persist request, since we don't have a ref
1410 // ... or we could just write an empty bitmap...
1411 // (true) will write an empty bitmap, (false) will flatten the pix
1412 if (true) {
1413 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1414 return;
1415 }
1416 }
1417 }
1418
1419 if (fPixelRef) {
djsollen@google.com5370cd92012-03-28 20:47:01 +00001420 if (fPixelRef->getFactory()) {
1421 buffer.write8(SERIALIZE_PIXELTYPE_REF_DATA);
1422 buffer.write32(fPixelRefOffset);
1423 buffer.writeFlattenable(fPixelRef);
1424 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001425 }
1426 // if we get here, we can't record the pixels
1427 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1428 } else if (fPixels) {
1429 if (fColorTable) {
1430 buffer.write8(SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE);
djsollen@google.com64a0ec32012-06-12 15:17:27 +00001431 buffer.writeFlattenable(fColorTable);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001432 } else {
1433 buffer.write8(SERIALIZE_PIXELTYPE_RAW_NO_CTABLE);
1434 }
wjmaclean@chromium.org3f0260d2011-02-22 22:32:14 +00001435 buffer.writePad(fPixels, this->getSafeSize());
1436 // There is no writeZeroPad() fcn, so write individual bytes.
1437 if (this->getSize() > this->getSafeSize()) {
1438 size_t deltaSize = this->getSize() - this->getSafeSize();
1439 // Need aligned pointer to write into due to internal implementa-
1440 // tion of SkWriter32.
1441 memset(buffer.reserve(SkAlign4(deltaSize)), 0, deltaSize);
1442 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001443 } else {
1444 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1445 }
1446}
1447
1448void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) {
1449 this->reset();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001450
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451 int width = buffer.readInt();
1452 int height = buffer.readInt();
1453 int rowBytes = buffer.readInt();
1454 int config = buffer.readU8();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001455
reed@android.com8a1c16f2008-12-17 15:59:43 +00001456 this->setConfig((Config)config, width, height, rowBytes);
1457 this->setIsOpaque(buffer.readBool());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001458
reed@android.com8a1c16f2008-12-17 15:59:43 +00001459 int reftype = buffer.readU8();
1460 switch (reftype) {
1461 case SERIALIZE_PIXELTYPE_REF_PTR: {
1462 size_t offset = buffer.readU32();
1463 SkPixelRef* pr = (SkPixelRef*)buffer.readRefCnt();
1464 this->setPixelRef(pr, offset);
1465 break;
1466 }
1467 case SERIALIZE_PIXELTYPE_REF_DATA: {
1468 size_t offset = buffer.readU32();
djsollen@google.com5370cd92012-03-28 20:47:01 +00001469 SkPixelRef* pr = static_cast<SkPixelRef*>(buffer.readFlattenable());
reed@google.com82065d62011-02-07 15:30:46 +00001470 SkSafeUnref(this->setPixelRef(pr, offset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001471 break;
1472 }
1473 case SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE:
1474 case SERIALIZE_PIXELTYPE_RAW_NO_CTABLE: {
1475 SkColorTable* ctable = NULL;
1476 if (SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE == reftype) {
djsollen@google.com64a0ec32012-06-12 15:17:27 +00001477 ctable = static_cast<SkColorTable*>(buffer.readFlattenable());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001478 }
wjmaclean@chromium.org3f0260d2011-02-22 22:32:14 +00001479 size_t size = this->getSize();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001480 if (this->allocPixels(ctable)) {
1481 this->lockPixels();
wjmaclean@chromium.org3f0260d2011-02-22 22:32:14 +00001482 // Just read what we need.
1483 buffer.read(this->getPixels(), this->getSafeSize());
1484 // Keep aligned for subsequent reads.
1485 buffer.skip(size - this->getSafeSize());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001486 this->unlockPixels();
1487 } else {
wjmaclean@chromium.org0bde1802010-12-22 17:43:54 +00001488 buffer.skip(size); // Still skip the full-sized buffer though.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001489 }
reed@android.com149e2f62009-05-22 14:39:03 +00001490 SkSafeUnref(ctable);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001491 break;
1492 }
1493 case SERIALIZE_PIXELTYPE_NONE:
1494 break;
1495 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001496 SkDEBUGFAIL("unrecognized pixeltype in serialized data");
reed@android.com8a1c16f2008-12-17 15:59:43 +00001497 sk_throw();
1498 }
1499}
1500
1501///////////////////////////////////////////////////////////////////////////////
1502
1503SkBitmap::RLEPixels::RLEPixels(int width, int height) {
1504 fHeight = height;
1505 fYPtrs = (uint8_t**)sk_malloc_throw(height * sizeof(uint8_t*));
reed@android.com4516f472009-06-29 16:25:36 +00001506 sk_bzero(fYPtrs, height * sizeof(uint8_t*));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001507}
1508
1509SkBitmap::RLEPixels::~RLEPixels() {
1510 sk_free(fYPtrs);
1511}
1512
1513///////////////////////////////////////////////////////////////////////////////
1514
1515#ifdef SK_DEBUG
1516void SkBitmap::validate() const {
1517 SkASSERT(fConfig < kConfigCount);
1518 SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
junov@google.com4ee7ae52011-06-30 17:30:49 +00001519 SkASSERT(fFlags <= (kImageIsOpaque_Flag | kImageIsVolatile_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001520 SkASSERT(fPixelLockCount >= 0);
1521 SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000);
1522 SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
1523
1524#if 0 // these asserts are not thread-correct, so disable for now
1525 if (fPixelRef) {
1526 if (fPixelLockCount > 0) {
reed@google.comff0da4f2012-05-17 13:14:52 +00001527 SkASSERT(fPixelRef->isLocked());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001528 } else {
1529 SkASSERT(NULL == fPixels);
1530 SkASSERT(NULL == fColorTable);
1531 }
1532 }
1533#endif
1534}
1535#endif