blob: 24a1839ff2114537176948b4a92d28cac13710e2 [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 {
412 if (fPixelRef) {
413 fPixelRef->notifyPixelsChanged();
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000414 } else {
415 fRawPixelGenerationID = 0; // will grab next ID in getGenerationID
reed@android.com8a1c16f2008-12-17 15:59:43 +0000416 }
417}
418
reed@android.comce4e53a2010-09-09 16:01:26 +0000419SkGpuTexture* SkBitmap::getTexture() const {
420 return fPixelRef ? fPixelRef->getTexture() : NULL;
421}
422
reed@android.com8a1c16f2008-12-17 15:59:43 +0000423///////////////////////////////////////////////////////////////////////////////
424
reed@android.com8a1c16f2008-12-17 15:59:43 +0000425/** We explicitly use the same allocator for our pixels that SkMask does,
426 so that we can freely assign memory allocated by one class to the other.
427 */
428bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
429 SkColorTable* ctable) {
430 Sk64 size = dst->getSize64();
431 if (size.isNeg() || !size.is32()) {
432 return false;
433 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000434
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435 void* addr = sk_malloc_flags(size.get32(), 0); // returns NULL on failure
436 if (NULL == addr) {
437 return false;
438 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000439
reed@android.com8a1c16f2008-12-17 15:59:43 +0000440 dst->setPixelRef(new SkMallocPixelRef(addr, size.get32(), ctable))->unref();
441 // since we're already allocated, we lockPixels right away
442 dst->lockPixels();
443 return true;
444}
445
446///////////////////////////////////////////////////////////////////////////////
447
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000448size_t SkBitmap::getSafeSize() const {
449 // This is intended to be a size_t version of ComputeSafeSize64(), just
450 // faster. The computation is meant to be identical.
451 return (fHeight ? ((fHeight - 1) * fRowBytes) +
452 ComputeRowBytes(getConfig(), fWidth): 0);
453}
454
455Sk64 SkBitmap::getSafeSize64() const {
456 return ComputeSafeSize64(getConfig(), fWidth, fHeight, fRowBytes);
457}
458
bsalomon@google.comc6980972011-11-02 19:57:21 +0000459bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize,
460 int dstRowBytes, bool preserveDstPad) const {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000461
462 if (dstRowBytes == -1)
463 dstRowBytes = fRowBytes;
464 SkASSERT(dstRowBytes >= 0);
465
466 if (getConfig() == kRLE_Index8_Config ||
467 dstRowBytes < ComputeRowBytes(getConfig(), fWidth) ||
468 dst == NULL || (getPixels() == NULL && pixelRef() == NULL))
469 return false;
470
bsalomon@google.comc6980972011-11-02 19:57:21 +0000471 if (!preserveDstPad && static_cast<uint32_t>(dstRowBytes) == fRowBytes) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000472 size_t safeSize = getSafeSize();
473 if (safeSize > dstSize || safeSize == 0)
474 return false;
475 else {
476 SkAutoLockPixels lock(*this);
477 // This implementation will write bytes beyond the end of each row,
478 // excluding the last row, if the bitmap's stride is greater than
479 // strictly required by the current config.
480 memcpy(dst, getPixels(), safeSize);
481
482 return true;
483 }
484 } else {
485 // If destination has different stride than us, then copy line by line.
486 if (ComputeSafeSize(getConfig(), fWidth, fHeight, dstRowBytes) >
487 dstSize)
488 return false;
489 else {
490 // Just copy what we need on each line.
491 uint32_t rowBytes = ComputeRowBytes(getConfig(), fWidth);
492 SkAutoLockPixels lock(*this);
493 const uint8_t* srcP = reinterpret_cast<const uint8_t*>(getPixels());
494 uint8_t* dstP = reinterpret_cast<uint8_t*>(dst);
495 for (uint32_t row = 0; row < fHeight;
496 row++, srcP += fRowBytes, dstP += dstRowBytes) {
497 memcpy(dstP, srcP, rowBytes);
498 }
499
500 return true;
501 }
502 }
503}
504
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000505///////////////////////////////////////////////////////////////////////////////
506
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507bool SkBitmap::isOpaque() const {
508 switch (fConfig) {
509 case kNo_Config:
510 return true;
511
512 case kA1_Config:
513 case kA8_Config:
514 case kARGB_4444_Config:
515 case kARGB_8888_Config:
516 return (fFlags & kImageIsOpaque_Flag) != 0;
517
518 case kIndex8_Config:
519 case kRLE_Index8_Config: {
520 uint32_t flags = 0;
521
522 this->lockPixels();
523 // if lockPixels failed, we may not have a ctable ptr
524 if (fColorTable) {
525 flags = fColorTable->getFlags();
526 }
527 this->unlockPixels();
528
529 return (flags & SkColorTable::kColorsAreOpaque_Flag) != 0;
530 }
531
532 case kRGB_565_Config:
533 return true;
534
535 default:
536 SkASSERT(!"unknown bitmap config pased to isOpaque");
537 return false;
538 }
539}
540
541void SkBitmap::setIsOpaque(bool isOpaque) {
542 /* we record this regardless of fConfig, though it is ignored in
543 isOpaque() for configs that can't support per-pixel alpha.
544 */
545 if (isOpaque) {
546 fFlags |= kImageIsOpaque_Flag;
547 } else {
548 fFlags &= ~kImageIsOpaque_Flag;
549 }
550}
551
junov@google.com4ee7ae52011-06-30 17:30:49 +0000552bool SkBitmap::isVolatile() const {
553 return (fFlags & kImageIsVolatile_Flag) != 0;
554}
555
556void SkBitmap::setIsVolatile(bool isVolatile) {
557 if (isVolatile) {
558 fFlags |= kImageIsVolatile_Flag;
559 } else {
560 fFlags &= ~kImageIsVolatile_Flag;
561 }
562}
563
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564void* SkBitmap::getAddr(int x, int y) const {
565 SkASSERT((unsigned)x < (unsigned)this->width());
566 SkASSERT((unsigned)y < (unsigned)this->height());
567
568 char* base = (char*)this->getPixels();
569 if (base) {
570 base += y * this->rowBytes();
571 switch (this->config()) {
572 case SkBitmap::kARGB_8888_Config:
573 base += x << 2;
574 break;
575 case SkBitmap::kARGB_4444_Config:
576 case SkBitmap::kRGB_565_Config:
577 base += x << 1;
578 break;
579 case SkBitmap::kA8_Config:
580 case SkBitmap::kIndex8_Config:
581 base += x;
582 break;
583 case SkBitmap::kA1_Config:
584 base += x >> 3;
585 break;
586 case kRLE_Index8_Config:
587 SkASSERT(!"Can't return addr for kRLE_Index8_Config");
588 base = NULL;
589 break;
590 default:
591 SkASSERT(!"Can't return addr for config");
592 base = NULL;
593 break;
594 }
595 }
596 return base;
597}
598
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000599SkColor SkBitmap::getColor(int x, int y) const {
600 SkASSERT((unsigned)x < (unsigned)this->width());
601 SkASSERT((unsigned)y < (unsigned)this->height());
602
603 switch (this->config()) {
604 case SkBitmap::kA1_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000605 uint8_t* addr = this->getAddr1(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000606 uint8_t mask = 1 << (7 - (x % 8));
607 if (addr[0] & mask) {
608 return SK_ColorBLACK;
609 } else {
610 return 0;
611 }
612 }
613 case SkBitmap::kA8_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000614 uint8_t* addr = this->getAddr8(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000615 return SkColorSetA(0, addr[0]);
616 }
617 case SkBitmap::kIndex8_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000618 SkPMColor c = this->getIndex8Color(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000619 return SkUnPreMultiply::PMColorToColor(c);
620 }
621 case SkBitmap::kRGB_565_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000622 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000623 return SkPixel16ToColor(addr[0]);
624 }
625 case SkBitmap::kARGB_4444_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000626 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000627 SkPMColor c = SkPixel4444ToPixel32(addr[0]);
628 return SkUnPreMultiply::PMColorToColor(c);
629 }
630 case SkBitmap::kARGB_8888_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000631 uint32_t* addr = this->getAddr32(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000632 return SkUnPreMultiply::PMColorToColor(addr[0]);
633 }
634 case kRLE_Index8_Config: {
635 uint8_t dst;
636 const SkBitmap::RLEPixels* rle =
reed@google.com3b521d02011-04-29 11:53:41 +0000637 (const SkBitmap::RLEPixels*)this->getPixels();
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000638 SkPackBits::Unpack8(&dst, x, 1, rle->packedAtY(y));
639 return SkUnPreMultiply::PMColorToColor((*fColorTable)[dst]);
640 }
641 case kNo_Config:
642 case kConfigCount:
643 SkASSERT(false);
644 return 0;
645 }
646 SkASSERT(false); // Not reached.
647 return 0;
648}
649
reed@android.com8a1c16f2008-12-17 15:59:43 +0000650///////////////////////////////////////////////////////////////////////////////
651///////////////////////////////////////////////////////////////////////////////
652
653void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
654 SkDEBUGCODE(this->validate();)
655
656 if (0 == fWidth || 0 == fHeight ||
657 kNo_Config == fConfig || kIndex8_Config == fConfig) {
658 return;
659 }
660
661 SkAutoLockPixels alp(*this);
662 // perform this check after the lock call
663 if (!this->readyToDraw()) {
664 return;
665 }
666
667 int height = fHeight;
668 const int width = fWidth;
669 const int rowBytes = fRowBytes;
670
671 // make rgb premultiplied
672 if (255 != a) {
673 r = SkAlphaMul(r, a);
674 g = SkAlphaMul(g, a);
675 b = SkAlphaMul(b, a);
676 }
677
678 switch (fConfig) {
679 case kA1_Config: {
680 uint8_t* p = (uint8_t*)fPixels;
681 const int count = (width + 7) >> 3;
682 a = (a >> 7) ? 0xFF : 0;
683 SkASSERT(count <= rowBytes);
684 while (--height >= 0) {
685 memset(p, a, count);
686 p += rowBytes;
687 }
688 break;
689 }
690 case kA8_Config: {
691 uint8_t* p = (uint8_t*)fPixels;
692 while (--height >= 0) {
693 memset(p, a, width);
694 p += rowBytes;
695 }
696 break;
697 }
698 case kARGB_4444_Config:
699 case kRGB_565_Config: {
700 uint16_t* p = (uint16_t*)fPixels;
701 uint16_t v;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000702
reed@android.com8a1c16f2008-12-17 15:59:43 +0000703 if (kARGB_4444_Config == fConfig) {
704 v = SkPackARGB4444(a >> 4, r >> 4, g >> 4, b >> 4);
705 } else { // kRGB_565_Config
706 v = SkPackRGB16(r >> (8 - SK_R16_BITS), g >> (8 - SK_G16_BITS),
707 b >> (8 - SK_B16_BITS));
708 }
709 while (--height >= 0) {
710 sk_memset16(p, v, width);
711 p = (uint16_t*)((char*)p + rowBytes);
712 }
713 break;
714 }
715 case kARGB_8888_Config: {
716 uint32_t* p = (uint32_t*)fPixels;
717 uint32_t v = SkPackARGB32(a, r, g, b);
718
719 while (--height >= 0) {
720 sk_memset32(p, v, width);
721 p = (uint32_t*)((char*)p + rowBytes);
722 }
723 break;
724 }
725 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000726
reed@android.com8a1c16f2008-12-17 15:59:43 +0000727 this->notifyPixelsChanged();
728}
729
730//////////////////////////////////////////////////////////////////////////////////////
731//////////////////////////////////////////////////////////////////////////////////////
732
733#define SUB_OFFSET_FAILURE ((size_t)-1)
734
735static size_t getSubOffset(const SkBitmap& bm, int x, int y) {
736 SkASSERT((unsigned)x < (unsigned)bm.width());
737 SkASSERT((unsigned)y < (unsigned)bm.height());
weita@google.comf9ab99a2009-05-03 18:23:30 +0000738
reed@android.com8a1c16f2008-12-17 15:59:43 +0000739 switch (bm.getConfig()) {
740 case SkBitmap::kA8_Config:
741 case SkBitmap:: kIndex8_Config:
742 // x is fine as is for the calculation
743 break;
744
745 case SkBitmap::kRGB_565_Config:
746 case SkBitmap::kARGB_4444_Config:
747 x <<= 1;
748 break;
749
750 case SkBitmap::kARGB_8888_Config:
751 x <<= 2;
752 break;
753
754 case SkBitmap::kNo_Config:
755 case SkBitmap::kA1_Config:
756 default:
757 return SUB_OFFSET_FAILURE;
758 }
759 return y * bm.rowBytes() + x;
760}
761
762bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
763 SkDEBUGCODE(this->validate();)
764
765 if (NULL == result || (NULL == fPixelRef && NULL == fPixels)) {
766 return false; // no src pixels
767 }
768
769 SkIRect srcRect, r;
770 srcRect.set(0, 0, this->width(), this->height());
771 if (!r.intersect(srcRect, subset)) {
772 return false; // r is empty (i.e. no intersection)
773 }
774
775 if (kRLE_Index8_Config == fConfig) {
776 SkAutoLockPixels alp(*this);
777 // don't call readyToDraw(), since we can operate w/o a colortable
778 // at this stage
779 if (this->getPixels() == NULL) {
780 return false;
781 }
782 SkBitmap bm;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000783
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784 bm.setConfig(kIndex8_Config, r.width(), r.height());
785 bm.allocPixels(this->getColorTable());
786 if (NULL == bm.getPixels()) {
787 return false;
788 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000789
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790 const RLEPixels* rle = (const RLEPixels*)this->getPixels();
791 uint8_t* dst = bm.getAddr8(0, 0);
792 const int width = bm.width();
793 const int rowBytes = bm.rowBytes();
weita@google.comf9ab99a2009-05-03 18:23:30 +0000794
reed@android.com8a1c16f2008-12-17 15:59:43 +0000795 for (int y = r.fTop; y < r.fBottom; y++) {
796 SkPackBits::Unpack8(dst, r.fLeft, width, rle->packedAtY(y));
797 dst += rowBytes;
798 }
799 result->swap(bm);
800 return true;
801 }
802
803 size_t offset = getSubOffset(*this, r.fLeft, r.fTop);
804 if (SUB_OFFSET_FAILURE == offset) {
805 return false; // config not supported
806 }
807
808 SkBitmap dst;
809 dst.setConfig(this->config(), r.width(), r.height(), this->rowBytes());
810
811 if (fPixelRef) {
812 // share the pixelref with a custom offset
813 dst.setPixelRef(fPixelRef, fPixelRefOffset + offset);
814 } else {
815 // share the pixels (owned by the caller)
816 dst.setPixels((char*)fPixels + offset, this->getColorTable());
817 }
818 SkDEBUGCODE(dst.validate();)
819
820 // we know we're good, so commit to result
821 result->swap(dst);
822 return true;
823}
824
825///////////////////////////////////////////////////////////////////////////////
826
827#include "SkCanvas.h"
828#include "SkPaint.h"
829
reed@android.comfbaa88d2009-05-06 17:44:34 +0000830bool SkBitmap::canCopyTo(Config dstConfig) const {
831 if (this->getConfig() == kNo_Config) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832 return false;
833 }
834
reed@android.comfbaa88d2009-05-06 17:44:34 +0000835 bool sameConfigs = (this->config() == dstConfig);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000836 switch (dstConfig) {
837 case kA8_Config:
838 case kARGB_4444_Config:
839 case kRGB_565_Config:
840 case kARGB_8888_Config:
841 break;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000842 case kA1_Config:
843 case kIndex8_Config:
844 if (!sameConfigs) {
845 return false;
846 }
847 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000848 default:
849 return false;
850 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000851
852 // do not copy src if srcConfig == kA1_Config while dstConfig != kA1_Config
853 if (this->getConfig() == kA1_Config && !sameConfigs) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000854 return false;
855 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000856
reed@android.comfbaa88d2009-05-06 17:44:34 +0000857 return true;
858}
859
860bool SkBitmap::copyTo(SkBitmap* dst, Config dstConfig, Allocator* alloc) const {
861 if (!this->canCopyTo(dstConfig)) {
862 return false;
863 }
864
reed@google.com50dfa012011-04-01 19:05:36 +0000865 // if we have a texture, first get those pixels
866 SkBitmap tmpSrc;
867 const SkBitmap* src = this;
868
bsalomon@google.com669fdc42011-04-05 17:08:27 +0000869 if (fPixelRef && fPixelRef->readPixels(&tmpSrc)) {
reed@google.com50dfa012011-04-01 19:05:36 +0000870 SkASSERT(tmpSrc.width() == this->width());
871 SkASSERT(tmpSrc.height() == this->height());
872
873 // did we get lucky and we can just return tmpSrc?
874 if (tmpSrc.config() == dstConfig && NULL == alloc) {
875 dst->swap(tmpSrc);
876 return true;
877 }
878
879 // fall through to the raster case
880 src = &tmpSrc;
reed@android.comfbaa88d2009-05-06 17:44:34 +0000881 }
reed@android.com311c82d2009-05-05 23:13:23 +0000882
reed@google.com50dfa012011-04-01 19:05:36 +0000883 // we lock this now, since we may need its colortable
884 SkAutoLockPixels srclock(*src);
885 if (!src->readyToDraw()) {
886 return false;
887 }
888
889 SkBitmap tmpDst;
890 tmpDst.setConfig(dstConfig, src->width(), src->height());
891
weita@google.comf9ab99a2009-05-03 18:23:30 +0000892 // allocate colortable if srcConfig == kIndex8_Config
893 SkColorTable* ctable = (dstConfig == kIndex8_Config) ?
reed@google.com50dfa012011-04-01 19:05:36 +0000894 new SkColorTable(*src->getColorTable()) : NULL;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000895 SkAutoUnref au(ctable);
reed@google.com50dfa012011-04-01 19:05:36 +0000896 if (!tmpDst.allocPixels(alloc, ctable)) {
weita@google.comf9ab99a2009-05-03 18:23:30 +0000897 return false;
898 }
reed@google.com50dfa012011-04-01 19:05:36 +0000899
900 SkAutoLockPixels dstlock(tmpDst);
901 if (!tmpDst.readyToDraw()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000902 // allocator/lock failed
903 return false;
904 }
reed@google.com50dfa012011-04-01 19:05:36 +0000905
reed@android.comfbaa88d2009-05-06 17:44:34 +0000906 /* do memcpy for the same configs cases, else use drawing
weita@google.comf9ab99a2009-05-03 18:23:30 +0000907 */
reed@google.com50dfa012011-04-01 19:05:36 +0000908 if (src->config() == dstConfig) {
909 if (tmpDst.getSize() == src->getSize()) {
910 memcpy(tmpDst.getPixels(), src->getPixels(), src->getSafeSize());
reed@android.com311c82d2009-05-05 23:13:23 +0000911 } else {
reed@google.com50dfa012011-04-01 19:05:36 +0000912 const char* srcP = reinterpret_cast<const char*>(src->getPixels());
913 char* dstP = reinterpret_cast<char*>(tmpDst.getPixels());
reed@android.com311c82d2009-05-05 23:13:23 +0000914 // to be sure we don't read too much, only copy our logical pixels
reed@google.com50dfa012011-04-01 19:05:36 +0000915 size_t bytesToCopy = tmpDst.width() * tmpDst.bytesPerPixel();
916 for (int y = 0; y < tmpDst.height(); y++) {
reed@android.com311c82d2009-05-05 23:13:23 +0000917 memcpy(dstP, srcP, bytesToCopy);
reed@google.com50dfa012011-04-01 19:05:36 +0000918 srcP += src->rowBytes();
919 dstP += tmpDst.rowBytes();
reed@android.com311c82d2009-05-05 23:13:23 +0000920 }
921 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000922 } else {
923 // if the src has alpha, we have to clear the dst first
reed@google.com50dfa012011-04-01 19:05:36 +0000924 if (!src->isOpaque()) {
925 tmpDst.eraseColor(0);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000926 }
927
reed@google.com50dfa012011-04-01 19:05:36 +0000928 SkCanvas canvas(tmpDst);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000929 SkPaint paint;
930
931 paint.setDither(true);
reed@google.com50dfa012011-04-01 19:05:36 +0000932 canvas.drawBitmap(*src, 0, 0, &paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000933 }
934
reed@google.com50dfa012011-04-01 19:05:36 +0000935 tmpDst.setIsOpaque(src->isOpaque());
reed@android.comcafc9f92009-08-22 03:44:57 +0000936
reed@google.com50dfa012011-04-01 19:05:36 +0000937 dst->swap(tmpDst);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000938 return true;
939}
940
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +0000941bool SkBitmap::deepCopyTo(SkBitmap* dst, Config dstConfig) const {
942 if (!this->canCopyTo(dstConfig)) {
943 return false;
944 }
945
946 // If we have a PixelRef, and it supports deep copy, use it.
947 // Currently supported only by texture-backed bitmaps.
948 if (fPixelRef) {
949 SkPixelRef* pixelRef = fPixelRef->deepCopy(dstConfig);
950 if (pixelRef) {
951 dst->setConfig(dstConfig, fWidth, fHeight);
952 dst->setPixelRef(pixelRef)->unref();
953 return true;
954 }
955 }
956
957 if (this->getTexture()) {
958 return false;
959 } else {
960 return this->copyTo(dst, dstConfig, NULL);
961 }
962}
963
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964///////////////////////////////////////////////////////////////////////////////
965///////////////////////////////////////////////////////////////////////////////
966
967static void downsampleby2_proc32(SkBitmap* dst, int x, int y,
968 const SkBitmap& src) {
969 x <<= 1;
970 y <<= 1;
971 const SkPMColor* p = src.getAddr32(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +0000972 const SkPMColor* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973 SkPMColor c, ag, rb;
974
975 c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF;
976 if (x < src.width() - 1) {
977 p += 1;
978 }
979 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
980
reed@android.com829c83c2009-06-08 12:05:31 +0000981 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000982 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +0000983 p += src.rowBytes() >> 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000984 }
985 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
986 if (x < src.width() - 1) {
987 p += 1;
988 }
989 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
990
991 *dst->getAddr32(x >> 1, y >> 1) =
992 ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00);
993}
994
995static inline uint32_t expand16(U16CPU c) {
996 return (c & ~SK_G16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16);
997}
998
999// returns dirt in the top 16bits, but we don't care, since we only
1000// store the low 16bits.
1001static inline U16CPU pack16(uint32_t c) {
1002 return (c & ~SK_G16_MASK_IN_PLACE) | ((c >> 16) & SK_G16_MASK_IN_PLACE);
1003}
1004
1005static void downsampleby2_proc16(SkBitmap* dst, int x, int y,
1006 const SkBitmap& src) {
1007 x <<= 1;
1008 y <<= 1;
1009 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001010 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001011 SkPMColor c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001012
reed@android.com8a1c16f2008-12-17 15:59:43 +00001013 c = expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001014 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001015 p += 1;
1016 }
1017 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001018
reed@android.com829c83c2009-06-08 12:05:31 +00001019 p = baseP;
1020 if (y < src.height() - 1) {
1021 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001022 }
1023 c += expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001024 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001025 p += 1;
1026 }
1027 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001028
reed@android.com8a1c16f2008-12-17 15:59:43 +00001029 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)pack16(c >> 2);
1030}
1031
1032static uint32_t expand4444(U16CPU c) {
1033 return (c & 0xF0F) | ((c & ~0xF0F) << 12);
1034}
1035
1036static U16CPU collaps4444(uint32_t c) {
1037 return (c & 0xF0F) | ((c >> 12) & ~0xF0F);
1038}
1039
1040static void downsampleby2_proc4444(SkBitmap* dst, int x, int y,
1041 const SkBitmap& src) {
1042 x <<= 1;
1043 y <<= 1;
1044 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001045 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001046 uint32_t c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001047
reed@android.com8a1c16f2008-12-17 15:59:43 +00001048 c = expand4444(*p);
1049 if (x < src.width() - 1) {
1050 p += 1;
1051 }
1052 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001053
reed@android.com829c83c2009-06-08 12:05:31 +00001054 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001055 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +00001056 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001057 }
1058 c += expand4444(*p);
1059 if (x < src.width() - 1) {
1060 p += 1;
1061 }
1062 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001063
reed@android.com8a1c16f2008-12-17 15:59:43 +00001064 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)collaps4444(c >> 2);
1065}
1066
1067void SkBitmap::buildMipMap(bool forceRebuild) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001068 if (forceRebuild)
1069 this->freeMipMap();
1070 else if (fMipMap)
1071 return; // we're already built
1072
1073 SkASSERT(NULL == fMipMap);
1074
1075 void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src);
1076
1077 const SkBitmap::Config config = this->getConfig();
1078
1079 switch (config) {
1080 case kARGB_8888_Config:
1081 proc = downsampleby2_proc32;
1082 break;
1083 case kRGB_565_Config:
1084 proc = downsampleby2_proc16;
1085 break;
1086 case kARGB_4444_Config:
1087 proc = downsampleby2_proc4444;
1088 break;
1089 case kIndex8_Config:
1090 case kA8_Config:
1091 default:
1092 return; // don't build mipmaps for these configs
1093 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001094
reed@android.com149e2f62009-05-22 14:39:03 +00001095 SkAutoLockPixels alp(*this);
1096 if (!this->readyToDraw()) {
1097 return;
1098 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001099
1100 // whip through our loop to compute the exact size needed
1101 size_t size = 0;
1102 int maxLevels = 0;
1103 {
reed@android.com149e2f62009-05-22 14:39:03 +00001104 int width = this->width();
1105 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001106 for (;;) {
1107 width >>= 1;
1108 height >>= 1;
1109 if (0 == width || 0 == height) {
1110 break;
1111 }
1112 size += ComputeRowBytes(config, width) * height;
1113 maxLevels += 1;
1114 }
1115 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001116
reed@android.com149e2f62009-05-22 14:39:03 +00001117 // nothing to build
reed@android.com8a1c16f2008-12-17 15:59:43 +00001118 if (0 == maxLevels) {
1119 return;
1120 }
1121
reed@android.com149e2f62009-05-22 14:39:03 +00001122 SkBitmap srcBM(*this);
1123 srcBM.lockPixels();
1124 if (!srcBM.readyToDraw()) {
1125 return;
1126 }
1127
1128 MipMap* mm = MipMap::Alloc(maxLevels, size);
1129 if (NULL == mm) {
1130 return;
1131 }
1132
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133 MipLevel* level = mm->levels();
1134 uint8_t* addr = (uint8_t*)mm->pixels();
reed@android.com149e2f62009-05-22 14:39:03 +00001135 int width = this->width();
1136 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001137 unsigned rowBytes = this->rowBytes();
reed@android.com149e2f62009-05-22 14:39:03 +00001138 SkBitmap dstBM;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001139
1140 for (int i = 0; i < maxLevels; i++) {
1141 width >>= 1;
1142 height >>= 1;
1143 rowBytes = ComputeRowBytes(config, width);
1144
1145 level[i].fPixels = addr;
reed@android.comf459a492009-03-27 12:33:50 +00001146 level[i].fWidth = width;
1147 level[i].fHeight = height;
reed@android.com49f0ff22009-03-19 21:52:42 +00001148 level[i].fRowBytes = rowBytes;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001149
1150 dstBM.setConfig(config, width, height, rowBytes);
1151 dstBM.setPixels(addr);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001152
reed@android.com149e2f62009-05-22 14:39:03 +00001153 for (int y = 0; y < height; y++) {
1154 for (int x = 0; x < width; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155 proc(&dstBM, x, y, srcBM);
1156 }
1157 }
1158
1159 srcBM = dstBM;
1160 addr += height * rowBytes;
1161 }
1162 SkASSERT(addr == (uint8_t*)mm->pixels() + size);
1163 fMipMap = mm;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001164}
1165
1166bool SkBitmap::hasMipMap() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001167 return fMipMap != NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001168}
1169
1170int SkBitmap::extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy) {
reed@android.com83f7bc32009-07-17 02:42:41 +00001171 if (NULL == fMipMap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001172 return 0;
reed@android.com83f7bc32009-07-17 02:42:41 +00001173 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001174
reed@android.com8a1c16f2008-12-17 15:59:43 +00001175 int level = ComputeMipLevel(sx, sy) >> 16;
1176 SkASSERT(level >= 0);
1177 if (level <= 0) {
1178 return 0;
1179 }
1180
1181 if (level >= fMipMap->fLevelCount) {
1182 level = fMipMap->fLevelCount - 1;
1183 }
1184 if (dst) {
1185 const MipLevel& mip = fMipMap->levels()[level - 1];
1186 dst->setConfig((SkBitmap::Config)this->config(),
1187 mip.fWidth, mip.fHeight, mip.fRowBytes);
1188 dst->setPixels(mip.fPixels);
1189 }
1190 return level;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001191}
1192
1193SkFixed SkBitmap::ComputeMipLevel(SkFixed sx, SkFixed sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001194 sx = SkAbs32(sx);
1195 sy = SkAbs32(sy);
1196 if (sx < sy) {
1197 sx = sy;
1198 }
1199 if (sx < SK_Fixed1) {
1200 return 0;
1201 }
1202 int clz = SkCLZ(sx);
1203 SkASSERT(clz >= 1 && clz <= 15);
1204 return SkIntToFixed(15 - clz) + ((unsigned)(sx << (clz + 1)) >> 16);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001205}
1206
1207///////////////////////////////////////////////////////////////////////////////
1208
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +00001209static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001210 int alphaRowBytes) {
1211 SkASSERT(alpha != NULL);
1212 SkASSERT(alphaRowBytes >= src.width());
1213
1214 SkBitmap::Config config = src.getConfig();
1215 int w = src.width();
1216 int h = src.height();
1217 int rb = src.rowBytes();
1218
reed@android.com1cdcb512009-08-24 19:11:00 +00001219 SkAutoLockPixels alp(src);
1220 if (!src.readyToDraw()) {
1221 // zero out the alpha buffer and return
1222 while (--h >= 0) {
1223 memset(alpha, 0, w);
1224 alpha += alphaRowBytes;
1225 }
1226 return false;
1227 }
reed@google.com82065d62011-02-07 15:30:46 +00001228
reed@android.com8a1c16f2008-12-17 15:59:43 +00001229 if (SkBitmap::kA8_Config == config && !src.isOpaque()) {
1230 const uint8_t* s = src.getAddr8(0, 0);
1231 while (--h >= 0) {
1232 memcpy(alpha, s, w);
1233 s += rb;
1234 alpha += alphaRowBytes;
1235 }
1236 } else if (SkBitmap::kARGB_8888_Config == config && !src.isOpaque()) {
1237 const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0);
1238 while (--h >= 0) {
1239 for (int x = 0; x < w; x++) {
1240 alpha[x] = SkGetPackedA32(s[x]);
1241 }
1242 s = (const SkPMColor*)((const char*)s + rb);
1243 alpha += alphaRowBytes;
1244 }
1245 } else if (SkBitmap::kARGB_4444_Config == config && !src.isOpaque()) {
1246 const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0);
1247 while (--h >= 0) {
1248 for (int x = 0; x < w; x++) {
1249 alpha[x] = SkPacked4444ToA32(s[x]);
1250 }
1251 s = (const SkPMColor16*)((const char*)s + rb);
1252 alpha += alphaRowBytes;
1253 }
1254 } else if (SkBitmap::kIndex8_Config == config && !src.isOpaque()) {
1255 SkColorTable* ct = src.getColorTable();
1256 if (ct) {
1257 const SkPMColor* SK_RESTRICT table = ct->lockColors();
1258 const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0);
1259 while (--h >= 0) {
1260 for (int x = 0; x < w; x++) {
1261 alpha[x] = SkGetPackedA32(table[s[x]]);
1262 }
1263 s += rb;
1264 alpha += alphaRowBytes;
1265 }
1266 ct->unlockColors(false);
1267 }
1268 } else { // src is opaque, so just fill alpha[] with 0xFF
1269 memset(alpha, 0xFF, h * alphaRowBytes);
1270 }
reed@android.com1cdcb512009-08-24 19:11:00 +00001271 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272}
1273
1274#include "SkPaint.h"
1275#include "SkMaskFilter.h"
1276#include "SkMatrix.h"
1277
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001278bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
djsollen@google.com57f49692011-02-23 20:46:31 +00001279 Allocator *allocator, SkIPoint* offset) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280 SkDEBUGCODE(this->validate();)
1281
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001282 SkBitmap tmpBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283 SkMatrix identity;
1284 SkMask srcM, dstM;
1285
1286 srcM.fBounds.set(0, 0, this->width(), this->height());
1287 srcM.fRowBytes = SkAlign4(this->width());
1288 srcM.fFormat = SkMask::kA8_Format;
1289
1290 SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL;
1291
1292 // compute our (larger?) dst bounds if we have a filter
1293 if (NULL != filter) {
1294 identity.reset();
1295 srcM.fImage = NULL;
1296 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1297 goto NO_FILTER_CASE;
1298 }
1299 dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
1300 } else {
1301 NO_FILTER_CASE:
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001302 tmpBitmap.setConfig(SkBitmap::kA8_Config, this->width(), this->height(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001303 srcM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001304 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1305 // Allocation of pixels for alpha bitmap failed.
1306 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1307 tmpBitmap.width(), tmpBitmap.height());
1308 return false;
1309 }
1310 GetBitmapAlpha(*this, tmpBitmap.getAddr8(0, 0), srcM.fRowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001311 if (offset) {
1312 offset->set(0, 0);
1313 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001314 tmpBitmap.swap(*dst);
1315 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001316 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001317 srcM.fImage = SkMask::AllocImage(srcM.computeImageSize());
1318 SkAutoMaskFreeImage srcCleanup(srcM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001319
1320 GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
1321 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1322 goto NO_FILTER_CASE;
1323 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001324 SkAutoMaskFreeImage dstCleanup(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001325
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001326 tmpBitmap.setConfig(SkBitmap::kA8_Config, dstM.fBounds.width(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327 dstM.fBounds.height(), dstM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001328 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1329 // Allocation of pixels for alpha bitmap failed.
1330 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1331 tmpBitmap.width(), tmpBitmap.height());
1332 return false;
1333 }
1334 memcpy(tmpBitmap.getPixels(), dstM.fImage, dstM.computeImageSize());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001335 if (offset) {
1336 offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
1337 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001338 SkDEBUGCODE(tmpBitmap.validate();)
1339
1340 tmpBitmap.swap(*dst);
1341 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001342}
1343
1344///////////////////////////////////////////////////////////////////////////////
1345
1346enum {
1347 SERIALIZE_PIXELTYPE_NONE,
1348 SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE,
1349 SERIALIZE_PIXELTYPE_RAW_NO_CTABLE,
1350 SERIALIZE_PIXELTYPE_REF_DATA,
1351 SERIALIZE_PIXELTYPE_REF_PTR,
1352};
1353
1354static void writeString(SkFlattenableWriteBuffer& buffer, const char str[]) {
1355 size_t len = strlen(str);
1356 buffer.write32(len);
1357 buffer.writePad(str, len);
1358}
1359
1360static SkPixelRef::Factory deserialize_factory(SkFlattenableReadBuffer& buffer) {
1361 size_t len = buffer.readInt();
1362 SkAutoSMalloc<256> storage(len + 1);
1363 char* str = (char*)storage.get();
1364 buffer.read(str, len);
1365 str[len] = 0;
1366 return SkPixelRef::NameToFactory(str);
1367}
1368
1369/*
1370 It is tricky to know how much to flatten. If we don't have a pixelref (i.e.
1371 we just have pixels, then we can only flatten the pixels, or write out an
1372 empty bitmap.
weita@google.comf9ab99a2009-05-03 18:23:30 +00001373
reed@android.com8a1c16f2008-12-17 15:59:43 +00001374 With a pixelref, we still have the question of recognizing when two sitings
1375 of the same pixelref are the same, and when they are different. Perhaps we
1376 should look at the generationID and keep a record of that in some dictionary
1377 associated with the buffer. SkGLTextureCache does this sort of thing to know
1378 when to create a new texture.
1379*/
1380void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
1381 buffer.write32(fWidth);
1382 buffer.write32(fHeight);
1383 buffer.write32(fRowBytes);
1384 buffer.write8(fConfig);
1385 buffer.writeBool(this->isOpaque());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001386
reed@android.com8a1c16f2008-12-17 15:59:43 +00001387 /* If we are called in this mode, then it is up to the caller to manage
1388 the owner-counts on the pixelref, as we just record the ptr itself.
1389 */
1390 if (!buffer.persistBitmapPixels()) {
1391 if (fPixelRef) {
1392 buffer.write8(SERIALIZE_PIXELTYPE_REF_PTR);
1393 buffer.write32(fPixelRefOffset);
1394 buffer.writeRefCnt(fPixelRef);
1395 return;
1396 } else {
1397 // we ignore the non-persist request, since we don't have a ref
1398 // ... or we could just write an empty bitmap...
1399 // (true) will write an empty bitmap, (false) will flatten the pix
1400 if (true) {
1401 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1402 return;
1403 }
1404 }
1405 }
1406
1407 if (fPixelRef) {
1408 SkPixelRef::Factory fact = fPixelRef->getFactory();
1409 if (fact) {
1410 const char* name = SkPixelRef::FactoryToName(fact);
1411 if (name && *name) {
1412 buffer.write8(SERIALIZE_PIXELTYPE_REF_DATA);
1413 buffer.write32(fPixelRefOffset);
1414 writeString(buffer, name);
1415 fPixelRef->flatten(buffer);
1416 return;
1417 }
1418 }
1419 // if we get here, we can't record the pixels
1420 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1421 } else if (fPixels) {
1422 if (fColorTable) {
1423 buffer.write8(SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE);
1424 fColorTable->flatten(buffer);
1425 } else {
1426 buffer.write8(SERIALIZE_PIXELTYPE_RAW_NO_CTABLE);
1427 }
wjmaclean@chromium.org3f0260d2011-02-22 22:32:14 +00001428 buffer.writePad(fPixels, this->getSafeSize());
1429 // There is no writeZeroPad() fcn, so write individual bytes.
1430 if (this->getSize() > this->getSafeSize()) {
1431 size_t deltaSize = this->getSize() - this->getSafeSize();
1432 // Need aligned pointer to write into due to internal implementa-
1433 // tion of SkWriter32.
1434 memset(buffer.reserve(SkAlign4(deltaSize)), 0, deltaSize);
1435 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001436 } else {
1437 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1438 }
1439}
1440
1441void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) {
1442 this->reset();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001443
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444 int width = buffer.readInt();
1445 int height = buffer.readInt();
1446 int rowBytes = buffer.readInt();
1447 int config = buffer.readU8();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001448
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449 this->setConfig((Config)config, width, height, rowBytes);
1450 this->setIsOpaque(buffer.readBool());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001451
reed@android.com8a1c16f2008-12-17 15:59:43 +00001452 int reftype = buffer.readU8();
1453 switch (reftype) {
1454 case SERIALIZE_PIXELTYPE_REF_PTR: {
1455 size_t offset = buffer.readU32();
1456 SkPixelRef* pr = (SkPixelRef*)buffer.readRefCnt();
1457 this->setPixelRef(pr, offset);
1458 break;
1459 }
1460 case SERIALIZE_PIXELTYPE_REF_DATA: {
1461 size_t offset = buffer.readU32();
1462 SkPixelRef::Factory fact = deserialize_factory(buffer);
1463 SkPixelRef* pr = fact(buffer);
reed@google.com82065d62011-02-07 15:30:46 +00001464 SkSafeUnref(this->setPixelRef(pr, offset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001465 break;
1466 }
1467 case SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE:
1468 case SERIALIZE_PIXELTYPE_RAW_NO_CTABLE: {
1469 SkColorTable* ctable = NULL;
1470 if (SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE == reftype) {
1471 ctable = SkNEW_ARGS(SkColorTable, (buffer));
1472 }
wjmaclean@chromium.org3f0260d2011-02-22 22:32:14 +00001473 size_t size = this->getSize();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001474 if (this->allocPixels(ctable)) {
1475 this->lockPixels();
wjmaclean@chromium.org3f0260d2011-02-22 22:32:14 +00001476 // Just read what we need.
1477 buffer.read(this->getPixels(), this->getSafeSize());
1478 // Keep aligned for subsequent reads.
1479 buffer.skip(size - this->getSafeSize());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001480 this->unlockPixels();
1481 } else {
wjmaclean@chromium.org0bde1802010-12-22 17:43:54 +00001482 buffer.skip(size); // Still skip the full-sized buffer though.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001483 }
reed@android.com149e2f62009-05-22 14:39:03 +00001484 SkSafeUnref(ctable);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001485 break;
1486 }
1487 case SERIALIZE_PIXELTYPE_NONE:
1488 break;
1489 default:
1490 SkASSERT(!"unrecognized pixeltype in serialized data");
1491 sk_throw();
1492 }
1493}
1494
1495///////////////////////////////////////////////////////////////////////////////
1496
1497SkBitmap::RLEPixels::RLEPixels(int width, int height) {
1498 fHeight = height;
1499 fYPtrs = (uint8_t**)sk_malloc_throw(height * sizeof(uint8_t*));
reed@android.com4516f472009-06-29 16:25:36 +00001500 sk_bzero(fYPtrs, height * sizeof(uint8_t*));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001501}
1502
1503SkBitmap::RLEPixels::~RLEPixels() {
1504 sk_free(fYPtrs);
1505}
1506
1507///////////////////////////////////////////////////////////////////////////////
1508
1509#ifdef SK_DEBUG
1510void SkBitmap::validate() const {
1511 SkASSERT(fConfig < kConfigCount);
1512 SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
junov@google.com4ee7ae52011-06-30 17:30:49 +00001513 SkASSERT(fFlags <= (kImageIsOpaque_Flag | kImageIsVolatile_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514 SkASSERT(fPixelLockCount >= 0);
1515 SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000);
1516 SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
1517
1518#if 0 // these asserts are not thread-correct, so disable for now
1519 if (fPixelRef) {
1520 if (fPixelLockCount > 0) {
1521 SkASSERT(fPixelRef->getLockCount() > 0);
1522 } else {
1523 SkASSERT(NULL == fPixels);
1524 SkASSERT(NULL == fColorTable);
1525 }
1526 }
1527#endif
1528}
1529#endif