blob: 2376dd3117b573289bb7f4c0a7f83a56d32812c8 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com8a1c16f2008-12-17 15:59:43 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkBitmap.h"
11#include "SkColorPriv.h"
12#include "SkDither.h"
13#include "SkFlattenable.h"
14#include "SkMallocPixelRef.h"
15#include "SkMask.h"
16#include "SkPixelRef.h"
17#include "SkThread.h"
vandebo@chromium.org112706d2011-02-24 22:50:55 +000018#include "SkUnPreMultiply.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#include "SkUtils.h"
20#include "SkPackBits.h"
21#include <new>
22
bsalomon@google.com586f48c2011-04-14 15:07:22 +000023extern int32_t SkNextPixelRefGenerationID();
24
reed@android.com149e2f62009-05-22 14:39:03 +000025static bool isPos32Bits(const Sk64& value) {
26 return !value.isNeg() && value.is32();
27}
28
reed@android.com8a1c16f2008-12-17 15:59:43 +000029struct MipLevel {
30 void* fPixels;
31 uint32_t fRowBytes;
reed@android.comf459a492009-03-27 12:33:50 +000032 uint32_t fWidth, fHeight;
reed@android.com8a1c16f2008-12-17 15:59:43 +000033};
34
35struct SkBitmap::MipMap : SkNoncopyable {
36 int32_t fRefCnt;
37 int fLevelCount;
38// MipLevel fLevel[fLevelCount];
39// Pixels[]
weita@google.comf9ab99a2009-05-03 18:23:30 +000040
reed@android.com8a1c16f2008-12-17 15:59:43 +000041 static MipMap* Alloc(int levelCount, size_t pixelSize) {
reed@android.com149e2f62009-05-22 14:39:03 +000042 if (levelCount < 0) {
43 return NULL;
44 }
45 Sk64 size;
46 size.setMul(levelCount + 1, sizeof(MipLevel));
47 size.add(sizeof(MipMap));
48 size.add(pixelSize);
49 if (!isPos32Bits(size)) {
50 return NULL;
51 }
52 MipMap* mm = (MipMap*)sk_malloc_throw(size.get32());
reed@android.com8a1c16f2008-12-17 15:59:43 +000053 mm->fRefCnt = 1;
54 mm->fLevelCount = levelCount;
55 return mm;
56 }
57
58 const MipLevel* levels() const { return (const MipLevel*)(this + 1); }
59 MipLevel* levels() { return (MipLevel*)(this + 1); }
60
61 const void* pixels() const { return levels() + fLevelCount; }
62 void* pixels() { return levels() + fLevelCount; }
weita@google.comf9ab99a2009-05-03 18:23:30 +000063
reed@android.com149e2f62009-05-22 14:39:03 +000064 void ref() {
65 if (SK_MaxS32 == sk_atomic_inc(&fRefCnt)) {
66 sk_throw();
reed@android.com8a1c16f2008-12-17 15:59:43 +000067 }
68 }
reed@android.com149e2f62009-05-22 14:39:03 +000069 void unref() {
70 SkASSERT(fRefCnt > 0);
71 if (sk_atomic_dec(&fRefCnt) == 1) {
72 sk_free(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +000073 }
74 }
75};
reed@android.com8a1c16f2008-12-17 15:59:43 +000076
77///////////////////////////////////////////////////////////////////////////////
78///////////////////////////////////////////////////////////////////////////////
79
80SkBitmap::SkBitmap() {
reed@android.com4516f472009-06-29 16:25:36 +000081 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000082}
83
84SkBitmap::SkBitmap(const SkBitmap& src) {
85 SkDEBUGCODE(src.validate();)
reed@android.com4516f472009-06-29 16:25:36 +000086 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000087 *this = src;
88 SkDEBUGCODE(this->validate();)
89}
90
91SkBitmap::~SkBitmap() {
92 SkDEBUGCODE(this->validate();)
93 this->freePixels();
94}
95
96SkBitmap& SkBitmap::operator=(const SkBitmap& src) {
97 if (this != &src) {
98 this->freePixels();
99 memcpy(this, &src, sizeof(src));
100
101 // inc src reference counts
reed@android.com83f7bc32009-07-17 02:42:41 +0000102 SkSafeRef(src.fPixelRef);
reed@android.com149e2f62009-05-22 14:39:03 +0000103 SkSafeRef(src.fMipMap);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104
105 // we reset our locks if we get blown away
106 fPixelLockCount = 0;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000107
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108 /* The src could be in 3 states
109 1. no pixelref, in which case we just copy/ref the pixels/ctable
110 2. unlocked pixelref, pixels/ctable should be null
111 3. locked pixelref, we should lock the ref again ourselves
112 */
113 if (NULL == fPixelRef) {
114 // leave fPixels as it is
reed@google.com82065d62011-02-07 15:30:46 +0000115 SkSafeRef(fColorTable); // ref the user's ctable if present
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116 } else { // we have a pixelref, so pixels/ctable reflect it
117 // ignore the values from the memcpy
118 fPixels = NULL;
119 fColorTable = NULL;
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000120 // Note that what to for genID is somewhat arbitrary. We have no
121 // way to track changes to raw pixels across multiple SkBitmaps.
122 // Would benefit from an SkRawPixelRef type created by
123 // setPixels.
124 // Just leave the memcpy'ed one but they'll get out of sync
125 // as soon either is modified.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126 }
127 }
128
129 SkDEBUGCODE(this->validate();)
130 return *this;
131}
132
133void SkBitmap::swap(SkBitmap& other) {
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000134 SkTSwap(fColorTable, other.fColorTable);
135 SkTSwap(fPixelRef, other.fPixelRef);
136 SkTSwap(fPixelRefOffset, other.fPixelRefOffset);
137 SkTSwap(fPixelLockCount, other.fPixelLockCount);
138 SkTSwap(fMipMap, other.fMipMap);
139 SkTSwap(fPixels, other.fPixels);
140 SkTSwap(fRawPixelGenerationID, other.fRawPixelGenerationID);
141 SkTSwap(fRowBytes, other.fRowBytes);
142 SkTSwap(fWidth, other.fWidth);
143 SkTSwap(fHeight, other.fHeight);
144 SkTSwap(fConfig, other.fConfig);
145 SkTSwap(fFlags, other.fFlags);
146 SkTSwap(fBytesPerPixel, other.fBytesPerPixel);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147
148 SkDEBUGCODE(this->validate();)
149}
150
151void SkBitmap::reset() {
152 this->freePixels();
reed@android.com4516f472009-06-29 16:25:36 +0000153 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000154}
155
156int SkBitmap::ComputeBytesPerPixel(SkBitmap::Config config) {
157 int bpp;
158 switch (config) {
159 case kNo_Config:
160 case kA1_Config:
161 bpp = 0; // not applicable
162 break;
163 case kRLE_Index8_Config:
164 case kA8_Config:
165 case kIndex8_Config:
166 bpp = 1;
167 break;
168 case kRGB_565_Config:
169 case kARGB_4444_Config:
170 bpp = 2;
171 break;
172 case kARGB_8888_Config:
173 bpp = 4;
174 break;
175 default:
176 SkASSERT(!"unknown config");
177 bpp = 0; // error
178 break;
179 }
180 return bpp;
181}
182
183int SkBitmap::ComputeRowBytes(Config c, int width) {
reed@android.com149e2f62009-05-22 14:39:03 +0000184 if (width < 0) {
185 return 0;
186 }
187
188 Sk64 rowBytes;
189 rowBytes.setZero();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000190
191 switch (c) {
192 case kNo_Config:
193 case kRLE_Index8_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194 break;
195 case kA1_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000196 rowBytes.set(width);
197 rowBytes.add(7);
198 rowBytes.shiftRight(3);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 break;
200 case kA8_Config:
201 case kIndex8_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000202 rowBytes.set(width);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203 break;
204 case kRGB_565_Config:
205 case kARGB_4444_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000206 rowBytes.set(width);
207 rowBytes.shiftLeft(1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208 break;
209 case kARGB_8888_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000210 rowBytes.set(width);
211 rowBytes.shiftLeft(2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212 break;
213 default:
214 SkASSERT(!"unknown config");
215 break;
216 }
reed@android.com149e2f62009-05-22 14:39:03 +0000217 return isPos32Bits(rowBytes) ? rowBytes.get32() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218}
219
220Sk64 SkBitmap::ComputeSize64(Config c, int width, int height) {
221 Sk64 size;
222 size.setMul(SkBitmap::ComputeRowBytes(c, width), height);
223 return size;
224}
225
226size_t SkBitmap::ComputeSize(Config c, int width, int height) {
227 Sk64 size = SkBitmap::ComputeSize64(c, width, height);
reed@android.com149e2f62009-05-22 14:39:03 +0000228 return isPos32Bits(size) ? size.get32() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229}
230
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000231Sk64 SkBitmap::ComputeSafeSize64(Config config,
232 uint32_t width,
233 uint32_t height,
234 uint32_t rowBytes) {
235 Sk64 safeSize;
236 safeSize.setZero();
237 if (height > 0) {
238 safeSize.set(ComputeRowBytes(config, width));
239 Sk64 sizeAllButLastRow;
240 sizeAllButLastRow.setMul(height - 1, rowBytes);
241 safeSize.add(sizeAllButLastRow);
242 }
243 SkASSERT(!safeSize.isNeg());
244 return safeSize;
245}
246
247size_t SkBitmap::ComputeSafeSize(Config config,
248 uint32_t width,
249 uint32_t height,
250 uint32_t rowBytes) {
251 Sk64 safeSize = ComputeSafeSize64(config, width, height, rowBytes);
252 return (safeSize.is32() ? safeSize.get32() : 0);
253}
254
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255void SkBitmap::setConfig(Config c, int width, int height, int rowBytes) {
256 this->freePixels();
257
reed@android.com149e2f62009-05-22 14:39:03 +0000258 if ((width | height | rowBytes) < 0) {
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000259 goto err;
reed@android.com149e2f62009-05-22 14:39:03 +0000260 }
261
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262 if (rowBytes == 0) {
263 rowBytes = SkBitmap::ComputeRowBytes(c, width);
reed@android.com149e2f62009-05-22 14:39:03 +0000264 if (0 == rowBytes && kNo_Config != c) {
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000265 goto err;
reed@android.com149e2f62009-05-22 14:39:03 +0000266 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267 }
reed@android.com89bb83a2009-05-29 21:30:42 +0000268
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269 fConfig = SkToU8(c);
reed@android.comf459a492009-03-27 12:33:50 +0000270 fWidth = width;
271 fHeight = height;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272 fRowBytes = rowBytes;
273
274 fBytesPerPixel = (uint8_t)ComputeBytesPerPixel(c);
275
276 SkDEBUGCODE(this->validate();)
reed@android.com9e0c2fc2009-06-02 18:54:28 +0000277 return;
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000278
reed@android.com9e0c2fc2009-06-02 18:54:28 +0000279 // if we got here, we had an error, so we reset the bitmap to empty
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000280err:
281 this->reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282}
283
284void SkBitmap::updatePixelsFromRef() const {
285 if (NULL != fPixelRef) {
286 if (fPixelLockCount > 0) {
287 SkASSERT(fPixelRef->getLockCount() > 0);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000288
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289 void* p = fPixelRef->pixels();
290 if (NULL != p) {
291 p = (char*)p + fPixelRefOffset;
292 }
293 fPixels = p;
294 SkRefCnt_SafeAssign(fColorTable, fPixelRef->colorTable());
295 } else {
296 SkASSERT(0 == fPixelLockCount);
297 fPixels = NULL;
reed@android.com149e2f62009-05-22 14:39:03 +0000298 if (fColorTable) {
299 fColorTable->unref();
300 fColorTable = NULL;
301 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302 }
303 }
304}
305
306SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, size_t offset) {
307 // do this first, we that we never have a non-zero offset with a null ref
308 if (NULL == pr) {
309 offset = 0;
310 }
311
312 if (fPixelRef != pr || fPixelRefOffset != offset) {
313 if (fPixelRef != pr) {
314 this->freePixels();
315 SkASSERT(NULL == fPixelRef);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000316
reed@google.com82065d62011-02-07 15:30:46 +0000317 SkSafeRef(pr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000318 fPixelRef = pr;
319 }
320 fPixelRefOffset = offset;
321 this->updatePixelsFromRef();
322 }
323
324 SkDEBUGCODE(this->validate();)
325 return pr;
326}
327
328void SkBitmap::lockPixels() const {
329 if (NULL != fPixelRef && 1 == ++fPixelLockCount) {
330 fPixelRef->lockPixels();
331 this->updatePixelsFromRef();
332 }
333 SkDEBUGCODE(this->validate();)
334}
335
336void SkBitmap::unlockPixels() const {
337 SkASSERT(NULL == fPixelRef || fPixelLockCount > 0);
338
339 if (NULL != fPixelRef && 0 == --fPixelLockCount) {
340 fPixelRef->unlockPixels();
341 this->updatePixelsFromRef();
342 }
343 SkDEBUGCODE(this->validate();)
344}
345
reed@google.com9c49bc32011-07-07 13:42:37 +0000346bool SkBitmap::lockPixelsAreWritable() const {
347 if (fPixelRef) {
348 return fPixelRef->lockPixelsAreWritable();
349 } else {
350 return fPixels != NULL;
351 }
352}
353
reed@android.com8a1c16f2008-12-17 15:59:43 +0000354void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
355 this->freePixels();
356 fPixels = p;
357 SkRefCnt_SafeAssign(fColorTable, ctable);
358
359 SkDEBUGCODE(this->validate();)
360}
361
362bool SkBitmap::allocPixels(Allocator* allocator, SkColorTable* ctable) {
363 HeapAllocator stdalloc;
364
365 if (NULL == allocator) {
366 allocator = &stdalloc;
367 }
368 return allocator->allocPixelRef(this, ctable);
369}
370
371void SkBitmap::freePixels() {
372 // if we're gonna free the pixels, we certainly need to free the mipmap
373 this->freeMipMap();
374
reed@android.com149e2f62009-05-22 14:39:03 +0000375 if (fColorTable) {
376 fColorTable->unref();
377 fColorTable = NULL;
378 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379
380 if (NULL != fPixelRef) {
381 if (fPixelLockCount > 0) {
382 fPixelRef->unlockPixels();
383 }
384 fPixelRef->unref();
385 fPixelRef = NULL;
386 fPixelRefOffset = 0;
387 }
388 fPixelLockCount = 0;
389 fPixels = NULL;
390}
391
392void SkBitmap::freeMipMap() {
reed@android.com149e2f62009-05-22 14:39:03 +0000393 if (fMipMap) {
394 fMipMap->unref();
395 fMipMap = NULL;
396 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000397}
398
399uint32_t SkBitmap::getGenerationID() const {
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000400 if (fPixelRef) {
401 return fPixelRef->getGenerationID();
402 } else {
403 SkASSERT(fPixels || !fRawPixelGenerationID);
404 if (fPixels && !fRawPixelGenerationID) {
405 fRawPixelGenerationID = SkNextPixelRefGenerationID();
406 }
407 return fRawPixelGenerationID;
408 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409}
410
411void SkBitmap::notifyPixelsChanged() const {
junov@chromium.orgb0521292011-12-15 20:14:06 +0000412 SkASSERT(!this->isImmutable());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000413 if (fPixelRef) {
414 fPixelRef->notifyPixelsChanged();
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000415 } else {
416 fRawPixelGenerationID = 0; // will grab next ID in getGenerationID
reed@android.com8a1c16f2008-12-17 15:59:43 +0000417 }
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:
550 SkASSERT(!"unknown bitmap config pased to isOpaque");
551 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:
601 SkASSERT(!"Can't return addr for kRLE_Index8_Config");
602 base = NULL;
603 break;
604 default:
605 SkASSERT(!"Can't return addr for config");
606 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
779 if (NULL == result || (NULL == fPixelRef && NULL == fPixels)) {
780 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());
824
825 if (fPixelRef) {
826 // share the pixelref with a custom offset
827 dst.setPixelRef(fPixelRef, fPixelRefOffset + offset);
828 } else {
829 // share the pixels (owned by the caller)
830 dst.setPixels((char*)fPixels + offset, this->getColorTable());
831 }
832 SkDEBUGCODE(dst.validate();)
833
834 // we know we're good, so commit to result
835 result->swap(dst);
836 return true;
837}
838
839///////////////////////////////////////////////////////////////////////////////
840
841#include "SkCanvas.h"
842#include "SkPaint.h"
843
reed@android.comfbaa88d2009-05-06 17:44:34 +0000844bool SkBitmap::canCopyTo(Config dstConfig) const {
845 if (this->getConfig() == kNo_Config) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000846 return false;
847 }
848
reed@android.comfbaa88d2009-05-06 17:44:34 +0000849 bool sameConfigs = (this->config() == dstConfig);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000850 switch (dstConfig) {
851 case kA8_Config:
852 case kARGB_4444_Config:
853 case kRGB_565_Config:
854 case kARGB_8888_Config:
855 break;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000856 case kA1_Config:
857 case kIndex8_Config:
858 if (!sameConfigs) {
859 return false;
860 }
861 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862 default:
863 return false;
864 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000865
866 // do not copy src if srcConfig == kA1_Config while dstConfig != kA1_Config
867 if (this->getConfig() == kA1_Config && !sameConfigs) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000868 return false;
869 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000870
reed@android.comfbaa88d2009-05-06 17:44:34 +0000871 return true;
872}
873
874bool SkBitmap::copyTo(SkBitmap* dst, Config dstConfig, Allocator* alloc) const {
875 if (!this->canCopyTo(dstConfig)) {
876 return false;
877 }
878
reed@google.com50dfa012011-04-01 19:05:36 +0000879 // if we have a texture, first get those pixels
880 SkBitmap tmpSrc;
881 const SkBitmap* src = this;
882
bsalomon@google.com669fdc42011-04-05 17:08:27 +0000883 if (fPixelRef && fPixelRef->readPixels(&tmpSrc)) {
reed@google.com50dfa012011-04-01 19:05:36 +0000884 SkASSERT(tmpSrc.width() == this->width());
885 SkASSERT(tmpSrc.height() == this->height());
886
887 // did we get lucky and we can just return tmpSrc?
888 if (tmpSrc.config() == dstConfig && NULL == alloc) {
889 dst->swap(tmpSrc);
890 return true;
891 }
892
893 // fall through to the raster case
894 src = &tmpSrc;
reed@android.comfbaa88d2009-05-06 17:44:34 +0000895 }
reed@android.com311c82d2009-05-05 23:13:23 +0000896
reed@google.com50dfa012011-04-01 19:05:36 +0000897 // we lock this now, since we may need its colortable
898 SkAutoLockPixels srclock(*src);
899 if (!src->readyToDraw()) {
900 return false;
901 }
902
903 SkBitmap tmpDst;
904 tmpDst.setConfig(dstConfig, src->width(), src->height());
905
weita@google.comf9ab99a2009-05-03 18:23:30 +0000906 // allocate colortable if srcConfig == kIndex8_Config
907 SkColorTable* ctable = (dstConfig == kIndex8_Config) ?
reed@google.com50dfa012011-04-01 19:05:36 +0000908 new SkColorTable(*src->getColorTable()) : NULL;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000909 SkAutoUnref au(ctable);
reed@google.com50dfa012011-04-01 19:05:36 +0000910 if (!tmpDst.allocPixels(alloc, ctable)) {
weita@google.comf9ab99a2009-05-03 18:23:30 +0000911 return false;
912 }
reed@google.com50dfa012011-04-01 19:05:36 +0000913
914 SkAutoLockPixels dstlock(tmpDst);
915 if (!tmpDst.readyToDraw()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000916 // allocator/lock failed
917 return false;
918 }
reed@google.com50dfa012011-04-01 19:05:36 +0000919
reed@android.comfbaa88d2009-05-06 17:44:34 +0000920 /* do memcpy for the same configs cases, else use drawing
weita@google.comf9ab99a2009-05-03 18:23:30 +0000921 */
reed@google.com50dfa012011-04-01 19:05:36 +0000922 if (src->config() == dstConfig) {
923 if (tmpDst.getSize() == src->getSize()) {
924 memcpy(tmpDst.getPixels(), src->getPixels(), src->getSafeSize());
reed@android.com311c82d2009-05-05 23:13:23 +0000925 } else {
reed@google.com50dfa012011-04-01 19:05:36 +0000926 const char* srcP = reinterpret_cast<const char*>(src->getPixels());
927 char* dstP = reinterpret_cast<char*>(tmpDst.getPixels());
reed@android.com311c82d2009-05-05 23:13:23 +0000928 // to be sure we don't read too much, only copy our logical pixels
reed@google.com50dfa012011-04-01 19:05:36 +0000929 size_t bytesToCopy = tmpDst.width() * tmpDst.bytesPerPixel();
930 for (int y = 0; y < tmpDst.height(); y++) {
reed@android.com311c82d2009-05-05 23:13:23 +0000931 memcpy(dstP, srcP, bytesToCopy);
reed@google.com50dfa012011-04-01 19:05:36 +0000932 srcP += src->rowBytes();
933 dstP += tmpDst.rowBytes();
reed@android.com311c82d2009-05-05 23:13:23 +0000934 }
935 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000936 } else {
937 // if the src has alpha, we have to clear the dst first
reed@google.com50dfa012011-04-01 19:05:36 +0000938 if (!src->isOpaque()) {
939 tmpDst.eraseColor(0);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000940 }
941
reed@google.com50dfa012011-04-01 19:05:36 +0000942 SkCanvas canvas(tmpDst);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000943 SkPaint paint;
944
945 paint.setDither(true);
reed@google.com50dfa012011-04-01 19:05:36 +0000946 canvas.drawBitmap(*src, 0, 0, &paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000947 }
948
reed@google.com50dfa012011-04-01 19:05:36 +0000949 tmpDst.setIsOpaque(src->isOpaque());
reed@android.comcafc9f92009-08-22 03:44:57 +0000950
reed@google.com50dfa012011-04-01 19:05:36 +0000951 dst->swap(tmpDst);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000952 return true;
953}
954
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +0000955bool SkBitmap::deepCopyTo(SkBitmap* dst, Config dstConfig) const {
956 if (!this->canCopyTo(dstConfig)) {
957 return false;
958 }
959
960 // If we have a PixelRef, and it supports deep copy, use it.
961 // Currently supported only by texture-backed bitmaps.
962 if (fPixelRef) {
963 SkPixelRef* pixelRef = fPixelRef->deepCopy(dstConfig);
964 if (pixelRef) {
965 dst->setConfig(dstConfig, fWidth, fHeight);
966 dst->setPixelRef(pixelRef)->unref();
967 return true;
968 }
969 }
970
971 if (this->getTexture()) {
972 return false;
973 } else {
974 return this->copyTo(dst, dstConfig, NULL);
975 }
976}
977
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978///////////////////////////////////////////////////////////////////////////////
979///////////////////////////////////////////////////////////////////////////////
980
981static void downsampleby2_proc32(SkBitmap* dst, int x, int y,
982 const SkBitmap& src) {
983 x <<= 1;
984 y <<= 1;
985 const SkPMColor* p = src.getAddr32(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +0000986 const SkPMColor* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000987 SkPMColor c, ag, rb;
988
989 c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF;
990 if (x < src.width() - 1) {
991 p += 1;
992 }
993 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
994
reed@android.com829c83c2009-06-08 12:05:31 +0000995 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000996 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +0000997 p += src.rowBytes() >> 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000998 }
999 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1000 if (x < src.width() - 1) {
1001 p += 1;
1002 }
1003 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1004
1005 *dst->getAddr32(x >> 1, y >> 1) =
1006 ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00);
1007}
1008
1009static inline uint32_t expand16(U16CPU c) {
1010 return (c & ~SK_G16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16);
1011}
1012
1013// returns dirt in the top 16bits, but we don't care, since we only
1014// store the low 16bits.
1015static inline U16CPU pack16(uint32_t c) {
1016 return (c & ~SK_G16_MASK_IN_PLACE) | ((c >> 16) & SK_G16_MASK_IN_PLACE);
1017}
1018
1019static void downsampleby2_proc16(SkBitmap* dst, int x, int y,
1020 const SkBitmap& src) {
1021 x <<= 1;
1022 y <<= 1;
1023 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001024 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001025 SkPMColor c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001026
reed@android.com8a1c16f2008-12-17 15:59:43 +00001027 c = expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001028 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001029 p += 1;
1030 }
1031 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001032
reed@android.com829c83c2009-06-08 12:05:31 +00001033 p = baseP;
1034 if (y < src.height() - 1) {
1035 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001036 }
1037 c += expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001038 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001039 p += 1;
1040 }
1041 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001042
reed@android.com8a1c16f2008-12-17 15:59:43 +00001043 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)pack16(c >> 2);
1044}
1045
1046static uint32_t expand4444(U16CPU c) {
1047 return (c & 0xF0F) | ((c & ~0xF0F) << 12);
1048}
1049
1050static U16CPU collaps4444(uint32_t c) {
1051 return (c & 0xF0F) | ((c >> 12) & ~0xF0F);
1052}
1053
1054static void downsampleby2_proc4444(SkBitmap* dst, int x, int y,
1055 const SkBitmap& src) {
1056 x <<= 1;
1057 y <<= 1;
1058 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001059 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060 uint32_t c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001061
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062 c = expand4444(*p);
1063 if (x < src.width() - 1) {
1064 p += 1;
1065 }
1066 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001067
reed@android.com829c83c2009-06-08 12:05:31 +00001068 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001069 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +00001070 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071 }
1072 c += expand4444(*p);
1073 if (x < src.width() - 1) {
1074 p += 1;
1075 }
1076 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001077
reed@android.com8a1c16f2008-12-17 15:59:43 +00001078 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)collaps4444(c >> 2);
1079}
1080
1081void SkBitmap::buildMipMap(bool forceRebuild) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001082 if (forceRebuild)
1083 this->freeMipMap();
1084 else if (fMipMap)
1085 return; // we're already built
1086
1087 SkASSERT(NULL == fMipMap);
1088
1089 void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src);
1090
1091 const SkBitmap::Config config = this->getConfig();
1092
1093 switch (config) {
1094 case kARGB_8888_Config:
1095 proc = downsampleby2_proc32;
1096 break;
1097 case kRGB_565_Config:
1098 proc = downsampleby2_proc16;
1099 break;
1100 case kARGB_4444_Config:
1101 proc = downsampleby2_proc4444;
1102 break;
1103 case kIndex8_Config:
1104 case kA8_Config:
1105 default:
1106 return; // don't build mipmaps for these configs
1107 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001108
reed@android.com149e2f62009-05-22 14:39:03 +00001109 SkAutoLockPixels alp(*this);
1110 if (!this->readyToDraw()) {
1111 return;
1112 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001113
1114 // whip through our loop to compute the exact size needed
1115 size_t size = 0;
1116 int maxLevels = 0;
1117 {
reed@android.com149e2f62009-05-22 14:39:03 +00001118 int width = this->width();
1119 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001120 for (;;) {
1121 width >>= 1;
1122 height >>= 1;
1123 if (0 == width || 0 == height) {
1124 break;
1125 }
1126 size += ComputeRowBytes(config, width) * height;
1127 maxLevels += 1;
1128 }
1129 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001130
reed@android.com149e2f62009-05-22 14:39:03 +00001131 // nothing to build
reed@android.com8a1c16f2008-12-17 15:59:43 +00001132 if (0 == maxLevels) {
1133 return;
1134 }
1135
reed@android.com149e2f62009-05-22 14:39:03 +00001136 SkBitmap srcBM(*this);
1137 srcBM.lockPixels();
1138 if (!srcBM.readyToDraw()) {
1139 return;
1140 }
1141
1142 MipMap* mm = MipMap::Alloc(maxLevels, size);
1143 if (NULL == mm) {
1144 return;
1145 }
1146
reed@android.com8a1c16f2008-12-17 15:59:43 +00001147 MipLevel* level = mm->levels();
1148 uint8_t* addr = (uint8_t*)mm->pixels();
reed@android.com149e2f62009-05-22 14:39:03 +00001149 int width = this->width();
1150 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001151 unsigned rowBytes = this->rowBytes();
reed@android.com149e2f62009-05-22 14:39:03 +00001152 SkBitmap dstBM;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001153
1154 for (int i = 0; i < maxLevels; i++) {
1155 width >>= 1;
1156 height >>= 1;
1157 rowBytes = ComputeRowBytes(config, width);
1158
1159 level[i].fPixels = addr;
reed@android.comf459a492009-03-27 12:33:50 +00001160 level[i].fWidth = width;
1161 level[i].fHeight = height;
reed@android.com49f0ff22009-03-19 21:52:42 +00001162 level[i].fRowBytes = rowBytes;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001163
1164 dstBM.setConfig(config, width, height, rowBytes);
1165 dstBM.setPixels(addr);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001166
reed@android.com149e2f62009-05-22 14:39:03 +00001167 for (int y = 0; y < height; y++) {
1168 for (int x = 0; x < width; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001169 proc(&dstBM, x, y, srcBM);
1170 }
1171 }
1172
1173 srcBM = dstBM;
1174 addr += height * rowBytes;
1175 }
1176 SkASSERT(addr == (uint8_t*)mm->pixels() + size);
1177 fMipMap = mm;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178}
1179
1180bool SkBitmap::hasMipMap() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001181 return fMipMap != NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001182}
1183
1184int SkBitmap::extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy) {
reed@android.com83f7bc32009-07-17 02:42:41 +00001185 if (NULL == fMipMap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001186 return 0;
reed@android.com83f7bc32009-07-17 02:42:41 +00001187 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001188
reed@android.com8a1c16f2008-12-17 15:59:43 +00001189 int level = ComputeMipLevel(sx, sy) >> 16;
1190 SkASSERT(level >= 0);
1191 if (level <= 0) {
1192 return 0;
1193 }
1194
1195 if (level >= fMipMap->fLevelCount) {
1196 level = fMipMap->fLevelCount - 1;
1197 }
1198 if (dst) {
1199 const MipLevel& mip = fMipMap->levels()[level - 1];
1200 dst->setConfig((SkBitmap::Config)this->config(),
1201 mip.fWidth, mip.fHeight, mip.fRowBytes);
1202 dst->setPixels(mip.fPixels);
1203 }
1204 return level;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001205}
1206
1207SkFixed SkBitmap::ComputeMipLevel(SkFixed sx, SkFixed sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001208 sx = SkAbs32(sx);
1209 sy = SkAbs32(sy);
1210 if (sx < sy) {
1211 sx = sy;
1212 }
1213 if (sx < SK_Fixed1) {
1214 return 0;
1215 }
1216 int clz = SkCLZ(sx);
1217 SkASSERT(clz >= 1 && clz <= 15);
1218 return SkIntToFixed(15 - clz) + ((unsigned)(sx << (clz + 1)) >> 16);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001219}
1220
1221///////////////////////////////////////////////////////////////////////////////
1222
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +00001223static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001224 int alphaRowBytes) {
1225 SkASSERT(alpha != NULL);
1226 SkASSERT(alphaRowBytes >= src.width());
1227
1228 SkBitmap::Config config = src.getConfig();
1229 int w = src.width();
1230 int h = src.height();
1231 int rb = src.rowBytes();
1232
reed@android.com1cdcb512009-08-24 19:11:00 +00001233 SkAutoLockPixels alp(src);
1234 if (!src.readyToDraw()) {
1235 // zero out the alpha buffer and return
1236 while (--h >= 0) {
1237 memset(alpha, 0, w);
1238 alpha += alphaRowBytes;
1239 }
1240 return false;
1241 }
reed@google.com82065d62011-02-07 15:30:46 +00001242
reed@android.com8a1c16f2008-12-17 15:59:43 +00001243 if (SkBitmap::kA8_Config == config && !src.isOpaque()) {
1244 const uint8_t* s = src.getAddr8(0, 0);
1245 while (--h >= 0) {
1246 memcpy(alpha, s, w);
1247 s += rb;
1248 alpha += alphaRowBytes;
1249 }
1250 } else if (SkBitmap::kARGB_8888_Config == config && !src.isOpaque()) {
1251 const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0);
1252 while (--h >= 0) {
1253 for (int x = 0; x < w; x++) {
1254 alpha[x] = SkGetPackedA32(s[x]);
1255 }
1256 s = (const SkPMColor*)((const char*)s + rb);
1257 alpha += alphaRowBytes;
1258 }
1259 } else if (SkBitmap::kARGB_4444_Config == config && !src.isOpaque()) {
1260 const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0);
1261 while (--h >= 0) {
1262 for (int x = 0; x < w; x++) {
1263 alpha[x] = SkPacked4444ToA32(s[x]);
1264 }
1265 s = (const SkPMColor16*)((const char*)s + rb);
1266 alpha += alphaRowBytes;
1267 }
1268 } else if (SkBitmap::kIndex8_Config == config && !src.isOpaque()) {
1269 SkColorTable* ct = src.getColorTable();
1270 if (ct) {
1271 const SkPMColor* SK_RESTRICT table = ct->lockColors();
1272 const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0);
1273 while (--h >= 0) {
1274 for (int x = 0; x < w; x++) {
1275 alpha[x] = SkGetPackedA32(table[s[x]]);
1276 }
1277 s += rb;
1278 alpha += alphaRowBytes;
1279 }
1280 ct->unlockColors(false);
1281 }
1282 } else { // src is opaque, so just fill alpha[] with 0xFF
1283 memset(alpha, 0xFF, h * alphaRowBytes);
1284 }
reed@android.com1cdcb512009-08-24 19:11:00 +00001285 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001286}
1287
1288#include "SkPaint.h"
1289#include "SkMaskFilter.h"
1290#include "SkMatrix.h"
1291
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001292bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
djsollen@google.com57f49692011-02-23 20:46:31 +00001293 Allocator *allocator, SkIPoint* offset) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001294 SkDEBUGCODE(this->validate();)
1295
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001296 SkBitmap tmpBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001297 SkMatrix identity;
1298 SkMask srcM, dstM;
1299
1300 srcM.fBounds.set(0, 0, this->width(), this->height());
1301 srcM.fRowBytes = SkAlign4(this->width());
1302 srcM.fFormat = SkMask::kA8_Format;
1303
1304 SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL;
1305
1306 // compute our (larger?) dst bounds if we have a filter
1307 if (NULL != filter) {
1308 identity.reset();
1309 srcM.fImage = NULL;
1310 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1311 goto NO_FILTER_CASE;
1312 }
1313 dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
1314 } else {
1315 NO_FILTER_CASE:
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001316 tmpBitmap.setConfig(SkBitmap::kA8_Config, this->width(), this->height(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001317 srcM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001318 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1319 // Allocation of pixels for alpha bitmap failed.
1320 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1321 tmpBitmap.width(), tmpBitmap.height());
1322 return false;
1323 }
1324 GetBitmapAlpha(*this, tmpBitmap.getAddr8(0, 0), srcM.fRowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001325 if (offset) {
1326 offset->set(0, 0);
1327 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001328 tmpBitmap.swap(*dst);
1329 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001331 srcM.fImage = SkMask::AllocImage(srcM.computeImageSize());
1332 SkAutoMaskFreeImage srcCleanup(srcM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333
1334 GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
1335 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1336 goto NO_FILTER_CASE;
1337 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001338 SkAutoMaskFreeImage dstCleanup(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001339
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001340 tmpBitmap.setConfig(SkBitmap::kA8_Config, dstM.fBounds.width(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001341 dstM.fBounds.height(), dstM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001342 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1343 // Allocation of pixels for alpha bitmap failed.
1344 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1345 tmpBitmap.width(), tmpBitmap.height());
1346 return false;
1347 }
1348 memcpy(tmpBitmap.getPixels(), dstM.fImage, dstM.computeImageSize());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001349 if (offset) {
1350 offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
1351 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001352 SkDEBUGCODE(tmpBitmap.validate();)
1353
1354 tmpBitmap.swap(*dst);
1355 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001356}
1357
1358///////////////////////////////////////////////////////////////////////////////
1359
1360enum {
1361 SERIALIZE_PIXELTYPE_NONE,
1362 SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE,
1363 SERIALIZE_PIXELTYPE_RAW_NO_CTABLE,
1364 SERIALIZE_PIXELTYPE_REF_DATA,
1365 SERIALIZE_PIXELTYPE_REF_PTR,
1366};
1367
1368static void writeString(SkFlattenableWriteBuffer& buffer, const char str[]) {
1369 size_t len = strlen(str);
1370 buffer.write32(len);
1371 buffer.writePad(str, len);
1372}
1373
1374static SkPixelRef::Factory deserialize_factory(SkFlattenableReadBuffer& buffer) {
1375 size_t len = buffer.readInt();
1376 SkAutoSMalloc<256> storage(len + 1);
1377 char* str = (char*)storage.get();
1378 buffer.read(str, len);
1379 str[len] = 0;
1380 return SkPixelRef::NameToFactory(str);
1381}
1382
1383/*
1384 It is tricky to know how much to flatten. If we don't have a pixelref (i.e.
1385 we just have pixels, then we can only flatten the pixels, or write out an
1386 empty bitmap.
weita@google.comf9ab99a2009-05-03 18:23:30 +00001387
reed@android.com8a1c16f2008-12-17 15:59:43 +00001388 With a pixelref, we still have the question of recognizing when two sitings
1389 of the same pixelref are the same, and when they are different. Perhaps we
1390 should look at the generationID and keep a record of that in some dictionary
1391 associated with the buffer. SkGLTextureCache does this sort of thing to know
1392 when to create a new texture.
1393*/
1394void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
1395 buffer.write32(fWidth);
1396 buffer.write32(fHeight);
1397 buffer.write32(fRowBytes);
1398 buffer.write8(fConfig);
1399 buffer.writeBool(this->isOpaque());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001400
reed@android.com8a1c16f2008-12-17 15:59:43 +00001401 /* If we are called in this mode, then it is up to the caller to manage
1402 the owner-counts on the pixelref, as we just record the ptr itself.
1403 */
1404 if (!buffer.persistBitmapPixels()) {
1405 if (fPixelRef) {
1406 buffer.write8(SERIALIZE_PIXELTYPE_REF_PTR);
1407 buffer.write32(fPixelRefOffset);
1408 buffer.writeRefCnt(fPixelRef);
1409 return;
1410 } else {
1411 // we ignore the non-persist request, since we don't have a ref
1412 // ... or we could just write an empty bitmap...
1413 // (true) will write an empty bitmap, (false) will flatten the pix
1414 if (true) {
1415 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1416 return;
1417 }
1418 }
1419 }
1420
1421 if (fPixelRef) {
1422 SkPixelRef::Factory fact = fPixelRef->getFactory();
1423 if (fact) {
1424 const char* name = SkPixelRef::FactoryToName(fact);
1425 if (name && *name) {
1426 buffer.write8(SERIALIZE_PIXELTYPE_REF_DATA);
1427 buffer.write32(fPixelRefOffset);
1428 writeString(buffer, name);
1429 fPixelRef->flatten(buffer);
1430 return;
1431 }
1432 }
1433 // if we get here, we can't record the pixels
1434 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1435 } else if (fPixels) {
1436 if (fColorTable) {
1437 buffer.write8(SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE);
1438 fColorTable->flatten(buffer);
1439 } else {
1440 buffer.write8(SERIALIZE_PIXELTYPE_RAW_NO_CTABLE);
1441 }
wjmaclean@chromium.org3f0260d2011-02-22 22:32:14 +00001442 buffer.writePad(fPixels, this->getSafeSize());
1443 // There is no writeZeroPad() fcn, so write individual bytes.
1444 if (this->getSize() > this->getSafeSize()) {
1445 size_t deltaSize = this->getSize() - this->getSafeSize();
1446 // Need aligned pointer to write into due to internal implementa-
1447 // tion of SkWriter32.
1448 memset(buffer.reserve(SkAlign4(deltaSize)), 0, deltaSize);
1449 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001450 } else {
1451 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1452 }
1453}
1454
1455void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) {
1456 this->reset();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001457
reed@android.com8a1c16f2008-12-17 15:59:43 +00001458 int width = buffer.readInt();
1459 int height = buffer.readInt();
1460 int rowBytes = buffer.readInt();
1461 int config = buffer.readU8();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001462
reed@android.com8a1c16f2008-12-17 15:59:43 +00001463 this->setConfig((Config)config, width, height, rowBytes);
1464 this->setIsOpaque(buffer.readBool());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001465
reed@android.com8a1c16f2008-12-17 15:59:43 +00001466 int reftype = buffer.readU8();
1467 switch (reftype) {
1468 case SERIALIZE_PIXELTYPE_REF_PTR: {
1469 size_t offset = buffer.readU32();
1470 SkPixelRef* pr = (SkPixelRef*)buffer.readRefCnt();
1471 this->setPixelRef(pr, offset);
1472 break;
1473 }
1474 case SERIALIZE_PIXELTYPE_REF_DATA: {
1475 size_t offset = buffer.readU32();
1476 SkPixelRef::Factory fact = deserialize_factory(buffer);
1477 SkPixelRef* pr = fact(buffer);
reed@google.com82065d62011-02-07 15:30:46 +00001478 SkSafeUnref(this->setPixelRef(pr, offset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001479 break;
1480 }
1481 case SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE:
1482 case SERIALIZE_PIXELTYPE_RAW_NO_CTABLE: {
1483 SkColorTable* ctable = NULL;
1484 if (SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE == reftype) {
1485 ctable = SkNEW_ARGS(SkColorTable, (buffer));
1486 }
wjmaclean@chromium.org3f0260d2011-02-22 22:32:14 +00001487 size_t size = this->getSize();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001488 if (this->allocPixels(ctable)) {
1489 this->lockPixels();
wjmaclean@chromium.org3f0260d2011-02-22 22:32:14 +00001490 // Just read what we need.
1491 buffer.read(this->getPixels(), this->getSafeSize());
1492 // Keep aligned for subsequent reads.
1493 buffer.skip(size - this->getSafeSize());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001494 this->unlockPixels();
1495 } else {
wjmaclean@chromium.org0bde1802010-12-22 17:43:54 +00001496 buffer.skip(size); // Still skip the full-sized buffer though.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001497 }
reed@android.com149e2f62009-05-22 14:39:03 +00001498 SkSafeUnref(ctable);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001499 break;
1500 }
1501 case SERIALIZE_PIXELTYPE_NONE:
1502 break;
1503 default:
1504 SkASSERT(!"unrecognized pixeltype in serialized data");
1505 sk_throw();
1506 }
1507}
1508
1509///////////////////////////////////////////////////////////////////////////////
1510
1511SkBitmap::RLEPixels::RLEPixels(int width, int height) {
1512 fHeight = height;
1513 fYPtrs = (uint8_t**)sk_malloc_throw(height * sizeof(uint8_t*));
reed@android.com4516f472009-06-29 16:25:36 +00001514 sk_bzero(fYPtrs, height * sizeof(uint8_t*));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001515}
1516
1517SkBitmap::RLEPixels::~RLEPixels() {
1518 sk_free(fYPtrs);
1519}
1520
1521///////////////////////////////////////////////////////////////////////////////
1522
1523#ifdef SK_DEBUG
1524void SkBitmap::validate() const {
1525 SkASSERT(fConfig < kConfigCount);
1526 SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
junov@google.com4ee7ae52011-06-30 17:30:49 +00001527 SkASSERT(fFlags <= (kImageIsOpaque_Flag | kImageIsVolatile_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001528 SkASSERT(fPixelLockCount >= 0);
1529 SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000);
1530 SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
1531
1532#if 0 // these asserts are not thread-correct, so disable for now
1533 if (fPixelRef) {
1534 if (fPixelLockCount > 0) {
1535 SkASSERT(fPixelRef->getLockCount() > 0);
1536 } else {
1537 SkASSERT(NULL == fPixels);
1538 SkASSERT(NULL == fColorTable);
1539 }
1540 }
1541#endif
1542}
1543#endif