blob: 0a8e9ffddee4e976d791cff0509692b1b960e632 [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 +000033#ifdef SK_SUPPORT_MIPMAP
34struct MipLevel {
35 void* fPixels;
36 uint32_t fRowBytes;
reed@android.comf459a492009-03-27 12:33:50 +000037 uint32_t fWidth, fHeight;
reed@android.com8a1c16f2008-12-17 15:59:43 +000038};
39
40struct SkBitmap::MipMap : SkNoncopyable {
41 int32_t fRefCnt;
42 int fLevelCount;
43// MipLevel fLevel[fLevelCount];
44// Pixels[]
weita@google.comf9ab99a2009-05-03 18:23:30 +000045
reed@android.com8a1c16f2008-12-17 15:59:43 +000046 static MipMap* Alloc(int levelCount, size_t pixelSize) {
reed@android.com149e2f62009-05-22 14:39:03 +000047 if (levelCount < 0) {
48 return NULL;
49 }
50 Sk64 size;
51 size.setMul(levelCount + 1, sizeof(MipLevel));
52 size.add(sizeof(MipMap));
53 size.add(pixelSize);
54 if (!isPos32Bits(size)) {
55 return NULL;
56 }
57 MipMap* mm = (MipMap*)sk_malloc_throw(size.get32());
reed@android.com8a1c16f2008-12-17 15:59:43 +000058 mm->fRefCnt = 1;
59 mm->fLevelCount = levelCount;
60 return mm;
61 }
62
63 const MipLevel* levels() const { return (const MipLevel*)(this + 1); }
64 MipLevel* levels() { return (MipLevel*)(this + 1); }
65
66 const void* pixels() const { return levels() + fLevelCount; }
67 void* pixels() { return levels() + fLevelCount; }
weita@google.comf9ab99a2009-05-03 18:23:30 +000068
reed@android.com149e2f62009-05-22 14:39:03 +000069 void ref() {
70 if (SK_MaxS32 == sk_atomic_inc(&fRefCnt)) {
71 sk_throw();
reed@android.com8a1c16f2008-12-17 15:59:43 +000072 }
73 }
reed@android.com149e2f62009-05-22 14:39:03 +000074 void unref() {
75 SkASSERT(fRefCnt > 0);
76 if (sk_atomic_dec(&fRefCnt) == 1) {
77 sk_free(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +000078 }
79 }
80};
81#endif
82
83///////////////////////////////////////////////////////////////////////////////
84///////////////////////////////////////////////////////////////////////////////
85
86SkBitmap::SkBitmap() {
87 bzero(this, sizeof(*this));
88}
89
90SkBitmap::SkBitmap(const SkBitmap& src) {
91 SkDEBUGCODE(src.validate();)
92 bzero(this, sizeof(*this));
93 *this = src;
94 SkDEBUGCODE(this->validate();)
95}
96
97SkBitmap::~SkBitmap() {
98 SkDEBUGCODE(this->validate();)
99 this->freePixels();
100}
101
102SkBitmap& SkBitmap::operator=(const SkBitmap& src) {
103 if (this != &src) {
104 this->freePixels();
105 memcpy(this, &src, sizeof(src));
106
107 // inc src reference counts
108 src.fPixelRef->safeRef();
109#ifdef SK_SUPPORT_MIPMAP
reed@android.com149e2f62009-05-22 14:39:03 +0000110 SkSafeRef(src.fMipMap);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000111#endif
112
113 // we reset our locks if we get blown away
114 fPixelLockCount = 0;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000115
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116 /* The src could be in 3 states
117 1. no pixelref, in which case we just copy/ref the pixels/ctable
118 2. unlocked pixelref, pixels/ctable should be null
119 3. locked pixelref, we should lock the ref again ourselves
120 */
121 if (NULL == fPixelRef) {
122 // leave fPixels as it is
123 fColorTable->safeRef(); // ref the user's ctable if present
124 } else { // we have a pixelref, so pixels/ctable reflect it
125 // ignore the values from the memcpy
126 fPixels = NULL;
127 fColorTable = NULL;
128 }
129 }
130
131 SkDEBUGCODE(this->validate();)
132 return *this;
133}
134
135void SkBitmap::swap(SkBitmap& other) {
136 SkTSwap<SkColorTable*>(fColorTable, other.fColorTable);
137 SkTSwap<SkPixelRef*>(fPixelRef, other.fPixelRef);
138 SkTSwap<size_t>(fPixelRefOffset, other.fPixelRefOffset);
139 SkTSwap<int>(fPixelLockCount, other.fPixelLockCount);
140#ifdef SK_SUPPORT_MIPMAP
141 SkTSwap<MipMap*>(fMipMap, other.fMipMap);
142#endif
143 SkTSwap<void*>(fPixels, other.fPixels);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000144 SkTSwap<uint32_t>(fRowBytes, other.fRowBytes);
reed@android.comf459a492009-03-27 12:33:50 +0000145 SkTSwap<uint32_t>(fWidth, other.fWidth);
146 SkTSwap<uint32_t>(fHeight, other.fHeight);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147 SkTSwap<uint8_t>(fConfig, other.fConfig);
148 SkTSwap<uint8_t>(fFlags, other.fFlags);
149 SkTSwap<uint8_t>(fBytesPerPixel, other.fBytesPerPixel);
150
151 SkDEBUGCODE(this->validate();)
152}
153
154void SkBitmap::reset() {
155 this->freePixels();
156 bzero(this, sizeof(*this));
157}
158
159int SkBitmap::ComputeBytesPerPixel(SkBitmap::Config config) {
160 int bpp;
161 switch (config) {
162 case kNo_Config:
163 case kA1_Config:
164 bpp = 0; // not applicable
165 break;
166 case kRLE_Index8_Config:
167 case kA8_Config:
168 case kIndex8_Config:
169 bpp = 1;
170 break;
171 case kRGB_565_Config:
172 case kARGB_4444_Config:
173 bpp = 2;
174 break;
175 case kARGB_8888_Config:
176 bpp = 4;
177 break;
178 default:
179 SkASSERT(!"unknown config");
180 bpp = 0; // error
181 break;
182 }
183 return bpp;
184}
185
186int SkBitmap::ComputeRowBytes(Config c, int width) {
reed@android.com149e2f62009-05-22 14:39:03 +0000187 if (width < 0) {
188 return 0;
189 }
190
191 Sk64 rowBytes;
192 rowBytes.setZero();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193
194 switch (c) {
195 case kNo_Config:
196 case kRLE_Index8_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000197 break;
198 case kA1_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000199 rowBytes.set(width);
200 rowBytes.add(7);
201 rowBytes.shiftRight(3);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202 break;
203 case kA8_Config:
204 case kIndex8_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000205 rowBytes.set(width);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206 break;
207 case kRGB_565_Config:
208 case kARGB_4444_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000209 rowBytes.set(width);
210 rowBytes.shiftLeft(1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 break;
212 case kARGB_8888_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000213 rowBytes.set(width);
214 rowBytes.shiftLeft(2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215 break;
216 default:
217 SkASSERT(!"unknown config");
218 break;
219 }
reed@android.com149e2f62009-05-22 14:39:03 +0000220 return isPos32Bits(rowBytes) ? rowBytes.get32() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221}
222
223Sk64 SkBitmap::ComputeSize64(Config c, int width, int height) {
224 Sk64 size;
225 size.setMul(SkBitmap::ComputeRowBytes(c, width), height);
226 return size;
227}
228
229size_t SkBitmap::ComputeSize(Config c, int width, int height) {
230 Sk64 size = SkBitmap::ComputeSize64(c, width, height);
reed@android.com149e2f62009-05-22 14:39:03 +0000231 return isPos32Bits(size) ? size.get32() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232}
233
234void SkBitmap::setConfig(Config c, int width, int height, int rowBytes) {
235 this->freePixels();
236
reed@android.com149e2f62009-05-22 14:39:03 +0000237 if ((width | height | rowBytes) < 0) {
238 ERROR:
239 this->reset();
240 return;
241 }
242
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 if (rowBytes == 0) {
244 rowBytes = SkBitmap::ComputeRowBytes(c, width);
reed@android.com149e2f62009-05-22 14:39:03 +0000245 if (0 == rowBytes && kNo_Config != c) {
246 goto ERROR;
247 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248 }
reed@android.com89bb83a2009-05-29 21:30:42 +0000249
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250 fConfig = SkToU8(c);
reed@android.comf459a492009-03-27 12:33:50 +0000251 fWidth = width;
252 fHeight = height;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 fRowBytes = rowBytes;
254
255 fBytesPerPixel = (uint8_t)ComputeBytesPerPixel(c);
256
257 SkDEBUGCODE(this->validate();)
258}
259
260void SkBitmap::updatePixelsFromRef() const {
261 if (NULL != fPixelRef) {
262 if (fPixelLockCount > 0) {
263 SkASSERT(fPixelRef->getLockCount() > 0);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000264
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265 void* p = fPixelRef->pixels();
266 if (NULL != p) {
267 p = (char*)p + fPixelRefOffset;
268 }
269 fPixels = p;
270 SkRefCnt_SafeAssign(fColorTable, fPixelRef->colorTable());
271 } else {
272 SkASSERT(0 == fPixelLockCount);
273 fPixels = NULL;
reed@android.com149e2f62009-05-22 14:39:03 +0000274 if (fColorTable) {
275 fColorTable->unref();
276 fColorTable = NULL;
277 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278 }
279 }
280}
281
282SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, size_t offset) {
283 // do this first, we that we never have a non-zero offset with a null ref
284 if (NULL == pr) {
285 offset = 0;
286 }
287
288 if (fPixelRef != pr || fPixelRefOffset != offset) {
289 if (fPixelRef != pr) {
290 this->freePixels();
291 SkASSERT(NULL == fPixelRef);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000292
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 pr->safeRef();
294 fPixelRef = pr;
295 }
296 fPixelRefOffset = offset;
297 this->updatePixelsFromRef();
298 }
299
300 SkDEBUGCODE(this->validate();)
301 return pr;
302}
303
304void SkBitmap::lockPixels() const {
305 if (NULL != fPixelRef && 1 == ++fPixelLockCount) {
306 fPixelRef->lockPixels();
307 this->updatePixelsFromRef();
308 }
309 SkDEBUGCODE(this->validate();)
310}
311
312void SkBitmap::unlockPixels() const {
313 SkASSERT(NULL == fPixelRef || fPixelLockCount > 0);
314
315 if (NULL != fPixelRef && 0 == --fPixelLockCount) {
316 fPixelRef->unlockPixels();
317 this->updatePixelsFromRef();
318 }
319 SkDEBUGCODE(this->validate();)
320}
321
322void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
323 this->freePixels();
324 fPixels = p;
325 SkRefCnt_SafeAssign(fColorTable, ctable);
326
327 SkDEBUGCODE(this->validate();)
328}
329
330bool SkBitmap::allocPixels(Allocator* allocator, SkColorTable* ctable) {
331 HeapAllocator stdalloc;
332
333 if (NULL == allocator) {
334 allocator = &stdalloc;
335 }
336 return allocator->allocPixelRef(this, ctable);
337}
338
339void SkBitmap::freePixels() {
340 // if we're gonna free the pixels, we certainly need to free the mipmap
341 this->freeMipMap();
342
reed@android.com149e2f62009-05-22 14:39:03 +0000343 if (fColorTable) {
344 fColorTable->unref();
345 fColorTable = NULL;
346 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347
348 if (NULL != fPixelRef) {
349 if (fPixelLockCount > 0) {
350 fPixelRef->unlockPixels();
351 }
352 fPixelRef->unref();
353 fPixelRef = NULL;
354 fPixelRefOffset = 0;
355 }
356 fPixelLockCount = 0;
357 fPixels = NULL;
358}
359
360void SkBitmap::freeMipMap() {
361#ifdef SK_SUPPORT_MIPMAP
reed@android.com149e2f62009-05-22 14:39:03 +0000362 if (fMipMap) {
363 fMipMap->unref();
364 fMipMap = NULL;
365 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366#endif
367}
368
369uint32_t SkBitmap::getGenerationID() const {
370 return fPixelRef ? fPixelRef->getGenerationID() : 0;
371}
372
373void SkBitmap::notifyPixelsChanged() const {
374 if (fPixelRef) {
375 fPixelRef->notifyPixelsChanged();
376 }
377}
378
379///////////////////////////////////////////////////////////////////////////////
380
381SkMallocPixelRef::SkMallocPixelRef(void* storage, size_t size,
382 SkColorTable* ctable) {
383 SkASSERT(storage);
384 fStorage = storage;
385 fSize = size;
386 fCTable = ctable;
387 ctable->safeRef();
388}
389
390SkMallocPixelRef::~SkMallocPixelRef() {
reed@android.com149e2f62009-05-22 14:39:03 +0000391 SkSafeUnref(fCTable);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000392 sk_free(fStorage);
393}
394
395void* SkMallocPixelRef::onLockPixels(SkColorTable** ct) {
396 *ct = fCTable;
397 return fStorage;
398}
399
400void SkMallocPixelRef::onUnlockPixels() {
401 // nothing to do
402}
403
404void SkMallocPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const {
405 this->INHERITED::flatten(buffer);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000406
reed@android.com8a1c16f2008-12-17 15:59:43 +0000407 buffer.write32(fSize);
408 buffer.writePad(fStorage, fSize);
409 if (fCTable) {
410 buffer.writeBool(true);
411 fCTable->flatten(buffer);
412 } else {
413 buffer.writeBool(false);
414 }
415}
416
417SkMallocPixelRef::SkMallocPixelRef(SkFlattenableReadBuffer& buffer) : INHERITED(buffer, NULL) {
418 fSize = buffer.readU32();
419 fStorage = sk_malloc_throw(fSize);
420 buffer.read(fStorage, fSize);
421 if (buffer.readBool()) {
422 fCTable = SkNEW_ARGS(SkColorTable, (buffer));
423 } else {
424 fCTable = NULL;
425 }
426}
427
428static SkPixelRef::Registrar reg("SkMallocPixelRef",
429 SkMallocPixelRef::Create);
430
431/** We explicitly use the same allocator for our pixels that SkMask does,
432 so that we can freely assign memory allocated by one class to the other.
433 */
434bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
435 SkColorTable* ctable) {
436 Sk64 size = dst->getSize64();
437 if (size.isNeg() || !size.is32()) {
438 return false;
439 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000440
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441 void* addr = sk_malloc_flags(size.get32(), 0); // returns NULL on failure
442 if (NULL == addr) {
443 return false;
444 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000445
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446 dst->setPixelRef(new SkMallocPixelRef(addr, size.get32(), ctable))->unref();
447 // since we're already allocated, we lockPixels right away
448 dst->lockPixels();
449 return true;
450}
451
452///////////////////////////////////////////////////////////////////////////////
453
454bool SkBitmap::isOpaque() const {
455 switch (fConfig) {
456 case kNo_Config:
457 return true;
458
459 case kA1_Config:
460 case kA8_Config:
461 case kARGB_4444_Config:
462 case kARGB_8888_Config:
463 return (fFlags & kImageIsOpaque_Flag) != 0;
464
465 case kIndex8_Config:
466 case kRLE_Index8_Config: {
467 uint32_t flags = 0;
468
469 this->lockPixels();
470 // if lockPixels failed, we may not have a ctable ptr
471 if (fColorTable) {
472 flags = fColorTable->getFlags();
473 }
474 this->unlockPixels();
475
476 return (flags & SkColorTable::kColorsAreOpaque_Flag) != 0;
477 }
478
479 case kRGB_565_Config:
480 return true;
481
482 default:
483 SkASSERT(!"unknown bitmap config pased to isOpaque");
484 return false;
485 }
486}
487
488void SkBitmap::setIsOpaque(bool isOpaque) {
489 /* we record this regardless of fConfig, though it is ignored in
490 isOpaque() for configs that can't support per-pixel alpha.
491 */
492 if (isOpaque) {
493 fFlags |= kImageIsOpaque_Flag;
494 } else {
495 fFlags &= ~kImageIsOpaque_Flag;
496 }
497}
498
499void* SkBitmap::getAddr(int x, int y) const {
500 SkASSERT((unsigned)x < (unsigned)this->width());
501 SkASSERT((unsigned)y < (unsigned)this->height());
502
503 char* base = (char*)this->getPixels();
504 if (base) {
505 base += y * this->rowBytes();
506 switch (this->config()) {
507 case SkBitmap::kARGB_8888_Config:
508 base += x << 2;
509 break;
510 case SkBitmap::kARGB_4444_Config:
511 case SkBitmap::kRGB_565_Config:
512 base += x << 1;
513 break;
514 case SkBitmap::kA8_Config:
515 case SkBitmap::kIndex8_Config:
516 base += x;
517 break;
518 case SkBitmap::kA1_Config:
519 base += x >> 3;
520 break;
521 case kRLE_Index8_Config:
522 SkASSERT(!"Can't return addr for kRLE_Index8_Config");
523 base = NULL;
524 break;
525 default:
526 SkASSERT(!"Can't return addr for config");
527 base = NULL;
528 break;
529 }
530 }
531 return base;
532}
533
534///////////////////////////////////////////////////////////////////////////////
535///////////////////////////////////////////////////////////////////////////////
536
537void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
538 SkDEBUGCODE(this->validate();)
539
540 if (0 == fWidth || 0 == fHeight ||
541 kNo_Config == fConfig || kIndex8_Config == fConfig) {
542 return;
543 }
544
545 SkAutoLockPixels alp(*this);
546 // perform this check after the lock call
547 if (!this->readyToDraw()) {
548 return;
549 }
550
551 int height = fHeight;
552 const int width = fWidth;
553 const int rowBytes = fRowBytes;
554
555 // make rgb premultiplied
556 if (255 != a) {
557 r = SkAlphaMul(r, a);
558 g = SkAlphaMul(g, a);
559 b = SkAlphaMul(b, a);
560 }
561
562 switch (fConfig) {
563 case kA1_Config: {
564 uint8_t* p = (uint8_t*)fPixels;
565 const int count = (width + 7) >> 3;
566 a = (a >> 7) ? 0xFF : 0;
567 SkASSERT(count <= rowBytes);
568 while (--height >= 0) {
569 memset(p, a, count);
570 p += rowBytes;
571 }
572 break;
573 }
574 case kA8_Config: {
575 uint8_t* p = (uint8_t*)fPixels;
576 while (--height >= 0) {
577 memset(p, a, width);
578 p += rowBytes;
579 }
580 break;
581 }
582 case kARGB_4444_Config:
583 case kRGB_565_Config: {
584 uint16_t* p = (uint16_t*)fPixels;
585 uint16_t v;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000586
reed@android.com8a1c16f2008-12-17 15:59:43 +0000587 if (kARGB_4444_Config == fConfig) {
588 v = SkPackARGB4444(a >> 4, r >> 4, g >> 4, b >> 4);
589 } else { // kRGB_565_Config
590 v = SkPackRGB16(r >> (8 - SK_R16_BITS), g >> (8 - SK_G16_BITS),
591 b >> (8 - SK_B16_BITS));
592 }
593 while (--height >= 0) {
594 sk_memset16(p, v, width);
595 p = (uint16_t*)((char*)p + rowBytes);
596 }
597 break;
598 }
599 case kARGB_8888_Config: {
600 uint32_t* p = (uint32_t*)fPixels;
601 uint32_t v = SkPackARGB32(a, r, g, b);
602
603 while (--height >= 0) {
604 sk_memset32(p, v, width);
605 p = (uint32_t*)((char*)p + rowBytes);
606 }
607 break;
608 }
609 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000610
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611 this->notifyPixelsChanged();
612}
613
614//////////////////////////////////////////////////////////////////////////////////////
615//////////////////////////////////////////////////////////////////////////////////////
616
617#define SUB_OFFSET_FAILURE ((size_t)-1)
618
619static size_t getSubOffset(const SkBitmap& bm, int x, int y) {
620 SkASSERT((unsigned)x < (unsigned)bm.width());
621 SkASSERT((unsigned)y < (unsigned)bm.height());
weita@google.comf9ab99a2009-05-03 18:23:30 +0000622
reed@android.com8a1c16f2008-12-17 15:59:43 +0000623 switch (bm.getConfig()) {
624 case SkBitmap::kA8_Config:
625 case SkBitmap:: kIndex8_Config:
626 // x is fine as is for the calculation
627 break;
628
629 case SkBitmap::kRGB_565_Config:
630 case SkBitmap::kARGB_4444_Config:
631 x <<= 1;
632 break;
633
634 case SkBitmap::kARGB_8888_Config:
635 x <<= 2;
636 break;
637
638 case SkBitmap::kNo_Config:
639 case SkBitmap::kA1_Config:
640 default:
641 return SUB_OFFSET_FAILURE;
642 }
643 return y * bm.rowBytes() + x;
644}
645
646bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
647 SkDEBUGCODE(this->validate();)
648
649 if (NULL == result || (NULL == fPixelRef && NULL == fPixels)) {
650 return false; // no src pixels
651 }
652
653 SkIRect srcRect, r;
654 srcRect.set(0, 0, this->width(), this->height());
655 if (!r.intersect(srcRect, subset)) {
656 return false; // r is empty (i.e. no intersection)
657 }
658
659 if (kRLE_Index8_Config == fConfig) {
660 SkAutoLockPixels alp(*this);
661 // don't call readyToDraw(), since we can operate w/o a colortable
662 // at this stage
663 if (this->getPixels() == NULL) {
664 return false;
665 }
666 SkBitmap bm;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000667
reed@android.com8a1c16f2008-12-17 15:59:43 +0000668 bm.setConfig(kIndex8_Config, r.width(), r.height());
669 bm.allocPixels(this->getColorTable());
670 if (NULL == bm.getPixels()) {
671 return false;
672 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000673
reed@android.com8a1c16f2008-12-17 15:59:43 +0000674 const RLEPixels* rle = (const RLEPixels*)this->getPixels();
675 uint8_t* dst = bm.getAddr8(0, 0);
676 const int width = bm.width();
677 const int rowBytes = bm.rowBytes();
weita@google.comf9ab99a2009-05-03 18:23:30 +0000678
reed@android.com8a1c16f2008-12-17 15:59:43 +0000679 for (int y = r.fTop; y < r.fBottom; y++) {
680 SkPackBits::Unpack8(dst, r.fLeft, width, rle->packedAtY(y));
681 dst += rowBytes;
682 }
683 result->swap(bm);
684 return true;
685 }
686
687 size_t offset = getSubOffset(*this, r.fLeft, r.fTop);
688 if (SUB_OFFSET_FAILURE == offset) {
689 return false; // config not supported
690 }
691
692 SkBitmap dst;
693 dst.setConfig(this->config(), r.width(), r.height(), this->rowBytes());
694
695 if (fPixelRef) {
696 // share the pixelref with a custom offset
697 dst.setPixelRef(fPixelRef, fPixelRefOffset + offset);
698 } else {
699 // share the pixels (owned by the caller)
700 dst.setPixels((char*)fPixels + offset, this->getColorTable());
701 }
702 SkDEBUGCODE(dst.validate();)
703
704 // we know we're good, so commit to result
705 result->swap(dst);
706 return true;
707}
708
709///////////////////////////////////////////////////////////////////////////////
710
711#include "SkCanvas.h"
712#include "SkPaint.h"
713
reed@android.comfbaa88d2009-05-06 17:44:34 +0000714bool SkBitmap::canCopyTo(Config dstConfig) const {
715 if (this->getConfig() == kNo_Config) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716 return false;
717 }
718
reed@android.comfbaa88d2009-05-06 17:44:34 +0000719 bool sameConfigs = (this->config() == dstConfig);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720 switch (dstConfig) {
721 case kA8_Config:
722 case kARGB_4444_Config:
723 case kRGB_565_Config:
724 case kARGB_8888_Config:
725 break;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000726 case kA1_Config:
727 case kIndex8_Config:
728 if (!sameConfigs) {
729 return false;
730 }
731 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000732 default:
733 return false;
734 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000735
736 // do not copy src if srcConfig == kA1_Config while dstConfig != kA1_Config
737 if (this->getConfig() == kA1_Config && !sameConfigs) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000738 return false;
739 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000740
reed@android.comfbaa88d2009-05-06 17:44:34 +0000741 return true;
742}
743
744bool SkBitmap::copyTo(SkBitmap* dst, Config dstConfig, Allocator* alloc) const {
745 if (!this->canCopyTo(dstConfig)) {
746 return false;
747 }
748
reed@android.com311c82d2009-05-05 23:13:23 +0000749 // we lock this now, since we may need its colortable
750 SkAutoLockPixels srclock(*this);
reed@android.comfbaa88d2009-05-06 17:44:34 +0000751 if (!this->readyToDraw()) {
752 return false;
753 }
reed@android.com311c82d2009-05-05 23:13:23 +0000754
weita@google.comf9ab99a2009-05-03 18:23:30 +0000755 SkBitmap tmp;
756 tmp.setConfig(dstConfig, this->width(), this->height());
757
758 // allocate colortable if srcConfig == kIndex8_Config
759 SkColorTable* ctable = (dstConfig == kIndex8_Config) ?
760 new SkColorTable(*this->getColorTable()) : NULL;
761 SkAutoUnref au(ctable);
762 if (!tmp.allocPixels(alloc, ctable)) {
763 return false;
764 }
765
reed@android.com8a1c16f2008-12-17 15:59:43 +0000766 SkAutoLockPixels dstlock(tmp);
reed@android.comfbaa88d2009-05-06 17:44:34 +0000767 if (!tmp.readyToDraw()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000768 // allocator/lock failed
769 return false;
770 }
771
reed@android.comfbaa88d2009-05-06 17:44:34 +0000772 /* do memcpy for the same configs cases, else use drawing
weita@google.comf9ab99a2009-05-03 18:23:30 +0000773 */
reed@android.comfbaa88d2009-05-06 17:44:34 +0000774 if (this->config() == dstConfig) {
reed@android.com311c82d2009-05-05 23:13:23 +0000775 if (tmp.getSize() == this->getSize()) {
776 memcpy(tmp.getPixels(), this->getPixels(), this->getSize());
777 } else {
778 const char* srcP = reinterpret_cast<const char*>(this->getPixels());
779 char* dstP = reinterpret_cast<char*>(tmp.getPixels());
780 // to be sure we don't read too much, only copy our logical pixels
781 size_t bytesToCopy = tmp.width() * tmp.bytesPerPixel();
782 for (int y = 0; y < tmp.height(); y++) {
783 memcpy(dstP, srcP, bytesToCopy);
784 srcP += this->rowBytes();
785 dstP += tmp.rowBytes();
786 }
787 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000788 } else {
789 // if the src has alpha, we have to clear the dst first
790 if (!this->isOpaque()) {
791 tmp.eraseColor(0);
792 }
793
794 SkCanvas canvas(tmp);
795 SkPaint paint;
796
797 paint.setDither(true);
798 canvas.drawBitmap(*this, 0, 0, &paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799 }
800
reed@android.com8a1c16f2008-12-17 15:59:43 +0000801 dst->swap(tmp);
802 return true;
803}
804
805///////////////////////////////////////////////////////////////////////////////
806///////////////////////////////////////////////////////////////////////////////
807
808static void downsampleby2_proc32(SkBitmap* dst, int x, int y,
809 const SkBitmap& src) {
810 x <<= 1;
811 y <<= 1;
812 const SkPMColor* p = src.getAddr32(x, y);
813 SkPMColor c, ag, rb;
814
815 c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF;
816 if (x < src.width() - 1) {
817 p += 1;
818 }
819 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
820
821 if (y < src.height() - 1) {
822 p = src.getAddr32(x, y + 1);
823 }
824 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
825 if (x < src.width() - 1) {
826 p += 1;
827 }
828 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
829
830 *dst->getAddr32(x >> 1, y >> 1) =
831 ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00);
832}
833
834static inline uint32_t expand16(U16CPU c) {
835 return (c & ~SK_G16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16);
836}
837
838// returns dirt in the top 16bits, but we don't care, since we only
839// store the low 16bits.
840static inline U16CPU pack16(uint32_t c) {
841 return (c & ~SK_G16_MASK_IN_PLACE) | ((c >> 16) & SK_G16_MASK_IN_PLACE);
842}
843
844static void downsampleby2_proc16(SkBitmap* dst, int x, int y,
845 const SkBitmap& src) {
846 x <<= 1;
847 y <<= 1;
848 const uint16_t* p = src.getAddr16(x, y);
849 SkPMColor c;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000850
reed@android.com8a1c16f2008-12-17 15:59:43 +0000851 c = expand16(*p);
852 if (x < (int)src.width() - 1) {
853 p += 1;
854 }
855 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000856
reed@android.com8a1c16f2008-12-17 15:59:43 +0000857 if (y < (int)src.height() - 1) {
858 p = src.getAddr16(x, y + 1);
859 }
860 c += expand16(*p);
861 if (x < (int)src.width() - 1) {
862 p += 1;
863 }
864 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000865
reed@android.com8a1c16f2008-12-17 15:59:43 +0000866 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)pack16(c >> 2);
867}
868
869static uint32_t expand4444(U16CPU c) {
870 return (c & 0xF0F) | ((c & ~0xF0F) << 12);
871}
872
873static U16CPU collaps4444(uint32_t c) {
874 return (c & 0xF0F) | ((c >> 12) & ~0xF0F);
875}
876
877static void downsampleby2_proc4444(SkBitmap* dst, int x, int y,
878 const SkBitmap& src) {
879 x <<= 1;
880 y <<= 1;
881 const uint16_t* p = src.getAddr16(x, y);
882 uint32_t c;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000883
reed@android.com8a1c16f2008-12-17 15:59:43 +0000884 c = expand4444(*p);
885 if (x < src.width() - 1) {
886 p += 1;
887 }
888 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000889
reed@android.com8a1c16f2008-12-17 15:59:43 +0000890 if (y < src.height() - 1) {
891 p = src.getAddr16(x, y + 1);
892 }
893 c += expand4444(*p);
894 if (x < src.width() - 1) {
895 p += 1;
896 }
897 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000898
reed@android.com8a1c16f2008-12-17 15:59:43 +0000899 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)collaps4444(c >> 2);
900}
901
902void SkBitmap::buildMipMap(bool forceRebuild) {
903#ifdef SK_SUPPORT_MIPMAP
904 if (forceRebuild)
905 this->freeMipMap();
906 else if (fMipMap)
907 return; // we're already built
908
909 SkASSERT(NULL == fMipMap);
910
911 void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src);
912
913 const SkBitmap::Config config = this->getConfig();
914
915 switch (config) {
916 case kARGB_8888_Config:
917 proc = downsampleby2_proc32;
918 break;
919 case kRGB_565_Config:
920 proc = downsampleby2_proc16;
921 break;
922 case kARGB_4444_Config:
923 proc = downsampleby2_proc4444;
924 break;
925 case kIndex8_Config:
926 case kA8_Config:
927 default:
928 return; // don't build mipmaps for these configs
929 }
reed@android.com89bb83a2009-05-29 21:30:42 +0000930
reed@android.com149e2f62009-05-22 14:39:03 +0000931 SkAutoLockPixels alp(*this);
932 if (!this->readyToDraw()) {
933 return;
934 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000935
936 // whip through our loop to compute the exact size needed
937 size_t size = 0;
938 int maxLevels = 0;
939 {
reed@android.com149e2f62009-05-22 14:39:03 +0000940 int width = this->width();
941 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000942 for (;;) {
943 width >>= 1;
944 height >>= 1;
945 if (0 == width || 0 == height) {
946 break;
947 }
948 size += ComputeRowBytes(config, width) * height;
949 maxLevels += 1;
950 }
951 }
reed@android.com89bb83a2009-05-29 21:30:42 +0000952
reed@android.com149e2f62009-05-22 14:39:03 +0000953 // nothing to build
reed@android.com8a1c16f2008-12-17 15:59:43 +0000954 if (0 == maxLevels) {
955 return;
956 }
957
reed@android.com149e2f62009-05-22 14:39:03 +0000958 SkBitmap srcBM(*this);
959 srcBM.lockPixels();
960 if (!srcBM.readyToDraw()) {
961 return;
962 }
963
964 MipMap* mm = MipMap::Alloc(maxLevels, size);
965 if (NULL == mm) {
966 return;
967 }
968
reed@android.com8a1c16f2008-12-17 15:59:43 +0000969 MipLevel* level = mm->levels();
970 uint8_t* addr = (uint8_t*)mm->pixels();
reed@android.com149e2f62009-05-22 14:39:03 +0000971 int width = this->width();
972 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973 unsigned rowBytes = this->rowBytes();
reed@android.com149e2f62009-05-22 14:39:03 +0000974 SkBitmap dstBM;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975
976 for (int i = 0; i < maxLevels; i++) {
977 width >>= 1;
978 height >>= 1;
979 rowBytes = ComputeRowBytes(config, width);
980
981 level[i].fPixels = addr;
reed@android.comf459a492009-03-27 12:33:50 +0000982 level[i].fWidth = width;
983 level[i].fHeight = height;
reed@android.com49f0ff22009-03-19 21:52:42 +0000984 level[i].fRowBytes = rowBytes;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000985
986 dstBM.setConfig(config, width, height, rowBytes);
987 dstBM.setPixels(addr);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000988
reed@android.com149e2f62009-05-22 14:39:03 +0000989 for (int y = 0; y < height; y++) {
990 for (int x = 0; x < width; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991 proc(&dstBM, x, y, srcBM);
992 }
993 }
994
995 srcBM = dstBM;
996 addr += height * rowBytes;
997 }
998 SkASSERT(addr == (uint8_t*)mm->pixels() + size);
999 fMipMap = mm;
1000#endif
1001}
1002
1003bool SkBitmap::hasMipMap() const {
1004#ifdef SK_SUPPORT_MIPMAP
1005 return fMipMap != NULL;
1006#else
1007 return false;
1008#endif
1009}
1010
1011int SkBitmap::extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy) {
1012#ifdef SK_SUPPORT_MIPMAP
1013 if (NULL == fMipMap)
1014 return 0;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001015
reed@android.com8a1c16f2008-12-17 15:59:43 +00001016 int level = ComputeMipLevel(sx, sy) >> 16;
1017 SkASSERT(level >= 0);
1018 if (level <= 0) {
1019 return 0;
1020 }
1021
1022 if (level >= fMipMap->fLevelCount) {
1023 level = fMipMap->fLevelCount - 1;
1024 }
1025 if (dst) {
1026 const MipLevel& mip = fMipMap->levels()[level - 1];
1027 dst->setConfig((SkBitmap::Config)this->config(),
1028 mip.fWidth, mip.fHeight, mip.fRowBytes);
1029 dst->setPixels(mip.fPixels);
1030 }
1031 return level;
1032#else
1033 return 0;
1034#endif
1035}
1036
1037SkFixed SkBitmap::ComputeMipLevel(SkFixed sx, SkFixed sy) {
1038#ifdef SK_SUPPORT_MIPMAP
1039 sx = SkAbs32(sx);
1040 sy = SkAbs32(sy);
1041 if (sx < sy) {
1042 sx = sy;
1043 }
1044 if (sx < SK_Fixed1) {
1045 return 0;
1046 }
1047 int clz = SkCLZ(sx);
1048 SkASSERT(clz >= 1 && clz <= 15);
1049 return SkIntToFixed(15 - clz) + ((unsigned)(sx << (clz + 1)) >> 16);
1050#else
1051 return 0;
1052#endif
1053}
1054
1055///////////////////////////////////////////////////////////////////////////////
1056
1057static void GetBitmapAlpha(const SkBitmap& src, uint8_t SK_RESTRICT alpha[],
1058 int alphaRowBytes) {
1059 SkASSERT(alpha != NULL);
1060 SkASSERT(alphaRowBytes >= src.width());
1061
1062 SkBitmap::Config config = src.getConfig();
1063 int w = src.width();
1064 int h = src.height();
1065 int rb = src.rowBytes();
1066
1067 if (SkBitmap::kA8_Config == config && !src.isOpaque()) {
1068 const uint8_t* s = src.getAddr8(0, 0);
1069 while (--h >= 0) {
1070 memcpy(alpha, s, w);
1071 s += rb;
1072 alpha += alphaRowBytes;
1073 }
1074 } else if (SkBitmap::kARGB_8888_Config == config && !src.isOpaque()) {
1075 const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0);
1076 while (--h >= 0) {
1077 for (int x = 0; x < w; x++) {
1078 alpha[x] = SkGetPackedA32(s[x]);
1079 }
1080 s = (const SkPMColor*)((const char*)s + rb);
1081 alpha += alphaRowBytes;
1082 }
1083 } else if (SkBitmap::kARGB_4444_Config == config && !src.isOpaque()) {
1084 const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0);
1085 while (--h >= 0) {
1086 for (int x = 0; x < w; x++) {
1087 alpha[x] = SkPacked4444ToA32(s[x]);
1088 }
1089 s = (const SkPMColor16*)((const char*)s + rb);
1090 alpha += alphaRowBytes;
1091 }
1092 } else if (SkBitmap::kIndex8_Config == config && !src.isOpaque()) {
1093 SkColorTable* ct = src.getColorTable();
1094 if (ct) {
1095 const SkPMColor* SK_RESTRICT table = ct->lockColors();
1096 const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0);
1097 while (--h >= 0) {
1098 for (int x = 0; x < w; x++) {
1099 alpha[x] = SkGetPackedA32(table[s[x]]);
1100 }
1101 s += rb;
1102 alpha += alphaRowBytes;
1103 }
1104 ct->unlockColors(false);
1105 }
1106 } else { // src is opaque, so just fill alpha[] with 0xFF
1107 memset(alpha, 0xFF, h * alphaRowBytes);
1108 }
1109}
1110
1111#include "SkPaint.h"
1112#include "SkMaskFilter.h"
1113#include "SkMatrix.h"
1114
1115void SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
1116 SkIPoint* offset) const {
1117 SkDEBUGCODE(this->validate();)
1118
1119 SkMatrix identity;
1120 SkMask srcM, dstM;
1121
1122 srcM.fBounds.set(0, 0, this->width(), this->height());
1123 srcM.fRowBytes = SkAlign4(this->width());
1124 srcM.fFormat = SkMask::kA8_Format;
1125
1126 SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL;
1127
1128 // compute our (larger?) dst bounds if we have a filter
1129 if (NULL != filter) {
1130 identity.reset();
1131 srcM.fImage = NULL;
1132 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1133 goto NO_FILTER_CASE;
1134 }
1135 dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
1136 } else {
1137 NO_FILTER_CASE:
1138 dst->setConfig(SkBitmap::kA8_Config, this->width(), this->height(),
1139 srcM.fRowBytes);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001140 dst->allocPixels();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001141 GetBitmapAlpha(*this, dst->getAddr8(0, 0), srcM.fRowBytes);
1142 if (offset) {
1143 offset->set(0, 0);
1144 }
1145 return;
1146 }
1147
1148 SkAutoMaskImage srcCleanup(&srcM, true);
1149
1150 GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
1151 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1152 goto NO_FILTER_CASE;
1153 }
1154
1155 SkAutoMaskImage dstCleanup(&dstM, false);
1156
1157 dst->setConfig(SkBitmap::kA8_Config, dstM.fBounds.width(),
1158 dstM.fBounds.height(), dstM.fRowBytes);
1159 dst->allocPixels();
1160 memcpy(dst->getPixels(), dstM.fImage, dstM.computeImageSize());
1161 if (offset) {
1162 offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
1163 }
1164 SkDEBUGCODE(dst->validate();)
1165}
1166
1167///////////////////////////////////////////////////////////////////////////////
1168
1169enum {
1170 SERIALIZE_PIXELTYPE_NONE,
1171 SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE,
1172 SERIALIZE_PIXELTYPE_RAW_NO_CTABLE,
1173 SERIALIZE_PIXELTYPE_REF_DATA,
1174 SERIALIZE_PIXELTYPE_REF_PTR,
1175};
1176
1177static void writeString(SkFlattenableWriteBuffer& buffer, const char str[]) {
1178 size_t len = strlen(str);
1179 buffer.write32(len);
1180 buffer.writePad(str, len);
1181}
1182
1183static SkPixelRef::Factory deserialize_factory(SkFlattenableReadBuffer& buffer) {
1184 size_t len = buffer.readInt();
1185 SkAutoSMalloc<256> storage(len + 1);
1186 char* str = (char*)storage.get();
1187 buffer.read(str, len);
1188 str[len] = 0;
1189 return SkPixelRef::NameToFactory(str);
1190}
1191
1192/*
1193 It is tricky to know how much to flatten. If we don't have a pixelref (i.e.
1194 we just have pixels, then we can only flatten the pixels, or write out an
1195 empty bitmap.
weita@google.comf9ab99a2009-05-03 18:23:30 +00001196
reed@android.com8a1c16f2008-12-17 15:59:43 +00001197 With a pixelref, we still have the question of recognizing when two sitings
1198 of the same pixelref are the same, and when they are different. Perhaps we
1199 should look at the generationID and keep a record of that in some dictionary
1200 associated with the buffer. SkGLTextureCache does this sort of thing to know
1201 when to create a new texture.
1202*/
1203void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
1204 buffer.write32(fWidth);
1205 buffer.write32(fHeight);
1206 buffer.write32(fRowBytes);
1207 buffer.write8(fConfig);
1208 buffer.writeBool(this->isOpaque());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001209
reed@android.com8a1c16f2008-12-17 15:59:43 +00001210 /* If we are called in this mode, then it is up to the caller to manage
1211 the owner-counts on the pixelref, as we just record the ptr itself.
1212 */
1213 if (!buffer.persistBitmapPixels()) {
1214 if (fPixelRef) {
1215 buffer.write8(SERIALIZE_PIXELTYPE_REF_PTR);
1216 buffer.write32(fPixelRefOffset);
1217 buffer.writeRefCnt(fPixelRef);
1218 return;
1219 } else {
1220 // we ignore the non-persist request, since we don't have a ref
1221 // ... or we could just write an empty bitmap...
1222 // (true) will write an empty bitmap, (false) will flatten the pix
1223 if (true) {
1224 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1225 return;
1226 }
1227 }
1228 }
1229
1230 if (fPixelRef) {
1231 SkPixelRef::Factory fact = fPixelRef->getFactory();
1232 if (fact) {
1233 const char* name = SkPixelRef::FactoryToName(fact);
1234 if (name && *name) {
1235 buffer.write8(SERIALIZE_PIXELTYPE_REF_DATA);
1236 buffer.write32(fPixelRefOffset);
1237 writeString(buffer, name);
1238 fPixelRef->flatten(buffer);
1239 return;
1240 }
1241 }
1242 // if we get here, we can't record the pixels
1243 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1244 } else if (fPixels) {
1245 if (fColorTable) {
1246 buffer.write8(SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE);
1247 fColorTable->flatten(buffer);
1248 } else {
1249 buffer.write8(SERIALIZE_PIXELTYPE_RAW_NO_CTABLE);
1250 }
1251 buffer.writePad(fPixels, this->getSize());
1252 } else {
1253 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1254 }
1255}
1256
1257void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) {
1258 this->reset();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001259
reed@android.com8a1c16f2008-12-17 15:59:43 +00001260 int width = buffer.readInt();
1261 int height = buffer.readInt();
1262 int rowBytes = buffer.readInt();
1263 int config = buffer.readU8();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001264
reed@android.com8a1c16f2008-12-17 15:59:43 +00001265 this->setConfig((Config)config, width, height, rowBytes);
1266 this->setIsOpaque(buffer.readBool());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001267
reed@android.com8a1c16f2008-12-17 15:59:43 +00001268 size_t size = this->getSize();
1269 int reftype = buffer.readU8();
1270 switch (reftype) {
1271 case SERIALIZE_PIXELTYPE_REF_PTR: {
1272 size_t offset = buffer.readU32();
1273 SkPixelRef* pr = (SkPixelRef*)buffer.readRefCnt();
1274 this->setPixelRef(pr, offset);
1275 break;
1276 }
1277 case SERIALIZE_PIXELTYPE_REF_DATA: {
1278 size_t offset = buffer.readU32();
1279 SkPixelRef::Factory fact = deserialize_factory(buffer);
1280 SkPixelRef* pr = fact(buffer);
1281 this->setPixelRef(pr, offset)->safeUnref();
1282 break;
1283 }
1284 case SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE:
1285 case SERIALIZE_PIXELTYPE_RAW_NO_CTABLE: {
1286 SkColorTable* ctable = NULL;
1287 if (SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE == reftype) {
1288 ctable = SkNEW_ARGS(SkColorTable, (buffer));
1289 }
1290 if (this->allocPixels(ctable)) {
1291 this->lockPixels();
1292 buffer.read(this->getPixels(), size);
1293 this->unlockPixels();
1294 } else {
1295 buffer.skip(size);
1296 }
reed@android.com149e2f62009-05-22 14:39:03 +00001297 SkSafeUnref(ctable);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001298 break;
1299 }
1300 case SERIALIZE_PIXELTYPE_NONE:
1301 break;
1302 default:
1303 SkASSERT(!"unrecognized pixeltype in serialized data");
1304 sk_throw();
1305 }
1306}
1307
1308///////////////////////////////////////////////////////////////////////////////
1309
1310SkBitmap::RLEPixels::RLEPixels(int width, int height) {
1311 fHeight = height;
1312 fYPtrs = (uint8_t**)sk_malloc_throw(height * sizeof(uint8_t*));
1313 bzero(fYPtrs, height * sizeof(uint8_t*));
1314}
1315
1316SkBitmap::RLEPixels::~RLEPixels() {
1317 sk_free(fYPtrs);
1318}
1319
1320///////////////////////////////////////////////////////////////////////////////
1321
1322#ifdef SK_DEBUG
1323void SkBitmap::validate() const {
1324 SkASSERT(fConfig < kConfigCount);
1325 SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
1326 SkASSERT(fFlags <= kImageIsOpaque_Flag);
1327 SkASSERT(fPixelLockCount >= 0);
1328 SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000);
1329 SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
1330
1331#if 0 // these asserts are not thread-correct, so disable for now
1332 if (fPixelRef) {
1333 if (fPixelLockCount > 0) {
1334 SkASSERT(fPixelRef->getLockCount() > 0);
1335 } else {
1336 SkASSERT(NULL == fPixels);
1337 SkASSERT(NULL == fColorTable);
1338 }
1339 }
1340#endif
1341}
1342#endif
1343