blob: 3e26639d0e03bc5ef2be79361a2e89af601b9ff2 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
2 * Copyright (C) 2006-2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "SkBitmap.h"
18#include "SkColorPriv.h"
19#include "SkDither.h"
20#include "SkFlattenable.h"
21#include "SkMallocPixelRef.h"
22#include "SkMask.h"
23#include "SkPixelRef.h"
24#include "SkThread.h"
25#include "SkUtils.h"
26#include "SkPackBits.h"
27#include <new>
28
reed@android.com149e2f62009-05-22 14:39:03 +000029static bool isPos32Bits(const Sk64& value) {
30 return !value.isNeg() && value.is32();
31}
32
reed@android.com8a1c16f2008-12-17 15:59:43 +000033struct MipLevel {
34 void* fPixels;
35 uint32_t fRowBytes;
reed@android.comf459a492009-03-27 12:33:50 +000036 uint32_t fWidth, fHeight;
reed@android.com8a1c16f2008-12-17 15:59:43 +000037};
38
39struct SkBitmap::MipMap : SkNoncopyable {
40 int32_t fRefCnt;
41 int fLevelCount;
42// MipLevel fLevel[fLevelCount];
43// Pixels[]
weita@google.comf9ab99a2009-05-03 18:23:30 +000044
reed@android.com8a1c16f2008-12-17 15:59:43 +000045 static MipMap* Alloc(int levelCount, size_t pixelSize) {
reed@android.com149e2f62009-05-22 14:39:03 +000046 if (levelCount < 0) {
47 return NULL;
48 }
49 Sk64 size;
50 size.setMul(levelCount + 1, sizeof(MipLevel));
51 size.add(sizeof(MipMap));
52 size.add(pixelSize);
53 if (!isPos32Bits(size)) {
54 return NULL;
55 }
56 MipMap* mm = (MipMap*)sk_malloc_throw(size.get32());
reed@android.com8a1c16f2008-12-17 15:59:43 +000057 mm->fRefCnt = 1;
58 mm->fLevelCount = levelCount;
59 return mm;
60 }
61
62 const MipLevel* levels() const { return (const MipLevel*)(this + 1); }
63 MipLevel* levels() { return (MipLevel*)(this + 1); }
64
65 const void* pixels() const { return levels() + fLevelCount; }
66 void* pixels() { return levels() + fLevelCount; }
weita@google.comf9ab99a2009-05-03 18:23:30 +000067
reed@android.com149e2f62009-05-22 14:39:03 +000068 void ref() {
69 if (SK_MaxS32 == sk_atomic_inc(&fRefCnt)) {
70 sk_throw();
reed@android.com8a1c16f2008-12-17 15:59:43 +000071 }
72 }
reed@android.com149e2f62009-05-22 14:39:03 +000073 void unref() {
74 SkASSERT(fRefCnt > 0);
75 if (sk_atomic_dec(&fRefCnt) == 1) {
76 sk_free(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +000077 }
78 }
79};
reed@android.com8a1c16f2008-12-17 15:59:43 +000080
81///////////////////////////////////////////////////////////////////////////////
82///////////////////////////////////////////////////////////////////////////////
83
84SkBitmap::SkBitmap() {
reed@android.com4516f472009-06-29 16:25:36 +000085 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000086}
87
88SkBitmap::SkBitmap(const SkBitmap& src) {
89 SkDEBUGCODE(src.validate();)
reed@android.com4516f472009-06-29 16:25:36 +000090 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000091 *this = src;
92 SkDEBUGCODE(this->validate();)
93}
94
95SkBitmap::~SkBitmap() {
96 SkDEBUGCODE(this->validate();)
97 this->freePixels();
98}
99
100SkBitmap& SkBitmap::operator=(const SkBitmap& src) {
101 if (this != &src) {
102 this->freePixels();
103 memcpy(this, &src, sizeof(src));
104
105 // inc src reference counts
reed@android.com83f7bc32009-07-17 02:42:41 +0000106 SkSafeRef(src.fPixelRef);
reed@android.com149e2f62009-05-22 14:39:03 +0000107 SkSafeRef(src.fMipMap);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108
109 // we reset our locks if we get blown away
110 fPixelLockCount = 0;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000111
reed@android.com8a1c16f2008-12-17 15:59:43 +0000112 /* The src could be in 3 states
113 1. no pixelref, in which case we just copy/ref the pixels/ctable
114 2. unlocked pixelref, pixels/ctable should be null
115 3. locked pixelref, we should lock the ref again ourselves
116 */
117 if (NULL == fPixelRef) {
118 // leave fPixels as it is
reed@google.com82065d62011-02-07 15:30:46 +0000119 SkSafeRef(fColorTable); // ref the user's ctable if present
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120 } else { // we have a pixelref, so pixels/ctable reflect it
121 // ignore the values from the memcpy
122 fPixels = NULL;
123 fColorTable = NULL;
124 }
125 }
126
127 SkDEBUGCODE(this->validate();)
128 return *this;
129}
130
131void SkBitmap::swap(SkBitmap& other) {
132 SkTSwap<SkColorTable*>(fColorTable, other.fColorTable);
133 SkTSwap<SkPixelRef*>(fPixelRef, other.fPixelRef);
134 SkTSwap<size_t>(fPixelRefOffset, other.fPixelRefOffset);
135 SkTSwap<int>(fPixelLockCount, other.fPixelLockCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000136 SkTSwap<MipMap*>(fMipMap, other.fMipMap);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000137 SkTSwap<void*>(fPixels, other.fPixels);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000138 SkTSwap<uint32_t>(fRowBytes, other.fRowBytes);
reed@android.comf459a492009-03-27 12:33:50 +0000139 SkTSwap<uint32_t>(fWidth, other.fWidth);
140 SkTSwap<uint32_t>(fHeight, other.fHeight);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000141 SkTSwap<uint8_t>(fConfig, other.fConfig);
142 SkTSwap<uint8_t>(fFlags, other.fFlags);
143 SkTSwap<uint8_t>(fBytesPerPixel, other.fBytesPerPixel);
144
145 SkDEBUGCODE(this->validate();)
146}
147
148void SkBitmap::reset() {
149 this->freePixels();
reed@android.com4516f472009-06-29 16:25:36 +0000150 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151}
152
153int SkBitmap::ComputeBytesPerPixel(SkBitmap::Config config) {
154 int bpp;
155 switch (config) {
156 case kNo_Config:
157 case kA1_Config:
158 bpp = 0; // not applicable
159 break;
160 case kRLE_Index8_Config:
161 case kA8_Config:
162 case kIndex8_Config:
163 bpp = 1;
164 break;
165 case kRGB_565_Config:
166 case kARGB_4444_Config:
167 bpp = 2;
168 break;
169 case kARGB_8888_Config:
170 bpp = 4;
171 break;
172 default:
173 SkASSERT(!"unknown config");
174 bpp = 0; // error
175 break;
176 }
177 return bpp;
178}
179
180int SkBitmap::ComputeRowBytes(Config c, int width) {
reed@android.com149e2f62009-05-22 14:39:03 +0000181 if (width < 0) {
182 return 0;
183 }
184
185 Sk64 rowBytes;
186 rowBytes.setZero();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187
188 switch (c) {
189 case kNo_Config:
190 case kRLE_Index8_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000191 break;
192 case kA1_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000193 rowBytes.set(width);
194 rowBytes.add(7);
195 rowBytes.shiftRight(3);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196 break;
197 case kA8_Config:
198 case kIndex8_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000199 rowBytes.set(width);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000200 break;
201 case kRGB_565_Config:
202 case kARGB_4444_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000203 rowBytes.set(width);
204 rowBytes.shiftLeft(1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205 break;
206 case kARGB_8888_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000207 rowBytes.set(width);
208 rowBytes.shiftLeft(2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209 break;
210 default:
211 SkASSERT(!"unknown config");
212 break;
213 }
reed@android.com149e2f62009-05-22 14:39:03 +0000214 return isPos32Bits(rowBytes) ? rowBytes.get32() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215}
216
217Sk64 SkBitmap::ComputeSize64(Config c, int width, int height) {
218 Sk64 size;
219 size.setMul(SkBitmap::ComputeRowBytes(c, width), height);
220 return size;
221}
222
223size_t SkBitmap::ComputeSize(Config c, int width, int height) {
224 Sk64 size = SkBitmap::ComputeSize64(c, width, height);
reed@android.com149e2f62009-05-22 14:39:03 +0000225 return isPos32Bits(size) ? size.get32() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226}
227
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000228Sk64 SkBitmap::ComputeSafeSize64(Config config,
229 uint32_t width,
230 uint32_t height,
231 uint32_t rowBytes) {
232 Sk64 safeSize;
233 safeSize.setZero();
234 if (height > 0) {
235 safeSize.set(ComputeRowBytes(config, width));
236 Sk64 sizeAllButLastRow;
237 sizeAllButLastRow.setMul(height - 1, rowBytes);
238 safeSize.add(sizeAllButLastRow);
239 }
240 SkASSERT(!safeSize.isNeg());
241 return safeSize;
242}
243
244size_t SkBitmap::ComputeSafeSize(Config config,
245 uint32_t width,
246 uint32_t height,
247 uint32_t rowBytes) {
248 Sk64 safeSize = ComputeSafeSize64(config, width, height, rowBytes);
249 return (safeSize.is32() ? safeSize.get32() : 0);
250}
251
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252void SkBitmap::setConfig(Config c, int width, int height, int rowBytes) {
253 this->freePixels();
254
reed@android.com149e2f62009-05-22 14:39:03 +0000255 if ((width | height | rowBytes) < 0) {
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000256 goto err;
reed@android.com149e2f62009-05-22 14:39:03 +0000257 }
258
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 if (rowBytes == 0) {
260 rowBytes = SkBitmap::ComputeRowBytes(c, width);
reed@android.com149e2f62009-05-22 14:39:03 +0000261 if (0 == rowBytes && kNo_Config != c) {
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000262 goto err;
reed@android.com149e2f62009-05-22 14:39:03 +0000263 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 }
reed@android.com89bb83a2009-05-29 21:30:42 +0000265
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266 fConfig = SkToU8(c);
reed@android.comf459a492009-03-27 12:33:50 +0000267 fWidth = width;
268 fHeight = height;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269 fRowBytes = rowBytes;
270
271 fBytesPerPixel = (uint8_t)ComputeBytesPerPixel(c);
272
273 SkDEBUGCODE(this->validate();)
reed@android.com9e0c2fc2009-06-02 18:54:28 +0000274 return;
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000275
reed@android.com9e0c2fc2009-06-02 18:54:28 +0000276 // if we got here, we had an error, so we reset the bitmap to empty
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000277err:
278 this->reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279}
280
281void SkBitmap::updatePixelsFromRef() const {
282 if (NULL != fPixelRef) {
283 if (fPixelLockCount > 0) {
284 SkASSERT(fPixelRef->getLockCount() > 0);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000285
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286 void* p = fPixelRef->pixels();
287 if (NULL != p) {
288 p = (char*)p + fPixelRefOffset;
289 }
290 fPixels = p;
291 SkRefCnt_SafeAssign(fColorTable, fPixelRef->colorTable());
292 } else {
293 SkASSERT(0 == fPixelLockCount);
294 fPixels = NULL;
reed@android.com149e2f62009-05-22 14:39:03 +0000295 if (fColorTable) {
296 fColorTable->unref();
297 fColorTable = NULL;
298 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299 }
300 }
301}
302
303SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, size_t offset) {
304 // do this first, we that we never have a non-zero offset with a null ref
305 if (NULL == pr) {
306 offset = 0;
307 }
308
309 if (fPixelRef != pr || fPixelRefOffset != offset) {
310 if (fPixelRef != pr) {
311 this->freePixels();
312 SkASSERT(NULL == fPixelRef);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000313
reed@google.com82065d62011-02-07 15:30:46 +0000314 SkSafeRef(pr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315 fPixelRef = pr;
316 }
317 fPixelRefOffset = offset;
318 this->updatePixelsFromRef();
319 }
320
321 SkDEBUGCODE(this->validate();)
322 return pr;
323}
324
325void SkBitmap::lockPixels() const {
326 if (NULL != fPixelRef && 1 == ++fPixelLockCount) {
327 fPixelRef->lockPixels();
328 this->updatePixelsFromRef();
329 }
330 SkDEBUGCODE(this->validate();)
331}
332
333void SkBitmap::unlockPixels() const {
334 SkASSERT(NULL == fPixelRef || fPixelLockCount > 0);
335
336 if (NULL != fPixelRef && 0 == --fPixelLockCount) {
337 fPixelRef->unlockPixels();
338 this->updatePixelsFromRef();
339 }
340 SkDEBUGCODE(this->validate();)
341}
342
343void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
344 this->freePixels();
345 fPixels = p;
346 SkRefCnt_SafeAssign(fColorTable, ctable);
347
348 SkDEBUGCODE(this->validate();)
349}
350
351bool SkBitmap::allocPixels(Allocator* allocator, SkColorTable* ctable) {
352 HeapAllocator stdalloc;
353
354 if (NULL == allocator) {
355 allocator = &stdalloc;
356 }
357 return allocator->allocPixelRef(this, ctable);
358}
359
360void SkBitmap::freePixels() {
361 // if we're gonna free the pixels, we certainly need to free the mipmap
362 this->freeMipMap();
363
reed@android.com149e2f62009-05-22 14:39:03 +0000364 if (fColorTable) {
365 fColorTable->unref();
366 fColorTable = NULL;
367 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000368
369 if (NULL != fPixelRef) {
370 if (fPixelLockCount > 0) {
371 fPixelRef->unlockPixels();
372 }
373 fPixelRef->unref();
374 fPixelRef = NULL;
375 fPixelRefOffset = 0;
376 }
377 fPixelLockCount = 0;
378 fPixels = NULL;
379}
380
381void SkBitmap::freeMipMap() {
reed@android.com149e2f62009-05-22 14:39:03 +0000382 if (fMipMap) {
383 fMipMap->unref();
384 fMipMap = NULL;
385 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000386}
387
388uint32_t SkBitmap::getGenerationID() const {
389 return fPixelRef ? fPixelRef->getGenerationID() : 0;
390}
391
392void SkBitmap::notifyPixelsChanged() const {
393 if (fPixelRef) {
394 fPixelRef->notifyPixelsChanged();
395 }
396}
397
reed@android.comce4e53a2010-09-09 16:01:26 +0000398SkGpuTexture* SkBitmap::getTexture() const {
399 return fPixelRef ? fPixelRef->getTexture() : NULL;
400}
401
reed@android.com8a1c16f2008-12-17 15:59:43 +0000402///////////////////////////////////////////////////////////////////////////////
403
reed@android.com8a1c16f2008-12-17 15:59:43 +0000404/** We explicitly use the same allocator for our pixels that SkMask does,
405 so that we can freely assign memory allocated by one class to the other.
406 */
407bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
408 SkColorTable* ctable) {
409 Sk64 size = dst->getSize64();
410 if (size.isNeg() || !size.is32()) {
411 return false;
412 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000413
reed@android.com8a1c16f2008-12-17 15:59:43 +0000414 void* addr = sk_malloc_flags(size.get32(), 0); // returns NULL on failure
415 if (NULL == addr) {
416 return false;
417 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000418
reed@android.com8a1c16f2008-12-17 15:59:43 +0000419 dst->setPixelRef(new SkMallocPixelRef(addr, size.get32(), ctable))->unref();
420 // since we're already allocated, we lockPixels right away
421 dst->lockPixels();
422 return true;
423}
424
425///////////////////////////////////////////////////////////////////////////////
426
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000427size_t SkBitmap::getSafeSize() const {
428 // This is intended to be a size_t version of ComputeSafeSize64(), just
429 // faster. The computation is meant to be identical.
430 return (fHeight ? ((fHeight - 1) * fRowBytes) +
431 ComputeRowBytes(getConfig(), fWidth): 0);
432}
433
434Sk64 SkBitmap::getSafeSize64() const {
435 return ComputeSafeSize64(getConfig(), fWidth, fHeight, fRowBytes);
436}
437
438bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize, int dstRowBytes)
439 const {
440
441 if (dstRowBytes == -1)
442 dstRowBytes = fRowBytes;
443 SkASSERT(dstRowBytes >= 0);
444
445 if (getConfig() == kRLE_Index8_Config ||
446 dstRowBytes < ComputeRowBytes(getConfig(), fWidth) ||
447 dst == NULL || (getPixels() == NULL && pixelRef() == NULL))
448 return false;
449
450 if (static_cast<uint32_t>(dstRowBytes) == fRowBytes) {
451 size_t safeSize = getSafeSize();
452 if (safeSize > dstSize || safeSize == 0)
453 return false;
454 else {
455 SkAutoLockPixels lock(*this);
456 // This implementation will write bytes beyond the end of each row,
457 // excluding the last row, if the bitmap's stride is greater than
458 // strictly required by the current config.
459 memcpy(dst, getPixels(), safeSize);
460
461 return true;
462 }
463 } else {
464 // If destination has different stride than us, then copy line by line.
465 if (ComputeSafeSize(getConfig(), fWidth, fHeight, dstRowBytes) >
466 dstSize)
467 return false;
468 else {
469 // Just copy what we need on each line.
470 uint32_t rowBytes = ComputeRowBytes(getConfig(), fWidth);
471 SkAutoLockPixels lock(*this);
472 const uint8_t* srcP = reinterpret_cast<const uint8_t*>(getPixels());
473 uint8_t* dstP = reinterpret_cast<uint8_t*>(dst);
474 for (uint32_t row = 0; row < fHeight;
475 row++, srcP += fRowBytes, dstP += dstRowBytes) {
476 memcpy(dstP, srcP, rowBytes);
477 }
478
479 return true;
480 }
481 }
482}
483
484bool SkBitmap::copyPixelsFrom(const void* const src, size_t srcSize,
485 int srcRowBytes) {
486
487 if (srcRowBytes == -1)
488 srcRowBytes = fRowBytes;
489 SkASSERT(srcRowBytes >= 0);
490
491 size_t safeSize = getSafeSize();
492 uint32_t rowBytes = ComputeRowBytes(getConfig(), fWidth);
493 if (getConfig() == kRLE_Index8_Config || src == NULL ||
494 static_cast<uint32_t>(srcRowBytes) < rowBytes ||
495 safeSize == 0 ||
496 srcSize < ComputeSafeSize(getConfig(), fWidth, fHeight, srcRowBytes)) {
497 return false;
498 }
499
500 SkAutoLockPixels lock(*this);
501 if (static_cast<uint32_t>(srcRowBytes) == fRowBytes) {
502 // This implementation will write bytes beyond the end of each row,
503 // excluding the last row, if the bitmap's stride is greater than
504 // strictly required by the current config.
505 memcpy(getPixels(), src, safeSize);
506 } else {
507 // Just copy the bytes we need on each line.
508 const uint8_t* srcP = reinterpret_cast<const uint8_t*>(src);
509 uint8_t* dstP = reinterpret_cast<uint8_t*>(getPixels());
510 for (uint32_t row = 0; row < fHeight;
511 row++, srcP += srcRowBytes, dstP += fRowBytes) {
512 memcpy(dstP, srcP, rowBytes);
513 }
514 }
515
516 return true;
517}
518
519///////////////////////////////////////////////////////////////////////////////
520
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521bool SkBitmap::isOpaque() const {
522 switch (fConfig) {
523 case kNo_Config:
524 return true;
525
526 case kA1_Config:
527 case kA8_Config:
528 case kARGB_4444_Config:
529 case kARGB_8888_Config:
530 return (fFlags & kImageIsOpaque_Flag) != 0;
531
532 case kIndex8_Config:
533 case kRLE_Index8_Config: {
534 uint32_t flags = 0;
535
536 this->lockPixels();
537 // if lockPixels failed, we may not have a ctable ptr
538 if (fColorTable) {
539 flags = fColorTable->getFlags();
540 }
541 this->unlockPixels();
542
543 return (flags & SkColorTable::kColorsAreOpaque_Flag) != 0;
544 }
545
546 case kRGB_565_Config:
547 return true;
548
549 default:
550 SkASSERT(!"unknown bitmap config pased to isOpaque");
551 return false;
552 }
553}
554
555void SkBitmap::setIsOpaque(bool isOpaque) {
556 /* we record this regardless of fConfig, though it is ignored in
557 isOpaque() for configs that can't support per-pixel alpha.
558 */
559 if (isOpaque) {
560 fFlags |= kImageIsOpaque_Flag;
561 } else {
562 fFlags &= ~kImageIsOpaque_Flag;
563 }
564}
565
566void* SkBitmap::getAddr(int x, int y) const {
567 SkASSERT((unsigned)x < (unsigned)this->width());
568 SkASSERT((unsigned)y < (unsigned)this->height());
569
570 char* base = (char*)this->getPixels();
571 if (base) {
572 base += y * this->rowBytes();
573 switch (this->config()) {
574 case SkBitmap::kARGB_8888_Config:
575 base += x << 2;
576 break;
577 case SkBitmap::kARGB_4444_Config:
578 case SkBitmap::kRGB_565_Config:
579 base += x << 1;
580 break;
581 case SkBitmap::kA8_Config:
582 case SkBitmap::kIndex8_Config:
583 base += x;
584 break;
585 case SkBitmap::kA1_Config:
586 base += x >> 3;
587 break;
588 case kRLE_Index8_Config:
589 SkASSERT(!"Can't return addr for kRLE_Index8_Config");
590 base = NULL;
591 break;
592 default:
593 SkASSERT(!"Can't return addr for config");
594 base = NULL;
595 break;
596 }
597 }
598 return base;
599}
600
601///////////////////////////////////////////////////////////////////////////////
602///////////////////////////////////////////////////////////////////////////////
603
604void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
605 SkDEBUGCODE(this->validate();)
606
607 if (0 == fWidth || 0 == fHeight ||
608 kNo_Config == fConfig || kIndex8_Config == fConfig) {
609 return;
610 }
611
612 SkAutoLockPixels alp(*this);
613 // perform this check after the lock call
614 if (!this->readyToDraw()) {
615 return;
616 }
617
618 int height = fHeight;
619 const int width = fWidth;
620 const int rowBytes = fRowBytes;
621
622 // make rgb premultiplied
623 if (255 != a) {
624 r = SkAlphaMul(r, a);
625 g = SkAlphaMul(g, a);
626 b = SkAlphaMul(b, a);
627 }
628
629 switch (fConfig) {
630 case kA1_Config: {
631 uint8_t* p = (uint8_t*)fPixels;
632 const int count = (width + 7) >> 3;
633 a = (a >> 7) ? 0xFF : 0;
634 SkASSERT(count <= rowBytes);
635 while (--height >= 0) {
636 memset(p, a, count);
637 p += rowBytes;
638 }
639 break;
640 }
641 case kA8_Config: {
642 uint8_t* p = (uint8_t*)fPixels;
643 while (--height >= 0) {
644 memset(p, a, width);
645 p += rowBytes;
646 }
647 break;
648 }
649 case kARGB_4444_Config:
650 case kRGB_565_Config: {
651 uint16_t* p = (uint16_t*)fPixels;
652 uint16_t v;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000653
reed@android.com8a1c16f2008-12-17 15:59:43 +0000654 if (kARGB_4444_Config == fConfig) {
655 v = SkPackARGB4444(a >> 4, r >> 4, g >> 4, b >> 4);
656 } else { // kRGB_565_Config
657 v = SkPackRGB16(r >> (8 - SK_R16_BITS), g >> (8 - SK_G16_BITS),
658 b >> (8 - SK_B16_BITS));
659 }
660 while (--height >= 0) {
661 sk_memset16(p, v, width);
662 p = (uint16_t*)((char*)p + rowBytes);
663 }
664 break;
665 }
666 case kARGB_8888_Config: {
667 uint32_t* p = (uint32_t*)fPixels;
668 uint32_t v = SkPackARGB32(a, r, g, b);
669
670 while (--height >= 0) {
671 sk_memset32(p, v, width);
672 p = (uint32_t*)((char*)p + rowBytes);
673 }
674 break;
675 }
676 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000677
reed@android.com8a1c16f2008-12-17 15:59:43 +0000678 this->notifyPixelsChanged();
679}
680
681//////////////////////////////////////////////////////////////////////////////////////
682//////////////////////////////////////////////////////////////////////////////////////
683
684#define SUB_OFFSET_FAILURE ((size_t)-1)
685
686static size_t getSubOffset(const SkBitmap& bm, int x, int y) {
687 SkASSERT((unsigned)x < (unsigned)bm.width());
688 SkASSERT((unsigned)y < (unsigned)bm.height());
weita@google.comf9ab99a2009-05-03 18:23:30 +0000689
reed@android.com8a1c16f2008-12-17 15:59:43 +0000690 switch (bm.getConfig()) {
691 case SkBitmap::kA8_Config:
692 case SkBitmap:: kIndex8_Config:
693 // x is fine as is for the calculation
694 break;
695
696 case SkBitmap::kRGB_565_Config:
697 case SkBitmap::kARGB_4444_Config:
698 x <<= 1;
699 break;
700
701 case SkBitmap::kARGB_8888_Config:
702 x <<= 2;
703 break;
704
705 case SkBitmap::kNo_Config:
706 case SkBitmap::kA1_Config:
707 default:
708 return SUB_OFFSET_FAILURE;
709 }
710 return y * bm.rowBytes() + x;
711}
712
713bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
714 SkDEBUGCODE(this->validate();)
715
716 if (NULL == result || (NULL == fPixelRef && NULL == fPixels)) {
717 return false; // no src pixels
718 }
719
720 SkIRect srcRect, r;
721 srcRect.set(0, 0, this->width(), this->height());
722 if (!r.intersect(srcRect, subset)) {
723 return false; // r is empty (i.e. no intersection)
724 }
725
726 if (kRLE_Index8_Config == fConfig) {
727 SkAutoLockPixels alp(*this);
728 // don't call readyToDraw(), since we can operate w/o a colortable
729 // at this stage
730 if (this->getPixels() == NULL) {
731 return false;
732 }
733 SkBitmap bm;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000734
reed@android.com8a1c16f2008-12-17 15:59:43 +0000735 bm.setConfig(kIndex8_Config, r.width(), r.height());
736 bm.allocPixels(this->getColorTable());
737 if (NULL == bm.getPixels()) {
738 return false;
739 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000740
reed@android.com8a1c16f2008-12-17 15:59:43 +0000741 const RLEPixels* rle = (const RLEPixels*)this->getPixels();
742 uint8_t* dst = bm.getAddr8(0, 0);
743 const int width = bm.width();
744 const int rowBytes = bm.rowBytes();
weita@google.comf9ab99a2009-05-03 18:23:30 +0000745
reed@android.com8a1c16f2008-12-17 15:59:43 +0000746 for (int y = r.fTop; y < r.fBottom; y++) {
747 SkPackBits::Unpack8(dst, r.fLeft, width, rle->packedAtY(y));
748 dst += rowBytes;
749 }
750 result->swap(bm);
751 return true;
752 }
753
754 size_t offset = getSubOffset(*this, r.fLeft, r.fTop);
755 if (SUB_OFFSET_FAILURE == offset) {
756 return false; // config not supported
757 }
758
759 SkBitmap dst;
760 dst.setConfig(this->config(), r.width(), r.height(), this->rowBytes());
761
762 if (fPixelRef) {
763 // share the pixelref with a custom offset
764 dst.setPixelRef(fPixelRef, fPixelRefOffset + offset);
765 } else {
766 // share the pixels (owned by the caller)
767 dst.setPixels((char*)fPixels + offset, this->getColorTable());
768 }
769 SkDEBUGCODE(dst.validate();)
770
771 // we know we're good, so commit to result
772 result->swap(dst);
773 return true;
774}
775
776///////////////////////////////////////////////////////////////////////////////
777
778#include "SkCanvas.h"
779#include "SkPaint.h"
780
reed@android.comfbaa88d2009-05-06 17:44:34 +0000781bool SkBitmap::canCopyTo(Config dstConfig) const {
782 if (this->getConfig() == kNo_Config) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783 return false;
784 }
785
reed@android.comfbaa88d2009-05-06 17:44:34 +0000786 bool sameConfigs = (this->config() == dstConfig);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787 switch (dstConfig) {
788 case kA8_Config:
789 case kARGB_4444_Config:
790 case kRGB_565_Config:
791 case kARGB_8888_Config:
792 break;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000793 case kA1_Config:
794 case kIndex8_Config:
795 if (!sameConfigs) {
796 return false;
797 }
798 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799 default:
800 return false;
801 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000802
803 // do not copy src if srcConfig == kA1_Config while dstConfig != kA1_Config
804 if (this->getConfig() == kA1_Config && !sameConfigs) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000805 return false;
806 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000807
reed@android.comfbaa88d2009-05-06 17:44:34 +0000808 return true;
809}
810
811bool SkBitmap::copyTo(SkBitmap* dst, Config dstConfig, Allocator* alloc) const {
812 if (!this->canCopyTo(dstConfig)) {
813 return false;
814 }
815
reed@android.com311c82d2009-05-05 23:13:23 +0000816 // we lock this now, since we may need its colortable
817 SkAutoLockPixels srclock(*this);
reed@android.comfbaa88d2009-05-06 17:44:34 +0000818 if (!this->readyToDraw()) {
819 return false;
820 }
reed@android.com311c82d2009-05-05 23:13:23 +0000821
weita@google.comf9ab99a2009-05-03 18:23:30 +0000822 SkBitmap tmp;
823 tmp.setConfig(dstConfig, this->width(), this->height());
824
825 // allocate colortable if srcConfig == kIndex8_Config
826 SkColorTable* ctable = (dstConfig == kIndex8_Config) ?
827 new SkColorTable(*this->getColorTable()) : NULL;
828 SkAutoUnref au(ctable);
829 if (!tmp.allocPixels(alloc, ctable)) {
830 return false;
831 }
832
reed@android.com8a1c16f2008-12-17 15:59:43 +0000833 SkAutoLockPixels dstlock(tmp);
reed@android.comfbaa88d2009-05-06 17:44:34 +0000834 if (!tmp.readyToDraw()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000835 // allocator/lock failed
836 return false;
837 }
838
reed@android.comfbaa88d2009-05-06 17:44:34 +0000839 /* do memcpy for the same configs cases, else use drawing
weita@google.comf9ab99a2009-05-03 18:23:30 +0000840 */
reed@android.comfbaa88d2009-05-06 17:44:34 +0000841 if (this->config() == dstConfig) {
reed@android.com311c82d2009-05-05 23:13:23 +0000842 if (tmp.getSize() == this->getSize()) {
wjmaclean@chromium.org0bde1802010-12-22 17:43:54 +0000843 memcpy(tmp.getPixels(), this->getPixels(), this->getSafeSize());
reed@android.com311c82d2009-05-05 23:13:23 +0000844 } else {
845 const char* srcP = reinterpret_cast<const char*>(this->getPixels());
846 char* dstP = reinterpret_cast<char*>(tmp.getPixels());
847 // to be sure we don't read too much, only copy our logical pixels
848 size_t bytesToCopy = tmp.width() * tmp.bytesPerPixel();
849 for (int y = 0; y < tmp.height(); y++) {
850 memcpy(dstP, srcP, bytesToCopy);
851 srcP += this->rowBytes();
852 dstP += tmp.rowBytes();
853 }
854 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000855 } else {
856 // if the src has alpha, we have to clear the dst first
857 if (!this->isOpaque()) {
858 tmp.eraseColor(0);
859 }
860
861 SkCanvas canvas(tmp);
862 SkPaint paint;
863
864 paint.setDither(true);
865 canvas.drawBitmap(*this, 0, 0, &paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000866 }
867
reed@android.comcafc9f92009-08-22 03:44:57 +0000868 tmp.setIsOpaque(this->isOpaque());
869
reed@android.com8a1c16f2008-12-17 15:59:43 +0000870 dst->swap(tmp);
871 return true;
872}
873
874///////////////////////////////////////////////////////////////////////////////
875///////////////////////////////////////////////////////////////////////////////
876
877static void downsampleby2_proc32(SkBitmap* dst, int x, int y,
878 const SkBitmap& src) {
879 x <<= 1;
880 y <<= 1;
881 const SkPMColor* p = src.getAddr32(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +0000882 const SkPMColor* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000883 SkPMColor c, ag, rb;
884
885 c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF;
886 if (x < src.width() - 1) {
887 p += 1;
888 }
889 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
890
reed@android.com829c83c2009-06-08 12:05:31 +0000891 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000892 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +0000893 p += src.rowBytes() >> 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000894 }
895 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
896 if (x < src.width() - 1) {
897 p += 1;
898 }
899 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
900
901 *dst->getAddr32(x >> 1, y >> 1) =
902 ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00);
903}
904
905static inline uint32_t expand16(U16CPU c) {
906 return (c & ~SK_G16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16);
907}
908
909// returns dirt in the top 16bits, but we don't care, since we only
910// store the low 16bits.
911static inline U16CPU pack16(uint32_t c) {
912 return (c & ~SK_G16_MASK_IN_PLACE) | ((c >> 16) & SK_G16_MASK_IN_PLACE);
913}
914
915static void downsampleby2_proc16(SkBitmap* dst, int x, int y,
916 const SkBitmap& src) {
917 x <<= 1;
918 y <<= 1;
919 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +0000920 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000921 SkPMColor c;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000922
reed@android.com8a1c16f2008-12-17 15:59:43 +0000923 c = expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +0000924 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000925 p += 1;
926 }
927 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000928
reed@android.com829c83c2009-06-08 12:05:31 +0000929 p = baseP;
930 if (y < src.height() - 1) {
931 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000932 }
933 c += expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +0000934 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000935 p += 1;
936 }
937 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000938
reed@android.com8a1c16f2008-12-17 15:59:43 +0000939 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)pack16(c >> 2);
940}
941
942static uint32_t expand4444(U16CPU c) {
943 return (c & 0xF0F) | ((c & ~0xF0F) << 12);
944}
945
946static U16CPU collaps4444(uint32_t c) {
947 return (c & 0xF0F) | ((c >> 12) & ~0xF0F);
948}
949
950static void downsampleby2_proc4444(SkBitmap* dst, int x, int y,
951 const SkBitmap& src) {
952 x <<= 1;
953 y <<= 1;
954 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +0000955 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000956 uint32_t c;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000957
reed@android.com8a1c16f2008-12-17 15:59:43 +0000958 c = expand4444(*p);
959 if (x < src.width() - 1) {
960 p += 1;
961 }
962 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000963
reed@android.com829c83c2009-06-08 12:05:31 +0000964 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000965 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +0000966 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000967 }
968 c += expand4444(*p);
969 if (x < src.width() - 1) {
970 p += 1;
971 }
972 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000973
reed@android.com8a1c16f2008-12-17 15:59:43 +0000974 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)collaps4444(c >> 2);
975}
976
977void SkBitmap::buildMipMap(bool forceRebuild) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978 if (forceRebuild)
979 this->freeMipMap();
980 else if (fMipMap)
981 return; // we're already built
982
983 SkASSERT(NULL == fMipMap);
984
985 void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src);
986
987 const SkBitmap::Config config = this->getConfig();
988
989 switch (config) {
990 case kARGB_8888_Config:
991 proc = downsampleby2_proc32;
992 break;
993 case kRGB_565_Config:
994 proc = downsampleby2_proc16;
995 break;
996 case kARGB_4444_Config:
997 proc = downsampleby2_proc4444;
998 break;
999 case kIndex8_Config:
1000 case kA8_Config:
1001 default:
1002 return; // don't build mipmaps for these configs
1003 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001004
reed@android.com149e2f62009-05-22 14:39:03 +00001005 SkAutoLockPixels alp(*this);
1006 if (!this->readyToDraw()) {
1007 return;
1008 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001009
1010 // whip through our loop to compute the exact size needed
1011 size_t size = 0;
1012 int maxLevels = 0;
1013 {
reed@android.com149e2f62009-05-22 14:39:03 +00001014 int width = this->width();
1015 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001016 for (;;) {
1017 width >>= 1;
1018 height >>= 1;
1019 if (0 == width || 0 == height) {
1020 break;
1021 }
1022 size += ComputeRowBytes(config, width) * height;
1023 maxLevels += 1;
1024 }
1025 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001026
reed@android.com149e2f62009-05-22 14:39:03 +00001027 // nothing to build
reed@android.com8a1c16f2008-12-17 15:59:43 +00001028 if (0 == maxLevels) {
1029 return;
1030 }
1031
reed@android.com149e2f62009-05-22 14:39:03 +00001032 SkBitmap srcBM(*this);
1033 srcBM.lockPixels();
1034 if (!srcBM.readyToDraw()) {
1035 return;
1036 }
1037
1038 MipMap* mm = MipMap::Alloc(maxLevels, size);
1039 if (NULL == mm) {
1040 return;
1041 }
1042
reed@android.com8a1c16f2008-12-17 15:59:43 +00001043 MipLevel* level = mm->levels();
1044 uint8_t* addr = (uint8_t*)mm->pixels();
reed@android.com149e2f62009-05-22 14:39:03 +00001045 int width = this->width();
1046 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001047 unsigned rowBytes = this->rowBytes();
reed@android.com149e2f62009-05-22 14:39:03 +00001048 SkBitmap dstBM;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001049
1050 for (int i = 0; i < maxLevels; i++) {
1051 width >>= 1;
1052 height >>= 1;
1053 rowBytes = ComputeRowBytes(config, width);
1054
1055 level[i].fPixels = addr;
reed@android.comf459a492009-03-27 12:33:50 +00001056 level[i].fWidth = width;
1057 level[i].fHeight = height;
reed@android.com49f0ff22009-03-19 21:52:42 +00001058 level[i].fRowBytes = rowBytes;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001059
1060 dstBM.setConfig(config, width, height, rowBytes);
1061 dstBM.setPixels(addr);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001062
reed@android.com149e2f62009-05-22 14:39:03 +00001063 for (int y = 0; y < height; y++) {
1064 for (int x = 0; x < width; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001065 proc(&dstBM, x, y, srcBM);
1066 }
1067 }
1068
1069 srcBM = dstBM;
1070 addr += height * rowBytes;
1071 }
1072 SkASSERT(addr == (uint8_t*)mm->pixels() + size);
1073 fMipMap = mm;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001074}
1075
1076bool SkBitmap::hasMipMap() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001077 return fMipMap != NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001078}
1079
1080int SkBitmap::extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy) {
reed@android.com83f7bc32009-07-17 02:42:41 +00001081 if (NULL == fMipMap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001082 return 0;
reed@android.com83f7bc32009-07-17 02:42:41 +00001083 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001084
reed@android.com8a1c16f2008-12-17 15:59:43 +00001085 int level = ComputeMipLevel(sx, sy) >> 16;
1086 SkASSERT(level >= 0);
1087 if (level <= 0) {
1088 return 0;
1089 }
1090
1091 if (level >= fMipMap->fLevelCount) {
1092 level = fMipMap->fLevelCount - 1;
1093 }
1094 if (dst) {
1095 const MipLevel& mip = fMipMap->levels()[level - 1];
1096 dst->setConfig((SkBitmap::Config)this->config(),
1097 mip.fWidth, mip.fHeight, mip.fRowBytes);
1098 dst->setPixels(mip.fPixels);
1099 }
1100 return level;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001101}
1102
1103SkFixed SkBitmap::ComputeMipLevel(SkFixed sx, SkFixed sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001104 sx = SkAbs32(sx);
1105 sy = SkAbs32(sy);
1106 if (sx < sy) {
1107 sx = sy;
1108 }
1109 if (sx < SK_Fixed1) {
1110 return 0;
1111 }
1112 int clz = SkCLZ(sx);
1113 SkASSERT(clz >= 1 && clz <= 15);
1114 return SkIntToFixed(15 - clz) + ((unsigned)(sx << (clz + 1)) >> 16);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001115}
1116
1117///////////////////////////////////////////////////////////////////////////////
1118
reed@android.com1cdcb512009-08-24 19:11:00 +00001119static bool GetBitmapAlpha(const SkBitmap& src, uint8_t SK_RESTRICT alpha[],
reed@android.com8a1c16f2008-12-17 15:59:43 +00001120 int alphaRowBytes) {
1121 SkASSERT(alpha != NULL);
1122 SkASSERT(alphaRowBytes >= src.width());
1123
1124 SkBitmap::Config config = src.getConfig();
1125 int w = src.width();
1126 int h = src.height();
1127 int rb = src.rowBytes();
1128
reed@android.com1cdcb512009-08-24 19:11:00 +00001129 SkAutoLockPixels alp(src);
1130 if (!src.readyToDraw()) {
1131 // zero out the alpha buffer and return
1132 while (--h >= 0) {
1133 memset(alpha, 0, w);
1134 alpha += alphaRowBytes;
1135 }
1136 return false;
1137 }
reed@google.com82065d62011-02-07 15:30:46 +00001138
reed@android.com8a1c16f2008-12-17 15:59:43 +00001139 if (SkBitmap::kA8_Config == config && !src.isOpaque()) {
1140 const uint8_t* s = src.getAddr8(0, 0);
1141 while (--h >= 0) {
1142 memcpy(alpha, s, w);
1143 s += rb;
1144 alpha += alphaRowBytes;
1145 }
1146 } else if (SkBitmap::kARGB_8888_Config == config && !src.isOpaque()) {
1147 const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0);
1148 while (--h >= 0) {
1149 for (int x = 0; x < w; x++) {
1150 alpha[x] = SkGetPackedA32(s[x]);
1151 }
1152 s = (const SkPMColor*)((const char*)s + rb);
1153 alpha += alphaRowBytes;
1154 }
1155 } else if (SkBitmap::kARGB_4444_Config == config && !src.isOpaque()) {
1156 const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0);
1157 while (--h >= 0) {
1158 for (int x = 0; x < w; x++) {
1159 alpha[x] = SkPacked4444ToA32(s[x]);
1160 }
1161 s = (const SkPMColor16*)((const char*)s + rb);
1162 alpha += alphaRowBytes;
1163 }
1164 } else if (SkBitmap::kIndex8_Config == config && !src.isOpaque()) {
1165 SkColorTable* ct = src.getColorTable();
1166 if (ct) {
1167 const SkPMColor* SK_RESTRICT table = ct->lockColors();
1168 const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0);
1169 while (--h >= 0) {
1170 for (int x = 0; x < w; x++) {
1171 alpha[x] = SkGetPackedA32(table[s[x]]);
1172 }
1173 s += rb;
1174 alpha += alphaRowBytes;
1175 }
1176 ct->unlockColors(false);
1177 }
1178 } else { // src is opaque, so just fill alpha[] with 0xFF
1179 memset(alpha, 0xFF, h * alphaRowBytes);
1180 }
reed@android.com1cdcb512009-08-24 19:11:00 +00001181 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001182}
1183
1184#include "SkPaint.h"
1185#include "SkMaskFilter.h"
1186#include "SkMatrix.h"
1187
1188void SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
1189 SkIPoint* offset) const {
1190 SkDEBUGCODE(this->validate();)
1191
1192 SkMatrix identity;
1193 SkMask srcM, dstM;
1194
1195 srcM.fBounds.set(0, 0, this->width(), this->height());
1196 srcM.fRowBytes = SkAlign4(this->width());
1197 srcM.fFormat = SkMask::kA8_Format;
1198
1199 SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL;
1200
1201 // compute our (larger?) dst bounds if we have a filter
1202 if (NULL != filter) {
1203 identity.reset();
1204 srcM.fImage = NULL;
1205 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1206 goto NO_FILTER_CASE;
1207 }
1208 dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
1209 } else {
1210 NO_FILTER_CASE:
1211 dst->setConfig(SkBitmap::kA8_Config, this->width(), this->height(),
1212 srcM.fRowBytes);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001213 dst->allocPixels();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001214 GetBitmapAlpha(*this, dst->getAddr8(0, 0), srcM.fRowBytes);
1215 if (offset) {
1216 offset->set(0, 0);
1217 }
1218 return;
1219 }
1220
1221 SkAutoMaskImage srcCleanup(&srcM, true);
1222
1223 GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
1224 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1225 goto NO_FILTER_CASE;
1226 }
1227
1228 SkAutoMaskImage dstCleanup(&dstM, false);
1229
1230 dst->setConfig(SkBitmap::kA8_Config, dstM.fBounds.width(),
1231 dstM.fBounds.height(), dstM.fRowBytes);
1232 dst->allocPixels();
1233 memcpy(dst->getPixels(), dstM.fImage, dstM.computeImageSize());
1234 if (offset) {
1235 offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
1236 }
1237 SkDEBUGCODE(dst->validate();)
1238}
1239
1240///////////////////////////////////////////////////////////////////////////////
1241
1242enum {
1243 SERIALIZE_PIXELTYPE_NONE,
1244 SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE,
1245 SERIALIZE_PIXELTYPE_RAW_NO_CTABLE,
1246 SERIALIZE_PIXELTYPE_REF_DATA,
1247 SERIALIZE_PIXELTYPE_REF_PTR,
1248};
1249
1250static void writeString(SkFlattenableWriteBuffer& buffer, const char str[]) {
1251 size_t len = strlen(str);
1252 buffer.write32(len);
1253 buffer.writePad(str, len);
1254}
1255
1256static SkPixelRef::Factory deserialize_factory(SkFlattenableReadBuffer& buffer) {
1257 size_t len = buffer.readInt();
1258 SkAutoSMalloc<256> storage(len + 1);
1259 char* str = (char*)storage.get();
1260 buffer.read(str, len);
1261 str[len] = 0;
1262 return SkPixelRef::NameToFactory(str);
1263}
1264
1265/*
1266 It is tricky to know how much to flatten. If we don't have a pixelref (i.e.
1267 we just have pixels, then we can only flatten the pixels, or write out an
1268 empty bitmap.
weita@google.comf9ab99a2009-05-03 18:23:30 +00001269
reed@android.com8a1c16f2008-12-17 15:59:43 +00001270 With a pixelref, we still have the question of recognizing when two sitings
1271 of the same pixelref are the same, and when they are different. Perhaps we
1272 should look at the generationID and keep a record of that in some dictionary
1273 associated with the buffer. SkGLTextureCache does this sort of thing to know
1274 when to create a new texture.
1275*/
1276void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
1277 buffer.write32(fWidth);
1278 buffer.write32(fHeight);
1279 buffer.write32(fRowBytes);
1280 buffer.write8(fConfig);
1281 buffer.writeBool(this->isOpaque());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001282
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283 /* If we are called in this mode, then it is up to the caller to manage
1284 the owner-counts on the pixelref, as we just record the ptr itself.
1285 */
1286 if (!buffer.persistBitmapPixels()) {
1287 if (fPixelRef) {
1288 buffer.write8(SERIALIZE_PIXELTYPE_REF_PTR);
1289 buffer.write32(fPixelRefOffset);
1290 buffer.writeRefCnt(fPixelRef);
1291 return;
1292 } else {
1293 // we ignore the non-persist request, since we don't have a ref
1294 // ... or we could just write an empty bitmap...
1295 // (true) will write an empty bitmap, (false) will flatten the pix
1296 if (true) {
1297 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1298 return;
1299 }
1300 }
1301 }
1302
1303 if (fPixelRef) {
1304 SkPixelRef::Factory fact = fPixelRef->getFactory();
1305 if (fact) {
1306 const char* name = SkPixelRef::FactoryToName(fact);
1307 if (name && *name) {
1308 buffer.write8(SERIALIZE_PIXELTYPE_REF_DATA);
1309 buffer.write32(fPixelRefOffset);
1310 writeString(buffer, name);
1311 fPixelRef->flatten(buffer);
1312 return;
1313 }
1314 }
1315 // if we get here, we can't record the pixels
1316 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1317 } else if (fPixels) {
1318 if (fColorTable) {
1319 buffer.write8(SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE);
1320 fColorTable->flatten(buffer);
1321 } else {
1322 buffer.write8(SERIALIZE_PIXELTYPE_RAW_NO_CTABLE);
1323 }
1324 buffer.writePad(fPixels, this->getSize());
1325 } else {
1326 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1327 }
1328}
1329
1330void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) {
1331 this->reset();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001332
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333 int width = buffer.readInt();
1334 int height = buffer.readInt();
1335 int rowBytes = buffer.readInt();
1336 int config = buffer.readU8();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001337
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338 this->setConfig((Config)config, width, height, rowBytes);
1339 this->setIsOpaque(buffer.readBool());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001340
reed@android.com8a1c16f2008-12-17 15:59:43 +00001341 size_t size = this->getSize();
1342 int reftype = buffer.readU8();
1343 switch (reftype) {
1344 case SERIALIZE_PIXELTYPE_REF_PTR: {
1345 size_t offset = buffer.readU32();
1346 SkPixelRef* pr = (SkPixelRef*)buffer.readRefCnt();
1347 this->setPixelRef(pr, offset);
1348 break;
1349 }
1350 case SERIALIZE_PIXELTYPE_REF_DATA: {
1351 size_t offset = buffer.readU32();
1352 SkPixelRef::Factory fact = deserialize_factory(buffer);
1353 SkPixelRef* pr = fact(buffer);
reed@google.com82065d62011-02-07 15:30:46 +00001354 SkSafeUnref(this->setPixelRef(pr, offset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355 break;
1356 }
1357 case SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE:
1358 case SERIALIZE_PIXELTYPE_RAW_NO_CTABLE: {
1359 SkColorTable* ctable = NULL;
1360 if (SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE == reftype) {
1361 ctable = SkNEW_ARGS(SkColorTable, (buffer));
1362 }
1363 if (this->allocPixels(ctable)) {
1364 this->lockPixels();
wjmaclean@chromium.org0bde1802010-12-22 17:43:54 +00001365 buffer.read(this->getPixels(), this->getSafeSize()); // Just read what we need.
1366 buffer.skip(size - this->getSafeSize()); // Keep aligned for subsequent reads.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001367 this->unlockPixels();
1368 } else {
wjmaclean@chromium.org0bde1802010-12-22 17:43:54 +00001369 buffer.skip(size); // Still skip the full-sized buffer though.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001370 }
reed@android.com149e2f62009-05-22 14:39:03 +00001371 SkSafeUnref(ctable);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001372 break;
1373 }
1374 case SERIALIZE_PIXELTYPE_NONE:
1375 break;
1376 default:
1377 SkASSERT(!"unrecognized pixeltype in serialized data");
1378 sk_throw();
1379 }
1380}
1381
1382///////////////////////////////////////////////////////////////////////////////
1383
1384SkBitmap::RLEPixels::RLEPixels(int width, int height) {
1385 fHeight = height;
1386 fYPtrs = (uint8_t**)sk_malloc_throw(height * sizeof(uint8_t*));
reed@android.com4516f472009-06-29 16:25:36 +00001387 sk_bzero(fYPtrs, height * sizeof(uint8_t*));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001388}
1389
1390SkBitmap::RLEPixels::~RLEPixels() {
1391 sk_free(fYPtrs);
1392}
1393
1394///////////////////////////////////////////////////////////////////////////////
1395
1396#ifdef SK_DEBUG
1397void SkBitmap::validate() const {
1398 SkASSERT(fConfig < kConfigCount);
1399 SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
1400 SkASSERT(fFlags <= kImageIsOpaque_Flag);
1401 SkASSERT(fPixelLockCount >= 0);
1402 SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000);
1403 SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
1404
1405#if 0 // these asserts are not thread-correct, so disable for now
1406 if (fPixelRef) {
1407 if (fPixelLockCount > 0) {
1408 SkASSERT(fPixelRef->getLockCount() > 0);
1409 } else {
1410 SkASSERT(NULL == fPixels);
1411 SkASSERT(NULL == fColorTable);
1412 }
1413 }
1414#endif
1415}
1416#endif
1417