blob: 9478faef5292508afeac8401fcc5cea2ec08d275 [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
119 fColorTable->safeRef(); // ref the user's ctable if present
120 } 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
228void SkBitmap::setConfig(Config c, int width, int height, int rowBytes) {
229 this->freePixels();
230
reed@android.com149e2f62009-05-22 14:39:03 +0000231 if ((width | height | rowBytes) < 0) {
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000232 goto err;
reed@android.com149e2f62009-05-22 14:39:03 +0000233 }
234
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235 if (rowBytes == 0) {
236 rowBytes = SkBitmap::ComputeRowBytes(c, width);
reed@android.com149e2f62009-05-22 14:39:03 +0000237 if (0 == rowBytes && kNo_Config != c) {
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000238 goto err;
reed@android.com149e2f62009-05-22 14:39:03 +0000239 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 }
reed@android.com89bb83a2009-05-29 21:30:42 +0000241
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 fConfig = SkToU8(c);
reed@android.comf459a492009-03-27 12:33:50 +0000243 fWidth = width;
244 fHeight = height;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245 fRowBytes = rowBytes;
246
247 fBytesPerPixel = (uint8_t)ComputeBytesPerPixel(c);
248
249 SkDEBUGCODE(this->validate();)
reed@android.com9e0c2fc2009-06-02 18:54:28 +0000250 return;
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000251
reed@android.com9e0c2fc2009-06-02 18:54:28 +0000252 // if we got here, we had an error, so we reset the bitmap to empty
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000253err:
254 this->reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255}
256
257void SkBitmap::updatePixelsFromRef() const {
258 if (NULL != fPixelRef) {
259 if (fPixelLockCount > 0) {
260 SkASSERT(fPixelRef->getLockCount() > 0);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000261
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262 void* p = fPixelRef->pixels();
263 if (NULL != p) {
264 p = (char*)p + fPixelRefOffset;
265 }
266 fPixels = p;
267 SkRefCnt_SafeAssign(fColorTable, fPixelRef->colorTable());
268 } else {
269 SkASSERT(0 == fPixelLockCount);
270 fPixels = NULL;
reed@android.com149e2f62009-05-22 14:39:03 +0000271 if (fColorTable) {
272 fColorTable->unref();
273 fColorTable = NULL;
274 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275 }
276 }
277}
278
279SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, size_t offset) {
280 // do this first, we that we never have a non-zero offset with a null ref
281 if (NULL == pr) {
282 offset = 0;
283 }
284
285 if (fPixelRef != pr || fPixelRefOffset != offset) {
286 if (fPixelRef != pr) {
287 this->freePixels();
288 SkASSERT(NULL == fPixelRef);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000289
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290 pr->safeRef();
291 fPixelRef = pr;
292 }
293 fPixelRefOffset = offset;
294 this->updatePixelsFromRef();
295 }
296
297 SkDEBUGCODE(this->validate();)
298 return pr;
299}
300
301void SkBitmap::lockPixels() const {
302 if (NULL != fPixelRef && 1 == ++fPixelLockCount) {
303 fPixelRef->lockPixels();
304 this->updatePixelsFromRef();
305 }
306 SkDEBUGCODE(this->validate();)
307}
308
309void SkBitmap::unlockPixels() const {
310 SkASSERT(NULL == fPixelRef || fPixelLockCount > 0);
311
312 if (NULL != fPixelRef && 0 == --fPixelLockCount) {
313 fPixelRef->unlockPixels();
314 this->updatePixelsFromRef();
315 }
316 SkDEBUGCODE(this->validate();)
317}
318
319void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
320 this->freePixels();
321 fPixels = p;
322 SkRefCnt_SafeAssign(fColorTable, ctable);
323
324 SkDEBUGCODE(this->validate();)
325}
326
327bool SkBitmap::allocPixels(Allocator* allocator, SkColorTable* ctable) {
328 HeapAllocator stdalloc;
329
330 if (NULL == allocator) {
331 allocator = &stdalloc;
332 }
333 return allocator->allocPixelRef(this, ctable);
334}
335
336void SkBitmap::freePixels() {
337 // if we're gonna free the pixels, we certainly need to free the mipmap
338 this->freeMipMap();
339
reed@android.com149e2f62009-05-22 14:39:03 +0000340 if (fColorTable) {
341 fColorTable->unref();
342 fColorTable = NULL;
343 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000344
345 if (NULL != fPixelRef) {
346 if (fPixelLockCount > 0) {
347 fPixelRef->unlockPixels();
348 }
349 fPixelRef->unref();
350 fPixelRef = NULL;
351 fPixelRefOffset = 0;
352 }
353 fPixelLockCount = 0;
354 fPixels = NULL;
355}
356
357void SkBitmap::freeMipMap() {
reed@android.com149e2f62009-05-22 14:39:03 +0000358 if (fMipMap) {
359 fMipMap->unref();
360 fMipMap = NULL;
361 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000362}
363
364uint32_t SkBitmap::getGenerationID() const {
365 return fPixelRef ? fPixelRef->getGenerationID() : 0;
366}
367
368void SkBitmap::notifyPixelsChanged() const {
369 if (fPixelRef) {
370 fPixelRef->notifyPixelsChanged();
371 }
372}
373
374///////////////////////////////////////////////////////////////////////////////
375
376SkMallocPixelRef::SkMallocPixelRef(void* storage, size_t size,
377 SkColorTable* ctable) {
378 SkASSERT(storage);
379 fStorage = storage;
380 fSize = size;
381 fCTable = ctable;
382 ctable->safeRef();
383}
384
385SkMallocPixelRef::~SkMallocPixelRef() {
reed@android.com149e2f62009-05-22 14:39:03 +0000386 SkSafeUnref(fCTable);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000387 sk_free(fStorage);
388}
389
390void* SkMallocPixelRef::onLockPixels(SkColorTable** ct) {
391 *ct = fCTable;
392 return fStorage;
393}
394
395void SkMallocPixelRef::onUnlockPixels() {
396 // nothing to do
397}
398
399void SkMallocPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const {
400 this->INHERITED::flatten(buffer);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000401
reed@android.com8a1c16f2008-12-17 15:59:43 +0000402 buffer.write32(fSize);
403 buffer.writePad(fStorage, fSize);
404 if (fCTable) {
405 buffer.writeBool(true);
406 fCTable->flatten(buffer);
407 } else {
408 buffer.writeBool(false);
409 }
410}
411
412SkMallocPixelRef::SkMallocPixelRef(SkFlattenableReadBuffer& buffer) : INHERITED(buffer, NULL) {
413 fSize = buffer.readU32();
414 fStorage = sk_malloc_throw(fSize);
415 buffer.read(fStorage, fSize);
416 if (buffer.readBool()) {
417 fCTable = SkNEW_ARGS(SkColorTable, (buffer));
418 } else {
419 fCTable = NULL;
420 }
421}
422
423static SkPixelRef::Registrar reg("SkMallocPixelRef",
424 SkMallocPixelRef::Create);
425
426/** We explicitly use the same allocator for our pixels that SkMask does,
427 so that we can freely assign memory allocated by one class to the other.
428 */
429bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
430 SkColorTable* ctable) {
431 Sk64 size = dst->getSize64();
432 if (size.isNeg() || !size.is32()) {
433 return false;
434 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000435
reed@android.com8a1c16f2008-12-17 15:59:43 +0000436 void* addr = sk_malloc_flags(size.get32(), 0); // returns NULL on failure
437 if (NULL == addr) {
438 return false;
439 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000440
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441 dst->setPixelRef(new SkMallocPixelRef(addr, size.get32(), ctable))->unref();
442 // since we're already allocated, we lockPixels right away
443 dst->lockPixels();
444 return true;
445}
446
447///////////////////////////////////////////////////////////////////////////////
448
449bool SkBitmap::isOpaque() const {
450 switch (fConfig) {
451 case kNo_Config:
452 return true;
453
454 case kA1_Config:
455 case kA8_Config:
456 case kARGB_4444_Config:
457 case kARGB_8888_Config:
458 return (fFlags & kImageIsOpaque_Flag) != 0;
459
460 case kIndex8_Config:
461 case kRLE_Index8_Config: {
462 uint32_t flags = 0;
463
464 this->lockPixels();
465 // if lockPixels failed, we may not have a ctable ptr
466 if (fColorTable) {
467 flags = fColorTable->getFlags();
468 }
469 this->unlockPixels();
470
471 return (flags & SkColorTable::kColorsAreOpaque_Flag) != 0;
472 }
473
474 case kRGB_565_Config:
475 return true;
476
477 default:
478 SkASSERT(!"unknown bitmap config pased to isOpaque");
479 return false;
480 }
481}
482
483void SkBitmap::setIsOpaque(bool isOpaque) {
484 /* we record this regardless of fConfig, though it is ignored in
485 isOpaque() for configs that can't support per-pixel alpha.
486 */
487 if (isOpaque) {
488 fFlags |= kImageIsOpaque_Flag;
489 } else {
490 fFlags &= ~kImageIsOpaque_Flag;
491 }
492}
493
494void* SkBitmap::getAddr(int x, int y) const {
495 SkASSERT((unsigned)x < (unsigned)this->width());
496 SkASSERT((unsigned)y < (unsigned)this->height());
497
498 char* base = (char*)this->getPixels();
499 if (base) {
500 base += y * this->rowBytes();
501 switch (this->config()) {
502 case SkBitmap::kARGB_8888_Config:
503 base += x << 2;
504 break;
505 case SkBitmap::kARGB_4444_Config:
506 case SkBitmap::kRGB_565_Config:
507 base += x << 1;
508 break;
509 case SkBitmap::kA8_Config:
510 case SkBitmap::kIndex8_Config:
511 base += x;
512 break;
513 case SkBitmap::kA1_Config:
514 base += x >> 3;
515 break;
516 case kRLE_Index8_Config:
517 SkASSERT(!"Can't return addr for kRLE_Index8_Config");
518 base = NULL;
519 break;
520 default:
521 SkASSERT(!"Can't return addr for config");
522 base = NULL;
523 break;
524 }
525 }
526 return base;
527}
528
529///////////////////////////////////////////////////////////////////////////////
530///////////////////////////////////////////////////////////////////////////////
531
532void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
533 SkDEBUGCODE(this->validate();)
534
535 if (0 == fWidth || 0 == fHeight ||
536 kNo_Config == fConfig || kIndex8_Config == fConfig) {
537 return;
538 }
539
540 SkAutoLockPixels alp(*this);
541 // perform this check after the lock call
542 if (!this->readyToDraw()) {
543 return;
544 }
545
546 int height = fHeight;
547 const int width = fWidth;
548 const int rowBytes = fRowBytes;
549
550 // make rgb premultiplied
551 if (255 != a) {
552 r = SkAlphaMul(r, a);
553 g = SkAlphaMul(g, a);
554 b = SkAlphaMul(b, a);
555 }
556
557 switch (fConfig) {
558 case kA1_Config: {
559 uint8_t* p = (uint8_t*)fPixels;
560 const int count = (width + 7) >> 3;
561 a = (a >> 7) ? 0xFF : 0;
562 SkASSERT(count <= rowBytes);
563 while (--height >= 0) {
564 memset(p, a, count);
565 p += rowBytes;
566 }
567 break;
568 }
569 case kA8_Config: {
570 uint8_t* p = (uint8_t*)fPixels;
571 while (--height >= 0) {
572 memset(p, a, width);
573 p += rowBytes;
574 }
575 break;
576 }
577 case kARGB_4444_Config:
578 case kRGB_565_Config: {
579 uint16_t* p = (uint16_t*)fPixels;
580 uint16_t v;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000581
reed@android.com8a1c16f2008-12-17 15:59:43 +0000582 if (kARGB_4444_Config == fConfig) {
583 v = SkPackARGB4444(a >> 4, r >> 4, g >> 4, b >> 4);
584 } else { // kRGB_565_Config
585 v = SkPackRGB16(r >> (8 - SK_R16_BITS), g >> (8 - SK_G16_BITS),
586 b >> (8 - SK_B16_BITS));
587 }
588 while (--height >= 0) {
589 sk_memset16(p, v, width);
590 p = (uint16_t*)((char*)p + rowBytes);
591 }
592 break;
593 }
594 case kARGB_8888_Config: {
595 uint32_t* p = (uint32_t*)fPixels;
596 uint32_t v = SkPackARGB32(a, r, g, b);
597
598 while (--height >= 0) {
599 sk_memset32(p, v, width);
600 p = (uint32_t*)((char*)p + rowBytes);
601 }
602 break;
603 }
604 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000605
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 this->notifyPixelsChanged();
607}
608
609//////////////////////////////////////////////////////////////////////////////////////
610//////////////////////////////////////////////////////////////////////////////////////
611
612#define SUB_OFFSET_FAILURE ((size_t)-1)
613
614static size_t getSubOffset(const SkBitmap& bm, int x, int y) {
615 SkASSERT((unsigned)x < (unsigned)bm.width());
616 SkASSERT((unsigned)y < (unsigned)bm.height());
weita@google.comf9ab99a2009-05-03 18:23:30 +0000617
reed@android.com8a1c16f2008-12-17 15:59:43 +0000618 switch (bm.getConfig()) {
619 case SkBitmap::kA8_Config:
620 case SkBitmap:: kIndex8_Config:
621 // x is fine as is for the calculation
622 break;
623
624 case SkBitmap::kRGB_565_Config:
625 case SkBitmap::kARGB_4444_Config:
626 x <<= 1;
627 break;
628
629 case SkBitmap::kARGB_8888_Config:
630 x <<= 2;
631 break;
632
633 case SkBitmap::kNo_Config:
634 case SkBitmap::kA1_Config:
635 default:
636 return SUB_OFFSET_FAILURE;
637 }
638 return y * bm.rowBytes() + x;
639}
640
641bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
642 SkDEBUGCODE(this->validate();)
643
644 if (NULL == result || (NULL == fPixelRef && NULL == fPixels)) {
645 return false; // no src pixels
646 }
647
648 SkIRect srcRect, r;
649 srcRect.set(0, 0, this->width(), this->height());
650 if (!r.intersect(srcRect, subset)) {
651 return false; // r is empty (i.e. no intersection)
652 }
653
654 if (kRLE_Index8_Config == fConfig) {
655 SkAutoLockPixels alp(*this);
656 // don't call readyToDraw(), since we can operate w/o a colortable
657 // at this stage
658 if (this->getPixels() == NULL) {
659 return false;
660 }
661 SkBitmap bm;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000662
reed@android.com8a1c16f2008-12-17 15:59:43 +0000663 bm.setConfig(kIndex8_Config, r.width(), r.height());
664 bm.allocPixels(this->getColorTable());
665 if (NULL == bm.getPixels()) {
666 return false;
667 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000668
reed@android.com8a1c16f2008-12-17 15:59:43 +0000669 const RLEPixels* rle = (const RLEPixels*)this->getPixels();
670 uint8_t* dst = bm.getAddr8(0, 0);
671 const int width = bm.width();
672 const int rowBytes = bm.rowBytes();
weita@google.comf9ab99a2009-05-03 18:23:30 +0000673
reed@android.com8a1c16f2008-12-17 15:59:43 +0000674 for (int y = r.fTop; y < r.fBottom; y++) {
675 SkPackBits::Unpack8(dst, r.fLeft, width, rle->packedAtY(y));
676 dst += rowBytes;
677 }
678 result->swap(bm);
679 return true;
680 }
681
682 size_t offset = getSubOffset(*this, r.fLeft, r.fTop);
683 if (SUB_OFFSET_FAILURE == offset) {
684 return false; // config not supported
685 }
686
687 SkBitmap dst;
688 dst.setConfig(this->config(), r.width(), r.height(), this->rowBytes());
689
690 if (fPixelRef) {
691 // share the pixelref with a custom offset
692 dst.setPixelRef(fPixelRef, fPixelRefOffset + offset);
693 } else {
694 // share the pixels (owned by the caller)
695 dst.setPixels((char*)fPixels + offset, this->getColorTable());
696 }
697 SkDEBUGCODE(dst.validate();)
698
699 // we know we're good, so commit to result
700 result->swap(dst);
701 return true;
702}
703
704///////////////////////////////////////////////////////////////////////////////
705
706#include "SkCanvas.h"
707#include "SkPaint.h"
708
reed@android.comfbaa88d2009-05-06 17:44:34 +0000709bool SkBitmap::canCopyTo(Config dstConfig) const {
710 if (this->getConfig() == kNo_Config) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000711 return false;
712 }
713
reed@android.comfbaa88d2009-05-06 17:44:34 +0000714 bool sameConfigs = (this->config() == dstConfig);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000715 switch (dstConfig) {
716 case kA8_Config:
717 case kARGB_4444_Config:
718 case kRGB_565_Config:
719 case kARGB_8888_Config:
720 break;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000721 case kA1_Config:
722 case kIndex8_Config:
723 if (!sameConfigs) {
724 return false;
725 }
726 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000727 default:
728 return false;
729 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000730
731 // do not copy src if srcConfig == kA1_Config while dstConfig != kA1_Config
732 if (this->getConfig() == kA1_Config && !sameConfigs) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000733 return false;
734 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000735
reed@android.comfbaa88d2009-05-06 17:44:34 +0000736 return true;
737}
738
739bool SkBitmap::copyTo(SkBitmap* dst, Config dstConfig, Allocator* alloc) const {
740 if (!this->canCopyTo(dstConfig)) {
741 return false;
742 }
743
reed@android.com311c82d2009-05-05 23:13:23 +0000744 // we lock this now, since we may need its colortable
745 SkAutoLockPixels srclock(*this);
reed@android.comfbaa88d2009-05-06 17:44:34 +0000746 if (!this->readyToDraw()) {
747 return false;
748 }
reed@android.com311c82d2009-05-05 23:13:23 +0000749
weita@google.comf9ab99a2009-05-03 18:23:30 +0000750 SkBitmap tmp;
751 tmp.setConfig(dstConfig, this->width(), this->height());
752
753 // allocate colortable if srcConfig == kIndex8_Config
754 SkColorTable* ctable = (dstConfig == kIndex8_Config) ?
755 new SkColorTable(*this->getColorTable()) : NULL;
756 SkAutoUnref au(ctable);
757 if (!tmp.allocPixels(alloc, ctable)) {
758 return false;
759 }
760
reed@android.com8a1c16f2008-12-17 15:59:43 +0000761 SkAutoLockPixels dstlock(tmp);
reed@android.comfbaa88d2009-05-06 17:44:34 +0000762 if (!tmp.readyToDraw()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000763 // allocator/lock failed
764 return false;
765 }
766
reed@android.comfbaa88d2009-05-06 17:44:34 +0000767 /* do memcpy for the same configs cases, else use drawing
weita@google.comf9ab99a2009-05-03 18:23:30 +0000768 */
reed@android.comfbaa88d2009-05-06 17:44:34 +0000769 if (this->config() == dstConfig) {
reed@android.com311c82d2009-05-05 23:13:23 +0000770 if (tmp.getSize() == this->getSize()) {
771 memcpy(tmp.getPixels(), this->getPixels(), this->getSize());
772 } else {
773 const char* srcP = reinterpret_cast<const char*>(this->getPixels());
774 char* dstP = reinterpret_cast<char*>(tmp.getPixels());
775 // to be sure we don't read too much, only copy our logical pixels
776 size_t bytesToCopy = tmp.width() * tmp.bytesPerPixel();
777 for (int y = 0; y < tmp.height(); y++) {
778 memcpy(dstP, srcP, bytesToCopy);
779 srcP += this->rowBytes();
780 dstP += tmp.rowBytes();
781 }
782 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000783 } else {
784 // if the src has alpha, we have to clear the dst first
785 if (!this->isOpaque()) {
786 tmp.eraseColor(0);
787 }
788
789 SkCanvas canvas(tmp);
790 SkPaint paint;
791
792 paint.setDither(true);
793 canvas.drawBitmap(*this, 0, 0, &paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000794 }
795
reed@android.comcafc9f92009-08-22 03:44:57 +0000796 tmp.setIsOpaque(this->isOpaque());
797
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798 dst->swap(tmp);
799 return true;
800}
801
802///////////////////////////////////////////////////////////////////////////////
803///////////////////////////////////////////////////////////////////////////////
804
805static void downsampleby2_proc32(SkBitmap* dst, int x, int y,
806 const SkBitmap& src) {
807 x <<= 1;
808 y <<= 1;
809 const SkPMColor* p = src.getAddr32(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +0000810 const SkPMColor* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811 SkPMColor c, ag, rb;
812
813 c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF;
814 if (x < src.width() - 1) {
815 p += 1;
816 }
817 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
818
reed@android.com829c83c2009-06-08 12:05:31 +0000819 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000820 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +0000821 p += src.rowBytes() >> 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000822 }
823 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
824 if (x < src.width() - 1) {
825 p += 1;
826 }
827 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
828
829 *dst->getAddr32(x >> 1, y >> 1) =
830 ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00);
831}
832
833static inline uint32_t expand16(U16CPU c) {
834 return (c & ~SK_G16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16);
835}
836
837// returns dirt in the top 16bits, but we don't care, since we only
838// store the low 16bits.
839static inline U16CPU pack16(uint32_t c) {
840 return (c & ~SK_G16_MASK_IN_PLACE) | ((c >> 16) & SK_G16_MASK_IN_PLACE);
841}
842
843static void downsampleby2_proc16(SkBitmap* dst, int x, int y,
844 const SkBitmap& src) {
845 x <<= 1;
846 y <<= 1;
847 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +0000848 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849 SkPMColor c;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000850
reed@android.com8a1c16f2008-12-17 15:59:43 +0000851 c = expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +0000852 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000853 p += 1;
854 }
855 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000856
reed@android.com829c83c2009-06-08 12:05:31 +0000857 p = baseP;
858 if (y < src.height() - 1) {
859 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000860 }
861 c += expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +0000862 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000863 p += 1;
864 }
865 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000866
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)pack16(c >> 2);
868}
869
870static uint32_t expand4444(U16CPU c) {
871 return (c & 0xF0F) | ((c & ~0xF0F) << 12);
872}
873
874static U16CPU collaps4444(uint32_t c) {
875 return (c & 0xF0F) | ((c >> 12) & ~0xF0F);
876}
877
878static void downsampleby2_proc4444(SkBitmap* dst, int x, int y,
879 const SkBitmap& src) {
880 x <<= 1;
881 y <<= 1;
882 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +0000883 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000884 uint32_t c;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000885
reed@android.com8a1c16f2008-12-17 15:59:43 +0000886 c = expand4444(*p);
887 if (x < src.width() - 1) {
888 p += 1;
889 }
890 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000891
reed@android.com829c83c2009-06-08 12:05:31 +0000892 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000893 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +0000894 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000895 }
896 c += expand4444(*p);
897 if (x < src.width() - 1) {
898 p += 1;
899 }
900 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000901
reed@android.com8a1c16f2008-12-17 15:59:43 +0000902 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)collaps4444(c >> 2);
903}
904
905void SkBitmap::buildMipMap(bool forceRebuild) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000906 if (forceRebuild)
907 this->freeMipMap();
908 else if (fMipMap)
909 return; // we're already built
910
911 SkASSERT(NULL == fMipMap);
912
913 void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src);
914
915 const SkBitmap::Config config = this->getConfig();
916
917 switch (config) {
918 case kARGB_8888_Config:
919 proc = downsampleby2_proc32;
920 break;
921 case kRGB_565_Config:
922 proc = downsampleby2_proc16;
923 break;
924 case kARGB_4444_Config:
925 proc = downsampleby2_proc4444;
926 break;
927 case kIndex8_Config:
928 case kA8_Config:
929 default:
930 return; // don't build mipmaps for these configs
931 }
reed@android.com89bb83a2009-05-29 21:30:42 +0000932
reed@android.com149e2f62009-05-22 14:39:03 +0000933 SkAutoLockPixels alp(*this);
934 if (!this->readyToDraw()) {
935 return;
936 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000937
938 // whip through our loop to compute the exact size needed
939 size_t size = 0;
940 int maxLevels = 0;
941 {
reed@android.com149e2f62009-05-22 14:39:03 +0000942 int width = this->width();
943 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000944 for (;;) {
945 width >>= 1;
946 height >>= 1;
947 if (0 == width || 0 == height) {
948 break;
949 }
950 size += ComputeRowBytes(config, width) * height;
951 maxLevels += 1;
952 }
953 }
reed@android.com89bb83a2009-05-29 21:30:42 +0000954
reed@android.com149e2f62009-05-22 14:39:03 +0000955 // nothing to build
reed@android.com8a1c16f2008-12-17 15:59:43 +0000956 if (0 == maxLevels) {
957 return;
958 }
959
reed@android.com149e2f62009-05-22 14:39:03 +0000960 SkBitmap srcBM(*this);
961 srcBM.lockPixels();
962 if (!srcBM.readyToDraw()) {
963 return;
964 }
965
966 MipMap* mm = MipMap::Alloc(maxLevels, size);
967 if (NULL == mm) {
968 return;
969 }
970
reed@android.com8a1c16f2008-12-17 15:59:43 +0000971 MipLevel* level = mm->levels();
972 uint8_t* addr = (uint8_t*)mm->pixels();
reed@android.com149e2f62009-05-22 14:39:03 +0000973 int width = this->width();
974 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975 unsigned rowBytes = this->rowBytes();
reed@android.com149e2f62009-05-22 14:39:03 +0000976 SkBitmap dstBM;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000977
978 for (int i = 0; i < maxLevels; i++) {
979 width >>= 1;
980 height >>= 1;
981 rowBytes = ComputeRowBytes(config, width);
982
983 level[i].fPixels = addr;
reed@android.comf459a492009-03-27 12:33:50 +0000984 level[i].fWidth = width;
985 level[i].fHeight = height;
reed@android.com49f0ff22009-03-19 21:52:42 +0000986 level[i].fRowBytes = rowBytes;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000987
988 dstBM.setConfig(config, width, height, rowBytes);
989 dstBM.setPixels(addr);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000990
reed@android.com149e2f62009-05-22 14:39:03 +0000991 for (int y = 0; y < height; y++) {
992 for (int x = 0; x < width; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993 proc(&dstBM, x, y, srcBM);
994 }
995 }
996
997 srcBM = dstBM;
998 addr += height * rowBytes;
999 }
1000 SkASSERT(addr == (uint8_t*)mm->pixels() + size);
1001 fMipMap = mm;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001002}
1003
1004bool SkBitmap::hasMipMap() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005 return fMipMap != NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001006}
1007
1008int SkBitmap::extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy) {
reed@android.com83f7bc32009-07-17 02:42:41 +00001009 if (NULL == fMipMap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001010 return 0;
reed@android.com83f7bc32009-07-17 02:42:41 +00001011 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001012
reed@android.com8a1c16f2008-12-17 15:59:43 +00001013 int level = ComputeMipLevel(sx, sy) >> 16;
1014 SkASSERT(level >= 0);
1015 if (level <= 0) {
1016 return 0;
1017 }
1018
1019 if (level >= fMipMap->fLevelCount) {
1020 level = fMipMap->fLevelCount - 1;
1021 }
1022 if (dst) {
1023 const MipLevel& mip = fMipMap->levels()[level - 1];
1024 dst->setConfig((SkBitmap::Config)this->config(),
1025 mip.fWidth, mip.fHeight, mip.fRowBytes);
1026 dst->setPixels(mip.fPixels);
1027 }
1028 return level;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001029}
1030
1031SkFixed SkBitmap::ComputeMipLevel(SkFixed sx, SkFixed sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001032 sx = SkAbs32(sx);
1033 sy = SkAbs32(sy);
1034 if (sx < sy) {
1035 sx = sy;
1036 }
1037 if (sx < SK_Fixed1) {
1038 return 0;
1039 }
1040 int clz = SkCLZ(sx);
1041 SkASSERT(clz >= 1 && clz <= 15);
1042 return SkIntToFixed(15 - clz) + ((unsigned)(sx << (clz + 1)) >> 16);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001043}
1044
1045///////////////////////////////////////////////////////////////////////////////
1046
1047static void GetBitmapAlpha(const SkBitmap& src, uint8_t SK_RESTRICT alpha[],
1048 int alphaRowBytes) {
1049 SkASSERT(alpha != NULL);
1050 SkASSERT(alphaRowBytes >= src.width());
1051
1052 SkBitmap::Config config = src.getConfig();
1053 int w = src.width();
1054 int h = src.height();
1055 int rb = src.rowBytes();
1056
1057 if (SkBitmap::kA8_Config == config && !src.isOpaque()) {
1058 const uint8_t* s = src.getAddr8(0, 0);
1059 while (--h >= 0) {
1060 memcpy(alpha, s, w);
1061 s += rb;
1062 alpha += alphaRowBytes;
1063 }
1064 } else if (SkBitmap::kARGB_8888_Config == config && !src.isOpaque()) {
1065 const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0);
1066 while (--h >= 0) {
1067 for (int x = 0; x < w; x++) {
1068 alpha[x] = SkGetPackedA32(s[x]);
1069 }
1070 s = (const SkPMColor*)((const char*)s + rb);
1071 alpha += alphaRowBytes;
1072 }
1073 } else if (SkBitmap::kARGB_4444_Config == config && !src.isOpaque()) {
1074 const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0);
1075 while (--h >= 0) {
1076 for (int x = 0; x < w; x++) {
1077 alpha[x] = SkPacked4444ToA32(s[x]);
1078 }
1079 s = (const SkPMColor16*)((const char*)s + rb);
1080 alpha += alphaRowBytes;
1081 }
1082 } else if (SkBitmap::kIndex8_Config == config && !src.isOpaque()) {
1083 SkColorTable* ct = src.getColorTable();
1084 if (ct) {
1085 const SkPMColor* SK_RESTRICT table = ct->lockColors();
1086 const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0);
1087 while (--h >= 0) {
1088 for (int x = 0; x < w; x++) {
1089 alpha[x] = SkGetPackedA32(table[s[x]]);
1090 }
1091 s += rb;
1092 alpha += alphaRowBytes;
1093 }
1094 ct->unlockColors(false);
1095 }
1096 } else { // src is opaque, so just fill alpha[] with 0xFF
1097 memset(alpha, 0xFF, h * alphaRowBytes);
1098 }
1099}
1100
1101#include "SkPaint.h"
1102#include "SkMaskFilter.h"
1103#include "SkMatrix.h"
1104
1105void SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
1106 SkIPoint* offset) const {
1107 SkDEBUGCODE(this->validate();)
1108
1109 SkMatrix identity;
1110 SkMask srcM, dstM;
1111
1112 srcM.fBounds.set(0, 0, this->width(), this->height());
1113 srcM.fRowBytes = SkAlign4(this->width());
1114 srcM.fFormat = SkMask::kA8_Format;
1115
1116 SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL;
1117
1118 // compute our (larger?) dst bounds if we have a filter
1119 if (NULL != filter) {
1120 identity.reset();
1121 srcM.fImage = NULL;
1122 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1123 goto NO_FILTER_CASE;
1124 }
1125 dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
1126 } else {
1127 NO_FILTER_CASE:
1128 dst->setConfig(SkBitmap::kA8_Config, this->width(), this->height(),
1129 srcM.fRowBytes);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001130 dst->allocPixels();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001131 GetBitmapAlpha(*this, dst->getAddr8(0, 0), srcM.fRowBytes);
1132 if (offset) {
1133 offset->set(0, 0);
1134 }
1135 return;
1136 }
1137
1138 SkAutoMaskImage srcCleanup(&srcM, true);
1139
1140 GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
1141 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1142 goto NO_FILTER_CASE;
1143 }
1144
1145 SkAutoMaskImage dstCleanup(&dstM, false);
1146
1147 dst->setConfig(SkBitmap::kA8_Config, dstM.fBounds.width(),
1148 dstM.fBounds.height(), dstM.fRowBytes);
1149 dst->allocPixels();
1150 memcpy(dst->getPixels(), dstM.fImage, dstM.computeImageSize());
1151 if (offset) {
1152 offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
1153 }
1154 SkDEBUGCODE(dst->validate();)
1155}
1156
1157///////////////////////////////////////////////////////////////////////////////
1158
1159enum {
1160 SERIALIZE_PIXELTYPE_NONE,
1161 SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE,
1162 SERIALIZE_PIXELTYPE_RAW_NO_CTABLE,
1163 SERIALIZE_PIXELTYPE_REF_DATA,
1164 SERIALIZE_PIXELTYPE_REF_PTR,
1165};
1166
1167static void writeString(SkFlattenableWriteBuffer& buffer, const char str[]) {
1168 size_t len = strlen(str);
1169 buffer.write32(len);
1170 buffer.writePad(str, len);
1171}
1172
1173static SkPixelRef::Factory deserialize_factory(SkFlattenableReadBuffer& buffer) {
1174 size_t len = buffer.readInt();
1175 SkAutoSMalloc<256> storage(len + 1);
1176 char* str = (char*)storage.get();
1177 buffer.read(str, len);
1178 str[len] = 0;
1179 return SkPixelRef::NameToFactory(str);
1180}
1181
1182/*
1183 It is tricky to know how much to flatten. If we don't have a pixelref (i.e.
1184 we just have pixels, then we can only flatten the pixels, or write out an
1185 empty bitmap.
weita@google.comf9ab99a2009-05-03 18:23:30 +00001186
reed@android.com8a1c16f2008-12-17 15:59:43 +00001187 With a pixelref, we still have the question of recognizing when two sitings
1188 of the same pixelref are the same, and when they are different. Perhaps we
1189 should look at the generationID and keep a record of that in some dictionary
1190 associated with the buffer. SkGLTextureCache does this sort of thing to know
1191 when to create a new texture.
1192*/
1193void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
1194 buffer.write32(fWidth);
1195 buffer.write32(fHeight);
1196 buffer.write32(fRowBytes);
1197 buffer.write8(fConfig);
1198 buffer.writeBool(this->isOpaque());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001199
reed@android.com8a1c16f2008-12-17 15:59:43 +00001200 /* If we are called in this mode, then it is up to the caller to manage
1201 the owner-counts on the pixelref, as we just record the ptr itself.
1202 */
1203 if (!buffer.persistBitmapPixels()) {
1204 if (fPixelRef) {
1205 buffer.write8(SERIALIZE_PIXELTYPE_REF_PTR);
1206 buffer.write32(fPixelRefOffset);
1207 buffer.writeRefCnt(fPixelRef);
1208 return;
1209 } else {
1210 // we ignore the non-persist request, since we don't have a ref
1211 // ... or we could just write an empty bitmap...
1212 // (true) will write an empty bitmap, (false) will flatten the pix
1213 if (true) {
1214 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1215 return;
1216 }
1217 }
1218 }
1219
1220 if (fPixelRef) {
1221 SkPixelRef::Factory fact = fPixelRef->getFactory();
1222 if (fact) {
1223 const char* name = SkPixelRef::FactoryToName(fact);
1224 if (name && *name) {
1225 buffer.write8(SERIALIZE_PIXELTYPE_REF_DATA);
1226 buffer.write32(fPixelRefOffset);
1227 writeString(buffer, name);
1228 fPixelRef->flatten(buffer);
1229 return;
1230 }
1231 }
1232 // if we get here, we can't record the pixels
1233 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1234 } else if (fPixels) {
1235 if (fColorTable) {
1236 buffer.write8(SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE);
1237 fColorTable->flatten(buffer);
1238 } else {
1239 buffer.write8(SERIALIZE_PIXELTYPE_RAW_NO_CTABLE);
1240 }
1241 buffer.writePad(fPixels, this->getSize());
1242 } else {
1243 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1244 }
1245}
1246
1247void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) {
1248 this->reset();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001249
reed@android.com8a1c16f2008-12-17 15:59:43 +00001250 int width = buffer.readInt();
1251 int height = buffer.readInt();
1252 int rowBytes = buffer.readInt();
1253 int config = buffer.readU8();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001254
reed@android.com8a1c16f2008-12-17 15:59:43 +00001255 this->setConfig((Config)config, width, height, rowBytes);
1256 this->setIsOpaque(buffer.readBool());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001257
reed@android.com8a1c16f2008-12-17 15:59:43 +00001258 size_t size = this->getSize();
1259 int reftype = buffer.readU8();
1260 switch (reftype) {
1261 case SERIALIZE_PIXELTYPE_REF_PTR: {
1262 size_t offset = buffer.readU32();
1263 SkPixelRef* pr = (SkPixelRef*)buffer.readRefCnt();
1264 this->setPixelRef(pr, offset);
1265 break;
1266 }
1267 case SERIALIZE_PIXELTYPE_REF_DATA: {
1268 size_t offset = buffer.readU32();
1269 SkPixelRef::Factory fact = deserialize_factory(buffer);
1270 SkPixelRef* pr = fact(buffer);
1271 this->setPixelRef(pr, offset)->safeUnref();
1272 break;
1273 }
1274 case SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE:
1275 case SERIALIZE_PIXELTYPE_RAW_NO_CTABLE: {
1276 SkColorTable* ctable = NULL;
1277 if (SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE == reftype) {
1278 ctable = SkNEW_ARGS(SkColorTable, (buffer));
1279 }
1280 if (this->allocPixels(ctable)) {
1281 this->lockPixels();
1282 buffer.read(this->getPixels(), size);
1283 this->unlockPixels();
1284 } else {
1285 buffer.skip(size);
1286 }
reed@android.com149e2f62009-05-22 14:39:03 +00001287 SkSafeUnref(ctable);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001288 break;
1289 }
1290 case SERIALIZE_PIXELTYPE_NONE:
1291 break;
1292 default:
1293 SkASSERT(!"unrecognized pixeltype in serialized data");
1294 sk_throw();
1295 }
1296}
1297
1298///////////////////////////////////////////////////////////////////////////////
1299
1300SkBitmap::RLEPixels::RLEPixels(int width, int height) {
1301 fHeight = height;
1302 fYPtrs = (uint8_t**)sk_malloc_throw(height * sizeof(uint8_t*));
reed@android.com4516f472009-06-29 16:25:36 +00001303 sk_bzero(fYPtrs, height * sizeof(uint8_t*));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001304}
1305
1306SkBitmap::RLEPixels::~RLEPixels() {
1307 sk_free(fYPtrs);
1308}
1309
1310///////////////////////////////////////////////////////////////////////////////
1311
1312#ifdef SK_DEBUG
1313void SkBitmap::validate() const {
1314 SkASSERT(fConfig < kConfigCount);
1315 SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
1316 SkASSERT(fFlags <= kImageIsOpaque_Flag);
1317 SkASSERT(fPixelLockCount >= 0);
1318 SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000);
1319 SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
1320
1321#if 0 // these asserts are not thread-correct, so disable for now
1322 if (fPixelRef) {
1323 if (fPixelLockCount > 0) {
1324 SkASSERT(fPixelRef->getLockCount() > 0);
1325 } else {
1326 SkASSERT(NULL == fPixels);
1327 SkASSERT(NULL == fColorTable);
1328 }
1329 }
1330#endif
1331}
1332#endif
1333