blob: 687cf7a000a6c24b01060f07b0f2c3ffc2403933 [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
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);
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000140 SkTSwap(fRowBytes, other.fRowBytes);
141 SkTSwap(fWidth, other.fWidth);
142 SkTSwap(fHeight, other.fHeight);
143 SkTSwap(fConfig, other.fConfig);
144 SkTSwap(fFlags, other.fFlags);
145 SkTSwap(fBytesPerPixel, other.fBytesPerPixel);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000146
147 SkDEBUGCODE(this->validate();)
148}
149
150void SkBitmap::reset() {
151 this->freePixels();
reed@android.com4516f472009-06-29 16:25:36 +0000152 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153}
154
155int SkBitmap::ComputeBytesPerPixel(SkBitmap::Config config) {
156 int bpp;
157 switch (config) {
158 case kNo_Config:
159 case kA1_Config:
160 bpp = 0; // not applicable
161 break;
162 case kRLE_Index8_Config:
163 case kA8_Config:
164 case kIndex8_Config:
165 bpp = 1;
166 break;
167 case kRGB_565_Config:
168 case kARGB_4444_Config:
169 bpp = 2;
170 break;
171 case kARGB_8888_Config:
172 bpp = 4;
173 break;
174 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000175 SkDEBUGFAIL("unknown config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176 bpp = 0; // error
177 break;
178 }
179 return bpp;
180}
181
182int SkBitmap::ComputeRowBytes(Config c, int width) {
reed@android.com149e2f62009-05-22 14:39:03 +0000183 if (width < 0) {
184 return 0;
185 }
186
187 Sk64 rowBytes;
188 rowBytes.setZero();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189
190 switch (c) {
191 case kNo_Config:
192 case kRLE_Index8_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193 break;
194 case kA1_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000195 rowBytes.set(width);
196 rowBytes.add(7);
197 rowBytes.shiftRight(3);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000198 break;
199 case kA8_Config:
200 case kIndex8_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000201 rowBytes.set(width);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202 break;
203 case kRGB_565_Config:
204 case kARGB_4444_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000205 rowBytes.set(width);
206 rowBytes.shiftLeft(1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207 break;
208 case kARGB_8888_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000209 rowBytes.set(width);
210 rowBytes.shiftLeft(2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 break;
212 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000213 SkDEBUGFAIL("unknown config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214 break;
215 }
reed@android.com149e2f62009-05-22 14:39:03 +0000216 return isPos32Bits(rowBytes) ? rowBytes.get32() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217}
218
219Sk64 SkBitmap::ComputeSize64(Config c, int width, int height) {
220 Sk64 size;
221 size.setMul(SkBitmap::ComputeRowBytes(c, width), height);
222 return size;
223}
224
225size_t SkBitmap::ComputeSize(Config c, int width, int height) {
226 Sk64 size = SkBitmap::ComputeSize64(c, width, height);
reed@android.com149e2f62009-05-22 14:39:03 +0000227 return isPos32Bits(size) ? size.get32() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000228}
229
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000230Sk64 SkBitmap::ComputeSafeSize64(Config config,
231 uint32_t width,
232 uint32_t height,
233 uint32_t rowBytes) {
234 Sk64 safeSize;
235 safeSize.setZero();
236 if (height > 0) {
237 safeSize.set(ComputeRowBytes(config, width));
238 Sk64 sizeAllButLastRow;
239 sizeAllButLastRow.setMul(height - 1, rowBytes);
240 safeSize.add(sizeAllButLastRow);
241 }
242 SkASSERT(!safeSize.isNeg());
243 return safeSize;
244}
245
246size_t SkBitmap::ComputeSafeSize(Config config,
247 uint32_t width,
248 uint32_t height,
249 uint32_t rowBytes) {
250 Sk64 safeSize = ComputeSafeSize64(config, width, height, rowBytes);
251 return (safeSize.is32() ? safeSize.get32() : 0);
252}
253
reed@google.com86b2e432012-03-15 21:17:03 +0000254void SkBitmap::getBounds(SkRect* bounds) const {
255 SkASSERT(bounds);
256 bounds->set(0, 0,
257 SkIntToScalar(fWidth), SkIntToScalar(fHeight));
258}
259
reed@google.com80e14592012-03-16 14:58:07 +0000260void SkBitmap::getBounds(SkIRect* bounds) const {
261 SkASSERT(bounds);
262 bounds->set(0, 0, fWidth, fHeight);
263}
264
reed@google.com86b2e432012-03-15 21:17:03 +0000265///////////////////////////////////////////////////////////////////////////////
266
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267void SkBitmap::setConfig(Config c, int width, int height, int rowBytes) {
268 this->freePixels();
269
reed@android.com149e2f62009-05-22 14:39:03 +0000270 if ((width | height | rowBytes) < 0) {
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000271 goto err;
reed@android.com149e2f62009-05-22 14:39:03 +0000272 }
273
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274 if (rowBytes == 0) {
275 rowBytes = SkBitmap::ComputeRowBytes(c, width);
reed@android.com149e2f62009-05-22 14:39:03 +0000276 if (0 == rowBytes && kNo_Config != c) {
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000277 goto err;
reed@android.com149e2f62009-05-22 14:39:03 +0000278 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 }
reed@android.com89bb83a2009-05-29 21:30:42 +0000280
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 fConfig = SkToU8(c);
reed@android.comf459a492009-03-27 12:33:50 +0000282 fWidth = width;
283 fHeight = height;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 fRowBytes = rowBytes;
285
286 fBytesPerPixel = (uint8_t)ComputeBytesPerPixel(c);
287
288 SkDEBUGCODE(this->validate();)
reed@android.com9e0c2fc2009-06-02 18:54:28 +0000289 return;
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000290
reed@android.com9e0c2fc2009-06-02 18:54:28 +0000291 // if we got here, we had an error, so we reset the bitmap to empty
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000292err:
293 this->reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294}
295
296void SkBitmap::updatePixelsFromRef() const {
297 if (NULL != fPixelRef) {
298 if (fPixelLockCount > 0) {
reed@google.comff0da4f2012-05-17 13:14:52 +0000299 SkASSERT(fPixelRef->isLocked());
weita@google.comf9ab99a2009-05-03 18:23:30 +0000300
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301 void* p = fPixelRef->pixels();
302 if (NULL != p) {
303 p = (char*)p + fPixelRefOffset;
304 }
305 fPixels = p;
306 SkRefCnt_SafeAssign(fColorTable, fPixelRef->colorTable());
307 } else {
308 SkASSERT(0 == fPixelLockCount);
309 fPixels = NULL;
reed@android.com149e2f62009-05-22 14:39:03 +0000310 if (fColorTable) {
311 fColorTable->unref();
312 fColorTable = NULL;
313 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314 }
315 }
316}
317
318SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, size_t offset) {
319 // do this first, we that we never have a non-zero offset with a null ref
320 if (NULL == pr) {
321 offset = 0;
322 }
323
324 if (fPixelRef != pr || fPixelRefOffset != offset) {
325 if (fPixelRef != pr) {
326 this->freePixels();
327 SkASSERT(NULL == fPixelRef);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000328
reed@google.com82065d62011-02-07 15:30:46 +0000329 SkSafeRef(pr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000330 fPixelRef = pr;
331 }
332 fPixelRefOffset = offset;
333 this->updatePixelsFromRef();
334 }
335
336 SkDEBUGCODE(this->validate();)
337 return pr;
338}
339
340void SkBitmap::lockPixels() const {
341 if (NULL != fPixelRef && 1 == ++fPixelLockCount) {
342 fPixelRef->lockPixels();
343 this->updatePixelsFromRef();
344 }
345 SkDEBUGCODE(this->validate();)
346}
347
348void SkBitmap::unlockPixels() const {
349 SkASSERT(NULL == fPixelRef || fPixelLockCount > 0);
350
351 if (NULL != fPixelRef && 0 == --fPixelLockCount) {
352 fPixelRef->unlockPixels();
353 this->updatePixelsFromRef();
354 }
355 SkDEBUGCODE(this->validate();)
356}
357
reed@google.com9c49bc32011-07-07 13:42:37 +0000358bool SkBitmap::lockPixelsAreWritable() const {
djsollen@google.comc1d1f412012-07-23 16:54:30 +0000359 return (fPixelRef) ? fPixelRef->lockPixelsAreWritable() : false;
reed@google.com9c49bc32011-07-07 13:42:37 +0000360}
361
reed@android.com8a1c16f2008-12-17 15:59:43 +0000362void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
djsollen@google.comc1d1f412012-07-23 16:54:30 +0000363 Sk64 size = this->getSize64();
364 SkASSERT(!size.isNeg() && size.is32());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000365
djsollen@google.comc1d1f412012-07-23 16:54:30 +0000366 this->setPixelRef(new SkMallocPixelRef(p, size.get32(), ctable, false))->unref();
367 // since we're already allocated, we lockPixels right away
368 this->lockPixels();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369 SkDEBUGCODE(this->validate();)
370}
371
372bool SkBitmap::allocPixels(Allocator* allocator, SkColorTable* ctable) {
373 HeapAllocator stdalloc;
374
375 if (NULL == allocator) {
376 allocator = &stdalloc;
377 }
378 return allocator->allocPixelRef(this, ctable);
379}
380
381void SkBitmap::freePixels() {
382 // if we're gonna free the pixels, we certainly need to free the mipmap
383 this->freeMipMap();
384
reed@android.com149e2f62009-05-22 14:39:03 +0000385 if (fColorTable) {
386 fColorTable->unref();
387 fColorTable = NULL;
388 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000389
390 if (NULL != fPixelRef) {
391 if (fPixelLockCount > 0) {
392 fPixelRef->unlockPixels();
393 }
394 fPixelRef->unref();
395 fPixelRef = NULL;
396 fPixelRefOffset = 0;
397 }
398 fPixelLockCount = 0;
399 fPixels = NULL;
400}
401
402void SkBitmap::freeMipMap() {
reed@android.com149e2f62009-05-22 14:39:03 +0000403 if (fMipMap) {
404 fMipMap->unref();
405 fMipMap = NULL;
406 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000407}
408
409uint32_t SkBitmap::getGenerationID() const {
djsollen@google.comc1d1f412012-07-23 16:54:30 +0000410 return (fPixelRef) ? fPixelRef->getGenerationID() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000411}
412
413void SkBitmap::notifyPixelsChanged() const {
junov@chromium.orgb0521292011-12-15 20:14:06 +0000414 SkASSERT(!this->isImmutable());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000415 if (fPixelRef) {
416 fPixelRef->notifyPixelsChanged();
417 }
418}
419
reed@android.comce4e53a2010-09-09 16:01:26 +0000420SkGpuTexture* SkBitmap::getTexture() const {
421 return fPixelRef ? fPixelRef->getTexture() : NULL;
422}
423
reed@android.com8a1c16f2008-12-17 15:59:43 +0000424///////////////////////////////////////////////////////////////////////////////
425
reed@android.com8a1c16f2008-12-17 15:59:43 +0000426/** We explicitly use the same allocator for our pixels that SkMask does,
427 so that we can freely assign memory allocated by one class to the other.
428 */
429bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
430 SkColorTable* ctable) {
431 Sk64 size = dst->getSize64();
432 if (size.isNeg() || !size.is32()) {
433 return false;
434 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000435
reed@android.com8a1c16f2008-12-17 15:59:43 +0000436 void* addr = sk_malloc_flags(size.get32(), 0); // returns NULL on failure
437 if (NULL == addr) {
438 return false;
439 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000440
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441 dst->setPixelRef(new SkMallocPixelRef(addr, size.get32(), ctable))->unref();
442 // since we're already allocated, we lockPixels right away
443 dst->lockPixels();
444 return true;
445}
446
447///////////////////////////////////////////////////////////////////////////////
448
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000449size_t SkBitmap::getSafeSize() const {
450 // This is intended to be a size_t version of ComputeSafeSize64(), just
451 // faster. The computation is meant to be identical.
452 return (fHeight ? ((fHeight - 1) * fRowBytes) +
453 ComputeRowBytes(getConfig(), fWidth): 0);
454}
455
456Sk64 SkBitmap::getSafeSize64() const {
457 return ComputeSafeSize64(getConfig(), fWidth, fHeight, fRowBytes);
458}
459
bsalomon@google.comc6980972011-11-02 19:57:21 +0000460bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize,
461 int dstRowBytes, bool preserveDstPad) const {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000462
463 if (dstRowBytes == -1)
464 dstRowBytes = fRowBytes;
465 SkASSERT(dstRowBytes >= 0);
466
467 if (getConfig() == kRLE_Index8_Config ||
468 dstRowBytes < ComputeRowBytes(getConfig(), fWidth) ||
469 dst == NULL || (getPixels() == NULL && pixelRef() == NULL))
470 return false;
471
bsalomon@google.comc6980972011-11-02 19:57:21 +0000472 if (!preserveDstPad && static_cast<uint32_t>(dstRowBytes) == fRowBytes) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000473 size_t safeSize = getSafeSize();
474 if (safeSize > dstSize || safeSize == 0)
475 return false;
476 else {
477 SkAutoLockPixels lock(*this);
478 // This implementation will write bytes beyond the end of each row,
479 // excluding the last row, if the bitmap's stride is greater than
480 // strictly required by the current config.
481 memcpy(dst, getPixels(), safeSize);
482
483 return true;
484 }
485 } else {
486 // If destination has different stride than us, then copy line by line.
487 if (ComputeSafeSize(getConfig(), fWidth, fHeight, dstRowBytes) >
488 dstSize)
489 return false;
490 else {
491 // Just copy what we need on each line.
492 uint32_t rowBytes = ComputeRowBytes(getConfig(), fWidth);
493 SkAutoLockPixels lock(*this);
494 const uint8_t* srcP = reinterpret_cast<const uint8_t*>(getPixels());
495 uint8_t* dstP = reinterpret_cast<uint8_t*>(dst);
496 for (uint32_t row = 0; row < fHeight;
497 row++, srcP += fRowBytes, dstP += dstRowBytes) {
498 memcpy(dstP, srcP, rowBytes);
499 }
500
501 return true;
502 }
503 }
504}
505
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000506///////////////////////////////////////////////////////////////////////////////
507
junov@chromium.orgb0521292011-12-15 20:14:06 +0000508bool SkBitmap::isImmutable() const {
509 return fPixelRef ? fPixelRef->isImmutable() :
510 fFlags & kImageIsImmutable_Flag;
511}
512
513void SkBitmap::setImmutable() {
514 if (fPixelRef) {
515 fPixelRef->setImmutable();
516 } else {
517 fFlags |= kImageIsImmutable_Flag;
518 }
519}
520
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521bool SkBitmap::isOpaque() const {
522 switch (fConfig) {
523 case kNo_Config:
524 return true;
525
526 case kA1_Config:
527 case kA8_Config:
528 case kARGB_4444_Config:
529 case kARGB_8888_Config:
530 return (fFlags & kImageIsOpaque_Flag) != 0;
531
532 case kIndex8_Config:
533 case kRLE_Index8_Config: {
534 uint32_t flags = 0;
535
536 this->lockPixels();
537 // if lockPixels failed, we may not have a ctable ptr
538 if (fColorTable) {
539 flags = fColorTable->getFlags();
540 }
541 this->unlockPixels();
542
543 return (flags & SkColorTable::kColorsAreOpaque_Flag) != 0;
544 }
545
546 case kRGB_565_Config:
547 return true;
548
549 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000550 SkDEBUGFAIL("unknown bitmap config pased to isOpaque");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000551 return false;
552 }
553}
554
555void SkBitmap::setIsOpaque(bool isOpaque) {
556 /* we record this regardless of fConfig, though it is ignored in
557 isOpaque() for configs that can't support per-pixel alpha.
558 */
559 if (isOpaque) {
560 fFlags |= kImageIsOpaque_Flag;
561 } else {
562 fFlags &= ~kImageIsOpaque_Flag;
563 }
564}
565
junov@google.com4ee7ae52011-06-30 17:30:49 +0000566bool SkBitmap::isVolatile() const {
567 return (fFlags & kImageIsVolatile_Flag) != 0;
568}
569
570void SkBitmap::setIsVolatile(bool isVolatile) {
571 if (isVolatile) {
572 fFlags |= kImageIsVolatile_Flag;
573 } else {
574 fFlags &= ~kImageIsVolatile_Flag;
575 }
576}
577
reed@android.com8a1c16f2008-12-17 15:59:43 +0000578void* SkBitmap::getAddr(int x, int y) const {
579 SkASSERT((unsigned)x < (unsigned)this->width());
580 SkASSERT((unsigned)y < (unsigned)this->height());
581
582 char* base = (char*)this->getPixels();
583 if (base) {
584 base += y * this->rowBytes();
585 switch (this->config()) {
586 case SkBitmap::kARGB_8888_Config:
587 base += x << 2;
588 break;
589 case SkBitmap::kARGB_4444_Config:
590 case SkBitmap::kRGB_565_Config:
591 base += x << 1;
592 break;
593 case SkBitmap::kA8_Config:
594 case SkBitmap::kIndex8_Config:
595 base += x;
596 break;
597 case SkBitmap::kA1_Config:
598 base += x >> 3;
599 break;
600 case kRLE_Index8_Config:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000601 SkDEBUGFAIL("Can't return addr for kRLE_Index8_Config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602 base = NULL;
603 break;
604 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000605 SkDEBUGFAIL("Can't return addr for config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 base = NULL;
607 break;
608 }
609 }
610 return base;
611}
612
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000613SkColor SkBitmap::getColor(int x, int y) const {
614 SkASSERT((unsigned)x < (unsigned)this->width());
615 SkASSERT((unsigned)y < (unsigned)this->height());
616
617 switch (this->config()) {
618 case SkBitmap::kA1_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000619 uint8_t* addr = this->getAddr1(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000620 uint8_t mask = 1 << (7 - (x % 8));
621 if (addr[0] & mask) {
622 return SK_ColorBLACK;
623 } else {
624 return 0;
625 }
626 }
627 case SkBitmap::kA8_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000628 uint8_t* addr = this->getAddr8(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000629 return SkColorSetA(0, addr[0]);
630 }
631 case SkBitmap::kIndex8_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000632 SkPMColor c = this->getIndex8Color(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000633 return SkUnPreMultiply::PMColorToColor(c);
634 }
635 case SkBitmap::kRGB_565_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000636 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000637 return SkPixel16ToColor(addr[0]);
638 }
639 case SkBitmap::kARGB_4444_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000640 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000641 SkPMColor c = SkPixel4444ToPixel32(addr[0]);
642 return SkUnPreMultiply::PMColorToColor(c);
643 }
644 case SkBitmap::kARGB_8888_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000645 uint32_t* addr = this->getAddr32(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000646 return SkUnPreMultiply::PMColorToColor(addr[0]);
647 }
648 case kRLE_Index8_Config: {
649 uint8_t dst;
650 const SkBitmap::RLEPixels* rle =
reed@google.com3b521d02011-04-29 11:53:41 +0000651 (const SkBitmap::RLEPixels*)this->getPixels();
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000652 SkPackBits::Unpack8(&dst, x, 1, rle->packedAtY(y));
653 return SkUnPreMultiply::PMColorToColor((*fColorTable)[dst]);
654 }
655 case kNo_Config:
656 case kConfigCount:
657 SkASSERT(false);
658 return 0;
659 }
660 SkASSERT(false); // Not reached.
661 return 0;
662}
663
reed@android.com8a1c16f2008-12-17 15:59:43 +0000664///////////////////////////////////////////////////////////////////////////////
665///////////////////////////////////////////////////////////////////////////////
666
667void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
668 SkDEBUGCODE(this->validate();)
669
670 if (0 == fWidth || 0 == fHeight ||
671 kNo_Config == fConfig || kIndex8_Config == fConfig) {
672 return;
673 }
674
675 SkAutoLockPixels alp(*this);
676 // perform this check after the lock call
677 if (!this->readyToDraw()) {
678 return;
679 }
680
681 int height = fHeight;
682 const int width = fWidth;
683 const int rowBytes = fRowBytes;
684
685 // make rgb premultiplied
686 if (255 != a) {
687 r = SkAlphaMul(r, a);
688 g = SkAlphaMul(g, a);
689 b = SkAlphaMul(b, a);
690 }
691
692 switch (fConfig) {
693 case kA1_Config: {
694 uint8_t* p = (uint8_t*)fPixels;
695 const int count = (width + 7) >> 3;
696 a = (a >> 7) ? 0xFF : 0;
697 SkASSERT(count <= rowBytes);
698 while (--height >= 0) {
699 memset(p, a, count);
700 p += rowBytes;
701 }
702 break;
703 }
704 case kA8_Config: {
705 uint8_t* p = (uint8_t*)fPixels;
706 while (--height >= 0) {
707 memset(p, a, width);
708 p += rowBytes;
709 }
710 break;
711 }
712 case kARGB_4444_Config:
713 case kRGB_565_Config: {
714 uint16_t* p = (uint16_t*)fPixels;
715 uint16_t v;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000716
reed@android.com8a1c16f2008-12-17 15:59:43 +0000717 if (kARGB_4444_Config == fConfig) {
718 v = SkPackARGB4444(a >> 4, r >> 4, g >> 4, b >> 4);
719 } else { // kRGB_565_Config
720 v = SkPackRGB16(r >> (8 - SK_R16_BITS), g >> (8 - SK_G16_BITS),
721 b >> (8 - SK_B16_BITS));
722 }
723 while (--height >= 0) {
724 sk_memset16(p, v, width);
725 p = (uint16_t*)((char*)p + rowBytes);
726 }
727 break;
728 }
729 case kARGB_8888_Config: {
730 uint32_t* p = (uint32_t*)fPixels;
731 uint32_t v = SkPackARGB32(a, r, g, b);
732
733 while (--height >= 0) {
734 sk_memset32(p, v, width);
735 p = (uint32_t*)((char*)p + rowBytes);
736 }
737 break;
738 }
739 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000740
reed@android.com8a1c16f2008-12-17 15:59:43 +0000741 this->notifyPixelsChanged();
742}
743
744//////////////////////////////////////////////////////////////////////////////////////
745//////////////////////////////////////////////////////////////////////////////////////
746
747#define SUB_OFFSET_FAILURE ((size_t)-1)
748
749static size_t getSubOffset(const SkBitmap& bm, int x, int y) {
750 SkASSERT((unsigned)x < (unsigned)bm.width());
751 SkASSERT((unsigned)y < (unsigned)bm.height());
weita@google.comf9ab99a2009-05-03 18:23:30 +0000752
reed@android.com8a1c16f2008-12-17 15:59:43 +0000753 switch (bm.getConfig()) {
754 case SkBitmap::kA8_Config:
755 case SkBitmap:: kIndex8_Config:
756 // x is fine as is for the calculation
757 break;
758
759 case SkBitmap::kRGB_565_Config:
760 case SkBitmap::kARGB_4444_Config:
761 x <<= 1;
762 break;
763
764 case SkBitmap::kARGB_8888_Config:
765 x <<= 2;
766 break;
767
768 case SkBitmap::kNo_Config:
769 case SkBitmap::kA1_Config:
770 default:
771 return SUB_OFFSET_FAILURE;
772 }
773 return y * bm.rowBytes() + x;
774}
775
776bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
777 SkDEBUGCODE(this->validate();)
778
djsollen@google.comc1d1f412012-07-23 16:54:30 +0000779 if (NULL == result || NULL == fPixelRef) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780 return false; // no src pixels
781 }
782
783 SkIRect srcRect, r;
784 srcRect.set(0, 0, this->width(), this->height());
785 if (!r.intersect(srcRect, subset)) {
786 return false; // r is empty (i.e. no intersection)
787 }
788
789 if (kRLE_Index8_Config == fConfig) {
790 SkAutoLockPixels alp(*this);
791 // don't call readyToDraw(), since we can operate w/o a colortable
792 // at this stage
793 if (this->getPixels() == NULL) {
794 return false;
795 }
796 SkBitmap bm;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000797
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798 bm.setConfig(kIndex8_Config, r.width(), r.height());
799 bm.allocPixels(this->getColorTable());
800 if (NULL == bm.getPixels()) {
801 return false;
802 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000803
reed@android.com8a1c16f2008-12-17 15:59:43 +0000804 const RLEPixels* rle = (const RLEPixels*)this->getPixels();
805 uint8_t* dst = bm.getAddr8(0, 0);
806 const int width = bm.width();
807 const int rowBytes = bm.rowBytes();
weita@google.comf9ab99a2009-05-03 18:23:30 +0000808
reed@android.com8a1c16f2008-12-17 15:59:43 +0000809 for (int y = r.fTop; y < r.fBottom; y++) {
810 SkPackBits::Unpack8(dst, r.fLeft, width, rle->packedAtY(y));
811 dst += rowBytes;
812 }
813 result->swap(bm);
814 return true;
815 }
816
817 size_t offset = getSubOffset(*this, r.fLeft, r.fTop);
818 if (SUB_OFFSET_FAILURE == offset) {
819 return false; // config not supported
820 }
821
822 SkBitmap dst;
823 dst.setConfig(this->config(), r.width(), r.height(), this->rowBytes());
skyostil@google.com0eb75762012-01-16 10:45:53 +0000824 dst.setIsVolatile(this->isVolatile());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825
826 if (fPixelRef) {
827 // share the pixelref with a custom offset
828 dst.setPixelRef(fPixelRef, fPixelRefOffset + offset);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000829 }
830 SkDEBUGCODE(dst.validate();)
831
832 // we know we're good, so commit to result
833 result->swap(dst);
834 return true;
835}
836
837///////////////////////////////////////////////////////////////////////////////
838
839#include "SkCanvas.h"
840#include "SkPaint.h"
841
reed@android.comfbaa88d2009-05-06 17:44:34 +0000842bool SkBitmap::canCopyTo(Config dstConfig) const {
843 if (this->getConfig() == kNo_Config) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000844 return false;
845 }
846
reed@android.comfbaa88d2009-05-06 17:44:34 +0000847 bool sameConfigs = (this->config() == dstConfig);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000848 switch (dstConfig) {
849 case kA8_Config:
850 case kARGB_4444_Config:
851 case kRGB_565_Config:
852 case kARGB_8888_Config:
853 break;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000854 case kA1_Config:
855 case kIndex8_Config:
856 if (!sameConfigs) {
857 return false;
858 }
859 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000860 default:
861 return false;
862 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000863
864 // do not copy src if srcConfig == kA1_Config while dstConfig != kA1_Config
865 if (this->getConfig() == kA1_Config && !sameConfigs) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000866 return false;
867 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000868
reed@android.comfbaa88d2009-05-06 17:44:34 +0000869 return true;
870}
871
872bool SkBitmap::copyTo(SkBitmap* dst, Config dstConfig, Allocator* alloc) const {
873 if (!this->canCopyTo(dstConfig)) {
874 return false;
875 }
876
reed@google.com50dfa012011-04-01 19:05:36 +0000877 // if we have a texture, first get those pixels
878 SkBitmap tmpSrc;
879 const SkBitmap* src = this;
880
bsalomon@google.com669fdc42011-04-05 17:08:27 +0000881 if (fPixelRef && fPixelRef->readPixels(&tmpSrc)) {
reed@google.com50dfa012011-04-01 19:05:36 +0000882 SkASSERT(tmpSrc.width() == this->width());
883 SkASSERT(tmpSrc.height() == this->height());
884
885 // did we get lucky and we can just return tmpSrc?
886 if (tmpSrc.config() == dstConfig && NULL == alloc) {
887 dst->swap(tmpSrc);
888 return true;
889 }
890
891 // fall through to the raster case
892 src = &tmpSrc;
reed@android.comfbaa88d2009-05-06 17:44:34 +0000893 }
reed@android.com311c82d2009-05-05 23:13:23 +0000894
reed@google.com50dfa012011-04-01 19:05:36 +0000895 // we lock this now, since we may need its colortable
896 SkAutoLockPixels srclock(*src);
897 if (!src->readyToDraw()) {
898 return false;
899 }
900
901 SkBitmap tmpDst;
902 tmpDst.setConfig(dstConfig, src->width(), src->height());
903
weita@google.comf9ab99a2009-05-03 18:23:30 +0000904 // allocate colortable if srcConfig == kIndex8_Config
905 SkColorTable* ctable = (dstConfig == kIndex8_Config) ?
reed@google.com50dfa012011-04-01 19:05:36 +0000906 new SkColorTable(*src->getColorTable()) : NULL;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000907 SkAutoUnref au(ctable);
reed@google.com50dfa012011-04-01 19:05:36 +0000908 if (!tmpDst.allocPixels(alloc, ctable)) {
weita@google.comf9ab99a2009-05-03 18:23:30 +0000909 return false;
910 }
reed@google.com50dfa012011-04-01 19:05:36 +0000911
reed@google.com50dfa012011-04-01 19:05:36 +0000912 if (!tmpDst.readyToDraw()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000913 // allocator/lock failed
914 return false;
915 }
reed@google.com50dfa012011-04-01 19:05:36 +0000916
reed@android.comfbaa88d2009-05-06 17:44:34 +0000917 /* do memcpy for the same configs cases, else use drawing
weita@google.comf9ab99a2009-05-03 18:23:30 +0000918 */
reed@google.com50dfa012011-04-01 19:05:36 +0000919 if (src->config() == dstConfig) {
920 if (tmpDst.getSize() == src->getSize()) {
921 memcpy(tmpDst.getPixels(), src->getPixels(), src->getSafeSize());
reed@android.com311c82d2009-05-05 23:13:23 +0000922 } else {
reed@google.com50dfa012011-04-01 19:05:36 +0000923 const char* srcP = reinterpret_cast<const char*>(src->getPixels());
924 char* dstP = reinterpret_cast<char*>(tmpDst.getPixels());
reed@android.com311c82d2009-05-05 23:13:23 +0000925 // to be sure we don't read too much, only copy our logical pixels
reed@google.com50dfa012011-04-01 19:05:36 +0000926 size_t bytesToCopy = tmpDst.width() * tmpDst.bytesPerPixel();
927 for (int y = 0; y < tmpDst.height(); y++) {
reed@android.com311c82d2009-05-05 23:13:23 +0000928 memcpy(dstP, srcP, bytesToCopy);
reed@google.com50dfa012011-04-01 19:05:36 +0000929 srcP += src->rowBytes();
930 dstP += tmpDst.rowBytes();
reed@android.com311c82d2009-05-05 23:13:23 +0000931 }
932 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000933 } else {
934 // if the src has alpha, we have to clear the dst first
reed@google.com50dfa012011-04-01 19:05:36 +0000935 if (!src->isOpaque()) {
936 tmpDst.eraseColor(0);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000937 }
938
reed@google.com50dfa012011-04-01 19:05:36 +0000939 SkCanvas canvas(tmpDst);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000940 SkPaint paint;
941
942 paint.setDither(true);
reed@google.com50dfa012011-04-01 19:05:36 +0000943 canvas.drawBitmap(*src, 0, 0, &paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000944 }
945
reed@google.com50dfa012011-04-01 19:05:36 +0000946 tmpDst.setIsOpaque(src->isOpaque());
reed@android.comcafc9f92009-08-22 03:44:57 +0000947
reed@google.com50dfa012011-04-01 19:05:36 +0000948 dst->swap(tmpDst);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000949 return true;
950}
951
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +0000952bool SkBitmap::deepCopyTo(SkBitmap* dst, Config dstConfig) const {
953 if (!this->canCopyTo(dstConfig)) {
954 return false;
955 }
956
957 // If we have a PixelRef, and it supports deep copy, use it.
958 // Currently supported only by texture-backed bitmaps.
959 if (fPixelRef) {
960 SkPixelRef* pixelRef = fPixelRef->deepCopy(dstConfig);
961 if (pixelRef) {
962 dst->setConfig(dstConfig, fWidth, fHeight);
963 dst->setPixelRef(pixelRef)->unref();
964 return true;
965 }
966 }
967
968 if (this->getTexture()) {
969 return false;
970 } else {
971 return this->copyTo(dst, dstConfig, NULL);
972 }
973}
974
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975///////////////////////////////////////////////////////////////////////////////
976///////////////////////////////////////////////////////////////////////////////
977
978static void downsampleby2_proc32(SkBitmap* dst, int x, int y,
979 const SkBitmap& src) {
980 x <<= 1;
981 y <<= 1;
982 const SkPMColor* p = src.getAddr32(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +0000983 const SkPMColor* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000984 SkPMColor c, ag, rb;
985
986 c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF;
987 if (x < src.width() - 1) {
988 p += 1;
989 }
990 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
991
reed@android.com829c83c2009-06-08 12:05:31 +0000992 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +0000994 p += src.rowBytes() >> 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000995 }
996 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
997 if (x < src.width() - 1) {
998 p += 1;
999 }
1000 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1001
1002 *dst->getAddr32(x >> 1, y >> 1) =
1003 ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00);
1004}
1005
1006static inline uint32_t expand16(U16CPU c) {
1007 return (c & ~SK_G16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16);
1008}
1009
1010// returns dirt in the top 16bits, but we don't care, since we only
1011// store the low 16bits.
1012static inline U16CPU pack16(uint32_t c) {
1013 return (c & ~SK_G16_MASK_IN_PLACE) | ((c >> 16) & SK_G16_MASK_IN_PLACE);
1014}
1015
1016static void downsampleby2_proc16(SkBitmap* dst, int x, int y,
1017 const SkBitmap& src) {
1018 x <<= 1;
1019 y <<= 1;
1020 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001021 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001022 SkPMColor c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001023
reed@android.com8a1c16f2008-12-17 15:59:43 +00001024 c = expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001025 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001026 p += 1;
1027 }
1028 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001029
reed@android.com829c83c2009-06-08 12:05:31 +00001030 p = baseP;
1031 if (y < src.height() - 1) {
1032 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001033 }
1034 c += expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001035 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001036 p += 1;
1037 }
1038 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001039
reed@android.com8a1c16f2008-12-17 15:59:43 +00001040 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)pack16(c >> 2);
1041}
1042
1043static uint32_t expand4444(U16CPU c) {
1044 return (c & 0xF0F) | ((c & ~0xF0F) << 12);
1045}
1046
1047static U16CPU collaps4444(uint32_t c) {
1048 return (c & 0xF0F) | ((c >> 12) & ~0xF0F);
1049}
1050
1051static void downsampleby2_proc4444(SkBitmap* dst, int x, int y,
1052 const SkBitmap& src) {
1053 x <<= 1;
1054 y <<= 1;
1055 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001056 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001057 uint32_t c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001058
reed@android.com8a1c16f2008-12-17 15:59:43 +00001059 c = expand4444(*p);
1060 if (x < src.width() - 1) {
1061 p += 1;
1062 }
1063 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001064
reed@android.com829c83c2009-06-08 12:05:31 +00001065 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +00001067 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001068 }
1069 c += expand4444(*p);
1070 if (x < src.width() - 1) {
1071 p += 1;
1072 }
1073 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001074
reed@android.com8a1c16f2008-12-17 15:59:43 +00001075 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)collaps4444(c >> 2);
1076}
1077
1078void SkBitmap::buildMipMap(bool forceRebuild) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001079 if (forceRebuild)
1080 this->freeMipMap();
1081 else if (fMipMap)
1082 return; // we're already built
1083
1084 SkASSERT(NULL == fMipMap);
1085
1086 void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src);
1087
1088 const SkBitmap::Config config = this->getConfig();
1089
1090 switch (config) {
1091 case kARGB_8888_Config:
1092 proc = downsampleby2_proc32;
1093 break;
1094 case kRGB_565_Config:
1095 proc = downsampleby2_proc16;
1096 break;
1097 case kARGB_4444_Config:
1098 proc = downsampleby2_proc4444;
1099 break;
1100 case kIndex8_Config:
1101 case kA8_Config:
1102 default:
1103 return; // don't build mipmaps for these configs
1104 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001105
reed@android.com149e2f62009-05-22 14:39:03 +00001106 SkAutoLockPixels alp(*this);
1107 if (!this->readyToDraw()) {
1108 return;
1109 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001110
1111 // whip through our loop to compute the exact size needed
1112 size_t size = 0;
1113 int maxLevels = 0;
1114 {
reed@android.com149e2f62009-05-22 14:39:03 +00001115 int width = this->width();
1116 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117 for (;;) {
1118 width >>= 1;
1119 height >>= 1;
1120 if (0 == width || 0 == height) {
1121 break;
1122 }
1123 size += ComputeRowBytes(config, width) * height;
1124 maxLevels += 1;
1125 }
1126 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001127
reed@android.com149e2f62009-05-22 14:39:03 +00001128 // nothing to build
reed@android.com8a1c16f2008-12-17 15:59:43 +00001129 if (0 == maxLevels) {
1130 return;
1131 }
1132
reed@android.com149e2f62009-05-22 14:39:03 +00001133 SkBitmap srcBM(*this);
1134 srcBM.lockPixels();
1135 if (!srcBM.readyToDraw()) {
1136 return;
1137 }
1138
1139 MipMap* mm = MipMap::Alloc(maxLevels, size);
1140 if (NULL == mm) {
1141 return;
1142 }
1143
reed@android.com8a1c16f2008-12-17 15:59:43 +00001144 MipLevel* level = mm->levels();
1145 uint8_t* addr = (uint8_t*)mm->pixels();
reed@android.com149e2f62009-05-22 14:39:03 +00001146 int width = this->width();
1147 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001148 unsigned rowBytes = this->rowBytes();
reed@android.com149e2f62009-05-22 14:39:03 +00001149 SkBitmap dstBM;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001150
1151 for (int i = 0; i < maxLevels; i++) {
1152 width >>= 1;
1153 height >>= 1;
1154 rowBytes = ComputeRowBytes(config, width);
1155
1156 level[i].fPixels = addr;
reed@android.comf459a492009-03-27 12:33:50 +00001157 level[i].fWidth = width;
1158 level[i].fHeight = height;
reed@android.com49f0ff22009-03-19 21:52:42 +00001159 level[i].fRowBytes = rowBytes;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001160
1161 dstBM.setConfig(config, width, height, rowBytes);
1162 dstBM.setPixels(addr);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001163
bungeman@google.com7cf0e9e2012-07-25 16:09:10 +00001164 srcBM.lockPixels();
reed@android.com149e2f62009-05-22 14:39:03 +00001165 for (int y = 0; y < height; y++) {
1166 for (int x = 0; x < width; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001167 proc(&dstBM, x, y, srcBM);
1168 }
1169 }
bungeman@google.com7cf0e9e2012-07-25 16:09:10 +00001170 srcBM.unlockPixels();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001171
1172 srcBM = dstBM;
1173 addr += height * rowBytes;
1174 }
1175 SkASSERT(addr == (uint8_t*)mm->pixels() + size);
1176 fMipMap = mm;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001177}
1178
1179bool SkBitmap::hasMipMap() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001180 return fMipMap != NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001181}
1182
1183int SkBitmap::extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy) {
reed@android.com83f7bc32009-07-17 02:42:41 +00001184 if (NULL == fMipMap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001185 return 0;
reed@android.com83f7bc32009-07-17 02:42:41 +00001186 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001187
reed@android.com8a1c16f2008-12-17 15:59:43 +00001188 int level = ComputeMipLevel(sx, sy) >> 16;
1189 SkASSERT(level >= 0);
1190 if (level <= 0) {
1191 return 0;
1192 }
1193
1194 if (level >= fMipMap->fLevelCount) {
1195 level = fMipMap->fLevelCount - 1;
1196 }
1197 if (dst) {
1198 const MipLevel& mip = fMipMap->levels()[level - 1];
1199 dst->setConfig((SkBitmap::Config)this->config(),
1200 mip.fWidth, mip.fHeight, mip.fRowBytes);
1201 dst->setPixels(mip.fPixels);
1202 }
1203 return level;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001204}
1205
1206SkFixed SkBitmap::ComputeMipLevel(SkFixed sx, SkFixed sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001207 sx = SkAbs32(sx);
1208 sy = SkAbs32(sy);
1209 if (sx < sy) {
1210 sx = sy;
1211 }
1212 if (sx < SK_Fixed1) {
1213 return 0;
1214 }
1215 int clz = SkCLZ(sx);
1216 SkASSERT(clz >= 1 && clz <= 15);
1217 return SkIntToFixed(15 - clz) + ((unsigned)(sx << (clz + 1)) >> 16);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001218}
1219
1220///////////////////////////////////////////////////////////////////////////////
1221
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +00001222static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223 int alphaRowBytes) {
1224 SkASSERT(alpha != NULL);
1225 SkASSERT(alphaRowBytes >= src.width());
1226
1227 SkBitmap::Config config = src.getConfig();
1228 int w = src.width();
1229 int h = src.height();
1230 int rb = src.rowBytes();
1231
reed@android.com1cdcb512009-08-24 19:11:00 +00001232 SkAutoLockPixels alp(src);
1233 if (!src.readyToDraw()) {
1234 // zero out the alpha buffer and return
1235 while (--h >= 0) {
1236 memset(alpha, 0, w);
1237 alpha += alphaRowBytes;
1238 }
1239 return false;
1240 }
reed@google.com82065d62011-02-07 15:30:46 +00001241
reed@android.com8a1c16f2008-12-17 15:59:43 +00001242 if (SkBitmap::kA8_Config == config && !src.isOpaque()) {
1243 const uint8_t* s = src.getAddr8(0, 0);
1244 while (--h >= 0) {
1245 memcpy(alpha, s, w);
1246 s += rb;
1247 alpha += alphaRowBytes;
1248 }
1249 } else if (SkBitmap::kARGB_8888_Config == config && !src.isOpaque()) {
1250 const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0);
1251 while (--h >= 0) {
1252 for (int x = 0; x < w; x++) {
1253 alpha[x] = SkGetPackedA32(s[x]);
1254 }
1255 s = (const SkPMColor*)((const char*)s + rb);
1256 alpha += alphaRowBytes;
1257 }
1258 } else if (SkBitmap::kARGB_4444_Config == config && !src.isOpaque()) {
1259 const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0);
1260 while (--h >= 0) {
1261 for (int x = 0; x < w; x++) {
1262 alpha[x] = SkPacked4444ToA32(s[x]);
1263 }
1264 s = (const SkPMColor16*)((const char*)s + rb);
1265 alpha += alphaRowBytes;
1266 }
1267 } else if (SkBitmap::kIndex8_Config == config && !src.isOpaque()) {
1268 SkColorTable* ct = src.getColorTable();
1269 if (ct) {
1270 const SkPMColor* SK_RESTRICT table = ct->lockColors();
1271 const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0);
1272 while (--h >= 0) {
1273 for (int x = 0; x < w; x++) {
1274 alpha[x] = SkGetPackedA32(table[s[x]]);
1275 }
1276 s += rb;
1277 alpha += alphaRowBytes;
1278 }
1279 ct->unlockColors(false);
1280 }
1281 } else { // src is opaque, so just fill alpha[] with 0xFF
1282 memset(alpha, 0xFF, h * alphaRowBytes);
1283 }
reed@android.com1cdcb512009-08-24 19:11:00 +00001284 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001285}
1286
1287#include "SkPaint.h"
1288#include "SkMaskFilter.h"
1289#include "SkMatrix.h"
1290
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001291bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
djsollen@google.com57f49692011-02-23 20:46:31 +00001292 Allocator *allocator, SkIPoint* offset) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001293 SkDEBUGCODE(this->validate();)
1294
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001295 SkBitmap tmpBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001296 SkMatrix identity;
1297 SkMask srcM, dstM;
1298
1299 srcM.fBounds.set(0, 0, this->width(), this->height());
1300 srcM.fRowBytes = SkAlign4(this->width());
1301 srcM.fFormat = SkMask::kA8_Format;
1302
1303 SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL;
1304
1305 // compute our (larger?) dst bounds if we have a filter
1306 if (NULL != filter) {
1307 identity.reset();
1308 srcM.fImage = NULL;
1309 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1310 goto NO_FILTER_CASE;
1311 }
1312 dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
1313 } else {
1314 NO_FILTER_CASE:
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001315 tmpBitmap.setConfig(SkBitmap::kA8_Config, this->width(), this->height(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001316 srcM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001317 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1318 // Allocation of pixels for alpha bitmap failed.
1319 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1320 tmpBitmap.width(), tmpBitmap.height());
1321 return false;
1322 }
1323 GetBitmapAlpha(*this, tmpBitmap.getAddr8(0, 0), srcM.fRowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324 if (offset) {
1325 offset->set(0, 0);
1326 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001327 tmpBitmap.swap(*dst);
1328 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001329 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001330 srcM.fImage = SkMask::AllocImage(srcM.computeImageSize());
1331 SkAutoMaskFreeImage srcCleanup(srcM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001332
1333 GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
1334 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1335 goto NO_FILTER_CASE;
1336 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001337 SkAutoMaskFreeImage dstCleanup(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001339 tmpBitmap.setConfig(SkBitmap::kA8_Config, dstM.fBounds.width(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001340 dstM.fBounds.height(), dstM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001341 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1342 // Allocation of pixels for alpha bitmap failed.
1343 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1344 tmpBitmap.width(), tmpBitmap.height());
1345 return false;
1346 }
1347 memcpy(tmpBitmap.getPixels(), dstM.fImage, dstM.computeImageSize());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001348 if (offset) {
1349 offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
1350 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001351 SkDEBUGCODE(tmpBitmap.validate();)
1352
1353 tmpBitmap.swap(*dst);
1354 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355}
1356
1357///////////////////////////////////////////////////////////////////////////////
1358
1359enum {
1360 SERIALIZE_PIXELTYPE_NONE,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001361 SERIALIZE_PIXELTYPE_REF_DATA,
tomhudson@google.com1f902872012-06-01 13:15:47 +00001362 SERIALIZE_PIXELTYPE_REF_PTR
reed@android.com8a1c16f2008-12-17 15:59:43 +00001363};
1364
reed@android.com8a1c16f2008-12-17 15:59:43 +00001365/*
1366 It is tricky to know how much to flatten. If we don't have a pixelref (i.e.
1367 we just have pixels, then we can only flatten the pixels, or write out an
1368 empty bitmap.
weita@google.comf9ab99a2009-05-03 18:23:30 +00001369
reed@android.com8a1c16f2008-12-17 15:59:43 +00001370 With a pixelref, we still have the question of recognizing when two sitings
1371 of the same pixelref are the same, and when they are different. Perhaps we
1372 should look at the generationID and keep a record of that in some dictionary
1373 associated with the buffer. SkGLTextureCache does this sort of thing to know
1374 when to create a new texture.
1375*/
1376void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
1377 buffer.write32(fWidth);
1378 buffer.write32(fHeight);
1379 buffer.write32(fRowBytes);
1380 buffer.write8(fConfig);
1381 buffer.writeBool(this->isOpaque());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001382
reed@android.com8a1c16f2008-12-17 15:59:43 +00001383 /* If we are called in this mode, then it is up to the caller to manage
1384 the owner-counts on the pixelref, as we just record the ptr itself.
1385 */
1386 if (!buffer.persistBitmapPixels()) {
1387 if (fPixelRef) {
1388 buffer.write8(SERIALIZE_PIXELTYPE_REF_PTR);
1389 buffer.write32(fPixelRefOffset);
1390 buffer.writeRefCnt(fPixelRef);
1391 return;
1392 } else {
1393 // we ignore the non-persist request, since we don't have a ref
1394 // ... or we could just write an empty bitmap...
1395 // (true) will write an empty bitmap, (false) will flatten the pix
1396 if (true) {
1397 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1398 return;
1399 }
1400 }
1401 }
1402
1403 if (fPixelRef) {
djsollen@google.com5370cd92012-03-28 20:47:01 +00001404 if (fPixelRef->getFactory()) {
1405 buffer.write8(SERIALIZE_PIXELTYPE_REF_DATA);
1406 buffer.write32(fPixelRefOffset);
1407 buffer.writeFlattenable(fPixelRef);
1408 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409 }
1410 // if we get here, we can't record the pixels
1411 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412 } else {
1413 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1414 }
1415}
1416
1417void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) {
1418 this->reset();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001419
reed@android.com8a1c16f2008-12-17 15:59:43 +00001420 int width = buffer.readInt();
1421 int height = buffer.readInt();
1422 int rowBytes = buffer.readInt();
1423 int config = buffer.readU8();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001424
reed@android.com8a1c16f2008-12-17 15:59:43 +00001425 this->setConfig((Config)config, width, height, rowBytes);
1426 this->setIsOpaque(buffer.readBool());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001427
reed@android.com8a1c16f2008-12-17 15:59:43 +00001428 int reftype = buffer.readU8();
1429 switch (reftype) {
1430 case SERIALIZE_PIXELTYPE_REF_PTR: {
1431 size_t offset = buffer.readU32();
1432 SkPixelRef* pr = (SkPixelRef*)buffer.readRefCnt();
1433 this->setPixelRef(pr, offset);
1434 break;
1435 }
1436 case SERIALIZE_PIXELTYPE_REF_DATA: {
1437 size_t offset = buffer.readU32();
djsollen@google.com5370cd92012-03-28 20:47:01 +00001438 SkPixelRef* pr = static_cast<SkPixelRef*>(buffer.readFlattenable());
reed@google.com82065d62011-02-07 15:30:46 +00001439 SkSafeUnref(this->setPixelRef(pr, offset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001440 break;
1441 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001442 case SERIALIZE_PIXELTYPE_NONE:
1443 break;
1444 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001445 SkDEBUGFAIL("unrecognized pixeltype in serialized data");
reed@android.com8a1c16f2008-12-17 15:59:43 +00001446 sk_throw();
1447 }
1448}
1449
1450///////////////////////////////////////////////////////////////////////////////
1451
1452SkBitmap::RLEPixels::RLEPixels(int width, int height) {
1453 fHeight = height;
1454 fYPtrs = (uint8_t**)sk_malloc_throw(height * sizeof(uint8_t*));
reed@android.com4516f472009-06-29 16:25:36 +00001455 sk_bzero(fYPtrs, height * sizeof(uint8_t*));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001456}
1457
1458SkBitmap::RLEPixels::~RLEPixels() {
1459 sk_free(fYPtrs);
1460}
1461
1462///////////////////////////////////////////////////////////////////////////////
1463
1464#ifdef SK_DEBUG
1465void SkBitmap::validate() const {
1466 SkASSERT(fConfig < kConfigCount);
1467 SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
junov@google.com4ee7ae52011-06-30 17:30:49 +00001468 SkASSERT(fFlags <= (kImageIsOpaque_Flag | kImageIsVolatile_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001469 SkASSERT(fPixelLockCount >= 0);
1470 SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000);
1471 SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
1472
1473#if 0 // these asserts are not thread-correct, so disable for now
1474 if (fPixelRef) {
1475 if (fPixelLockCount > 0) {
reed@google.comff0da4f2012-05-17 13:14:52 +00001476 SkASSERT(fPixelRef->isLocked());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001477 } else {
1478 SkASSERT(NULL == fPixels);
1479 SkASSERT(NULL == fColorTable);
1480 }
1481 }
1482#endif
1483}
1484#endif