blob: b1a4b7e374f740a8f638494f284631bbdd3d0b41 [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
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000023SK_DEFINE_INST_COUNT(SkBitmap::Allocator)
24
djsollen@google.com56f8f332012-07-26 14:48:52 +000025extern int32_t SkNextPixelRefGenerationID();
26
reed@android.com149e2f62009-05-22 14:39:03 +000027static bool isPos32Bits(const Sk64& value) {
28 return !value.isNeg() && value.is32();
29}
30
reed@android.com8a1c16f2008-12-17 15:59:43 +000031struct MipLevel {
32 void* fPixels;
33 uint32_t fRowBytes;
reed@android.comf459a492009-03-27 12:33:50 +000034 uint32_t fWidth, fHeight;
reed@android.com8a1c16f2008-12-17 15:59:43 +000035};
36
37struct SkBitmap::MipMap : SkNoncopyable {
38 int32_t fRefCnt;
39 int fLevelCount;
40// MipLevel fLevel[fLevelCount];
41// Pixels[]
weita@google.comf9ab99a2009-05-03 18:23:30 +000042
reed@android.com8a1c16f2008-12-17 15:59:43 +000043 static MipMap* Alloc(int levelCount, size_t pixelSize) {
reed@android.com149e2f62009-05-22 14:39:03 +000044 if (levelCount < 0) {
45 return NULL;
46 }
47 Sk64 size;
48 size.setMul(levelCount + 1, sizeof(MipLevel));
49 size.add(sizeof(MipMap));
50 size.add(pixelSize);
51 if (!isPos32Bits(size)) {
52 return NULL;
53 }
54 MipMap* mm = (MipMap*)sk_malloc_throw(size.get32());
reed@android.com8a1c16f2008-12-17 15:59:43 +000055 mm->fRefCnt = 1;
56 mm->fLevelCount = levelCount;
57 return mm;
58 }
59
60 const MipLevel* levels() const { return (const MipLevel*)(this + 1); }
61 MipLevel* levels() { return (MipLevel*)(this + 1); }
62
63 const void* pixels() const { return levels() + fLevelCount; }
64 void* pixels() { return levels() + fLevelCount; }
weita@google.comf9ab99a2009-05-03 18:23:30 +000065
reed@android.com149e2f62009-05-22 14:39:03 +000066 void ref() {
67 if (SK_MaxS32 == sk_atomic_inc(&fRefCnt)) {
68 sk_throw();
reed@android.com8a1c16f2008-12-17 15:59:43 +000069 }
70 }
reed@android.com149e2f62009-05-22 14:39:03 +000071 void unref() {
72 SkASSERT(fRefCnt > 0);
73 if (sk_atomic_dec(&fRefCnt) == 1) {
74 sk_free(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +000075 }
76 }
77};
reed@android.com8a1c16f2008-12-17 15:59:43 +000078
79///////////////////////////////////////////////////////////////////////////////
80///////////////////////////////////////////////////////////////////////////////
81
82SkBitmap::SkBitmap() {
reed@android.com4516f472009-06-29 16:25:36 +000083 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000084}
85
86SkBitmap::SkBitmap(const SkBitmap& src) {
87 SkDEBUGCODE(src.validate();)
reed@android.com4516f472009-06-29 16:25:36 +000088 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000089 *this = src;
90 SkDEBUGCODE(this->validate();)
91}
92
93SkBitmap::~SkBitmap() {
94 SkDEBUGCODE(this->validate();)
95 this->freePixels();
96}
97
98SkBitmap& SkBitmap::operator=(const SkBitmap& src) {
99 if (this != &src) {
100 this->freePixels();
101 memcpy(this, &src, sizeof(src));
102
103 // inc src reference counts
reed@android.com83f7bc32009-07-17 02:42:41 +0000104 SkSafeRef(src.fPixelRef);
reed@android.com149e2f62009-05-22 14:39:03 +0000105 SkSafeRef(src.fMipMap);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106
107 // we reset our locks if we get blown away
108 fPixelLockCount = 0;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000109
reed@android.com8a1c16f2008-12-17 15:59:43 +0000110 /* The src could be in 3 states
111 1. no pixelref, in which case we just copy/ref the pixels/ctable
112 2. unlocked pixelref, pixels/ctable should be null
113 3. locked pixelref, we should lock the ref again ourselves
114 */
115 if (NULL == fPixelRef) {
116 // leave fPixels as it is
reed@google.com82065d62011-02-07 15:30:46 +0000117 SkSafeRef(fColorTable); // ref the user's ctable if present
reed@android.com8a1c16f2008-12-17 15:59:43 +0000118 } else { // we have a pixelref, so pixels/ctable reflect it
119 // ignore the values from the memcpy
120 fPixels = NULL;
121 fColorTable = NULL;
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000122 // Note that what to for genID is somewhat arbitrary. We have no
123 // way to track changes to raw pixels across multiple SkBitmaps.
124 // Would benefit from an SkRawPixelRef type created by
125 // setPixels.
126 // Just leave the memcpy'ed one but they'll get out of sync
127 // as soon either is modified.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000128 }
129 }
130
131 SkDEBUGCODE(this->validate();)
132 return *this;
133}
134
135void SkBitmap::swap(SkBitmap& other) {
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000136 SkTSwap(fColorTable, other.fColorTable);
137 SkTSwap(fPixelRef, other.fPixelRef);
138 SkTSwap(fPixelRefOffset, other.fPixelRefOffset);
139 SkTSwap(fPixelLockCount, other.fPixelLockCount);
140 SkTSwap(fMipMap, other.fMipMap);
141 SkTSwap(fPixels, other.fPixels);
djsollen@google.com56f8f332012-07-26 14:48:52 +0000142 SkTSwap(fRawPixelGenerationID, other.fRawPixelGenerationID);
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000143 SkTSwap(fRowBytes, other.fRowBytes);
144 SkTSwap(fWidth, other.fWidth);
145 SkTSwap(fHeight, other.fHeight);
146 SkTSwap(fConfig, other.fConfig);
147 SkTSwap(fFlags, other.fFlags);
148 SkTSwap(fBytesPerPixel, other.fBytesPerPixel);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149
150 SkDEBUGCODE(this->validate();)
151}
152
153void SkBitmap::reset() {
154 this->freePixels();
reed@android.com4516f472009-06-29 16:25:36 +0000155 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000156}
157
158int SkBitmap::ComputeBytesPerPixel(SkBitmap::Config config) {
159 int bpp;
160 switch (config) {
161 case kNo_Config:
162 case kA1_Config:
163 bpp = 0; // not applicable
164 break;
165 case kRLE_Index8_Config:
166 case kA8_Config:
167 case kIndex8_Config:
168 bpp = 1;
169 break;
170 case kRGB_565_Config:
171 case kARGB_4444_Config:
172 bpp = 2;
173 break;
174 case kARGB_8888_Config:
175 bpp = 4;
176 break;
177 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000178 SkDEBUGFAIL("unknown config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 bpp = 0; // error
180 break;
181 }
182 return bpp;
183}
184
185int SkBitmap::ComputeRowBytes(Config c, int width) {
reed@android.com149e2f62009-05-22 14:39:03 +0000186 if (width < 0) {
187 return 0;
188 }
189
190 Sk64 rowBytes;
191 rowBytes.setZero();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000192
193 switch (c) {
194 case kNo_Config:
195 case kRLE_Index8_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196 break;
197 case kA1_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000198 rowBytes.set(width);
199 rowBytes.add(7);
200 rowBytes.shiftRight(3);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201 break;
202 case kA8_Config:
203 case kIndex8_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000204 rowBytes.set(width);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205 break;
206 case kRGB_565_Config:
207 case kARGB_4444_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000208 rowBytes.set(width);
209 rowBytes.shiftLeft(1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210 break;
211 case kARGB_8888_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000212 rowBytes.set(width);
213 rowBytes.shiftLeft(2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214 break;
215 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000216 SkDEBUGFAIL("unknown config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 break;
218 }
reed@android.com149e2f62009-05-22 14:39:03 +0000219 return isPos32Bits(rowBytes) ? rowBytes.get32() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220}
221
222Sk64 SkBitmap::ComputeSize64(Config c, int width, int height) {
223 Sk64 size;
224 size.setMul(SkBitmap::ComputeRowBytes(c, width), height);
225 return size;
226}
227
228size_t SkBitmap::ComputeSize(Config c, int width, int height) {
229 Sk64 size = SkBitmap::ComputeSize64(c, width, height);
reed@android.com149e2f62009-05-22 14:39:03 +0000230 return isPos32Bits(size) ? size.get32() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231}
232
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000233Sk64 SkBitmap::ComputeSafeSize64(Config config,
234 uint32_t width,
235 uint32_t height,
236 uint32_t rowBytes) {
237 Sk64 safeSize;
238 safeSize.setZero();
239 if (height > 0) {
240 safeSize.set(ComputeRowBytes(config, width));
241 Sk64 sizeAllButLastRow;
242 sizeAllButLastRow.setMul(height - 1, rowBytes);
243 safeSize.add(sizeAllButLastRow);
244 }
245 SkASSERT(!safeSize.isNeg());
246 return safeSize;
247}
248
249size_t SkBitmap::ComputeSafeSize(Config config,
250 uint32_t width,
251 uint32_t height,
252 uint32_t rowBytes) {
253 Sk64 safeSize = ComputeSafeSize64(config, width, height, rowBytes);
254 return (safeSize.is32() ? safeSize.get32() : 0);
255}
256
reed@google.com86b2e432012-03-15 21:17:03 +0000257void SkBitmap::getBounds(SkRect* bounds) const {
258 SkASSERT(bounds);
259 bounds->set(0, 0,
260 SkIntToScalar(fWidth), SkIntToScalar(fHeight));
261}
262
reed@google.com80e14592012-03-16 14:58:07 +0000263void SkBitmap::getBounds(SkIRect* bounds) const {
264 SkASSERT(bounds);
265 bounds->set(0, 0, fWidth, fHeight);
266}
267
reed@google.com86b2e432012-03-15 21:17:03 +0000268///////////////////////////////////////////////////////////////////////////////
269
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270void SkBitmap::setConfig(Config c, int width, int height, int rowBytes) {
271 this->freePixels();
272
reed@android.com149e2f62009-05-22 14:39:03 +0000273 if ((width | height | rowBytes) < 0) {
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000274 goto err;
reed@android.com149e2f62009-05-22 14:39:03 +0000275 }
276
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277 if (rowBytes == 0) {
278 rowBytes = SkBitmap::ComputeRowBytes(c, width);
reed@android.com149e2f62009-05-22 14:39:03 +0000279 if (0 == rowBytes && kNo_Config != c) {
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000280 goto err;
reed@android.com149e2f62009-05-22 14:39:03 +0000281 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 }
reed@android.com89bb83a2009-05-29 21:30:42 +0000283
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 fConfig = SkToU8(c);
reed@android.comf459a492009-03-27 12:33:50 +0000285 fWidth = width;
286 fHeight = height;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 fRowBytes = rowBytes;
288
289 fBytesPerPixel = (uint8_t)ComputeBytesPerPixel(c);
290
291 SkDEBUGCODE(this->validate();)
reed@android.com9e0c2fc2009-06-02 18:54:28 +0000292 return;
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000293
reed@android.com9e0c2fc2009-06-02 18:54:28 +0000294 // if we got here, we had an error, so we reset the bitmap to empty
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000295err:
296 this->reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297}
298
299void SkBitmap::updatePixelsFromRef() const {
300 if (NULL != fPixelRef) {
301 if (fPixelLockCount > 0) {
reed@google.comff0da4f2012-05-17 13:14:52 +0000302 SkASSERT(fPixelRef->isLocked());
weita@google.comf9ab99a2009-05-03 18:23:30 +0000303
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304 void* p = fPixelRef->pixels();
305 if (NULL != p) {
306 p = (char*)p + fPixelRefOffset;
307 }
308 fPixels = p;
309 SkRefCnt_SafeAssign(fColorTable, fPixelRef->colorTable());
310 } else {
311 SkASSERT(0 == fPixelLockCount);
312 fPixels = NULL;
reed@android.com149e2f62009-05-22 14:39:03 +0000313 if (fColorTable) {
314 fColorTable->unref();
315 fColorTable = NULL;
316 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317 }
318 }
319}
320
321SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, size_t offset) {
322 // do this first, we that we never have a non-zero offset with a null ref
323 if (NULL == pr) {
324 offset = 0;
325 }
326
327 if (fPixelRef != pr || fPixelRefOffset != offset) {
328 if (fPixelRef != pr) {
329 this->freePixels();
330 SkASSERT(NULL == fPixelRef);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000331
reed@google.com82065d62011-02-07 15:30:46 +0000332 SkSafeRef(pr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333 fPixelRef = pr;
334 }
335 fPixelRefOffset = offset;
336 this->updatePixelsFromRef();
337 }
338
339 SkDEBUGCODE(this->validate();)
340 return pr;
341}
342
343void SkBitmap::lockPixels() const {
344 if (NULL != fPixelRef && 1 == ++fPixelLockCount) {
345 fPixelRef->lockPixels();
346 this->updatePixelsFromRef();
347 }
348 SkDEBUGCODE(this->validate();)
349}
350
351void SkBitmap::unlockPixels() const {
352 SkASSERT(NULL == fPixelRef || fPixelLockCount > 0);
353
354 if (NULL != fPixelRef && 0 == --fPixelLockCount) {
355 fPixelRef->unlockPixels();
356 this->updatePixelsFromRef();
357 }
358 SkDEBUGCODE(this->validate();)
359}
360
reed@google.com9c49bc32011-07-07 13:42:37 +0000361bool SkBitmap::lockPixelsAreWritable() const {
djsollen@google.com56f8f332012-07-26 14:48:52 +0000362 if (fPixelRef) {
363 return fPixelRef->lockPixelsAreWritable();
364 } else {
365 return fPixels != NULL;
366 }
reed@google.com9c49bc32011-07-07 13:42:37 +0000367}
368
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
djsollen@google.com56f8f332012-07-26 14:48:52 +0000370 this->freePixels();
371 fPixels = p;
372 SkRefCnt_SafeAssign(fColorTable, ctable);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000373
374 SkDEBUGCODE(this->validate();)
375}
376
377bool SkBitmap::allocPixels(Allocator* allocator, SkColorTable* ctable) {
378 HeapAllocator stdalloc;
379
380 if (NULL == allocator) {
381 allocator = &stdalloc;
382 }
383 return allocator->allocPixelRef(this, ctable);
384}
385
386void SkBitmap::freePixels() {
387 // if we're gonna free the pixels, we certainly need to free the mipmap
388 this->freeMipMap();
389
reed@android.com149e2f62009-05-22 14:39:03 +0000390 if (fColorTable) {
391 fColorTable->unref();
392 fColorTable = NULL;
393 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000394
395 if (NULL != fPixelRef) {
396 if (fPixelLockCount > 0) {
397 fPixelRef->unlockPixels();
398 }
399 fPixelRef->unref();
400 fPixelRef = NULL;
401 fPixelRefOffset = 0;
402 }
403 fPixelLockCount = 0;
404 fPixels = NULL;
405}
406
407void SkBitmap::freeMipMap() {
reed@android.com149e2f62009-05-22 14:39:03 +0000408 if (fMipMap) {
409 fMipMap->unref();
410 fMipMap = NULL;
411 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000412}
413
414uint32_t SkBitmap::getGenerationID() const {
djsollen@google.com56f8f332012-07-26 14:48:52 +0000415 if (fPixelRef) {
416 return fPixelRef->getGenerationID();
417 } else {
418 SkASSERT(fPixels || !fRawPixelGenerationID);
419 if (fPixels && !fRawPixelGenerationID) {
420 fRawPixelGenerationID = SkNextPixelRefGenerationID();
421 }
422 return fRawPixelGenerationID;
423 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000424}
425
426void SkBitmap::notifyPixelsChanged() const {
junov@chromium.orgb0521292011-12-15 20:14:06 +0000427 SkASSERT(!this->isImmutable());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000428 if (fPixelRef) {
429 fPixelRef->notifyPixelsChanged();
djsollen@google.com56f8f332012-07-26 14:48:52 +0000430 } else {
431 fRawPixelGenerationID = 0; // will grab next ID in getGenerationID
reed@android.com8a1c16f2008-12-17 15:59:43 +0000432 }
433}
434
reed@android.comce4e53a2010-09-09 16:01:26 +0000435SkGpuTexture* SkBitmap::getTexture() const {
436 return fPixelRef ? fPixelRef->getTexture() : NULL;
437}
438
reed@android.com8a1c16f2008-12-17 15:59:43 +0000439///////////////////////////////////////////////////////////////////////////////
440
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441/** We explicitly use the same allocator for our pixels that SkMask does,
442 so that we can freely assign memory allocated by one class to the other.
443 */
444bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
445 SkColorTable* ctable) {
446 Sk64 size = dst->getSize64();
447 if (size.isNeg() || !size.is32()) {
448 return false;
449 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000450
reed@android.com8a1c16f2008-12-17 15:59:43 +0000451 void* addr = sk_malloc_flags(size.get32(), 0); // returns NULL on failure
452 if (NULL == addr) {
453 return false;
454 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000455
reed@android.com8a1c16f2008-12-17 15:59:43 +0000456 dst->setPixelRef(new SkMallocPixelRef(addr, size.get32(), ctable))->unref();
457 // since we're already allocated, we lockPixels right away
458 dst->lockPixels();
459 return true;
460}
461
462///////////////////////////////////////////////////////////////////////////////
463
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000464size_t SkBitmap::getSafeSize() const {
465 // This is intended to be a size_t version of ComputeSafeSize64(), just
466 // faster. The computation is meant to be identical.
467 return (fHeight ? ((fHeight - 1) * fRowBytes) +
468 ComputeRowBytes(getConfig(), fWidth): 0);
469}
470
471Sk64 SkBitmap::getSafeSize64() const {
472 return ComputeSafeSize64(getConfig(), fWidth, fHeight, fRowBytes);
473}
474
bsalomon@google.comc6980972011-11-02 19:57:21 +0000475bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize,
476 int dstRowBytes, bool preserveDstPad) const {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000477
478 if (dstRowBytes == -1)
479 dstRowBytes = fRowBytes;
480 SkASSERT(dstRowBytes >= 0);
481
482 if (getConfig() == kRLE_Index8_Config ||
483 dstRowBytes < ComputeRowBytes(getConfig(), fWidth) ||
484 dst == NULL || (getPixels() == NULL && pixelRef() == NULL))
485 return false;
486
bsalomon@google.comc6980972011-11-02 19:57:21 +0000487 if (!preserveDstPad && static_cast<uint32_t>(dstRowBytes) == fRowBytes) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000488 size_t safeSize = getSafeSize();
489 if (safeSize > dstSize || safeSize == 0)
490 return false;
491 else {
492 SkAutoLockPixels lock(*this);
493 // This implementation will write bytes beyond the end of each row,
494 // excluding the last row, if the bitmap's stride is greater than
495 // strictly required by the current config.
496 memcpy(dst, getPixels(), safeSize);
497
498 return true;
499 }
500 } else {
501 // If destination has different stride than us, then copy line by line.
502 if (ComputeSafeSize(getConfig(), fWidth, fHeight, dstRowBytes) >
503 dstSize)
504 return false;
505 else {
506 // Just copy what we need on each line.
507 uint32_t rowBytes = ComputeRowBytes(getConfig(), fWidth);
508 SkAutoLockPixels lock(*this);
509 const uint8_t* srcP = reinterpret_cast<const uint8_t*>(getPixels());
510 uint8_t* dstP = reinterpret_cast<uint8_t*>(dst);
511 for (uint32_t row = 0; row < fHeight;
512 row++, srcP += fRowBytes, dstP += dstRowBytes) {
513 memcpy(dstP, srcP, rowBytes);
514 }
515
516 return true;
517 }
518 }
519}
520
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000521///////////////////////////////////////////////////////////////////////////////
522
junov@chromium.orgb0521292011-12-15 20:14:06 +0000523bool SkBitmap::isImmutable() const {
524 return fPixelRef ? fPixelRef->isImmutable() :
525 fFlags & kImageIsImmutable_Flag;
526}
527
528void SkBitmap::setImmutable() {
529 if (fPixelRef) {
530 fPixelRef->setImmutable();
531 } else {
532 fFlags |= kImageIsImmutable_Flag;
533 }
534}
535
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536bool SkBitmap::isOpaque() const {
537 switch (fConfig) {
538 case kNo_Config:
539 return true;
540
541 case kA1_Config:
542 case kA8_Config:
543 case kARGB_4444_Config:
544 case kARGB_8888_Config:
545 return (fFlags & kImageIsOpaque_Flag) != 0;
546
547 case kIndex8_Config:
548 case kRLE_Index8_Config: {
549 uint32_t flags = 0;
550
551 this->lockPixels();
552 // if lockPixels failed, we may not have a ctable ptr
553 if (fColorTable) {
554 flags = fColorTable->getFlags();
555 }
556 this->unlockPixels();
557
558 return (flags & SkColorTable::kColorsAreOpaque_Flag) != 0;
559 }
560
561 case kRGB_565_Config:
562 return true;
563
564 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000565 SkDEBUGFAIL("unknown bitmap config pased to isOpaque");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000566 return false;
567 }
568}
569
570void SkBitmap::setIsOpaque(bool isOpaque) {
571 /* we record this regardless of fConfig, though it is ignored in
572 isOpaque() for configs that can't support per-pixel alpha.
573 */
574 if (isOpaque) {
575 fFlags |= kImageIsOpaque_Flag;
576 } else {
577 fFlags &= ~kImageIsOpaque_Flag;
578 }
579}
580
junov@google.com4ee7ae52011-06-30 17:30:49 +0000581bool SkBitmap::isVolatile() const {
582 return (fFlags & kImageIsVolatile_Flag) != 0;
583}
584
585void SkBitmap::setIsVolatile(bool isVolatile) {
586 if (isVolatile) {
587 fFlags |= kImageIsVolatile_Flag;
588 } else {
589 fFlags &= ~kImageIsVolatile_Flag;
590 }
591}
592
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593void* SkBitmap::getAddr(int x, int y) const {
594 SkASSERT((unsigned)x < (unsigned)this->width());
595 SkASSERT((unsigned)y < (unsigned)this->height());
596
597 char* base = (char*)this->getPixels();
598 if (base) {
599 base += y * this->rowBytes();
600 switch (this->config()) {
601 case SkBitmap::kARGB_8888_Config:
602 base += x << 2;
603 break;
604 case SkBitmap::kARGB_4444_Config:
605 case SkBitmap::kRGB_565_Config:
606 base += x << 1;
607 break;
608 case SkBitmap::kA8_Config:
609 case SkBitmap::kIndex8_Config:
610 base += x;
611 break;
612 case SkBitmap::kA1_Config:
613 base += x >> 3;
614 break;
615 case kRLE_Index8_Config:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000616 SkDEBUGFAIL("Can't return addr for kRLE_Index8_Config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000617 base = NULL;
618 break;
619 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000620 SkDEBUGFAIL("Can't return addr for config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 base = NULL;
622 break;
623 }
624 }
625 return base;
626}
627
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000628SkColor SkBitmap::getColor(int x, int y) const {
629 SkASSERT((unsigned)x < (unsigned)this->width());
630 SkASSERT((unsigned)y < (unsigned)this->height());
631
632 switch (this->config()) {
633 case SkBitmap::kA1_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000634 uint8_t* addr = this->getAddr1(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000635 uint8_t mask = 1 << (7 - (x % 8));
636 if (addr[0] & mask) {
637 return SK_ColorBLACK;
638 } else {
639 return 0;
640 }
641 }
642 case SkBitmap::kA8_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000643 uint8_t* addr = this->getAddr8(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000644 return SkColorSetA(0, addr[0]);
645 }
646 case SkBitmap::kIndex8_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000647 SkPMColor c = this->getIndex8Color(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000648 return SkUnPreMultiply::PMColorToColor(c);
649 }
650 case SkBitmap::kRGB_565_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000651 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000652 return SkPixel16ToColor(addr[0]);
653 }
654 case SkBitmap::kARGB_4444_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000655 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000656 SkPMColor c = SkPixel4444ToPixel32(addr[0]);
657 return SkUnPreMultiply::PMColorToColor(c);
658 }
659 case SkBitmap::kARGB_8888_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000660 uint32_t* addr = this->getAddr32(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000661 return SkUnPreMultiply::PMColorToColor(addr[0]);
662 }
663 case kRLE_Index8_Config: {
664 uint8_t dst;
665 const SkBitmap::RLEPixels* rle =
reed@google.com3b521d02011-04-29 11:53:41 +0000666 (const SkBitmap::RLEPixels*)this->getPixels();
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000667 SkPackBits::Unpack8(&dst, x, 1, rle->packedAtY(y));
668 return SkUnPreMultiply::PMColorToColor((*fColorTable)[dst]);
669 }
670 case kNo_Config:
671 case kConfigCount:
672 SkASSERT(false);
673 return 0;
674 }
675 SkASSERT(false); // Not reached.
676 return 0;
677}
678
reed@android.com8a1c16f2008-12-17 15:59:43 +0000679///////////////////////////////////////////////////////////////////////////////
680///////////////////////////////////////////////////////////////////////////////
681
682void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
683 SkDEBUGCODE(this->validate();)
684
685 if (0 == fWidth || 0 == fHeight ||
686 kNo_Config == fConfig || kIndex8_Config == fConfig) {
687 return;
688 }
689
690 SkAutoLockPixels alp(*this);
691 // perform this check after the lock call
692 if (!this->readyToDraw()) {
693 return;
694 }
695
696 int height = fHeight;
697 const int width = fWidth;
698 const int rowBytes = fRowBytes;
699
700 // make rgb premultiplied
701 if (255 != a) {
702 r = SkAlphaMul(r, a);
703 g = SkAlphaMul(g, a);
704 b = SkAlphaMul(b, a);
705 }
706
707 switch (fConfig) {
708 case kA1_Config: {
709 uint8_t* p = (uint8_t*)fPixels;
710 const int count = (width + 7) >> 3;
711 a = (a >> 7) ? 0xFF : 0;
712 SkASSERT(count <= rowBytes);
713 while (--height >= 0) {
714 memset(p, a, count);
715 p += rowBytes;
716 }
717 break;
718 }
719 case kA8_Config: {
720 uint8_t* p = (uint8_t*)fPixels;
721 while (--height >= 0) {
722 memset(p, a, width);
723 p += rowBytes;
724 }
725 break;
726 }
727 case kARGB_4444_Config:
728 case kRGB_565_Config: {
729 uint16_t* p = (uint16_t*)fPixels;
730 uint16_t v;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000731
reed@android.com8a1c16f2008-12-17 15:59:43 +0000732 if (kARGB_4444_Config == fConfig) {
733 v = SkPackARGB4444(a >> 4, r >> 4, g >> 4, b >> 4);
734 } else { // kRGB_565_Config
735 v = SkPackRGB16(r >> (8 - SK_R16_BITS), g >> (8 - SK_G16_BITS),
736 b >> (8 - SK_B16_BITS));
737 }
738 while (--height >= 0) {
739 sk_memset16(p, v, width);
740 p = (uint16_t*)((char*)p + rowBytes);
741 }
742 break;
743 }
744 case kARGB_8888_Config: {
745 uint32_t* p = (uint32_t*)fPixels;
746 uint32_t v = SkPackARGB32(a, r, g, b);
747
748 while (--height >= 0) {
749 sk_memset32(p, v, width);
750 p = (uint32_t*)((char*)p + rowBytes);
751 }
752 break;
753 }
754 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000755
reed@android.com8a1c16f2008-12-17 15:59:43 +0000756 this->notifyPixelsChanged();
757}
758
759//////////////////////////////////////////////////////////////////////////////////////
760//////////////////////////////////////////////////////////////////////////////////////
761
762#define SUB_OFFSET_FAILURE ((size_t)-1)
763
764static size_t getSubOffset(const SkBitmap& bm, int x, int y) {
765 SkASSERT((unsigned)x < (unsigned)bm.width());
766 SkASSERT((unsigned)y < (unsigned)bm.height());
weita@google.comf9ab99a2009-05-03 18:23:30 +0000767
reed@android.com8a1c16f2008-12-17 15:59:43 +0000768 switch (bm.getConfig()) {
769 case SkBitmap::kA8_Config:
770 case SkBitmap:: kIndex8_Config:
771 // x is fine as is for the calculation
772 break;
773
774 case SkBitmap::kRGB_565_Config:
775 case SkBitmap::kARGB_4444_Config:
776 x <<= 1;
777 break;
778
779 case SkBitmap::kARGB_8888_Config:
780 x <<= 2;
781 break;
782
783 case SkBitmap::kNo_Config:
784 case SkBitmap::kA1_Config:
785 default:
786 return SUB_OFFSET_FAILURE;
787 }
788 return y * bm.rowBytes() + x;
789}
790
791bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
792 SkDEBUGCODE(this->validate();)
793
djsollen@google.com56f8f332012-07-26 14:48:52 +0000794 if (NULL == result || (NULL == fPixelRef && NULL == fPixels)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000795 return false; // no src pixels
796 }
797
798 SkIRect srcRect, r;
799 srcRect.set(0, 0, this->width(), this->height());
800 if (!r.intersect(srcRect, subset)) {
801 return false; // r is empty (i.e. no intersection)
802 }
803
804 if (kRLE_Index8_Config == fConfig) {
805 SkAutoLockPixels alp(*this);
806 // don't call readyToDraw(), since we can operate w/o a colortable
807 // at this stage
808 if (this->getPixels() == NULL) {
809 return false;
810 }
811 SkBitmap bm;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000812
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813 bm.setConfig(kIndex8_Config, r.width(), r.height());
814 bm.allocPixels(this->getColorTable());
815 if (NULL == bm.getPixels()) {
816 return false;
817 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000818
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819 const RLEPixels* rle = (const RLEPixels*)this->getPixels();
820 uint8_t* dst = bm.getAddr8(0, 0);
821 const int width = bm.width();
822 const int rowBytes = bm.rowBytes();
weita@google.comf9ab99a2009-05-03 18:23:30 +0000823
reed@android.com8a1c16f2008-12-17 15:59:43 +0000824 for (int y = r.fTop; y < r.fBottom; y++) {
825 SkPackBits::Unpack8(dst, r.fLeft, width, rle->packedAtY(y));
826 dst += rowBytes;
827 }
828 result->swap(bm);
829 return true;
830 }
831
832 size_t offset = getSubOffset(*this, r.fLeft, r.fTop);
833 if (SUB_OFFSET_FAILURE == offset) {
834 return false; // config not supported
835 }
836
837 SkBitmap dst;
838 dst.setConfig(this->config(), r.width(), r.height(), this->rowBytes());
skyostil@google.com0eb75762012-01-16 10:45:53 +0000839 dst.setIsVolatile(this->isVolatile());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840
841 if (fPixelRef) {
842 // share the pixelref with a custom offset
843 dst.setPixelRef(fPixelRef, fPixelRefOffset + offset);
djsollen@google.com56f8f332012-07-26 14:48:52 +0000844 } else {
845 // share the pixels (owned by the caller)
846 dst.setPixels((char*)fPixels + offset, this->getColorTable());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000847 }
848 SkDEBUGCODE(dst.validate();)
849
850 // we know we're good, so commit to result
851 result->swap(dst);
852 return true;
853}
854
855///////////////////////////////////////////////////////////////////////////////
856
857#include "SkCanvas.h"
858#include "SkPaint.h"
859
reed@android.comfbaa88d2009-05-06 17:44:34 +0000860bool SkBitmap::canCopyTo(Config dstConfig) const {
861 if (this->getConfig() == kNo_Config) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862 return false;
863 }
864
reed@android.comfbaa88d2009-05-06 17:44:34 +0000865 bool sameConfigs = (this->config() == dstConfig);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000866 switch (dstConfig) {
867 case kA8_Config:
868 case kARGB_4444_Config:
869 case kRGB_565_Config:
870 case kARGB_8888_Config:
871 break;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000872 case kA1_Config:
873 case kIndex8_Config:
874 if (!sameConfigs) {
875 return false;
876 }
877 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000878 default:
879 return false;
880 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000881
882 // do not copy src if srcConfig == kA1_Config while dstConfig != kA1_Config
883 if (this->getConfig() == kA1_Config && !sameConfigs) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000884 return false;
885 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000886
reed@android.comfbaa88d2009-05-06 17:44:34 +0000887 return true;
888}
889
890bool SkBitmap::copyTo(SkBitmap* dst, Config dstConfig, Allocator* alloc) const {
891 if (!this->canCopyTo(dstConfig)) {
892 return false;
893 }
894
reed@google.com50dfa012011-04-01 19:05:36 +0000895 // if we have a texture, first get those pixels
896 SkBitmap tmpSrc;
897 const SkBitmap* src = this;
898
bsalomon@google.com669fdc42011-04-05 17:08:27 +0000899 if (fPixelRef && fPixelRef->readPixels(&tmpSrc)) {
reed@google.com50dfa012011-04-01 19:05:36 +0000900 SkASSERT(tmpSrc.width() == this->width());
901 SkASSERT(tmpSrc.height() == this->height());
902
903 // did we get lucky and we can just return tmpSrc?
904 if (tmpSrc.config() == dstConfig && NULL == alloc) {
905 dst->swap(tmpSrc);
906 return true;
907 }
908
909 // fall through to the raster case
910 src = &tmpSrc;
reed@android.comfbaa88d2009-05-06 17:44:34 +0000911 }
reed@android.com311c82d2009-05-05 23:13:23 +0000912
reed@google.com50dfa012011-04-01 19:05:36 +0000913 // we lock this now, since we may need its colortable
914 SkAutoLockPixels srclock(*src);
915 if (!src->readyToDraw()) {
916 return false;
917 }
918
919 SkBitmap tmpDst;
920 tmpDst.setConfig(dstConfig, src->width(), src->height());
921
weita@google.comf9ab99a2009-05-03 18:23:30 +0000922 // allocate colortable if srcConfig == kIndex8_Config
923 SkColorTable* ctable = (dstConfig == kIndex8_Config) ?
reed@google.com50dfa012011-04-01 19:05:36 +0000924 new SkColorTable(*src->getColorTable()) : NULL;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000925 SkAutoUnref au(ctable);
reed@google.com50dfa012011-04-01 19:05:36 +0000926 if (!tmpDst.allocPixels(alloc, ctable)) {
weita@google.comf9ab99a2009-05-03 18:23:30 +0000927 return false;
928 }
reed@google.com50dfa012011-04-01 19:05:36 +0000929
reed@google.com50dfa012011-04-01 19:05:36 +0000930 if (!tmpDst.readyToDraw()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000931 // allocator/lock failed
932 return false;
933 }
reed@google.com50dfa012011-04-01 19:05:36 +0000934
reed@android.comfbaa88d2009-05-06 17:44:34 +0000935 /* do memcpy for the same configs cases, else use drawing
weita@google.comf9ab99a2009-05-03 18:23:30 +0000936 */
reed@google.com50dfa012011-04-01 19:05:36 +0000937 if (src->config() == dstConfig) {
938 if (tmpDst.getSize() == src->getSize()) {
939 memcpy(tmpDst.getPixels(), src->getPixels(), src->getSafeSize());
reed@android.com311c82d2009-05-05 23:13:23 +0000940 } else {
reed@google.com50dfa012011-04-01 19:05:36 +0000941 const char* srcP = reinterpret_cast<const char*>(src->getPixels());
942 char* dstP = reinterpret_cast<char*>(tmpDst.getPixels());
reed@android.com311c82d2009-05-05 23:13:23 +0000943 // to be sure we don't read too much, only copy our logical pixels
reed@google.com50dfa012011-04-01 19:05:36 +0000944 size_t bytesToCopy = tmpDst.width() * tmpDst.bytesPerPixel();
945 for (int y = 0; y < tmpDst.height(); y++) {
reed@android.com311c82d2009-05-05 23:13:23 +0000946 memcpy(dstP, srcP, bytesToCopy);
reed@google.com50dfa012011-04-01 19:05:36 +0000947 srcP += src->rowBytes();
948 dstP += tmpDst.rowBytes();
reed@android.com311c82d2009-05-05 23:13:23 +0000949 }
950 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000951 } else {
952 // if the src has alpha, we have to clear the dst first
reed@google.com50dfa012011-04-01 19:05:36 +0000953 if (!src->isOpaque()) {
954 tmpDst.eraseColor(0);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000955 }
956
reed@google.com50dfa012011-04-01 19:05:36 +0000957 SkCanvas canvas(tmpDst);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000958 SkPaint paint;
959
960 paint.setDither(true);
reed@google.com50dfa012011-04-01 19:05:36 +0000961 canvas.drawBitmap(*src, 0, 0, &paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000962 }
963
reed@google.com50dfa012011-04-01 19:05:36 +0000964 tmpDst.setIsOpaque(src->isOpaque());
reed@android.comcafc9f92009-08-22 03:44:57 +0000965
reed@google.com50dfa012011-04-01 19:05:36 +0000966 dst->swap(tmpDst);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000967 return true;
968}
969
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +0000970bool SkBitmap::deepCopyTo(SkBitmap* dst, Config dstConfig) const {
971 if (!this->canCopyTo(dstConfig)) {
972 return false;
973 }
974
975 // If we have a PixelRef, and it supports deep copy, use it.
976 // Currently supported only by texture-backed bitmaps.
977 if (fPixelRef) {
978 SkPixelRef* pixelRef = fPixelRef->deepCopy(dstConfig);
979 if (pixelRef) {
980 dst->setConfig(dstConfig, fWidth, fHeight);
981 dst->setPixelRef(pixelRef)->unref();
982 return true;
983 }
984 }
985
986 if (this->getTexture()) {
987 return false;
988 } else {
989 return this->copyTo(dst, dstConfig, NULL);
990 }
991}
992
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993///////////////////////////////////////////////////////////////////////////////
994///////////////////////////////////////////////////////////////////////////////
995
996static void downsampleby2_proc32(SkBitmap* dst, int x, int y,
997 const SkBitmap& src) {
998 x <<= 1;
999 y <<= 1;
1000 const SkPMColor* p = src.getAddr32(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001001 const SkPMColor* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001002 SkPMColor c, ag, rb;
1003
1004 c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF;
1005 if (x < src.width() - 1) {
1006 p += 1;
1007 }
1008 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1009
reed@android.com829c83c2009-06-08 12:05:31 +00001010 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001011 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +00001012 p += src.rowBytes() >> 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001013 }
1014 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1015 if (x < src.width() - 1) {
1016 p += 1;
1017 }
1018 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1019
1020 *dst->getAddr32(x >> 1, y >> 1) =
1021 ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00);
1022}
1023
1024static inline uint32_t expand16(U16CPU c) {
1025 return (c & ~SK_G16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16);
1026}
1027
1028// returns dirt in the top 16bits, but we don't care, since we only
1029// store the low 16bits.
1030static inline U16CPU pack16(uint32_t c) {
1031 return (c & ~SK_G16_MASK_IN_PLACE) | ((c >> 16) & SK_G16_MASK_IN_PLACE);
1032}
1033
1034static void downsampleby2_proc16(SkBitmap* dst, int x, int y,
1035 const SkBitmap& src) {
1036 x <<= 1;
1037 y <<= 1;
1038 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001039 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001040 SkPMColor c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001041
reed@android.com8a1c16f2008-12-17 15:59:43 +00001042 c = expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001043 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001044 p += 1;
1045 }
1046 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001047
reed@android.com829c83c2009-06-08 12:05:31 +00001048 p = baseP;
1049 if (y < src.height() - 1) {
1050 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001051 }
1052 c += expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001053 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001054 p += 1;
1055 }
1056 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001057
reed@android.com8a1c16f2008-12-17 15:59:43 +00001058 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)pack16(c >> 2);
1059}
1060
1061static uint32_t expand4444(U16CPU c) {
1062 return (c & 0xF0F) | ((c & ~0xF0F) << 12);
1063}
1064
1065static U16CPU collaps4444(uint32_t c) {
1066 return (c & 0xF0F) | ((c >> 12) & ~0xF0F);
1067}
1068
1069static void downsampleby2_proc4444(SkBitmap* dst, int x, int y,
1070 const SkBitmap& src) {
1071 x <<= 1;
1072 y <<= 1;
1073 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001074 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001075 uint32_t c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001076
reed@android.com8a1c16f2008-12-17 15:59:43 +00001077 c = expand4444(*p);
1078 if (x < src.width() - 1) {
1079 p += 1;
1080 }
1081 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001082
reed@android.com829c83c2009-06-08 12:05:31 +00001083 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001084 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +00001085 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001086 }
1087 c += expand4444(*p);
1088 if (x < src.width() - 1) {
1089 p += 1;
1090 }
1091 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001092
reed@android.com8a1c16f2008-12-17 15:59:43 +00001093 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)collaps4444(c >> 2);
1094}
1095
1096void SkBitmap::buildMipMap(bool forceRebuild) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001097 if (forceRebuild)
1098 this->freeMipMap();
1099 else if (fMipMap)
1100 return; // we're already built
1101
1102 SkASSERT(NULL == fMipMap);
1103
1104 void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src);
1105
1106 const SkBitmap::Config config = this->getConfig();
1107
1108 switch (config) {
1109 case kARGB_8888_Config:
1110 proc = downsampleby2_proc32;
1111 break;
1112 case kRGB_565_Config:
1113 proc = downsampleby2_proc16;
1114 break;
1115 case kARGB_4444_Config:
1116 proc = downsampleby2_proc4444;
1117 break;
1118 case kIndex8_Config:
1119 case kA8_Config:
1120 default:
1121 return; // don't build mipmaps for these configs
1122 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001123
reed@android.com149e2f62009-05-22 14:39:03 +00001124 SkAutoLockPixels alp(*this);
1125 if (!this->readyToDraw()) {
1126 return;
1127 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001128
1129 // whip through our loop to compute the exact size needed
1130 size_t size = 0;
1131 int maxLevels = 0;
1132 {
reed@android.com149e2f62009-05-22 14:39:03 +00001133 int width = this->width();
1134 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001135 for (;;) {
1136 width >>= 1;
1137 height >>= 1;
1138 if (0 == width || 0 == height) {
1139 break;
1140 }
1141 size += ComputeRowBytes(config, width) * height;
1142 maxLevels += 1;
1143 }
1144 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001145
reed@android.com149e2f62009-05-22 14:39:03 +00001146 // nothing to build
reed@android.com8a1c16f2008-12-17 15:59:43 +00001147 if (0 == maxLevels) {
1148 return;
1149 }
1150
reed@android.com149e2f62009-05-22 14:39:03 +00001151 SkBitmap srcBM(*this);
1152 srcBM.lockPixels();
1153 if (!srcBM.readyToDraw()) {
1154 return;
1155 }
1156
1157 MipMap* mm = MipMap::Alloc(maxLevels, size);
1158 if (NULL == mm) {
1159 return;
1160 }
1161
reed@android.com8a1c16f2008-12-17 15:59:43 +00001162 MipLevel* level = mm->levels();
1163 uint8_t* addr = (uint8_t*)mm->pixels();
reed@android.com149e2f62009-05-22 14:39:03 +00001164 int width = this->width();
1165 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001166 unsigned rowBytes = this->rowBytes();
reed@android.com149e2f62009-05-22 14:39:03 +00001167 SkBitmap dstBM;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001168
1169 for (int i = 0; i < maxLevels; i++) {
1170 width >>= 1;
1171 height >>= 1;
1172 rowBytes = ComputeRowBytes(config, width);
1173
1174 level[i].fPixels = addr;
reed@android.comf459a492009-03-27 12:33:50 +00001175 level[i].fWidth = width;
1176 level[i].fHeight = height;
reed@android.com49f0ff22009-03-19 21:52:42 +00001177 level[i].fRowBytes = rowBytes;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178
1179 dstBM.setConfig(config, width, height, rowBytes);
1180 dstBM.setPixels(addr);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001181
bungeman@google.com7cf0e9e2012-07-25 16:09:10 +00001182 srcBM.lockPixels();
reed@android.com149e2f62009-05-22 14:39:03 +00001183 for (int y = 0; y < height; y++) {
1184 for (int x = 0; x < width; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001185 proc(&dstBM, x, y, srcBM);
1186 }
1187 }
bungeman@google.com7cf0e9e2012-07-25 16:09:10 +00001188 srcBM.unlockPixels();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001189
1190 srcBM = dstBM;
1191 addr += height * rowBytes;
1192 }
1193 SkASSERT(addr == (uint8_t*)mm->pixels() + size);
1194 fMipMap = mm;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001195}
1196
1197bool SkBitmap::hasMipMap() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001198 return fMipMap != NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001199}
1200
1201int SkBitmap::extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy) {
reed@android.com83f7bc32009-07-17 02:42:41 +00001202 if (NULL == fMipMap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001203 return 0;
reed@android.com83f7bc32009-07-17 02:42:41 +00001204 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001205
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206 int level = ComputeMipLevel(sx, sy) >> 16;
1207 SkASSERT(level >= 0);
1208 if (level <= 0) {
1209 return 0;
1210 }
1211
1212 if (level >= fMipMap->fLevelCount) {
1213 level = fMipMap->fLevelCount - 1;
1214 }
1215 if (dst) {
1216 const MipLevel& mip = fMipMap->levels()[level - 1];
1217 dst->setConfig((SkBitmap::Config)this->config(),
1218 mip.fWidth, mip.fHeight, mip.fRowBytes);
1219 dst->setPixels(mip.fPixels);
1220 }
1221 return level;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001222}
1223
1224SkFixed SkBitmap::ComputeMipLevel(SkFixed sx, SkFixed sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001225 sx = SkAbs32(sx);
1226 sy = SkAbs32(sy);
1227 if (sx < sy) {
1228 sx = sy;
1229 }
1230 if (sx < SK_Fixed1) {
1231 return 0;
1232 }
1233 int clz = SkCLZ(sx);
1234 SkASSERT(clz >= 1 && clz <= 15);
1235 return SkIntToFixed(15 - clz) + ((unsigned)(sx << (clz + 1)) >> 16);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001236}
1237
1238///////////////////////////////////////////////////////////////////////////////
1239
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +00001240static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241 int alphaRowBytes) {
1242 SkASSERT(alpha != NULL);
1243 SkASSERT(alphaRowBytes >= src.width());
1244
1245 SkBitmap::Config config = src.getConfig();
1246 int w = src.width();
1247 int h = src.height();
1248 int rb = src.rowBytes();
1249
reed@android.com1cdcb512009-08-24 19:11:00 +00001250 SkAutoLockPixels alp(src);
1251 if (!src.readyToDraw()) {
1252 // zero out the alpha buffer and return
1253 while (--h >= 0) {
1254 memset(alpha, 0, w);
1255 alpha += alphaRowBytes;
1256 }
1257 return false;
1258 }
reed@google.com82065d62011-02-07 15:30:46 +00001259
reed@android.com8a1c16f2008-12-17 15:59:43 +00001260 if (SkBitmap::kA8_Config == config && !src.isOpaque()) {
1261 const uint8_t* s = src.getAddr8(0, 0);
1262 while (--h >= 0) {
1263 memcpy(alpha, s, w);
1264 s += rb;
1265 alpha += alphaRowBytes;
1266 }
1267 } else if (SkBitmap::kARGB_8888_Config == config && !src.isOpaque()) {
1268 const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0);
1269 while (--h >= 0) {
1270 for (int x = 0; x < w; x++) {
1271 alpha[x] = SkGetPackedA32(s[x]);
1272 }
1273 s = (const SkPMColor*)((const char*)s + rb);
1274 alpha += alphaRowBytes;
1275 }
1276 } else if (SkBitmap::kARGB_4444_Config == config && !src.isOpaque()) {
1277 const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0);
1278 while (--h >= 0) {
1279 for (int x = 0; x < w; x++) {
1280 alpha[x] = SkPacked4444ToA32(s[x]);
1281 }
1282 s = (const SkPMColor16*)((const char*)s + rb);
1283 alpha += alphaRowBytes;
1284 }
1285 } else if (SkBitmap::kIndex8_Config == config && !src.isOpaque()) {
1286 SkColorTable* ct = src.getColorTable();
1287 if (ct) {
1288 const SkPMColor* SK_RESTRICT table = ct->lockColors();
1289 const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0);
1290 while (--h >= 0) {
1291 for (int x = 0; x < w; x++) {
1292 alpha[x] = SkGetPackedA32(table[s[x]]);
1293 }
1294 s += rb;
1295 alpha += alphaRowBytes;
1296 }
1297 ct->unlockColors(false);
1298 }
1299 } else { // src is opaque, so just fill alpha[] with 0xFF
1300 memset(alpha, 0xFF, h * alphaRowBytes);
1301 }
reed@android.com1cdcb512009-08-24 19:11:00 +00001302 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001303}
1304
1305#include "SkPaint.h"
1306#include "SkMaskFilter.h"
1307#include "SkMatrix.h"
1308
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001309bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
djsollen@google.com57f49692011-02-23 20:46:31 +00001310 Allocator *allocator, SkIPoint* offset) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001311 SkDEBUGCODE(this->validate();)
1312
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001313 SkBitmap tmpBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001314 SkMatrix identity;
1315 SkMask srcM, dstM;
1316
1317 srcM.fBounds.set(0, 0, this->width(), this->height());
1318 srcM.fRowBytes = SkAlign4(this->width());
1319 srcM.fFormat = SkMask::kA8_Format;
1320
1321 SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL;
1322
1323 // compute our (larger?) dst bounds if we have a filter
1324 if (NULL != filter) {
1325 identity.reset();
1326 srcM.fImage = NULL;
1327 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1328 goto NO_FILTER_CASE;
1329 }
1330 dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
1331 } else {
1332 NO_FILTER_CASE:
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001333 tmpBitmap.setConfig(SkBitmap::kA8_Config, this->width(), this->height(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001334 srcM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001335 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1336 // Allocation of pixels for alpha bitmap failed.
1337 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1338 tmpBitmap.width(), tmpBitmap.height());
1339 return false;
1340 }
1341 GetBitmapAlpha(*this, tmpBitmap.getAddr8(0, 0), srcM.fRowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001342 if (offset) {
1343 offset->set(0, 0);
1344 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001345 tmpBitmap.swap(*dst);
1346 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001347 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001348 srcM.fImage = SkMask::AllocImage(srcM.computeImageSize());
1349 SkAutoMaskFreeImage srcCleanup(srcM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001350
1351 GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
1352 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1353 goto NO_FILTER_CASE;
1354 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001355 SkAutoMaskFreeImage dstCleanup(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001356
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001357 tmpBitmap.setConfig(SkBitmap::kA8_Config, dstM.fBounds.width(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001358 dstM.fBounds.height(), dstM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001359 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1360 // Allocation of pixels for alpha bitmap failed.
1361 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1362 tmpBitmap.width(), tmpBitmap.height());
1363 return false;
1364 }
1365 memcpy(tmpBitmap.getPixels(), dstM.fImage, dstM.computeImageSize());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001366 if (offset) {
1367 offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
1368 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001369 SkDEBUGCODE(tmpBitmap.validate();)
1370
1371 tmpBitmap.swap(*dst);
1372 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001373}
1374
1375///////////////////////////////////////////////////////////////////////////////
1376
1377enum {
1378 SERIALIZE_PIXELTYPE_NONE,
djsollen@google.com56f8f332012-07-26 14:48:52 +00001379 SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE,
1380 SERIALIZE_PIXELTYPE_RAW_NO_CTABLE,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001381 SERIALIZE_PIXELTYPE_REF_DATA,
tomhudson@google.com1f902872012-06-01 13:15:47 +00001382 SERIALIZE_PIXELTYPE_REF_PTR
reed@android.com8a1c16f2008-12-17 15:59:43 +00001383};
1384
reed@android.com8a1c16f2008-12-17 15:59:43 +00001385/*
1386 It is tricky to know how much to flatten. If we don't have a pixelref (i.e.
1387 we just have pixels, then we can only flatten the pixels, or write out an
1388 empty bitmap.
weita@google.comf9ab99a2009-05-03 18:23:30 +00001389
reed@android.com8a1c16f2008-12-17 15:59:43 +00001390 With a pixelref, we still have the question of recognizing when two sitings
1391 of the same pixelref are the same, and when they are different. Perhaps we
1392 should look at the generationID and keep a record of that in some dictionary
1393 associated with the buffer. SkGLTextureCache does this sort of thing to know
1394 when to create a new texture.
1395*/
1396void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
1397 buffer.write32(fWidth);
1398 buffer.write32(fHeight);
1399 buffer.write32(fRowBytes);
1400 buffer.write8(fConfig);
1401 buffer.writeBool(this->isOpaque());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001402
reed@android.com8a1c16f2008-12-17 15:59:43 +00001403 /* If we are called in this mode, then it is up to the caller to manage
1404 the owner-counts on the pixelref, as we just record the ptr itself.
1405 */
1406 if (!buffer.persistBitmapPixels()) {
1407 if (fPixelRef) {
1408 buffer.write8(SERIALIZE_PIXELTYPE_REF_PTR);
1409 buffer.write32(fPixelRefOffset);
1410 buffer.writeRefCnt(fPixelRef);
1411 return;
1412 } else {
1413 // we ignore the non-persist request, since we don't have a ref
1414 // ... or we could just write an empty bitmap...
1415 // (true) will write an empty bitmap, (false) will flatten the pix
1416 if (true) {
1417 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1418 return;
1419 }
1420 }
1421 }
1422
1423 if (fPixelRef) {
djsollen@google.com5370cd92012-03-28 20:47:01 +00001424 if (fPixelRef->getFactory()) {
1425 buffer.write8(SERIALIZE_PIXELTYPE_REF_DATA);
1426 buffer.write32(fPixelRefOffset);
1427 buffer.writeFlattenable(fPixelRef);
1428 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001429 }
1430 // if we get here, we can't record the pixels
1431 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
djsollen@google.com56f8f332012-07-26 14:48:52 +00001432 } else if (fPixels) {
1433 if (fColorTable) {
1434 buffer.write8(SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE);
1435 buffer.writeFlattenable(fColorTable);
1436 } else {
1437 buffer.write8(SERIALIZE_PIXELTYPE_RAW_NO_CTABLE);
1438 }
1439 buffer.writePad(fPixels, this->getSafeSize());
1440 // There is no writeZeroPad() fcn, so write individual bytes.
1441 if (this->getSize() > this->getSafeSize()) {
1442 size_t deltaSize = this->getSize() - this->getSafeSize();
1443 // Need aligned pointer to write into due to internal implementa-
1444 // tion of SkWriter32.
1445 memset(buffer.reserve(SkAlign4(deltaSize)), 0, deltaSize);
1446 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001447 } else {
1448 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1449 }
1450}
1451
1452void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) {
1453 this->reset();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001454
reed@android.com8a1c16f2008-12-17 15:59:43 +00001455 int width = buffer.readInt();
1456 int height = buffer.readInt();
1457 int rowBytes = buffer.readInt();
1458 int config = buffer.readU8();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001459
reed@android.com8a1c16f2008-12-17 15:59:43 +00001460 this->setConfig((Config)config, width, height, rowBytes);
1461 this->setIsOpaque(buffer.readBool());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001462
reed@android.com8a1c16f2008-12-17 15:59:43 +00001463 int reftype = buffer.readU8();
1464 switch (reftype) {
1465 case SERIALIZE_PIXELTYPE_REF_PTR: {
1466 size_t offset = buffer.readU32();
1467 SkPixelRef* pr = (SkPixelRef*)buffer.readRefCnt();
1468 this->setPixelRef(pr, offset);
1469 break;
1470 }
1471 case SERIALIZE_PIXELTYPE_REF_DATA: {
1472 size_t offset = buffer.readU32();
djsollen@google.com5370cd92012-03-28 20:47:01 +00001473 SkPixelRef* pr = static_cast<SkPixelRef*>(buffer.readFlattenable());
reed@google.com82065d62011-02-07 15:30:46 +00001474 SkSafeUnref(this->setPixelRef(pr, offset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001475 break;
1476 }
djsollen@google.com56f8f332012-07-26 14:48:52 +00001477 case SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE:
1478 case SERIALIZE_PIXELTYPE_RAW_NO_CTABLE: {
1479 SkColorTable* ctable = NULL;
1480 if (SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE == reftype) {
1481 ctable = static_cast<SkColorTable*>(buffer.readFlattenable());
1482 }
1483 size_t size = this->getSize();
1484 if (this->allocPixels(ctable)) {
1485 this->lockPixels();
1486 // Just read what we need.
1487 buffer.read(this->getPixels(), this->getSafeSize());
1488 // Keep aligned for subsequent reads.
1489 buffer.skip(size - this->getSafeSize());
1490 this->unlockPixels();
1491 } else {
1492 buffer.skip(size); // Still skip the full-sized buffer though.
1493 }
1494 SkSafeUnref(ctable);
1495 break;
1496 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001497 case SERIALIZE_PIXELTYPE_NONE:
1498 break;
1499 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001500 SkDEBUGFAIL("unrecognized pixeltype in serialized data");
reed@android.com8a1c16f2008-12-17 15:59:43 +00001501 sk_throw();
1502 }
1503}
1504
1505///////////////////////////////////////////////////////////////////////////////
1506
1507SkBitmap::RLEPixels::RLEPixels(int width, int height) {
1508 fHeight = height;
1509 fYPtrs = (uint8_t**)sk_malloc_throw(height * sizeof(uint8_t*));
reed@android.com4516f472009-06-29 16:25:36 +00001510 sk_bzero(fYPtrs, height * sizeof(uint8_t*));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001511}
1512
1513SkBitmap::RLEPixels::~RLEPixels() {
1514 sk_free(fYPtrs);
1515}
1516
1517///////////////////////////////////////////////////////////////////////////////
1518
1519#ifdef SK_DEBUG
1520void SkBitmap::validate() const {
1521 SkASSERT(fConfig < kConfigCount);
1522 SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
junov@google.com4ee7ae52011-06-30 17:30:49 +00001523 SkASSERT(fFlags <= (kImageIsOpaque_Flag | kImageIsVolatile_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001524 SkASSERT(fPixelLockCount >= 0);
1525 SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000);
1526 SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
1527
1528#if 0 // these asserts are not thread-correct, so disable for now
1529 if (fPixelRef) {
1530 if (fPixelLockCount > 0) {
reed@google.comff0da4f2012-05-17 13:14:52 +00001531 SkASSERT(fPixelRef->isLocked());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001532 } else {
1533 SkASSERT(NULL == fPixels);
1534 SkASSERT(NULL == fColorTable);
1535 }
1536 }
1537#endif
1538}
1539#endif