blob: f135d48fa47e8446bc7815dc9a24acc0cb5c15c4 [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.comc84b8332012-07-27 13:41:44 +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) {
reed@google.com8e1034e2012-07-30 13:16:35 +0000363 if (NULL == p) {
364 this->setPixelRef(NULL, 0);
365 return;
366 }
367
djsollen@google.comc84b8332012-07-27 13:41:44 +0000368 Sk64 size = this->getSize64();
369 SkASSERT(!size.isNeg() && size.is32());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000370
djsollen@google.comc84b8332012-07-27 13:41:44 +0000371 this->setPixelRef(new SkMallocPixelRef(p, size.get32(), ctable, false))->unref();
372 // since we're already allocated, we lockPixels right away
373 this->lockPixels();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000374 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.comc84b8332012-07-27 13:41:44 +0000415 return (fPixelRef) ? fPixelRef->getGenerationID() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000416}
417
418void SkBitmap::notifyPixelsChanged() const {
junov@chromium.orgb0521292011-12-15 20:14:06 +0000419 SkASSERT(!this->isImmutable());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000420 if (fPixelRef) {
421 fPixelRef->notifyPixelsChanged();
422 }
423}
424
reed@android.comce4e53a2010-09-09 16:01:26 +0000425SkGpuTexture* SkBitmap::getTexture() const {
426 return fPixelRef ? fPixelRef->getTexture() : NULL;
427}
428
reed@android.com8a1c16f2008-12-17 15:59:43 +0000429///////////////////////////////////////////////////////////////////////////////
430
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431/** We explicitly use the same allocator for our pixels that SkMask does,
432 so that we can freely assign memory allocated by one class to the other.
433 */
434bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
435 SkColorTable* ctable) {
436 Sk64 size = dst->getSize64();
437 if (size.isNeg() || !size.is32()) {
438 return false;
439 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000440
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441 void* addr = sk_malloc_flags(size.get32(), 0); // returns NULL on failure
442 if (NULL == addr) {
443 return false;
444 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000445
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446 dst->setPixelRef(new SkMallocPixelRef(addr, size.get32(), ctable))->unref();
447 // since we're already allocated, we lockPixels right away
448 dst->lockPixels();
449 return true;
450}
451
452///////////////////////////////////////////////////////////////////////////////
453
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000454size_t SkBitmap::getSafeSize() const {
455 // This is intended to be a size_t version of ComputeSafeSize64(), just
456 // faster. The computation is meant to be identical.
457 return (fHeight ? ((fHeight - 1) * fRowBytes) +
458 ComputeRowBytes(getConfig(), fWidth): 0);
459}
460
461Sk64 SkBitmap::getSafeSize64() const {
462 return ComputeSafeSize64(getConfig(), fWidth, fHeight, fRowBytes);
463}
464
bsalomon@google.comc6980972011-11-02 19:57:21 +0000465bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize,
466 int dstRowBytes, bool preserveDstPad) const {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000467
468 if (dstRowBytes == -1)
469 dstRowBytes = fRowBytes;
470 SkASSERT(dstRowBytes >= 0);
471
472 if (getConfig() == kRLE_Index8_Config ||
473 dstRowBytes < ComputeRowBytes(getConfig(), fWidth) ||
474 dst == NULL || (getPixels() == NULL && pixelRef() == NULL))
475 return false;
476
bsalomon@google.comc6980972011-11-02 19:57:21 +0000477 if (!preserveDstPad && static_cast<uint32_t>(dstRowBytes) == fRowBytes) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000478 size_t safeSize = getSafeSize();
479 if (safeSize > dstSize || safeSize == 0)
480 return false;
481 else {
482 SkAutoLockPixels lock(*this);
483 // This implementation will write bytes beyond the end of each row,
484 // excluding the last row, if the bitmap's stride is greater than
485 // strictly required by the current config.
486 memcpy(dst, getPixels(), safeSize);
487
488 return true;
489 }
490 } else {
491 // If destination has different stride than us, then copy line by line.
492 if (ComputeSafeSize(getConfig(), fWidth, fHeight, dstRowBytes) >
493 dstSize)
494 return false;
495 else {
496 // Just copy what we need on each line.
497 uint32_t rowBytes = ComputeRowBytes(getConfig(), fWidth);
498 SkAutoLockPixels lock(*this);
499 const uint8_t* srcP = reinterpret_cast<const uint8_t*>(getPixels());
500 uint8_t* dstP = reinterpret_cast<uint8_t*>(dst);
501 for (uint32_t row = 0; row < fHeight;
502 row++, srcP += fRowBytes, dstP += dstRowBytes) {
503 memcpy(dstP, srcP, rowBytes);
504 }
505
506 return true;
507 }
508 }
509}
510
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000511///////////////////////////////////////////////////////////////////////////////
512
junov@chromium.orgb0521292011-12-15 20:14:06 +0000513bool SkBitmap::isImmutable() const {
514 return fPixelRef ? fPixelRef->isImmutable() :
515 fFlags & kImageIsImmutable_Flag;
516}
517
518void SkBitmap::setImmutable() {
519 if (fPixelRef) {
520 fPixelRef->setImmutable();
521 } else {
522 fFlags |= kImageIsImmutable_Flag;
523 }
524}
525
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526bool SkBitmap::isOpaque() const {
527 switch (fConfig) {
528 case kNo_Config:
529 return true;
530
531 case kA1_Config:
532 case kA8_Config:
533 case kARGB_4444_Config:
534 case kARGB_8888_Config:
535 return (fFlags & kImageIsOpaque_Flag) != 0;
536
537 case kIndex8_Config:
538 case kRLE_Index8_Config: {
539 uint32_t flags = 0;
540
541 this->lockPixels();
542 // if lockPixels failed, we may not have a ctable ptr
543 if (fColorTable) {
544 flags = fColorTable->getFlags();
545 }
546 this->unlockPixels();
547
548 return (flags & SkColorTable::kColorsAreOpaque_Flag) != 0;
549 }
550
551 case kRGB_565_Config:
552 return true;
553
554 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000555 SkDEBUGFAIL("unknown bitmap config pased to isOpaque");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000556 return false;
557 }
558}
559
560void SkBitmap::setIsOpaque(bool isOpaque) {
561 /* we record this regardless of fConfig, though it is ignored in
562 isOpaque() for configs that can't support per-pixel alpha.
563 */
564 if (isOpaque) {
565 fFlags |= kImageIsOpaque_Flag;
566 } else {
567 fFlags &= ~kImageIsOpaque_Flag;
568 }
569}
570
junov@google.com4ee7ae52011-06-30 17:30:49 +0000571bool SkBitmap::isVolatile() const {
572 return (fFlags & kImageIsVolatile_Flag) != 0;
573}
574
575void SkBitmap::setIsVolatile(bool isVolatile) {
576 if (isVolatile) {
577 fFlags |= kImageIsVolatile_Flag;
578 } else {
579 fFlags &= ~kImageIsVolatile_Flag;
580 }
581}
582
reed@android.com8a1c16f2008-12-17 15:59:43 +0000583void* SkBitmap::getAddr(int x, int y) const {
584 SkASSERT((unsigned)x < (unsigned)this->width());
585 SkASSERT((unsigned)y < (unsigned)this->height());
586
587 char* base = (char*)this->getPixels();
588 if (base) {
589 base += y * this->rowBytes();
590 switch (this->config()) {
591 case SkBitmap::kARGB_8888_Config:
592 base += x << 2;
593 break;
594 case SkBitmap::kARGB_4444_Config:
595 case SkBitmap::kRGB_565_Config:
596 base += x << 1;
597 break;
598 case SkBitmap::kA8_Config:
599 case SkBitmap::kIndex8_Config:
600 base += x;
601 break;
602 case SkBitmap::kA1_Config:
603 base += x >> 3;
604 break;
605 case kRLE_Index8_Config:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000606 SkDEBUGFAIL("Can't return addr for kRLE_Index8_Config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 base = NULL;
608 break;
609 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000610 SkDEBUGFAIL("Can't return addr for config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611 base = NULL;
612 break;
613 }
614 }
615 return base;
616}
617
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000618SkColor SkBitmap::getColor(int x, int y) const {
619 SkASSERT((unsigned)x < (unsigned)this->width());
620 SkASSERT((unsigned)y < (unsigned)this->height());
621
622 switch (this->config()) {
623 case SkBitmap::kA1_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000624 uint8_t* addr = this->getAddr1(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000625 uint8_t mask = 1 << (7 - (x % 8));
626 if (addr[0] & mask) {
627 return SK_ColorBLACK;
628 } else {
629 return 0;
630 }
631 }
632 case SkBitmap::kA8_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000633 uint8_t* addr = this->getAddr8(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000634 return SkColorSetA(0, addr[0]);
635 }
636 case SkBitmap::kIndex8_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000637 SkPMColor c = this->getIndex8Color(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000638 return SkUnPreMultiply::PMColorToColor(c);
639 }
640 case SkBitmap::kRGB_565_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000641 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000642 return SkPixel16ToColor(addr[0]);
643 }
644 case SkBitmap::kARGB_4444_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000645 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000646 SkPMColor c = SkPixel4444ToPixel32(addr[0]);
647 return SkUnPreMultiply::PMColorToColor(c);
648 }
649 case SkBitmap::kARGB_8888_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000650 uint32_t* addr = this->getAddr32(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000651 return SkUnPreMultiply::PMColorToColor(addr[0]);
652 }
653 case kRLE_Index8_Config: {
654 uint8_t dst;
655 const SkBitmap::RLEPixels* rle =
reed@google.com3b521d02011-04-29 11:53:41 +0000656 (const SkBitmap::RLEPixels*)this->getPixels();
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000657 SkPackBits::Unpack8(&dst, x, 1, rle->packedAtY(y));
658 return SkUnPreMultiply::PMColorToColor((*fColorTable)[dst]);
659 }
660 case kNo_Config:
661 case kConfigCount:
662 SkASSERT(false);
663 return 0;
664 }
665 SkASSERT(false); // Not reached.
666 return 0;
667}
668
reed@android.com8a1c16f2008-12-17 15:59:43 +0000669///////////////////////////////////////////////////////////////////////////////
670///////////////////////////////////////////////////////////////////////////////
671
672void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
673 SkDEBUGCODE(this->validate();)
674
675 if (0 == fWidth || 0 == fHeight ||
676 kNo_Config == fConfig || kIndex8_Config == fConfig) {
677 return;
678 }
679
680 SkAutoLockPixels alp(*this);
681 // perform this check after the lock call
682 if (!this->readyToDraw()) {
683 return;
684 }
685
686 int height = fHeight;
687 const int width = fWidth;
688 const int rowBytes = fRowBytes;
689
690 // make rgb premultiplied
691 if (255 != a) {
692 r = SkAlphaMul(r, a);
693 g = SkAlphaMul(g, a);
694 b = SkAlphaMul(b, a);
695 }
696
697 switch (fConfig) {
698 case kA1_Config: {
699 uint8_t* p = (uint8_t*)fPixels;
700 const int count = (width + 7) >> 3;
701 a = (a >> 7) ? 0xFF : 0;
702 SkASSERT(count <= rowBytes);
703 while (--height >= 0) {
704 memset(p, a, count);
705 p += rowBytes;
706 }
707 break;
708 }
709 case kA8_Config: {
710 uint8_t* p = (uint8_t*)fPixels;
711 while (--height >= 0) {
712 memset(p, a, width);
713 p += rowBytes;
714 }
715 break;
716 }
717 case kARGB_4444_Config:
718 case kRGB_565_Config: {
719 uint16_t* p = (uint16_t*)fPixels;
720 uint16_t v;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000721
reed@android.com8a1c16f2008-12-17 15:59:43 +0000722 if (kARGB_4444_Config == fConfig) {
723 v = SkPackARGB4444(a >> 4, r >> 4, g >> 4, b >> 4);
724 } else { // kRGB_565_Config
725 v = SkPackRGB16(r >> (8 - SK_R16_BITS), g >> (8 - SK_G16_BITS),
726 b >> (8 - SK_B16_BITS));
727 }
728 while (--height >= 0) {
729 sk_memset16(p, v, width);
730 p = (uint16_t*)((char*)p + rowBytes);
731 }
732 break;
733 }
734 case kARGB_8888_Config: {
735 uint32_t* p = (uint32_t*)fPixels;
736 uint32_t v = SkPackARGB32(a, r, g, b);
737
738 while (--height >= 0) {
739 sk_memset32(p, v, width);
740 p = (uint32_t*)((char*)p + rowBytes);
741 }
742 break;
743 }
744 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000745
reed@android.com8a1c16f2008-12-17 15:59:43 +0000746 this->notifyPixelsChanged();
747}
748
749//////////////////////////////////////////////////////////////////////////////////////
750//////////////////////////////////////////////////////////////////////////////////////
751
752#define SUB_OFFSET_FAILURE ((size_t)-1)
753
754static size_t getSubOffset(const SkBitmap& bm, int x, int y) {
755 SkASSERT((unsigned)x < (unsigned)bm.width());
756 SkASSERT((unsigned)y < (unsigned)bm.height());
weita@google.comf9ab99a2009-05-03 18:23:30 +0000757
reed@android.com8a1c16f2008-12-17 15:59:43 +0000758 switch (bm.getConfig()) {
759 case SkBitmap::kA8_Config:
760 case SkBitmap:: kIndex8_Config:
761 // x is fine as is for the calculation
762 break;
763
764 case SkBitmap::kRGB_565_Config:
765 case SkBitmap::kARGB_4444_Config:
766 x <<= 1;
767 break;
768
769 case SkBitmap::kARGB_8888_Config:
770 x <<= 2;
771 break;
772
773 case SkBitmap::kNo_Config:
774 case SkBitmap::kA1_Config:
775 default:
776 return SUB_OFFSET_FAILURE;
777 }
778 return y * bm.rowBytes() + x;
779}
780
781bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
782 SkDEBUGCODE(this->validate();)
783
djsollen@google.comc84b8332012-07-27 13:41:44 +0000784 if (NULL == result || NULL == fPixelRef) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785 return false; // no src pixels
786 }
787
788 SkIRect srcRect, r;
789 srcRect.set(0, 0, this->width(), this->height());
790 if (!r.intersect(srcRect, subset)) {
791 return false; // r is empty (i.e. no intersection)
792 }
793
794 if (kRLE_Index8_Config == fConfig) {
795 SkAutoLockPixels alp(*this);
796 // don't call readyToDraw(), since we can operate w/o a colortable
797 // at this stage
798 if (this->getPixels() == NULL) {
799 return false;
800 }
801 SkBitmap bm;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000802
reed@android.com8a1c16f2008-12-17 15:59:43 +0000803 bm.setConfig(kIndex8_Config, r.width(), r.height());
804 bm.allocPixels(this->getColorTable());
805 if (NULL == bm.getPixels()) {
806 return false;
807 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000808
reed@android.com8a1c16f2008-12-17 15:59:43 +0000809 const RLEPixels* rle = (const RLEPixels*)this->getPixels();
810 uint8_t* dst = bm.getAddr8(0, 0);
811 const int width = bm.width();
812 const int rowBytes = bm.rowBytes();
weita@google.comf9ab99a2009-05-03 18:23:30 +0000813
reed@android.com8a1c16f2008-12-17 15:59:43 +0000814 for (int y = r.fTop; y < r.fBottom; y++) {
815 SkPackBits::Unpack8(dst, r.fLeft, width, rle->packedAtY(y));
816 dst += rowBytes;
817 }
818 result->swap(bm);
819 return true;
820 }
821
822 size_t offset = getSubOffset(*this, r.fLeft, r.fTop);
823 if (SUB_OFFSET_FAILURE == offset) {
824 return false; // config not supported
825 }
826
827 SkBitmap dst;
828 dst.setConfig(this->config(), r.width(), r.height(), this->rowBytes());
skyostil@google.com0eb75762012-01-16 10:45:53 +0000829 dst.setIsVolatile(this->isVolatile());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830
831 if (fPixelRef) {
832 // share the pixelref with a custom offset
833 dst.setPixelRef(fPixelRef, fPixelRefOffset + offset);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834 }
835 SkDEBUGCODE(dst.validate();)
836
837 // we know we're good, so commit to result
838 result->swap(dst);
839 return true;
840}
841
842///////////////////////////////////////////////////////////////////////////////
843
844#include "SkCanvas.h"
845#include "SkPaint.h"
846
reed@android.comfbaa88d2009-05-06 17:44:34 +0000847bool SkBitmap::canCopyTo(Config dstConfig) const {
848 if (this->getConfig() == kNo_Config) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849 return false;
850 }
851
reed@android.comfbaa88d2009-05-06 17:44:34 +0000852 bool sameConfigs = (this->config() == dstConfig);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000853 switch (dstConfig) {
854 case kA8_Config:
855 case kARGB_4444_Config:
856 case kRGB_565_Config:
857 case kARGB_8888_Config:
858 break;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000859 case kA1_Config:
860 case kIndex8_Config:
861 if (!sameConfigs) {
862 return false;
863 }
864 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865 default:
866 return false;
867 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000868
869 // do not copy src if srcConfig == kA1_Config while dstConfig != kA1_Config
870 if (this->getConfig() == kA1_Config && !sameConfigs) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000871 return false;
872 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000873
reed@android.comfbaa88d2009-05-06 17:44:34 +0000874 return true;
875}
876
877bool SkBitmap::copyTo(SkBitmap* dst, Config dstConfig, Allocator* alloc) const {
878 if (!this->canCopyTo(dstConfig)) {
879 return false;
880 }
881
reed@google.com50dfa012011-04-01 19:05:36 +0000882 // if we have a texture, first get those pixels
883 SkBitmap tmpSrc;
884 const SkBitmap* src = this;
885
bsalomon@google.com669fdc42011-04-05 17:08:27 +0000886 if (fPixelRef && fPixelRef->readPixels(&tmpSrc)) {
reed@google.com50dfa012011-04-01 19:05:36 +0000887 SkASSERT(tmpSrc.width() == this->width());
888 SkASSERT(tmpSrc.height() == this->height());
889
890 // did we get lucky and we can just return tmpSrc?
891 if (tmpSrc.config() == dstConfig && NULL == alloc) {
892 dst->swap(tmpSrc);
893 return true;
894 }
895
896 // fall through to the raster case
897 src = &tmpSrc;
reed@android.comfbaa88d2009-05-06 17:44:34 +0000898 }
reed@android.com311c82d2009-05-05 23:13:23 +0000899
reed@google.com50dfa012011-04-01 19:05:36 +0000900 // we lock this now, since we may need its colortable
901 SkAutoLockPixels srclock(*src);
902 if (!src->readyToDraw()) {
903 return false;
904 }
905
906 SkBitmap tmpDst;
907 tmpDst.setConfig(dstConfig, src->width(), src->height());
908
weita@google.comf9ab99a2009-05-03 18:23:30 +0000909 // allocate colortable if srcConfig == kIndex8_Config
910 SkColorTable* ctable = (dstConfig == kIndex8_Config) ?
reed@google.com50dfa012011-04-01 19:05:36 +0000911 new SkColorTable(*src->getColorTable()) : NULL;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000912 SkAutoUnref au(ctable);
reed@google.com50dfa012011-04-01 19:05:36 +0000913 if (!tmpDst.allocPixels(alloc, ctable)) {
weita@google.comf9ab99a2009-05-03 18:23:30 +0000914 return false;
915 }
reed@google.com50dfa012011-04-01 19:05:36 +0000916
reed@google.com50dfa012011-04-01 19:05:36 +0000917 if (!tmpDst.readyToDraw()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000918 // allocator/lock failed
919 return false;
920 }
reed@google.com50dfa012011-04-01 19:05:36 +0000921
reed@android.comfbaa88d2009-05-06 17:44:34 +0000922 /* do memcpy for the same configs cases, else use drawing
weita@google.comf9ab99a2009-05-03 18:23:30 +0000923 */
reed@google.com50dfa012011-04-01 19:05:36 +0000924 if (src->config() == dstConfig) {
925 if (tmpDst.getSize() == src->getSize()) {
926 memcpy(tmpDst.getPixels(), src->getPixels(), src->getSafeSize());
reed@android.com311c82d2009-05-05 23:13:23 +0000927 } else {
reed@google.com50dfa012011-04-01 19:05:36 +0000928 const char* srcP = reinterpret_cast<const char*>(src->getPixels());
929 char* dstP = reinterpret_cast<char*>(tmpDst.getPixels());
reed@android.com311c82d2009-05-05 23:13:23 +0000930 // to be sure we don't read too much, only copy our logical pixels
reed@google.com50dfa012011-04-01 19:05:36 +0000931 size_t bytesToCopy = tmpDst.width() * tmpDst.bytesPerPixel();
932 for (int y = 0; y < tmpDst.height(); y++) {
reed@android.com311c82d2009-05-05 23:13:23 +0000933 memcpy(dstP, srcP, bytesToCopy);
reed@google.com50dfa012011-04-01 19:05:36 +0000934 srcP += src->rowBytes();
935 dstP += tmpDst.rowBytes();
reed@android.com311c82d2009-05-05 23:13:23 +0000936 }
937 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000938 } else {
939 // if the src has alpha, we have to clear the dst first
reed@google.com50dfa012011-04-01 19:05:36 +0000940 if (!src->isOpaque()) {
941 tmpDst.eraseColor(0);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000942 }
943
reed@google.com50dfa012011-04-01 19:05:36 +0000944 SkCanvas canvas(tmpDst);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000945 SkPaint paint;
946
947 paint.setDither(true);
reed@google.com50dfa012011-04-01 19:05:36 +0000948 canvas.drawBitmap(*src, 0, 0, &paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000949 }
950
reed@google.com50dfa012011-04-01 19:05:36 +0000951 tmpDst.setIsOpaque(src->isOpaque());
reed@android.comcafc9f92009-08-22 03:44:57 +0000952
reed@google.com50dfa012011-04-01 19:05:36 +0000953 dst->swap(tmpDst);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000954 return true;
955}
956
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +0000957bool SkBitmap::deepCopyTo(SkBitmap* dst, Config dstConfig) const {
958 if (!this->canCopyTo(dstConfig)) {
959 return false;
960 }
961
962 // If we have a PixelRef, and it supports deep copy, use it.
963 // Currently supported only by texture-backed bitmaps.
964 if (fPixelRef) {
965 SkPixelRef* pixelRef = fPixelRef->deepCopy(dstConfig);
966 if (pixelRef) {
967 dst->setConfig(dstConfig, fWidth, fHeight);
968 dst->setPixelRef(pixelRef)->unref();
969 return true;
970 }
971 }
972
973 if (this->getTexture()) {
974 return false;
975 } else {
976 return this->copyTo(dst, dstConfig, NULL);
977 }
978}
979
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980///////////////////////////////////////////////////////////////////////////////
981///////////////////////////////////////////////////////////////////////////////
982
983static void downsampleby2_proc32(SkBitmap* dst, int x, int y,
984 const SkBitmap& src) {
985 x <<= 1;
986 y <<= 1;
987 const SkPMColor* p = src.getAddr32(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +0000988 const SkPMColor* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000989 SkPMColor c, ag, rb;
990
991 c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF;
992 if (x < src.width() - 1) {
993 p += 1;
994 }
995 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
996
reed@android.com829c83c2009-06-08 12:05:31 +0000997 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000998 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +0000999 p += src.rowBytes() >> 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001000 }
1001 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1002 if (x < src.width() - 1) {
1003 p += 1;
1004 }
1005 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1006
1007 *dst->getAddr32(x >> 1, y >> 1) =
1008 ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00);
1009}
1010
1011static inline uint32_t expand16(U16CPU c) {
1012 return (c & ~SK_G16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16);
1013}
1014
1015// returns dirt in the top 16bits, but we don't care, since we only
1016// store the low 16bits.
1017static inline U16CPU pack16(uint32_t c) {
1018 return (c & ~SK_G16_MASK_IN_PLACE) | ((c >> 16) & SK_G16_MASK_IN_PLACE);
1019}
1020
1021static void downsampleby2_proc16(SkBitmap* dst, int x, int y,
1022 const SkBitmap& src) {
1023 x <<= 1;
1024 y <<= 1;
1025 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001026 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001027 SkPMColor c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001028
reed@android.com8a1c16f2008-12-17 15:59:43 +00001029 c = expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001030 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001031 p += 1;
1032 }
1033 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001034
reed@android.com829c83c2009-06-08 12:05:31 +00001035 p = baseP;
1036 if (y < src.height() - 1) {
1037 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001038 }
1039 c += expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001040 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001041 p += 1;
1042 }
1043 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001044
reed@android.com8a1c16f2008-12-17 15:59:43 +00001045 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)pack16(c >> 2);
1046}
1047
1048static uint32_t expand4444(U16CPU c) {
1049 return (c & 0xF0F) | ((c & ~0xF0F) << 12);
1050}
1051
1052static U16CPU collaps4444(uint32_t c) {
1053 return (c & 0xF0F) | ((c >> 12) & ~0xF0F);
1054}
1055
1056static void downsampleby2_proc4444(SkBitmap* dst, int x, int y,
1057 const SkBitmap& src) {
1058 x <<= 1;
1059 y <<= 1;
1060 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001061 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062 uint32_t c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001063
reed@android.com8a1c16f2008-12-17 15:59:43 +00001064 c = expand4444(*p);
1065 if (x < src.width() - 1) {
1066 p += 1;
1067 }
1068 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001069
reed@android.com829c83c2009-06-08 12:05:31 +00001070 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +00001072 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001073 }
1074 c += expand4444(*p);
1075 if (x < src.width() - 1) {
1076 p += 1;
1077 }
1078 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001079
reed@android.com8a1c16f2008-12-17 15:59:43 +00001080 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)collaps4444(c >> 2);
1081}
1082
1083void SkBitmap::buildMipMap(bool forceRebuild) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001084 if (forceRebuild)
1085 this->freeMipMap();
1086 else if (fMipMap)
1087 return; // we're already built
1088
1089 SkASSERT(NULL == fMipMap);
1090
1091 void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src);
1092
1093 const SkBitmap::Config config = this->getConfig();
1094
1095 switch (config) {
1096 case kARGB_8888_Config:
1097 proc = downsampleby2_proc32;
1098 break;
1099 case kRGB_565_Config:
1100 proc = downsampleby2_proc16;
1101 break;
1102 case kARGB_4444_Config:
1103 proc = downsampleby2_proc4444;
1104 break;
1105 case kIndex8_Config:
1106 case kA8_Config:
1107 default:
1108 return; // don't build mipmaps for these configs
1109 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001110
reed@android.com149e2f62009-05-22 14:39:03 +00001111 SkAutoLockPixels alp(*this);
1112 if (!this->readyToDraw()) {
1113 return;
1114 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001115
1116 // whip through our loop to compute the exact size needed
1117 size_t size = 0;
1118 int maxLevels = 0;
1119 {
reed@android.com149e2f62009-05-22 14:39:03 +00001120 int width = this->width();
1121 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001122 for (;;) {
1123 width >>= 1;
1124 height >>= 1;
1125 if (0 == width || 0 == height) {
1126 break;
1127 }
1128 size += ComputeRowBytes(config, width) * height;
1129 maxLevels += 1;
1130 }
1131 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001132
reed@android.com149e2f62009-05-22 14:39:03 +00001133 // nothing to build
reed@android.com8a1c16f2008-12-17 15:59:43 +00001134 if (0 == maxLevels) {
1135 return;
1136 }
1137
reed@android.com149e2f62009-05-22 14:39:03 +00001138 SkBitmap srcBM(*this);
1139 srcBM.lockPixels();
1140 if (!srcBM.readyToDraw()) {
1141 return;
1142 }
1143
1144 MipMap* mm = MipMap::Alloc(maxLevels, size);
1145 if (NULL == mm) {
1146 return;
1147 }
1148
reed@android.com8a1c16f2008-12-17 15:59:43 +00001149 MipLevel* level = mm->levels();
1150 uint8_t* addr = (uint8_t*)mm->pixels();
reed@android.com149e2f62009-05-22 14:39:03 +00001151 int width = this->width();
1152 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001153 unsigned rowBytes = this->rowBytes();
reed@android.com149e2f62009-05-22 14:39:03 +00001154 SkBitmap dstBM;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155
1156 for (int i = 0; i < maxLevels; i++) {
1157 width >>= 1;
1158 height >>= 1;
1159 rowBytes = ComputeRowBytes(config, width);
1160
1161 level[i].fPixels = addr;
reed@android.comf459a492009-03-27 12:33:50 +00001162 level[i].fWidth = width;
1163 level[i].fHeight = height;
reed@android.com49f0ff22009-03-19 21:52:42 +00001164 level[i].fRowBytes = rowBytes;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001165
1166 dstBM.setConfig(config, width, height, rowBytes);
1167 dstBM.setPixels(addr);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001168
bungeman@google.com7cf0e9e2012-07-25 16:09:10 +00001169 srcBM.lockPixels();
reed@android.com149e2f62009-05-22 14:39:03 +00001170 for (int y = 0; y < height; y++) {
1171 for (int x = 0; x < width; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001172 proc(&dstBM, x, y, srcBM);
1173 }
1174 }
bungeman@google.com7cf0e9e2012-07-25 16:09:10 +00001175 srcBM.unlockPixels();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001176
1177 srcBM = dstBM;
1178 addr += height * rowBytes;
1179 }
1180 SkASSERT(addr == (uint8_t*)mm->pixels() + size);
1181 fMipMap = mm;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001182}
1183
1184bool SkBitmap::hasMipMap() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001185 return fMipMap != NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001186}
1187
1188int SkBitmap::extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy) {
reed@android.com83f7bc32009-07-17 02:42:41 +00001189 if (NULL == fMipMap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001190 return 0;
reed@android.com83f7bc32009-07-17 02:42:41 +00001191 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001192
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193 int level = ComputeMipLevel(sx, sy) >> 16;
1194 SkASSERT(level >= 0);
1195 if (level <= 0) {
1196 return 0;
1197 }
1198
1199 if (level >= fMipMap->fLevelCount) {
1200 level = fMipMap->fLevelCount - 1;
1201 }
1202 if (dst) {
1203 const MipLevel& mip = fMipMap->levels()[level - 1];
1204 dst->setConfig((SkBitmap::Config)this->config(),
1205 mip.fWidth, mip.fHeight, mip.fRowBytes);
1206 dst->setPixels(mip.fPixels);
1207 }
1208 return level;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001209}
1210
1211SkFixed SkBitmap::ComputeMipLevel(SkFixed sx, SkFixed sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001212 sx = SkAbs32(sx);
1213 sy = SkAbs32(sy);
1214 if (sx < sy) {
1215 sx = sy;
1216 }
1217 if (sx < SK_Fixed1) {
1218 return 0;
1219 }
1220 int clz = SkCLZ(sx);
1221 SkASSERT(clz >= 1 && clz <= 15);
1222 return SkIntToFixed(15 - clz) + ((unsigned)(sx << (clz + 1)) >> 16);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223}
1224
1225///////////////////////////////////////////////////////////////////////////////
1226
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +00001227static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001228 int alphaRowBytes) {
1229 SkASSERT(alpha != NULL);
1230 SkASSERT(alphaRowBytes >= src.width());
1231
1232 SkBitmap::Config config = src.getConfig();
1233 int w = src.width();
1234 int h = src.height();
1235 int rb = src.rowBytes();
1236
reed@android.com1cdcb512009-08-24 19:11:00 +00001237 SkAutoLockPixels alp(src);
1238 if (!src.readyToDraw()) {
1239 // zero out the alpha buffer and return
1240 while (--h >= 0) {
1241 memset(alpha, 0, w);
1242 alpha += alphaRowBytes;
1243 }
1244 return false;
1245 }
reed@google.com82065d62011-02-07 15:30:46 +00001246
reed@android.com8a1c16f2008-12-17 15:59:43 +00001247 if (SkBitmap::kA8_Config == config && !src.isOpaque()) {
1248 const uint8_t* s = src.getAddr8(0, 0);
1249 while (--h >= 0) {
1250 memcpy(alpha, s, w);
1251 s += rb;
1252 alpha += alphaRowBytes;
1253 }
1254 } else if (SkBitmap::kARGB_8888_Config == config && !src.isOpaque()) {
1255 const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0);
1256 while (--h >= 0) {
1257 for (int x = 0; x < w; x++) {
1258 alpha[x] = SkGetPackedA32(s[x]);
1259 }
1260 s = (const SkPMColor*)((const char*)s + rb);
1261 alpha += alphaRowBytes;
1262 }
1263 } else if (SkBitmap::kARGB_4444_Config == config && !src.isOpaque()) {
1264 const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0);
1265 while (--h >= 0) {
1266 for (int x = 0; x < w; x++) {
1267 alpha[x] = SkPacked4444ToA32(s[x]);
1268 }
1269 s = (const SkPMColor16*)((const char*)s + rb);
1270 alpha += alphaRowBytes;
1271 }
1272 } else if (SkBitmap::kIndex8_Config == config && !src.isOpaque()) {
1273 SkColorTable* ct = src.getColorTable();
1274 if (ct) {
1275 const SkPMColor* SK_RESTRICT table = ct->lockColors();
1276 const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0);
1277 while (--h >= 0) {
1278 for (int x = 0; x < w; x++) {
1279 alpha[x] = SkGetPackedA32(table[s[x]]);
1280 }
1281 s += rb;
1282 alpha += alphaRowBytes;
1283 }
1284 ct->unlockColors(false);
1285 }
1286 } else { // src is opaque, so just fill alpha[] with 0xFF
1287 memset(alpha, 0xFF, h * alphaRowBytes);
1288 }
reed@android.com1cdcb512009-08-24 19:11:00 +00001289 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001290}
1291
1292#include "SkPaint.h"
1293#include "SkMaskFilter.h"
1294#include "SkMatrix.h"
1295
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001296bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
djsollen@google.com57f49692011-02-23 20:46:31 +00001297 Allocator *allocator, SkIPoint* offset) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001298 SkDEBUGCODE(this->validate();)
1299
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001300 SkBitmap tmpBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001301 SkMatrix identity;
1302 SkMask srcM, dstM;
1303
1304 srcM.fBounds.set(0, 0, this->width(), this->height());
1305 srcM.fRowBytes = SkAlign4(this->width());
1306 srcM.fFormat = SkMask::kA8_Format;
1307
1308 SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL;
1309
1310 // compute our (larger?) dst bounds if we have a filter
1311 if (NULL != filter) {
1312 identity.reset();
1313 srcM.fImage = NULL;
1314 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1315 goto NO_FILTER_CASE;
1316 }
1317 dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
1318 } else {
1319 NO_FILTER_CASE:
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001320 tmpBitmap.setConfig(SkBitmap::kA8_Config, this->width(), this->height(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321 srcM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001322 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1323 // Allocation of pixels for alpha bitmap failed.
1324 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1325 tmpBitmap.width(), tmpBitmap.height());
1326 return false;
1327 }
1328 GetBitmapAlpha(*this, tmpBitmap.getAddr8(0, 0), srcM.fRowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001329 if (offset) {
1330 offset->set(0, 0);
1331 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001332 tmpBitmap.swap(*dst);
1333 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001334 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001335 srcM.fImage = SkMask::AllocImage(srcM.computeImageSize());
1336 SkAutoMaskFreeImage srcCleanup(srcM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001337
1338 GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
1339 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1340 goto NO_FILTER_CASE;
1341 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001342 SkAutoMaskFreeImage dstCleanup(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001344 tmpBitmap.setConfig(SkBitmap::kA8_Config, dstM.fBounds.width(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001345 dstM.fBounds.height(), dstM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001346 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1347 // Allocation of pixels for alpha bitmap failed.
1348 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1349 tmpBitmap.width(), tmpBitmap.height());
1350 return false;
1351 }
1352 memcpy(tmpBitmap.getPixels(), dstM.fImage, dstM.computeImageSize());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001353 if (offset) {
1354 offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
1355 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001356 SkDEBUGCODE(tmpBitmap.validate();)
1357
1358 tmpBitmap.swap(*dst);
1359 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001360}
1361
1362///////////////////////////////////////////////////////////////////////////////
1363
1364enum {
1365 SERIALIZE_PIXELTYPE_NONE,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001366 SERIALIZE_PIXELTYPE_REF_DATA,
tomhudson@google.com1f902872012-06-01 13:15:47 +00001367 SERIALIZE_PIXELTYPE_REF_PTR
reed@android.com8a1c16f2008-12-17 15:59:43 +00001368};
1369
reed@android.com8a1c16f2008-12-17 15:59:43 +00001370/*
1371 It is tricky to know how much to flatten. If we don't have a pixelref (i.e.
1372 we just have pixels, then we can only flatten the pixels, or write out an
1373 empty bitmap.
weita@google.comf9ab99a2009-05-03 18:23:30 +00001374
reed@android.com8a1c16f2008-12-17 15:59:43 +00001375 With a pixelref, we still have the question of recognizing when two sitings
1376 of the same pixelref are the same, and when they are different. Perhaps we
1377 should look at the generationID and keep a record of that in some dictionary
1378 associated with the buffer. SkGLTextureCache does this sort of thing to know
1379 when to create a new texture.
1380*/
1381void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
1382 buffer.write32(fWidth);
1383 buffer.write32(fHeight);
1384 buffer.write32(fRowBytes);
1385 buffer.write8(fConfig);
1386 buffer.writeBool(this->isOpaque());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001387
reed@android.com8a1c16f2008-12-17 15:59:43 +00001388 /* If we are called in this mode, then it is up to the caller to manage
1389 the owner-counts on the pixelref, as we just record the ptr itself.
1390 */
1391 if (!buffer.persistBitmapPixels()) {
1392 if (fPixelRef) {
1393 buffer.write8(SERIALIZE_PIXELTYPE_REF_PTR);
1394 buffer.write32(fPixelRefOffset);
1395 buffer.writeRefCnt(fPixelRef);
1396 return;
1397 } else {
1398 // we ignore the non-persist request, since we don't have a ref
1399 // ... or we could just write an empty bitmap...
1400 // (true) will write an empty bitmap, (false) will flatten the pix
1401 if (true) {
1402 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1403 return;
1404 }
1405 }
1406 }
1407
1408 if (fPixelRef) {
djsollen@google.com5370cd92012-03-28 20:47:01 +00001409 if (fPixelRef->getFactory()) {
1410 buffer.write8(SERIALIZE_PIXELTYPE_REF_DATA);
1411 buffer.write32(fPixelRefOffset);
1412 buffer.writeFlattenable(fPixelRef);
1413 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001414 }
1415 // if we get here, we can't record the pixels
1416 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001417 } else {
1418 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1419 }
1420}
1421
1422void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) {
1423 this->reset();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001424
reed@android.com8a1c16f2008-12-17 15:59:43 +00001425 int width = buffer.readInt();
1426 int height = buffer.readInt();
1427 int rowBytes = buffer.readInt();
1428 int config = buffer.readU8();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001429
reed@android.com8a1c16f2008-12-17 15:59:43 +00001430 this->setConfig((Config)config, width, height, rowBytes);
1431 this->setIsOpaque(buffer.readBool());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001432
reed@android.com8a1c16f2008-12-17 15:59:43 +00001433 int reftype = buffer.readU8();
1434 switch (reftype) {
1435 case SERIALIZE_PIXELTYPE_REF_PTR: {
1436 size_t offset = buffer.readU32();
1437 SkPixelRef* pr = (SkPixelRef*)buffer.readRefCnt();
1438 this->setPixelRef(pr, offset);
1439 break;
1440 }
1441 case SERIALIZE_PIXELTYPE_REF_DATA: {
1442 size_t offset = buffer.readU32();
djsollen@google.com5370cd92012-03-28 20:47:01 +00001443 SkPixelRef* pr = static_cast<SkPixelRef*>(buffer.readFlattenable());
reed@google.com82065d62011-02-07 15:30:46 +00001444 SkSafeUnref(this->setPixelRef(pr, offset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001445 break;
1446 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001447 case SERIALIZE_PIXELTYPE_NONE:
1448 break;
1449 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001450 SkDEBUGFAIL("unrecognized pixeltype in serialized data");
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451 sk_throw();
1452 }
1453}
1454
1455///////////////////////////////////////////////////////////////////////////////
1456
1457SkBitmap::RLEPixels::RLEPixels(int width, int height) {
1458 fHeight = height;
1459 fYPtrs = (uint8_t**)sk_malloc_throw(height * sizeof(uint8_t*));
reed@android.com4516f472009-06-29 16:25:36 +00001460 sk_bzero(fYPtrs, height * sizeof(uint8_t*));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001461}
1462
1463SkBitmap::RLEPixels::~RLEPixels() {
1464 sk_free(fYPtrs);
1465}
1466
1467///////////////////////////////////////////////////////////////////////////////
1468
1469#ifdef SK_DEBUG
1470void SkBitmap::validate() const {
1471 SkASSERT(fConfig < kConfigCount);
1472 SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
junov@google.com4ee7ae52011-06-30 17:30:49 +00001473 SkASSERT(fFlags <= (kImageIsOpaque_Flag | kImageIsVolatile_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001474 SkASSERT(fPixelLockCount >= 0);
1475 SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000);
1476 SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
1477
1478#if 0 // these asserts are not thread-correct, so disable for now
1479 if (fPixelRef) {
1480 if (fPixelLockCount > 0) {
reed@google.comff0da4f2012-05-17 13:14:52 +00001481 SkASSERT(fPixelRef->isLocked());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001482 } else {
1483 SkASSERT(NULL == fPixels);
1484 SkASSERT(NULL == fColorTable);
1485 }
1486 }
1487#endif
1488}
1489#endif