blob: d87f001ed80108b4b83232a8edf91ef8afec68d3 [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.com8a1c16f2008-12-17 15:59:43 +0000796 dst->swap(tmp);
797 return true;
798}
799
800///////////////////////////////////////////////////////////////////////////////
801///////////////////////////////////////////////////////////////////////////////
802
803static void downsampleby2_proc32(SkBitmap* dst, int x, int y,
804 const SkBitmap& src) {
805 x <<= 1;
806 y <<= 1;
807 const SkPMColor* p = src.getAddr32(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +0000808 const SkPMColor* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000809 SkPMColor c, ag, rb;
810
811 c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF;
812 if (x < src.width() - 1) {
813 p += 1;
814 }
815 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
816
reed@android.com829c83c2009-06-08 12:05:31 +0000817 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +0000819 p += src.rowBytes() >> 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000820 }
821 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
822 if (x < src.width() - 1) {
823 p += 1;
824 }
825 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
826
827 *dst->getAddr32(x >> 1, y >> 1) =
828 ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00);
829}
830
831static inline uint32_t expand16(U16CPU c) {
832 return (c & ~SK_G16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16);
833}
834
835// returns dirt in the top 16bits, but we don't care, since we only
836// store the low 16bits.
837static inline U16CPU pack16(uint32_t c) {
838 return (c & ~SK_G16_MASK_IN_PLACE) | ((c >> 16) & SK_G16_MASK_IN_PLACE);
839}
840
841static void downsampleby2_proc16(SkBitmap* dst, int x, int y,
842 const SkBitmap& src) {
843 x <<= 1;
844 y <<= 1;
845 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +0000846 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000847 SkPMColor c;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000848
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849 c = expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +0000850 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000851 p += 1;
852 }
853 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000854
reed@android.com829c83c2009-06-08 12:05:31 +0000855 p = baseP;
856 if (y < src.height() - 1) {
857 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000858 }
859 c += expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +0000860 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000861 p += 1;
862 }
863 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000864
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)pack16(c >> 2);
866}
867
868static uint32_t expand4444(U16CPU c) {
869 return (c & 0xF0F) | ((c & ~0xF0F) << 12);
870}
871
872static U16CPU collaps4444(uint32_t c) {
873 return (c & 0xF0F) | ((c >> 12) & ~0xF0F);
874}
875
876static void downsampleby2_proc4444(SkBitmap* dst, int x, int y,
877 const SkBitmap& src) {
878 x <<= 1;
879 y <<= 1;
880 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +0000881 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000882 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.com829c83c2009-06-08 12:05:31 +0000890 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000891 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +0000892 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000893 }
894 c += expand4444(*p);
895 if (x < src.width() - 1) {
896 p += 1;
897 }
898 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000899
reed@android.com8a1c16f2008-12-17 15:59:43 +0000900 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)collaps4444(c >> 2);
901}
902
903void SkBitmap::buildMipMap(bool forceRebuild) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000904 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;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001000}
1001
1002bool SkBitmap::hasMipMap() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001003 return fMipMap != NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001004}
1005
1006int SkBitmap::extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy) {
reed@android.com83f7bc32009-07-17 02:42:41 +00001007 if (NULL == fMipMap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001008 return 0;
reed@android.com83f7bc32009-07-17 02:42:41 +00001009 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001010
reed@android.com8a1c16f2008-12-17 15:59:43 +00001011 int level = ComputeMipLevel(sx, sy) >> 16;
1012 SkASSERT(level >= 0);
1013 if (level <= 0) {
1014 return 0;
1015 }
1016
1017 if (level >= fMipMap->fLevelCount) {
1018 level = fMipMap->fLevelCount - 1;
1019 }
1020 if (dst) {
1021 const MipLevel& mip = fMipMap->levels()[level - 1];
1022 dst->setConfig((SkBitmap::Config)this->config(),
1023 mip.fWidth, mip.fHeight, mip.fRowBytes);
1024 dst->setPixels(mip.fPixels);
1025 }
1026 return level;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001027}
1028
1029SkFixed SkBitmap::ComputeMipLevel(SkFixed sx, SkFixed sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001030 sx = SkAbs32(sx);
1031 sy = SkAbs32(sy);
1032 if (sx < sy) {
1033 sx = sy;
1034 }
1035 if (sx < SK_Fixed1) {
1036 return 0;
1037 }
1038 int clz = SkCLZ(sx);
1039 SkASSERT(clz >= 1 && clz <= 15);
1040 return SkIntToFixed(15 - clz) + ((unsigned)(sx << (clz + 1)) >> 16);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001041}
1042
1043///////////////////////////////////////////////////////////////////////////////
1044
1045static void GetBitmapAlpha(const SkBitmap& src, uint8_t SK_RESTRICT alpha[],
1046 int alphaRowBytes) {
1047 SkASSERT(alpha != NULL);
1048 SkASSERT(alphaRowBytes >= src.width());
1049
1050 SkBitmap::Config config = src.getConfig();
1051 int w = src.width();
1052 int h = src.height();
1053 int rb = src.rowBytes();
1054
1055 if (SkBitmap::kA8_Config == config && !src.isOpaque()) {
1056 const uint8_t* s = src.getAddr8(0, 0);
1057 while (--h >= 0) {
1058 memcpy(alpha, s, w);
1059 s += rb;
1060 alpha += alphaRowBytes;
1061 }
1062 } else if (SkBitmap::kARGB_8888_Config == config && !src.isOpaque()) {
1063 const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0);
1064 while (--h >= 0) {
1065 for (int x = 0; x < w; x++) {
1066 alpha[x] = SkGetPackedA32(s[x]);
1067 }
1068 s = (const SkPMColor*)((const char*)s + rb);
1069 alpha += alphaRowBytes;
1070 }
1071 } else if (SkBitmap::kARGB_4444_Config == config && !src.isOpaque()) {
1072 const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0);
1073 while (--h >= 0) {
1074 for (int x = 0; x < w; x++) {
1075 alpha[x] = SkPacked4444ToA32(s[x]);
1076 }
1077 s = (const SkPMColor16*)((const char*)s + rb);
1078 alpha += alphaRowBytes;
1079 }
1080 } else if (SkBitmap::kIndex8_Config == config && !src.isOpaque()) {
1081 SkColorTable* ct = src.getColorTable();
1082 if (ct) {
1083 const SkPMColor* SK_RESTRICT table = ct->lockColors();
1084 const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0);
1085 while (--h >= 0) {
1086 for (int x = 0; x < w; x++) {
1087 alpha[x] = SkGetPackedA32(table[s[x]]);
1088 }
1089 s += rb;
1090 alpha += alphaRowBytes;
1091 }
1092 ct->unlockColors(false);
1093 }
1094 } else { // src is opaque, so just fill alpha[] with 0xFF
1095 memset(alpha, 0xFF, h * alphaRowBytes);
1096 }
1097}
1098
1099#include "SkPaint.h"
1100#include "SkMaskFilter.h"
1101#include "SkMatrix.h"
1102
1103void SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
1104 SkIPoint* offset) const {
1105 SkDEBUGCODE(this->validate();)
1106
1107 SkMatrix identity;
1108 SkMask srcM, dstM;
1109
1110 srcM.fBounds.set(0, 0, this->width(), this->height());
1111 srcM.fRowBytes = SkAlign4(this->width());
1112 srcM.fFormat = SkMask::kA8_Format;
1113
1114 SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL;
1115
1116 // compute our (larger?) dst bounds if we have a filter
1117 if (NULL != filter) {
1118 identity.reset();
1119 srcM.fImage = NULL;
1120 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1121 goto NO_FILTER_CASE;
1122 }
1123 dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
1124 } else {
1125 NO_FILTER_CASE:
1126 dst->setConfig(SkBitmap::kA8_Config, this->width(), this->height(),
1127 srcM.fRowBytes);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001128 dst->allocPixels();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001129 GetBitmapAlpha(*this, dst->getAddr8(0, 0), srcM.fRowBytes);
1130 if (offset) {
1131 offset->set(0, 0);
1132 }
1133 return;
1134 }
1135
1136 SkAutoMaskImage srcCleanup(&srcM, true);
1137
1138 GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
1139 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1140 goto NO_FILTER_CASE;
1141 }
1142
1143 SkAutoMaskImage dstCleanup(&dstM, false);
1144
1145 dst->setConfig(SkBitmap::kA8_Config, dstM.fBounds.width(),
1146 dstM.fBounds.height(), dstM.fRowBytes);
1147 dst->allocPixels();
1148 memcpy(dst->getPixels(), dstM.fImage, dstM.computeImageSize());
1149 if (offset) {
1150 offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
1151 }
1152 SkDEBUGCODE(dst->validate();)
1153}
1154
1155///////////////////////////////////////////////////////////////////////////////
1156
1157enum {
1158 SERIALIZE_PIXELTYPE_NONE,
1159 SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE,
1160 SERIALIZE_PIXELTYPE_RAW_NO_CTABLE,
1161 SERIALIZE_PIXELTYPE_REF_DATA,
1162 SERIALIZE_PIXELTYPE_REF_PTR,
1163};
1164
1165static void writeString(SkFlattenableWriteBuffer& buffer, const char str[]) {
1166 size_t len = strlen(str);
1167 buffer.write32(len);
1168 buffer.writePad(str, len);
1169}
1170
1171static SkPixelRef::Factory deserialize_factory(SkFlattenableReadBuffer& buffer) {
1172 size_t len = buffer.readInt();
1173 SkAutoSMalloc<256> storage(len + 1);
1174 char* str = (char*)storage.get();
1175 buffer.read(str, len);
1176 str[len] = 0;
1177 return SkPixelRef::NameToFactory(str);
1178}
1179
1180/*
1181 It is tricky to know how much to flatten. If we don't have a pixelref (i.e.
1182 we just have pixels, then we can only flatten the pixels, or write out an
1183 empty bitmap.
weita@google.comf9ab99a2009-05-03 18:23:30 +00001184
reed@android.com8a1c16f2008-12-17 15:59:43 +00001185 With a pixelref, we still have the question of recognizing when two sitings
1186 of the same pixelref are the same, and when they are different. Perhaps we
1187 should look at the generationID and keep a record of that in some dictionary
1188 associated with the buffer. SkGLTextureCache does this sort of thing to know
1189 when to create a new texture.
1190*/
1191void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
1192 buffer.write32(fWidth);
1193 buffer.write32(fHeight);
1194 buffer.write32(fRowBytes);
1195 buffer.write8(fConfig);
1196 buffer.writeBool(this->isOpaque());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001197
reed@android.com8a1c16f2008-12-17 15:59:43 +00001198 /* If we are called in this mode, then it is up to the caller to manage
1199 the owner-counts on the pixelref, as we just record the ptr itself.
1200 */
1201 if (!buffer.persistBitmapPixels()) {
1202 if (fPixelRef) {
1203 buffer.write8(SERIALIZE_PIXELTYPE_REF_PTR);
1204 buffer.write32(fPixelRefOffset);
1205 buffer.writeRefCnt(fPixelRef);
1206 return;
1207 } else {
1208 // we ignore the non-persist request, since we don't have a ref
1209 // ... or we could just write an empty bitmap...
1210 // (true) will write an empty bitmap, (false) will flatten the pix
1211 if (true) {
1212 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1213 return;
1214 }
1215 }
1216 }
1217
1218 if (fPixelRef) {
1219 SkPixelRef::Factory fact = fPixelRef->getFactory();
1220 if (fact) {
1221 const char* name = SkPixelRef::FactoryToName(fact);
1222 if (name && *name) {
1223 buffer.write8(SERIALIZE_PIXELTYPE_REF_DATA);
1224 buffer.write32(fPixelRefOffset);
1225 writeString(buffer, name);
1226 fPixelRef->flatten(buffer);
1227 return;
1228 }
1229 }
1230 // if we get here, we can't record the pixels
1231 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1232 } else if (fPixels) {
1233 if (fColorTable) {
1234 buffer.write8(SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE);
1235 fColorTable->flatten(buffer);
1236 } else {
1237 buffer.write8(SERIALIZE_PIXELTYPE_RAW_NO_CTABLE);
1238 }
1239 buffer.writePad(fPixels, this->getSize());
1240 } else {
1241 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1242 }
1243}
1244
1245void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) {
1246 this->reset();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001247
reed@android.com8a1c16f2008-12-17 15:59:43 +00001248 int width = buffer.readInt();
1249 int height = buffer.readInt();
1250 int rowBytes = buffer.readInt();
1251 int config = buffer.readU8();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001252
reed@android.com8a1c16f2008-12-17 15:59:43 +00001253 this->setConfig((Config)config, width, height, rowBytes);
1254 this->setIsOpaque(buffer.readBool());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001255
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256 size_t size = this->getSize();
1257 int reftype = buffer.readU8();
1258 switch (reftype) {
1259 case SERIALIZE_PIXELTYPE_REF_PTR: {
1260 size_t offset = buffer.readU32();
1261 SkPixelRef* pr = (SkPixelRef*)buffer.readRefCnt();
1262 this->setPixelRef(pr, offset);
1263 break;
1264 }
1265 case SERIALIZE_PIXELTYPE_REF_DATA: {
1266 size_t offset = buffer.readU32();
1267 SkPixelRef::Factory fact = deserialize_factory(buffer);
1268 SkPixelRef* pr = fact(buffer);
1269 this->setPixelRef(pr, offset)->safeUnref();
1270 break;
1271 }
1272 case SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE:
1273 case SERIALIZE_PIXELTYPE_RAW_NO_CTABLE: {
1274 SkColorTable* ctable = NULL;
1275 if (SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE == reftype) {
1276 ctable = SkNEW_ARGS(SkColorTable, (buffer));
1277 }
1278 if (this->allocPixels(ctable)) {
1279 this->lockPixels();
1280 buffer.read(this->getPixels(), size);
1281 this->unlockPixels();
1282 } else {
1283 buffer.skip(size);
1284 }
reed@android.com149e2f62009-05-22 14:39:03 +00001285 SkSafeUnref(ctable);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001286 break;
1287 }
1288 case SERIALIZE_PIXELTYPE_NONE:
1289 break;
1290 default:
1291 SkASSERT(!"unrecognized pixeltype in serialized data");
1292 sk_throw();
1293 }
1294}
1295
1296///////////////////////////////////////////////////////////////////////////////
1297
1298SkBitmap::RLEPixels::RLEPixels(int width, int height) {
1299 fHeight = height;
1300 fYPtrs = (uint8_t**)sk_malloc_throw(height * sizeof(uint8_t*));
reed@android.com4516f472009-06-29 16:25:36 +00001301 sk_bzero(fYPtrs, height * sizeof(uint8_t*));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302}
1303
1304SkBitmap::RLEPixels::~RLEPixels() {
1305 sk_free(fYPtrs);
1306}
1307
1308///////////////////////////////////////////////////////////////////////////////
1309
1310#ifdef SK_DEBUG
1311void SkBitmap::validate() const {
1312 SkASSERT(fConfig < kConfigCount);
1313 SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
1314 SkASSERT(fFlags <= kImageIsOpaque_Flag);
1315 SkASSERT(fPixelLockCount >= 0);
1316 SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000);
1317 SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
1318
1319#if 0 // these asserts are not thread-correct, so disable for now
1320 if (fPixelRef) {
1321 if (fPixelLockCount > 0) {
1322 SkASSERT(fPixelRef->getLockCount() > 0);
1323 } else {
1324 SkASSERT(NULL == fPixels);
1325 SkASSERT(NULL == fColorTable);
1326 }
1327 }
1328#endif
1329}
1330#endif
1331