blob: 581047ee4af23d16d8e00ad5b796db54164cae38 [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
459bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize, int dstRowBytes)
460 const {
461
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
471 if (static_cast<uint32_t>(dstRowBytes) == fRowBytes) {
472 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
505bool SkBitmap::copyPixelsFrom(const void* const src, size_t srcSize,
506 int srcRowBytes) {
507
508 if (srcRowBytes == -1)
509 srcRowBytes = fRowBytes;
510 SkASSERT(srcRowBytes >= 0);
511
512 size_t safeSize = getSafeSize();
513 uint32_t rowBytes = ComputeRowBytes(getConfig(), fWidth);
514 if (getConfig() == kRLE_Index8_Config || src == NULL ||
515 static_cast<uint32_t>(srcRowBytes) < rowBytes ||
516 safeSize == 0 ||
517 srcSize < ComputeSafeSize(getConfig(), fWidth, fHeight, srcRowBytes)) {
518 return false;
519 }
520
521 SkAutoLockPixels lock(*this);
522 if (static_cast<uint32_t>(srcRowBytes) == fRowBytes) {
523 // This implementation will write bytes beyond the end of each row,
524 // excluding the last row, if the bitmap's stride is greater than
525 // strictly required by the current config.
526 memcpy(getPixels(), src, safeSize);
527 } else {
528 // Just copy the bytes we need on each line.
529 const uint8_t* srcP = reinterpret_cast<const uint8_t*>(src);
530 uint8_t* dstP = reinterpret_cast<uint8_t*>(getPixels());
531 for (uint32_t row = 0; row < fHeight;
532 row++, srcP += srcRowBytes, dstP += fRowBytes) {
533 memcpy(dstP, srcP, rowBytes);
534 }
535 }
536
537 return true;
538}
539
540///////////////////////////////////////////////////////////////////////////////
541
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542bool SkBitmap::isOpaque() const {
543 switch (fConfig) {
544 case kNo_Config:
545 return true;
546
547 case kA1_Config:
548 case kA8_Config:
549 case kARGB_4444_Config:
550 case kARGB_8888_Config:
551 return (fFlags & kImageIsOpaque_Flag) != 0;
552
553 case kIndex8_Config:
554 case kRLE_Index8_Config: {
555 uint32_t flags = 0;
556
557 this->lockPixels();
558 // if lockPixels failed, we may not have a ctable ptr
559 if (fColorTable) {
560 flags = fColorTable->getFlags();
561 }
562 this->unlockPixels();
563
564 return (flags & SkColorTable::kColorsAreOpaque_Flag) != 0;
565 }
566
567 case kRGB_565_Config:
568 return true;
569
570 default:
571 SkASSERT(!"unknown bitmap config pased to isOpaque");
572 return false;
573 }
574}
575
576void SkBitmap::setIsOpaque(bool isOpaque) {
577 /* we record this regardless of fConfig, though it is ignored in
578 isOpaque() for configs that can't support per-pixel alpha.
579 */
580 if (isOpaque) {
581 fFlags |= kImageIsOpaque_Flag;
582 } else {
583 fFlags &= ~kImageIsOpaque_Flag;
584 }
585}
586
junov@google.com4ee7ae52011-06-30 17:30:49 +0000587bool SkBitmap::isVolatile() const {
588 return (fFlags & kImageIsVolatile_Flag) != 0;
589}
590
591void SkBitmap::setIsVolatile(bool isVolatile) {
592 if (isVolatile) {
593 fFlags |= kImageIsVolatile_Flag;
594 } else {
595 fFlags &= ~kImageIsVolatile_Flag;
596 }
597}
598
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599void* SkBitmap::getAddr(int x, int y) const {
600 SkASSERT((unsigned)x < (unsigned)this->width());
601 SkASSERT((unsigned)y < (unsigned)this->height());
602
603 char* base = (char*)this->getPixels();
604 if (base) {
605 base += y * this->rowBytes();
606 switch (this->config()) {
607 case SkBitmap::kARGB_8888_Config:
608 base += x << 2;
609 break;
610 case SkBitmap::kARGB_4444_Config:
611 case SkBitmap::kRGB_565_Config:
612 base += x << 1;
613 break;
614 case SkBitmap::kA8_Config:
615 case SkBitmap::kIndex8_Config:
616 base += x;
617 break;
618 case SkBitmap::kA1_Config:
619 base += x >> 3;
620 break;
621 case kRLE_Index8_Config:
622 SkASSERT(!"Can't return addr for kRLE_Index8_Config");
623 base = NULL;
624 break;
625 default:
626 SkASSERT(!"Can't return addr for config");
627 base = NULL;
628 break;
629 }
630 }
631 return base;
632}
633
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000634SkColor SkBitmap::getColor(int x, int y) const {
635 SkASSERT((unsigned)x < (unsigned)this->width());
636 SkASSERT((unsigned)y < (unsigned)this->height());
637
638 switch (this->config()) {
639 case SkBitmap::kA1_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000640 uint8_t* addr = this->getAddr1(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000641 uint8_t mask = 1 << (7 - (x % 8));
642 if (addr[0] & mask) {
643 return SK_ColorBLACK;
644 } else {
645 return 0;
646 }
647 }
648 case SkBitmap::kA8_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000649 uint8_t* addr = this->getAddr8(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000650 return SkColorSetA(0, addr[0]);
651 }
652 case SkBitmap::kIndex8_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000653 SkPMColor c = this->getIndex8Color(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000654 return SkUnPreMultiply::PMColorToColor(c);
655 }
656 case SkBitmap::kRGB_565_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000657 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000658 return SkPixel16ToColor(addr[0]);
659 }
660 case SkBitmap::kARGB_4444_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000661 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000662 SkPMColor c = SkPixel4444ToPixel32(addr[0]);
663 return SkUnPreMultiply::PMColorToColor(c);
664 }
665 case SkBitmap::kARGB_8888_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000666 uint32_t* addr = this->getAddr32(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000667 return SkUnPreMultiply::PMColorToColor(addr[0]);
668 }
669 case kRLE_Index8_Config: {
670 uint8_t dst;
671 const SkBitmap::RLEPixels* rle =
reed@google.com3b521d02011-04-29 11:53:41 +0000672 (const SkBitmap::RLEPixels*)this->getPixels();
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000673 SkPackBits::Unpack8(&dst, x, 1, rle->packedAtY(y));
674 return SkUnPreMultiply::PMColorToColor((*fColorTable)[dst]);
675 }
676 case kNo_Config:
677 case kConfigCount:
678 SkASSERT(false);
679 return 0;
680 }
681 SkASSERT(false); // Not reached.
682 return 0;
683}
684
reed@android.com8a1c16f2008-12-17 15:59:43 +0000685///////////////////////////////////////////////////////////////////////////////
686///////////////////////////////////////////////////////////////////////////////
687
688void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
689 SkDEBUGCODE(this->validate();)
690
691 if (0 == fWidth || 0 == fHeight ||
692 kNo_Config == fConfig || kIndex8_Config == fConfig) {
693 return;
694 }
695
696 SkAutoLockPixels alp(*this);
697 // perform this check after the lock call
698 if (!this->readyToDraw()) {
699 return;
700 }
701
702 int height = fHeight;
703 const int width = fWidth;
704 const int rowBytes = fRowBytes;
705
706 // make rgb premultiplied
707 if (255 != a) {
708 r = SkAlphaMul(r, a);
709 g = SkAlphaMul(g, a);
710 b = SkAlphaMul(b, a);
711 }
712
713 switch (fConfig) {
714 case kA1_Config: {
715 uint8_t* p = (uint8_t*)fPixels;
716 const int count = (width + 7) >> 3;
717 a = (a >> 7) ? 0xFF : 0;
718 SkASSERT(count <= rowBytes);
719 while (--height >= 0) {
720 memset(p, a, count);
721 p += rowBytes;
722 }
723 break;
724 }
725 case kA8_Config: {
726 uint8_t* p = (uint8_t*)fPixels;
727 while (--height >= 0) {
728 memset(p, a, width);
729 p += rowBytes;
730 }
731 break;
732 }
733 case kARGB_4444_Config:
734 case kRGB_565_Config: {
735 uint16_t* p = (uint16_t*)fPixels;
736 uint16_t v;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000737
reed@android.com8a1c16f2008-12-17 15:59:43 +0000738 if (kARGB_4444_Config == fConfig) {
739 v = SkPackARGB4444(a >> 4, r >> 4, g >> 4, b >> 4);
740 } else { // kRGB_565_Config
741 v = SkPackRGB16(r >> (8 - SK_R16_BITS), g >> (8 - SK_G16_BITS),
742 b >> (8 - SK_B16_BITS));
743 }
744 while (--height >= 0) {
745 sk_memset16(p, v, width);
746 p = (uint16_t*)((char*)p + rowBytes);
747 }
748 break;
749 }
750 case kARGB_8888_Config: {
751 uint32_t* p = (uint32_t*)fPixels;
752 uint32_t v = SkPackARGB32(a, r, g, b);
753
754 while (--height >= 0) {
755 sk_memset32(p, v, width);
756 p = (uint32_t*)((char*)p + rowBytes);
757 }
758 break;
759 }
760 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000761
reed@android.com8a1c16f2008-12-17 15:59:43 +0000762 this->notifyPixelsChanged();
763}
764
765//////////////////////////////////////////////////////////////////////////////////////
766//////////////////////////////////////////////////////////////////////////////////////
767
768#define SUB_OFFSET_FAILURE ((size_t)-1)
769
770static size_t getSubOffset(const SkBitmap& bm, int x, int y) {
771 SkASSERT((unsigned)x < (unsigned)bm.width());
772 SkASSERT((unsigned)y < (unsigned)bm.height());
weita@google.comf9ab99a2009-05-03 18:23:30 +0000773
reed@android.com8a1c16f2008-12-17 15:59:43 +0000774 switch (bm.getConfig()) {
775 case SkBitmap::kA8_Config:
776 case SkBitmap:: kIndex8_Config:
777 // x is fine as is for the calculation
778 break;
779
780 case SkBitmap::kRGB_565_Config:
781 case SkBitmap::kARGB_4444_Config:
782 x <<= 1;
783 break;
784
785 case SkBitmap::kARGB_8888_Config:
786 x <<= 2;
787 break;
788
789 case SkBitmap::kNo_Config:
790 case SkBitmap::kA1_Config:
791 default:
792 return SUB_OFFSET_FAILURE;
793 }
794 return y * bm.rowBytes() + x;
795}
796
797bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
798 SkDEBUGCODE(this->validate();)
799
800 if (NULL == result || (NULL == fPixelRef && NULL == fPixels)) {
801 return false; // no src pixels
802 }
803
804 SkIRect srcRect, r;
805 srcRect.set(0, 0, this->width(), this->height());
806 if (!r.intersect(srcRect, subset)) {
807 return false; // r is empty (i.e. no intersection)
808 }
809
810 if (kRLE_Index8_Config == fConfig) {
811 SkAutoLockPixels alp(*this);
812 // don't call readyToDraw(), since we can operate w/o a colortable
813 // at this stage
814 if (this->getPixels() == NULL) {
815 return false;
816 }
817 SkBitmap bm;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000818
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819 bm.setConfig(kIndex8_Config, r.width(), r.height());
820 bm.allocPixels(this->getColorTable());
821 if (NULL == bm.getPixels()) {
822 return false;
823 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000824
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 const RLEPixels* rle = (const RLEPixels*)this->getPixels();
826 uint8_t* dst = bm.getAddr8(0, 0);
827 const int width = bm.width();
828 const int rowBytes = bm.rowBytes();
weita@google.comf9ab99a2009-05-03 18:23:30 +0000829
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830 for (int y = r.fTop; y < r.fBottom; y++) {
831 SkPackBits::Unpack8(dst, r.fLeft, width, rle->packedAtY(y));
832 dst += rowBytes;
833 }
834 result->swap(bm);
835 return true;
836 }
837
838 size_t offset = getSubOffset(*this, r.fLeft, r.fTop);
839 if (SUB_OFFSET_FAILURE == offset) {
840 return false; // config not supported
841 }
842
843 SkBitmap dst;
844 dst.setConfig(this->config(), r.width(), r.height(), this->rowBytes());
845
846 if (fPixelRef) {
847 // share the pixelref with a custom offset
848 dst.setPixelRef(fPixelRef, fPixelRefOffset + offset);
849 } else {
850 // share the pixels (owned by the caller)
851 dst.setPixels((char*)fPixels + offset, this->getColorTable());
852 }
853 SkDEBUGCODE(dst.validate();)
854
855 // we know we're good, so commit to result
856 result->swap(dst);
857 return true;
858}
859
860///////////////////////////////////////////////////////////////////////////////
861
862#include "SkCanvas.h"
863#include "SkPaint.h"
864
reed@android.comfbaa88d2009-05-06 17:44:34 +0000865bool SkBitmap::canCopyTo(Config dstConfig) const {
866 if (this->getConfig() == kNo_Config) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867 return false;
868 }
869
reed@android.comfbaa88d2009-05-06 17:44:34 +0000870 bool sameConfigs = (this->config() == dstConfig);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000871 switch (dstConfig) {
872 case kA8_Config:
873 case kARGB_4444_Config:
874 case kRGB_565_Config:
875 case kARGB_8888_Config:
876 break;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000877 case kA1_Config:
878 case kIndex8_Config:
879 if (!sameConfigs) {
880 return false;
881 }
882 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000883 default:
884 return false;
885 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000886
887 // do not copy src if srcConfig == kA1_Config while dstConfig != kA1_Config
888 if (this->getConfig() == kA1_Config && !sameConfigs) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000889 return false;
890 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000891
reed@android.comfbaa88d2009-05-06 17:44:34 +0000892 return true;
893}
894
895bool SkBitmap::copyTo(SkBitmap* dst, Config dstConfig, Allocator* alloc) const {
896 if (!this->canCopyTo(dstConfig)) {
897 return false;
898 }
899
reed@google.com50dfa012011-04-01 19:05:36 +0000900 // if we have a texture, first get those pixels
901 SkBitmap tmpSrc;
902 const SkBitmap* src = this;
903
bsalomon@google.com669fdc42011-04-05 17:08:27 +0000904 if (fPixelRef && fPixelRef->readPixels(&tmpSrc)) {
reed@google.com50dfa012011-04-01 19:05:36 +0000905 SkASSERT(tmpSrc.width() == this->width());
906 SkASSERT(tmpSrc.height() == this->height());
907
908 // did we get lucky and we can just return tmpSrc?
909 if (tmpSrc.config() == dstConfig && NULL == alloc) {
910 dst->swap(tmpSrc);
911 return true;
912 }
913
914 // fall through to the raster case
915 src = &tmpSrc;
reed@android.comfbaa88d2009-05-06 17:44:34 +0000916 }
reed@android.com311c82d2009-05-05 23:13:23 +0000917
reed@google.com50dfa012011-04-01 19:05:36 +0000918 // we lock this now, since we may need its colortable
919 SkAutoLockPixels srclock(*src);
920 if (!src->readyToDraw()) {
921 return false;
922 }
923
924 SkBitmap tmpDst;
925 tmpDst.setConfig(dstConfig, src->width(), src->height());
926
weita@google.comf9ab99a2009-05-03 18:23:30 +0000927 // allocate colortable if srcConfig == kIndex8_Config
928 SkColorTable* ctable = (dstConfig == kIndex8_Config) ?
reed@google.com50dfa012011-04-01 19:05:36 +0000929 new SkColorTable(*src->getColorTable()) : NULL;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000930 SkAutoUnref au(ctable);
reed@google.com50dfa012011-04-01 19:05:36 +0000931 if (!tmpDst.allocPixels(alloc, ctable)) {
weita@google.comf9ab99a2009-05-03 18:23:30 +0000932 return false;
933 }
reed@google.com50dfa012011-04-01 19:05:36 +0000934
935 SkAutoLockPixels dstlock(tmpDst);
936 if (!tmpDst.readyToDraw()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000937 // allocator/lock failed
938 return false;
939 }
reed@google.com50dfa012011-04-01 19:05:36 +0000940
reed@android.comfbaa88d2009-05-06 17:44:34 +0000941 /* do memcpy for the same configs cases, else use drawing
weita@google.comf9ab99a2009-05-03 18:23:30 +0000942 */
reed@google.com50dfa012011-04-01 19:05:36 +0000943 if (src->config() == dstConfig) {
944 if (tmpDst.getSize() == src->getSize()) {
945 memcpy(tmpDst.getPixels(), src->getPixels(), src->getSafeSize());
reed@android.com311c82d2009-05-05 23:13:23 +0000946 } else {
reed@google.com50dfa012011-04-01 19:05:36 +0000947 const char* srcP = reinterpret_cast<const char*>(src->getPixels());
948 char* dstP = reinterpret_cast<char*>(tmpDst.getPixels());
reed@android.com311c82d2009-05-05 23:13:23 +0000949 // to be sure we don't read too much, only copy our logical pixels
reed@google.com50dfa012011-04-01 19:05:36 +0000950 size_t bytesToCopy = tmpDst.width() * tmpDst.bytesPerPixel();
951 for (int y = 0; y < tmpDst.height(); y++) {
reed@android.com311c82d2009-05-05 23:13:23 +0000952 memcpy(dstP, srcP, bytesToCopy);
reed@google.com50dfa012011-04-01 19:05:36 +0000953 srcP += src->rowBytes();
954 dstP += tmpDst.rowBytes();
reed@android.com311c82d2009-05-05 23:13:23 +0000955 }
956 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000957 } else {
958 // if the src has alpha, we have to clear the dst first
reed@google.com50dfa012011-04-01 19:05:36 +0000959 if (!src->isOpaque()) {
960 tmpDst.eraseColor(0);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000961 }
962
reed@google.com50dfa012011-04-01 19:05:36 +0000963 SkCanvas canvas(tmpDst);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000964 SkPaint paint;
965
966 paint.setDither(true);
reed@google.com50dfa012011-04-01 19:05:36 +0000967 canvas.drawBitmap(*src, 0, 0, &paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000968 }
969
reed@google.com50dfa012011-04-01 19:05:36 +0000970 tmpDst.setIsOpaque(src->isOpaque());
reed@android.comcafc9f92009-08-22 03:44:57 +0000971
reed@google.com50dfa012011-04-01 19:05:36 +0000972 dst->swap(tmpDst);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973 return true;
974}
975
976///////////////////////////////////////////////////////////////////////////////
977///////////////////////////////////////////////////////////////////////////////
978
979static void downsampleby2_proc32(SkBitmap* dst, int x, int y,
980 const SkBitmap& src) {
981 x <<= 1;
982 y <<= 1;
983 const SkPMColor* p = src.getAddr32(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +0000984 const SkPMColor* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000985 SkPMColor c, ag, rb;
986
987 c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF;
988 if (x < src.width() - 1) {
989 p += 1;
990 }
991 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
992
reed@android.com829c83c2009-06-08 12:05:31 +0000993 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000994 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +0000995 p += src.rowBytes() >> 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000996 }
997 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
998 if (x < src.width() - 1) {
999 p += 1;
1000 }
1001 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1002
1003 *dst->getAddr32(x >> 1, y >> 1) =
1004 ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00);
1005}
1006
1007static inline uint32_t expand16(U16CPU c) {
1008 return (c & ~SK_G16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16);
1009}
1010
1011// returns dirt in the top 16bits, but we don't care, since we only
1012// store the low 16bits.
1013static inline U16CPU pack16(uint32_t c) {
1014 return (c & ~SK_G16_MASK_IN_PLACE) | ((c >> 16) & SK_G16_MASK_IN_PLACE);
1015}
1016
1017static void downsampleby2_proc16(SkBitmap* dst, int x, int y,
1018 const SkBitmap& src) {
1019 x <<= 1;
1020 y <<= 1;
1021 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001022 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001023 SkPMColor c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001024
reed@android.com8a1c16f2008-12-17 15:59:43 +00001025 c = expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001026 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001027 p += 1;
1028 }
1029 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001030
reed@android.com829c83c2009-06-08 12:05:31 +00001031 p = baseP;
1032 if (y < src.height() - 1) {
1033 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001034 }
1035 c += expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001036 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001037 p += 1;
1038 }
1039 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001040
reed@android.com8a1c16f2008-12-17 15:59:43 +00001041 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)pack16(c >> 2);
1042}
1043
1044static uint32_t expand4444(U16CPU c) {
1045 return (c & 0xF0F) | ((c & ~0xF0F) << 12);
1046}
1047
1048static U16CPU collaps4444(uint32_t c) {
1049 return (c & 0xF0F) | ((c >> 12) & ~0xF0F);
1050}
1051
1052static void downsampleby2_proc4444(SkBitmap* dst, int x, int y,
1053 const SkBitmap& src) {
1054 x <<= 1;
1055 y <<= 1;
1056 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001057 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001058 uint32_t c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001059
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060 c = expand4444(*p);
1061 if (x < src.width() - 1) {
1062 p += 1;
1063 }
1064 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001065
reed@android.com829c83c2009-06-08 12:05:31 +00001066 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001067 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +00001068 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001069 }
1070 c += expand4444(*p);
1071 if (x < src.width() - 1) {
1072 p += 1;
1073 }
1074 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001075
reed@android.com8a1c16f2008-12-17 15:59:43 +00001076 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)collaps4444(c >> 2);
1077}
1078
1079void SkBitmap::buildMipMap(bool forceRebuild) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001080 if (forceRebuild)
1081 this->freeMipMap();
1082 else if (fMipMap)
1083 return; // we're already built
1084
1085 SkASSERT(NULL == fMipMap);
1086
1087 void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src);
1088
1089 const SkBitmap::Config config = this->getConfig();
1090
1091 switch (config) {
1092 case kARGB_8888_Config:
1093 proc = downsampleby2_proc32;
1094 break;
1095 case kRGB_565_Config:
1096 proc = downsampleby2_proc16;
1097 break;
1098 case kARGB_4444_Config:
1099 proc = downsampleby2_proc4444;
1100 break;
1101 case kIndex8_Config:
1102 case kA8_Config:
1103 default:
1104 return; // don't build mipmaps for these configs
1105 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001106
reed@android.com149e2f62009-05-22 14:39:03 +00001107 SkAutoLockPixels alp(*this);
1108 if (!this->readyToDraw()) {
1109 return;
1110 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111
1112 // whip through our loop to compute the exact size needed
1113 size_t size = 0;
1114 int maxLevels = 0;
1115 {
reed@android.com149e2f62009-05-22 14:39:03 +00001116 int width = this->width();
1117 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001118 for (;;) {
1119 width >>= 1;
1120 height >>= 1;
1121 if (0 == width || 0 == height) {
1122 break;
1123 }
1124 size += ComputeRowBytes(config, width) * height;
1125 maxLevels += 1;
1126 }
1127 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001128
reed@android.com149e2f62009-05-22 14:39:03 +00001129 // nothing to build
reed@android.com8a1c16f2008-12-17 15:59:43 +00001130 if (0 == maxLevels) {
1131 return;
1132 }
1133
reed@android.com149e2f62009-05-22 14:39:03 +00001134 SkBitmap srcBM(*this);
1135 srcBM.lockPixels();
1136 if (!srcBM.readyToDraw()) {
1137 return;
1138 }
1139
1140 MipMap* mm = MipMap::Alloc(maxLevels, size);
1141 if (NULL == mm) {
1142 return;
1143 }
1144
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145 MipLevel* level = mm->levels();
1146 uint8_t* addr = (uint8_t*)mm->pixels();
reed@android.com149e2f62009-05-22 14:39:03 +00001147 int width = this->width();
1148 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001149 unsigned rowBytes = this->rowBytes();
reed@android.com149e2f62009-05-22 14:39:03 +00001150 SkBitmap dstBM;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001151
1152 for (int i = 0; i < maxLevels; i++) {
1153 width >>= 1;
1154 height >>= 1;
1155 rowBytes = ComputeRowBytes(config, width);
1156
1157 level[i].fPixels = addr;
reed@android.comf459a492009-03-27 12:33:50 +00001158 level[i].fWidth = width;
1159 level[i].fHeight = height;
reed@android.com49f0ff22009-03-19 21:52:42 +00001160 level[i].fRowBytes = rowBytes;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001161
1162 dstBM.setConfig(config, width, height, rowBytes);
1163 dstBM.setPixels(addr);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001164
reed@android.com149e2f62009-05-22 14:39:03 +00001165 for (int y = 0; y < height; y++) {
1166 for (int x = 0; x < width; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001167 proc(&dstBM, x, y, srcBM);
1168 }
1169 }
1170
1171 srcBM = dstBM;
1172 addr += height * rowBytes;
1173 }
1174 SkASSERT(addr == (uint8_t*)mm->pixels() + size);
1175 fMipMap = mm;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001176}
1177
1178bool SkBitmap::hasMipMap() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001179 return fMipMap != NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001180}
1181
1182int SkBitmap::extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy) {
reed@android.com83f7bc32009-07-17 02:42:41 +00001183 if (NULL == fMipMap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001184 return 0;
reed@android.com83f7bc32009-07-17 02:42:41 +00001185 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001186
reed@android.com8a1c16f2008-12-17 15:59:43 +00001187 int level = ComputeMipLevel(sx, sy) >> 16;
1188 SkASSERT(level >= 0);
1189 if (level <= 0) {
1190 return 0;
1191 }
1192
1193 if (level >= fMipMap->fLevelCount) {
1194 level = fMipMap->fLevelCount - 1;
1195 }
1196 if (dst) {
1197 const MipLevel& mip = fMipMap->levels()[level - 1];
1198 dst->setConfig((SkBitmap::Config)this->config(),
1199 mip.fWidth, mip.fHeight, mip.fRowBytes);
1200 dst->setPixels(mip.fPixels);
1201 }
1202 return level;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001203}
1204
1205SkFixed SkBitmap::ComputeMipLevel(SkFixed sx, SkFixed sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206 sx = SkAbs32(sx);
1207 sy = SkAbs32(sy);
1208 if (sx < sy) {
1209 sx = sy;
1210 }
1211 if (sx < SK_Fixed1) {
1212 return 0;
1213 }
1214 int clz = SkCLZ(sx);
1215 SkASSERT(clz >= 1 && clz <= 15);
1216 return SkIntToFixed(15 - clz) + ((unsigned)(sx << (clz + 1)) >> 16);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001217}
1218
1219///////////////////////////////////////////////////////////////////////////////
1220
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +00001221static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001222 int alphaRowBytes) {
1223 SkASSERT(alpha != NULL);
1224 SkASSERT(alphaRowBytes >= src.width());
1225
1226 SkBitmap::Config config = src.getConfig();
1227 int w = src.width();
1228 int h = src.height();
1229 int rb = src.rowBytes();
1230
reed@android.com1cdcb512009-08-24 19:11:00 +00001231 SkAutoLockPixels alp(src);
1232 if (!src.readyToDraw()) {
1233 // zero out the alpha buffer and return
1234 while (--h >= 0) {
1235 memset(alpha, 0, w);
1236 alpha += alphaRowBytes;
1237 }
1238 return false;
1239 }
reed@google.com82065d62011-02-07 15:30:46 +00001240
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241 if (SkBitmap::kA8_Config == config && !src.isOpaque()) {
1242 const uint8_t* s = src.getAddr8(0, 0);
1243 while (--h >= 0) {
1244 memcpy(alpha, s, w);
1245 s += rb;
1246 alpha += alphaRowBytes;
1247 }
1248 } else if (SkBitmap::kARGB_8888_Config == config && !src.isOpaque()) {
1249 const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0);
1250 while (--h >= 0) {
1251 for (int x = 0; x < w; x++) {
1252 alpha[x] = SkGetPackedA32(s[x]);
1253 }
1254 s = (const SkPMColor*)((const char*)s + rb);
1255 alpha += alphaRowBytes;
1256 }
1257 } else if (SkBitmap::kARGB_4444_Config == config && !src.isOpaque()) {
1258 const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0);
1259 while (--h >= 0) {
1260 for (int x = 0; x < w; x++) {
1261 alpha[x] = SkPacked4444ToA32(s[x]);
1262 }
1263 s = (const SkPMColor16*)((const char*)s + rb);
1264 alpha += alphaRowBytes;
1265 }
1266 } else if (SkBitmap::kIndex8_Config == config && !src.isOpaque()) {
1267 SkColorTable* ct = src.getColorTable();
1268 if (ct) {
1269 const SkPMColor* SK_RESTRICT table = ct->lockColors();
1270 const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0);
1271 while (--h >= 0) {
1272 for (int x = 0; x < w; x++) {
1273 alpha[x] = SkGetPackedA32(table[s[x]]);
1274 }
1275 s += rb;
1276 alpha += alphaRowBytes;
1277 }
1278 ct->unlockColors(false);
1279 }
1280 } else { // src is opaque, so just fill alpha[] with 0xFF
1281 memset(alpha, 0xFF, h * alphaRowBytes);
1282 }
reed@android.com1cdcb512009-08-24 19:11:00 +00001283 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001284}
1285
1286#include "SkPaint.h"
1287#include "SkMaskFilter.h"
1288#include "SkMatrix.h"
1289
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001290bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
djsollen@google.com57f49692011-02-23 20:46:31 +00001291 Allocator *allocator, SkIPoint* offset) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001292 SkDEBUGCODE(this->validate();)
1293
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001294 SkBitmap tmpBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001295 SkMatrix identity;
1296 SkMask srcM, dstM;
1297
1298 srcM.fBounds.set(0, 0, this->width(), this->height());
1299 srcM.fRowBytes = SkAlign4(this->width());
1300 srcM.fFormat = SkMask::kA8_Format;
1301
1302 SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL;
1303
1304 // compute our (larger?) dst bounds if we have a filter
1305 if (NULL != filter) {
1306 identity.reset();
1307 srcM.fImage = NULL;
1308 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1309 goto NO_FILTER_CASE;
1310 }
1311 dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
1312 } else {
1313 NO_FILTER_CASE:
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001314 tmpBitmap.setConfig(SkBitmap::kA8_Config, this->width(), this->height(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315 srcM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001316 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1317 // Allocation of pixels for alpha bitmap failed.
1318 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1319 tmpBitmap.width(), tmpBitmap.height());
1320 return false;
1321 }
1322 GetBitmapAlpha(*this, tmpBitmap.getAddr8(0, 0), srcM.fRowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323 if (offset) {
1324 offset->set(0, 0);
1325 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001326 tmpBitmap.swap(*dst);
1327 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001328 }
1329
1330 SkAutoMaskImage srcCleanup(&srcM, true);
1331
1332 GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
1333 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1334 goto NO_FILTER_CASE;
1335 }
1336
1337 SkAutoMaskImage dstCleanup(&dstM, false);
1338
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001339 tmpBitmap.setConfig(SkBitmap::kA8_Config, dstM.fBounds.width(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001340 dstM.fBounds.height(), dstM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001341 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1342 // Allocation of pixels for alpha bitmap failed.
1343 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1344 tmpBitmap.width(), tmpBitmap.height());
1345 return false;
1346 }
1347 memcpy(tmpBitmap.getPixels(), dstM.fImage, dstM.computeImageSize());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001348 if (offset) {
1349 offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
1350 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001351 SkDEBUGCODE(tmpBitmap.validate();)
1352
1353 tmpBitmap.swap(*dst);
1354 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355}
1356
1357///////////////////////////////////////////////////////////////////////////////
1358
1359enum {
1360 SERIALIZE_PIXELTYPE_NONE,
1361 SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE,
1362 SERIALIZE_PIXELTYPE_RAW_NO_CTABLE,
1363 SERIALIZE_PIXELTYPE_REF_DATA,
1364 SERIALIZE_PIXELTYPE_REF_PTR,
1365};
1366
1367static void writeString(SkFlattenableWriteBuffer& buffer, const char str[]) {
1368 size_t len = strlen(str);
1369 buffer.write32(len);
1370 buffer.writePad(str, len);
1371}
1372
1373static SkPixelRef::Factory deserialize_factory(SkFlattenableReadBuffer& buffer) {
1374 size_t len = buffer.readInt();
1375 SkAutoSMalloc<256> storage(len + 1);
1376 char* str = (char*)storage.get();
1377 buffer.read(str, len);
1378 str[len] = 0;
1379 return SkPixelRef::NameToFactory(str);
1380}
1381
1382/*
1383 It is tricky to know how much to flatten. If we don't have a pixelref (i.e.
1384 we just have pixels, then we can only flatten the pixels, or write out an
1385 empty bitmap.
weita@google.comf9ab99a2009-05-03 18:23:30 +00001386
reed@android.com8a1c16f2008-12-17 15:59:43 +00001387 With a pixelref, we still have the question of recognizing when two sitings
1388 of the same pixelref are the same, and when they are different. Perhaps we
1389 should look at the generationID and keep a record of that in some dictionary
1390 associated with the buffer. SkGLTextureCache does this sort of thing to know
1391 when to create a new texture.
1392*/
1393void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
1394 buffer.write32(fWidth);
1395 buffer.write32(fHeight);
1396 buffer.write32(fRowBytes);
1397 buffer.write8(fConfig);
1398 buffer.writeBool(this->isOpaque());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001399
reed@android.com8a1c16f2008-12-17 15:59:43 +00001400 /* If we are called in this mode, then it is up to the caller to manage
1401 the owner-counts on the pixelref, as we just record the ptr itself.
1402 */
1403 if (!buffer.persistBitmapPixels()) {
1404 if (fPixelRef) {
1405 buffer.write8(SERIALIZE_PIXELTYPE_REF_PTR);
1406 buffer.write32(fPixelRefOffset);
1407 buffer.writeRefCnt(fPixelRef);
1408 return;
1409 } else {
1410 // we ignore the non-persist request, since we don't have a ref
1411 // ... or we could just write an empty bitmap...
1412 // (true) will write an empty bitmap, (false) will flatten the pix
1413 if (true) {
1414 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1415 return;
1416 }
1417 }
1418 }
1419
1420 if (fPixelRef) {
1421 SkPixelRef::Factory fact = fPixelRef->getFactory();
1422 if (fact) {
1423 const char* name = SkPixelRef::FactoryToName(fact);
1424 if (name && *name) {
1425 buffer.write8(SERIALIZE_PIXELTYPE_REF_DATA);
1426 buffer.write32(fPixelRefOffset);
1427 writeString(buffer, name);
1428 fPixelRef->flatten(buffer);
1429 return;
1430 }
1431 }
1432 // if we get here, we can't record the pixels
1433 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1434 } else if (fPixels) {
1435 if (fColorTable) {
1436 buffer.write8(SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE);
1437 fColorTable->flatten(buffer);
1438 } else {
1439 buffer.write8(SERIALIZE_PIXELTYPE_RAW_NO_CTABLE);
1440 }
wjmaclean@chromium.org3f0260d2011-02-22 22:32:14 +00001441 buffer.writePad(fPixels, this->getSafeSize());
1442 // There is no writeZeroPad() fcn, so write individual bytes.
1443 if (this->getSize() > this->getSafeSize()) {
1444 size_t deltaSize = this->getSize() - this->getSafeSize();
1445 // Need aligned pointer to write into due to internal implementa-
1446 // tion of SkWriter32.
1447 memset(buffer.reserve(SkAlign4(deltaSize)), 0, deltaSize);
1448 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449 } else {
1450 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1451 }
1452}
1453
1454void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) {
1455 this->reset();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001456
reed@android.com8a1c16f2008-12-17 15:59:43 +00001457 int width = buffer.readInt();
1458 int height = buffer.readInt();
1459 int rowBytes = buffer.readInt();
1460 int config = buffer.readU8();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001461
reed@android.com8a1c16f2008-12-17 15:59:43 +00001462 this->setConfig((Config)config, width, height, rowBytes);
1463 this->setIsOpaque(buffer.readBool());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001464
reed@android.com8a1c16f2008-12-17 15:59:43 +00001465 int reftype = buffer.readU8();
1466 switch (reftype) {
1467 case SERIALIZE_PIXELTYPE_REF_PTR: {
1468 size_t offset = buffer.readU32();
1469 SkPixelRef* pr = (SkPixelRef*)buffer.readRefCnt();
1470 this->setPixelRef(pr, offset);
1471 break;
1472 }
1473 case SERIALIZE_PIXELTYPE_REF_DATA: {
1474 size_t offset = buffer.readU32();
1475 SkPixelRef::Factory fact = deserialize_factory(buffer);
1476 SkPixelRef* pr = fact(buffer);
reed@google.com82065d62011-02-07 15:30:46 +00001477 SkSafeUnref(this->setPixelRef(pr, offset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001478 break;
1479 }
1480 case SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE:
1481 case SERIALIZE_PIXELTYPE_RAW_NO_CTABLE: {
1482 SkColorTable* ctable = NULL;
1483 if (SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE == reftype) {
1484 ctable = SkNEW_ARGS(SkColorTable, (buffer));
1485 }
wjmaclean@chromium.org3f0260d2011-02-22 22:32:14 +00001486 size_t size = this->getSize();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001487 if (this->allocPixels(ctable)) {
1488 this->lockPixels();
wjmaclean@chromium.org3f0260d2011-02-22 22:32:14 +00001489 // Just read what we need.
1490 buffer.read(this->getPixels(), this->getSafeSize());
1491 // Keep aligned for subsequent reads.
1492 buffer.skip(size - this->getSafeSize());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001493 this->unlockPixels();
1494 } else {
wjmaclean@chromium.org0bde1802010-12-22 17:43:54 +00001495 buffer.skip(size); // Still skip the full-sized buffer though.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001496 }
reed@android.com149e2f62009-05-22 14:39:03 +00001497 SkSafeUnref(ctable);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001498 break;
1499 }
1500 case SERIALIZE_PIXELTYPE_NONE:
1501 break;
1502 default:
1503 SkASSERT(!"unrecognized pixeltype in serialized data");
1504 sk_throw();
1505 }
1506}
1507
1508///////////////////////////////////////////////////////////////////////////////
1509
1510SkBitmap::RLEPixels::RLEPixels(int width, int height) {
1511 fHeight = height;
1512 fYPtrs = (uint8_t**)sk_malloc_throw(height * sizeof(uint8_t*));
reed@android.com4516f472009-06-29 16:25:36 +00001513 sk_bzero(fYPtrs, height * sizeof(uint8_t*));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514}
1515
1516SkBitmap::RLEPixels::~RLEPixels() {
1517 sk_free(fYPtrs);
1518}
1519
1520///////////////////////////////////////////////////////////////////////////////
1521
1522#ifdef SK_DEBUG
1523void SkBitmap::validate() const {
1524 SkASSERT(fConfig < kConfigCount);
1525 SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
junov@google.com4ee7ae52011-06-30 17:30:49 +00001526 SkASSERT(fFlags <= (kImageIsOpaque_Flag | kImageIsVolatile_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001527 SkASSERT(fPixelLockCount >= 0);
1528 SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000);
1529 SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
1530
1531#if 0 // these asserts are not thread-correct, so disable for now
1532 if (fPixelRef) {
1533 if (fPixelLockCount > 0) {
1534 SkASSERT(fPixelRef->getLockCount() > 0);
1535 } else {
1536 SkASSERT(NULL == fPixels);
1537 SkASSERT(NULL == fColorTable);
1538 }
1539 }
1540#endif
1541}
1542#endif