blob: 3debd81dc76013611d55558b6ecebefc3bcd8671 [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
29#ifdef SK_SUPPORT_MIPMAP
30struct MipLevel {
31 void* fPixels;
32 uint32_t fRowBytes;
reed@android.comf459a492009-03-27 12:33:50 +000033 uint32_t fWidth, fHeight;
reed@android.com8a1c16f2008-12-17 15:59:43 +000034};
35
36struct SkBitmap::MipMap : SkNoncopyable {
37 int32_t fRefCnt;
38 int fLevelCount;
39// MipLevel fLevel[fLevelCount];
40// Pixels[]
weita@google.comf9ab99a2009-05-03 18:23:30 +000041
reed@android.com8a1c16f2008-12-17 15:59:43 +000042 static MipMap* Alloc(int levelCount, size_t pixelSize) {
43 MipMap* mm = (MipMap*)sk_malloc_throw(sizeof(MipMap) +
44 levelCount * sizeof(MipLevel) +
45 pixelSize);
46 mm->fRefCnt = 1;
47 mm->fLevelCount = levelCount;
48 return mm;
49 }
50
51 const MipLevel* levels() const { return (const MipLevel*)(this + 1); }
52 MipLevel* levels() { return (MipLevel*)(this + 1); }
53
54 const void* pixels() const { return levels() + fLevelCount; }
55 void* pixels() { return levels() + fLevelCount; }
weita@google.comf9ab99a2009-05-03 18:23:30 +000056
reed@android.com8a1c16f2008-12-17 15:59:43 +000057 void safeRef() {
58 if (this) {
59 SkASSERT(fRefCnt > 0);
60 sk_atomic_inc(&fRefCnt);
61 }
62 }
63 void safeUnref() {
64 if (this) {
65 SkASSERT(fRefCnt > 0);
66 if (sk_atomic_dec(&fRefCnt) == 1) {
67 sk_free(this);
68 }
69 }
70 }
71};
72#endif
73
74///////////////////////////////////////////////////////////////////////////////
75///////////////////////////////////////////////////////////////////////////////
76
77SkBitmap::SkBitmap() {
78 bzero(this, sizeof(*this));
79}
80
81SkBitmap::SkBitmap(const SkBitmap& src) {
82 SkDEBUGCODE(src.validate();)
83 bzero(this, sizeof(*this));
84 *this = src;
85 SkDEBUGCODE(this->validate();)
86}
87
88SkBitmap::~SkBitmap() {
89 SkDEBUGCODE(this->validate();)
90 this->freePixels();
91}
92
93SkBitmap& SkBitmap::operator=(const SkBitmap& src) {
94 if (this != &src) {
95 this->freePixels();
96 memcpy(this, &src, sizeof(src));
97
98 // inc src reference counts
99 src.fPixelRef->safeRef();
100#ifdef SK_SUPPORT_MIPMAP
101 src.fMipMap->safeRef();
102#endif
103
104 // we reset our locks if we get blown away
105 fPixelLockCount = 0;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000106
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107 /* The src could be in 3 states
108 1. no pixelref, in which case we just copy/ref the pixels/ctable
109 2. unlocked pixelref, pixels/ctable should be null
110 3. locked pixelref, we should lock the ref again ourselves
111 */
112 if (NULL == fPixelRef) {
113 // leave fPixels as it is
114 fColorTable->safeRef(); // ref the user's ctable if present
115 } else { // we have a pixelref, so pixels/ctable reflect it
116 // ignore the values from the memcpy
117 fPixels = NULL;
118 fColorTable = NULL;
119 }
120 }
121
122 SkDEBUGCODE(this->validate();)
123 return *this;
124}
125
126void SkBitmap::swap(SkBitmap& other) {
127 SkTSwap<SkColorTable*>(fColorTable, other.fColorTable);
128 SkTSwap<SkPixelRef*>(fPixelRef, other.fPixelRef);
129 SkTSwap<size_t>(fPixelRefOffset, other.fPixelRefOffset);
130 SkTSwap<int>(fPixelLockCount, other.fPixelLockCount);
131#ifdef SK_SUPPORT_MIPMAP
132 SkTSwap<MipMap*>(fMipMap, other.fMipMap);
133#endif
134 SkTSwap<void*>(fPixels, other.fPixels);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000135 SkTSwap<uint32_t>(fRowBytes, other.fRowBytes);
reed@android.comf459a492009-03-27 12:33:50 +0000136 SkTSwap<uint32_t>(fWidth, other.fWidth);
137 SkTSwap<uint32_t>(fHeight, other.fHeight);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000138 SkTSwap<uint8_t>(fConfig, other.fConfig);
139 SkTSwap<uint8_t>(fFlags, other.fFlags);
140 SkTSwap<uint8_t>(fBytesPerPixel, other.fBytesPerPixel);
141
142 SkDEBUGCODE(this->validate();)
143}
144
145void SkBitmap::reset() {
146 this->freePixels();
147 bzero(this, sizeof(*this));
148}
149
150int SkBitmap::ComputeBytesPerPixel(SkBitmap::Config config) {
151 int bpp;
152 switch (config) {
153 case kNo_Config:
154 case kA1_Config:
155 bpp = 0; // not applicable
156 break;
157 case kRLE_Index8_Config:
158 case kA8_Config:
159 case kIndex8_Config:
160 bpp = 1;
161 break;
162 case kRGB_565_Config:
163 case kARGB_4444_Config:
164 bpp = 2;
165 break;
166 case kARGB_8888_Config:
167 bpp = 4;
168 break;
169 default:
170 SkASSERT(!"unknown config");
171 bpp = 0; // error
172 break;
173 }
174 return bpp;
175}
176
177int SkBitmap::ComputeRowBytes(Config c, int width) {
178 int rowBytes = 0;
179
180 switch (c) {
181 case kNo_Config:
182 case kRLE_Index8_Config:
183 // assume that the bitmap has no pixels to draw to
184 rowBytes = 0;
185 break;
186 case kA1_Config:
187 rowBytes = (width + 7) >> 3;
188 break;
189 case kA8_Config:
190 case kIndex8_Config:
191 rowBytes = width;
192 break;
193 case kRGB_565_Config:
194 case kARGB_4444_Config:
195 rowBytes = width << 1;
196 break;
197 case kARGB_8888_Config:
198 rowBytes = width << 2;
199 break;
200 default:
201 SkASSERT(!"unknown config");
202 break;
203 }
204 return rowBytes;
205}
206
207Sk64 SkBitmap::ComputeSize64(Config c, int width, int height) {
208 Sk64 size;
209 size.setMul(SkBitmap::ComputeRowBytes(c, width), height);
210 return size;
211}
212
213size_t SkBitmap::ComputeSize(Config c, int width, int height) {
214 Sk64 size = SkBitmap::ComputeSize64(c, width, height);
215 if (size.isNeg() || !size.is32()) {
216 return 0;
217 }
218 return size.get32();
219}
220
221void SkBitmap::setConfig(Config c, int width, int height, int rowBytes) {
222 this->freePixels();
223
224 if (rowBytes == 0) {
225 rowBytes = SkBitmap::ComputeRowBytes(c, width);
226 }
227 fConfig = SkToU8(c);
reed@android.comf459a492009-03-27 12:33:50 +0000228 fWidth = width;
229 fHeight = height;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230 fRowBytes = rowBytes;
231
232 fBytesPerPixel = (uint8_t)ComputeBytesPerPixel(c);
233
234 SkDEBUGCODE(this->validate();)
235}
236
237void SkBitmap::updatePixelsFromRef() const {
238 if (NULL != fPixelRef) {
239 if (fPixelLockCount > 0) {
240 SkASSERT(fPixelRef->getLockCount() > 0);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000241
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 void* p = fPixelRef->pixels();
243 if (NULL != p) {
244 p = (char*)p + fPixelRefOffset;
245 }
246 fPixels = p;
247 SkRefCnt_SafeAssign(fColorTable, fPixelRef->colorTable());
248 } else {
249 SkASSERT(0 == fPixelLockCount);
250 fPixels = NULL;
251 fColorTable->safeUnref();
252 fColorTable = NULL;
253 }
254 }
255}
256
257SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, size_t offset) {
258 // do this first, we that we never have a non-zero offset with a null ref
259 if (NULL == pr) {
260 offset = 0;
261 }
262
263 if (fPixelRef != pr || fPixelRefOffset != offset) {
264 if (fPixelRef != pr) {
265 this->freePixels();
266 SkASSERT(NULL == fPixelRef);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000267
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268 pr->safeRef();
269 fPixelRef = pr;
270 }
271 fPixelRefOffset = offset;
272 this->updatePixelsFromRef();
273 }
274
275 SkDEBUGCODE(this->validate();)
276 return pr;
277}
278
279void SkBitmap::lockPixels() const {
280 if (NULL != fPixelRef && 1 == ++fPixelLockCount) {
281 fPixelRef->lockPixels();
282 this->updatePixelsFromRef();
283 }
284 SkDEBUGCODE(this->validate();)
285}
286
287void SkBitmap::unlockPixels() const {
288 SkASSERT(NULL == fPixelRef || fPixelLockCount > 0);
289
290 if (NULL != fPixelRef && 0 == --fPixelLockCount) {
291 fPixelRef->unlockPixels();
292 this->updatePixelsFromRef();
293 }
294 SkDEBUGCODE(this->validate();)
295}
296
297void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
298 this->freePixels();
299 fPixels = p;
300 SkRefCnt_SafeAssign(fColorTable, ctable);
301
302 SkDEBUGCODE(this->validate();)
303}
304
305bool SkBitmap::allocPixels(Allocator* allocator, SkColorTable* ctable) {
306 HeapAllocator stdalloc;
307
308 if (NULL == allocator) {
309 allocator = &stdalloc;
310 }
311 return allocator->allocPixelRef(this, ctable);
312}
313
314void SkBitmap::freePixels() {
315 // if we're gonna free the pixels, we certainly need to free the mipmap
316 this->freeMipMap();
317
318 fColorTable->safeUnref();
319 fColorTable = NULL;
320
321 if (NULL != fPixelRef) {
322 if (fPixelLockCount > 0) {
323 fPixelRef->unlockPixels();
324 }
325 fPixelRef->unref();
326 fPixelRef = NULL;
327 fPixelRefOffset = 0;
328 }
329 fPixelLockCount = 0;
330 fPixels = NULL;
331}
332
333void SkBitmap::freeMipMap() {
334#ifdef SK_SUPPORT_MIPMAP
335 fMipMap->safeUnref();
336 fMipMap = NULL;
337#endif
338}
339
340uint32_t SkBitmap::getGenerationID() const {
341 return fPixelRef ? fPixelRef->getGenerationID() : 0;
342}
343
344void SkBitmap::notifyPixelsChanged() const {
345 if (fPixelRef) {
346 fPixelRef->notifyPixelsChanged();
347 }
348}
349
350///////////////////////////////////////////////////////////////////////////////
351
352SkMallocPixelRef::SkMallocPixelRef(void* storage, size_t size,
353 SkColorTable* ctable) {
354 SkASSERT(storage);
355 fStorage = storage;
356 fSize = size;
357 fCTable = ctable;
358 ctable->safeRef();
359}
360
361SkMallocPixelRef::~SkMallocPixelRef() {
362 fCTable->safeUnref();
363 sk_free(fStorage);
364}
365
366void* SkMallocPixelRef::onLockPixels(SkColorTable** ct) {
367 *ct = fCTable;
368 return fStorage;
369}
370
371void SkMallocPixelRef::onUnlockPixels() {
372 // nothing to do
373}
374
375void SkMallocPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const {
376 this->INHERITED::flatten(buffer);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000377
reed@android.com8a1c16f2008-12-17 15:59:43 +0000378 buffer.write32(fSize);
379 buffer.writePad(fStorage, fSize);
380 if (fCTable) {
381 buffer.writeBool(true);
382 fCTable->flatten(buffer);
383 } else {
384 buffer.writeBool(false);
385 }
386}
387
388SkMallocPixelRef::SkMallocPixelRef(SkFlattenableReadBuffer& buffer) : INHERITED(buffer, NULL) {
389 fSize = buffer.readU32();
390 fStorage = sk_malloc_throw(fSize);
391 buffer.read(fStorage, fSize);
392 if (buffer.readBool()) {
393 fCTable = SkNEW_ARGS(SkColorTable, (buffer));
394 } else {
395 fCTable = NULL;
396 }
397}
398
399static SkPixelRef::Registrar reg("SkMallocPixelRef",
400 SkMallocPixelRef::Create);
401
402/** We explicitly use the same allocator for our pixels that SkMask does,
403 so that we can freely assign memory allocated by one class to the other.
404 */
405bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
406 SkColorTable* ctable) {
407 Sk64 size = dst->getSize64();
408 if (size.isNeg() || !size.is32()) {
409 return false;
410 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000411
reed@android.com8a1c16f2008-12-17 15:59:43 +0000412 void* addr = sk_malloc_flags(size.get32(), 0); // returns NULL on failure
413 if (NULL == addr) {
414 return false;
415 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000416
reed@android.com8a1c16f2008-12-17 15:59:43 +0000417 dst->setPixelRef(new SkMallocPixelRef(addr, size.get32(), ctable))->unref();
418 // since we're already allocated, we lockPixels right away
419 dst->lockPixels();
420 return true;
421}
422
423///////////////////////////////////////////////////////////////////////////////
424
425bool SkBitmap::isOpaque() const {
426 switch (fConfig) {
427 case kNo_Config:
428 return true;
429
430 case kA1_Config:
431 case kA8_Config:
432 case kARGB_4444_Config:
433 case kARGB_8888_Config:
434 return (fFlags & kImageIsOpaque_Flag) != 0;
435
436 case kIndex8_Config:
437 case kRLE_Index8_Config: {
438 uint32_t flags = 0;
439
440 this->lockPixels();
441 // if lockPixels failed, we may not have a ctable ptr
442 if (fColorTable) {
443 flags = fColorTable->getFlags();
444 }
445 this->unlockPixels();
446
447 return (flags & SkColorTable::kColorsAreOpaque_Flag) != 0;
448 }
449
450 case kRGB_565_Config:
451 return true;
452
453 default:
454 SkASSERT(!"unknown bitmap config pased to isOpaque");
455 return false;
456 }
457}
458
459void SkBitmap::setIsOpaque(bool isOpaque) {
460 /* we record this regardless of fConfig, though it is ignored in
461 isOpaque() for configs that can't support per-pixel alpha.
462 */
463 if (isOpaque) {
464 fFlags |= kImageIsOpaque_Flag;
465 } else {
466 fFlags &= ~kImageIsOpaque_Flag;
467 }
468}
469
470void* SkBitmap::getAddr(int x, int y) const {
471 SkASSERT((unsigned)x < (unsigned)this->width());
472 SkASSERT((unsigned)y < (unsigned)this->height());
473
474 char* base = (char*)this->getPixels();
475 if (base) {
476 base += y * this->rowBytes();
477 switch (this->config()) {
478 case SkBitmap::kARGB_8888_Config:
479 base += x << 2;
480 break;
481 case SkBitmap::kARGB_4444_Config:
482 case SkBitmap::kRGB_565_Config:
483 base += x << 1;
484 break;
485 case SkBitmap::kA8_Config:
486 case SkBitmap::kIndex8_Config:
487 base += x;
488 break;
489 case SkBitmap::kA1_Config:
490 base += x >> 3;
491 break;
492 case kRLE_Index8_Config:
493 SkASSERT(!"Can't return addr for kRLE_Index8_Config");
494 base = NULL;
495 break;
496 default:
497 SkASSERT(!"Can't return addr for config");
498 base = NULL;
499 break;
500 }
501 }
502 return base;
503}
504
505///////////////////////////////////////////////////////////////////////////////
506///////////////////////////////////////////////////////////////////////////////
507
508void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
509 SkDEBUGCODE(this->validate();)
510
511 if (0 == fWidth || 0 == fHeight ||
512 kNo_Config == fConfig || kIndex8_Config == fConfig) {
513 return;
514 }
515
516 SkAutoLockPixels alp(*this);
517 // perform this check after the lock call
518 if (!this->readyToDraw()) {
519 return;
520 }
521
522 int height = fHeight;
523 const int width = fWidth;
524 const int rowBytes = fRowBytes;
525
526 // make rgb premultiplied
527 if (255 != a) {
528 r = SkAlphaMul(r, a);
529 g = SkAlphaMul(g, a);
530 b = SkAlphaMul(b, a);
531 }
532
533 switch (fConfig) {
534 case kA1_Config: {
535 uint8_t* p = (uint8_t*)fPixels;
536 const int count = (width + 7) >> 3;
537 a = (a >> 7) ? 0xFF : 0;
538 SkASSERT(count <= rowBytes);
539 while (--height >= 0) {
540 memset(p, a, count);
541 p += rowBytes;
542 }
543 break;
544 }
545 case kA8_Config: {
546 uint8_t* p = (uint8_t*)fPixels;
547 while (--height >= 0) {
548 memset(p, a, width);
549 p += rowBytes;
550 }
551 break;
552 }
553 case kARGB_4444_Config:
554 case kRGB_565_Config: {
555 uint16_t* p = (uint16_t*)fPixels;
556 uint16_t v;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000557
reed@android.com8a1c16f2008-12-17 15:59:43 +0000558 if (kARGB_4444_Config == fConfig) {
559 v = SkPackARGB4444(a >> 4, r >> 4, g >> 4, b >> 4);
560 } else { // kRGB_565_Config
561 v = SkPackRGB16(r >> (8 - SK_R16_BITS), g >> (8 - SK_G16_BITS),
562 b >> (8 - SK_B16_BITS));
563 }
564 while (--height >= 0) {
565 sk_memset16(p, v, width);
566 p = (uint16_t*)((char*)p + rowBytes);
567 }
568 break;
569 }
570 case kARGB_8888_Config: {
571 uint32_t* p = (uint32_t*)fPixels;
572 uint32_t v = SkPackARGB32(a, r, g, b);
573
574 while (--height >= 0) {
575 sk_memset32(p, v, width);
576 p = (uint32_t*)((char*)p + rowBytes);
577 }
578 break;
579 }
580 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000581
reed@android.com8a1c16f2008-12-17 15:59:43 +0000582 this->notifyPixelsChanged();
583}
584
585//////////////////////////////////////////////////////////////////////////////////////
586//////////////////////////////////////////////////////////////////////////////////////
587
588#define SUB_OFFSET_FAILURE ((size_t)-1)
589
590static size_t getSubOffset(const SkBitmap& bm, int x, int y) {
591 SkASSERT((unsigned)x < (unsigned)bm.width());
592 SkASSERT((unsigned)y < (unsigned)bm.height());
weita@google.comf9ab99a2009-05-03 18:23:30 +0000593
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594 switch (bm.getConfig()) {
595 case SkBitmap::kA8_Config:
596 case SkBitmap:: kIndex8_Config:
597 // x is fine as is for the calculation
598 break;
599
600 case SkBitmap::kRGB_565_Config:
601 case SkBitmap::kARGB_4444_Config:
602 x <<= 1;
603 break;
604
605 case SkBitmap::kARGB_8888_Config:
606 x <<= 2;
607 break;
608
609 case SkBitmap::kNo_Config:
610 case SkBitmap::kA1_Config:
611 default:
612 return SUB_OFFSET_FAILURE;
613 }
614 return y * bm.rowBytes() + x;
615}
616
617bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
618 SkDEBUGCODE(this->validate();)
619
620 if (NULL == result || (NULL == fPixelRef && NULL == fPixels)) {
621 return false; // no src pixels
622 }
623
624 SkIRect srcRect, r;
625 srcRect.set(0, 0, this->width(), this->height());
626 if (!r.intersect(srcRect, subset)) {
627 return false; // r is empty (i.e. no intersection)
628 }
629
630 if (kRLE_Index8_Config == fConfig) {
631 SkAutoLockPixels alp(*this);
632 // don't call readyToDraw(), since we can operate w/o a colortable
633 // at this stage
634 if (this->getPixels() == NULL) {
635 return false;
636 }
637 SkBitmap bm;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000638
reed@android.com8a1c16f2008-12-17 15:59:43 +0000639 bm.setConfig(kIndex8_Config, r.width(), r.height());
640 bm.allocPixels(this->getColorTable());
641 if (NULL == bm.getPixels()) {
642 return false;
643 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000644
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645 const RLEPixels* rle = (const RLEPixels*)this->getPixels();
646 uint8_t* dst = bm.getAddr8(0, 0);
647 const int width = bm.width();
648 const int rowBytes = bm.rowBytes();
weita@google.comf9ab99a2009-05-03 18:23:30 +0000649
reed@android.com8a1c16f2008-12-17 15:59:43 +0000650 for (int y = r.fTop; y < r.fBottom; y++) {
651 SkPackBits::Unpack8(dst, r.fLeft, width, rle->packedAtY(y));
652 dst += rowBytes;
653 }
654 result->swap(bm);
655 return true;
656 }
657
658 size_t offset = getSubOffset(*this, r.fLeft, r.fTop);
659 if (SUB_OFFSET_FAILURE == offset) {
660 return false; // config not supported
661 }
662
663 SkBitmap dst;
664 dst.setConfig(this->config(), r.width(), r.height(), this->rowBytes());
665
666 if (fPixelRef) {
667 // share the pixelref with a custom offset
668 dst.setPixelRef(fPixelRef, fPixelRefOffset + offset);
669 } else {
670 // share the pixels (owned by the caller)
671 dst.setPixels((char*)fPixels + offset, this->getColorTable());
672 }
673 SkDEBUGCODE(dst.validate();)
674
675 // we know we're good, so commit to result
676 result->swap(dst);
677 return true;
678}
679
680///////////////////////////////////////////////////////////////////////////////
681
682#include "SkCanvas.h"
683#include "SkPaint.h"
684
685bool SkBitmap::copyTo(SkBitmap* dst, Config dstConfig, Allocator* alloc) const {
weita@google.comf9ab99a2009-05-03 18:23:30 +0000686 if (NULL == dst || this->getConfig() == kNo_Config
687 || this->width() == 0 || this->height() == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000688 return false;
689 }
690
weita@google.comf9ab99a2009-05-03 18:23:30 +0000691 bool sameConfigs = (dstConfig == this->config());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000692 switch (dstConfig) {
693 case kA8_Config:
694 case kARGB_4444_Config:
695 case kRGB_565_Config:
696 case kARGB_8888_Config:
697 break;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000698 case kA1_Config:
699 case kIndex8_Config:
700 if (!sameConfigs) {
701 return false;
702 }
703 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000704 default:
705 return false;
706 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000707
708 // do not copy src if srcConfig == kA1_Config while dstConfig != kA1_Config
709 if (this->getConfig() == kA1_Config && !sameConfigs) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000710 return false;
711 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000712
713 SkBitmap tmp;
714 tmp.setConfig(dstConfig, this->width(), this->height());
715
716 // allocate colortable if srcConfig == kIndex8_Config
717 SkColorTable* ctable = (dstConfig == kIndex8_Config) ?
718 new SkColorTable(*this->getColorTable()) : NULL;
719 SkAutoUnref au(ctable);
720 if (!tmp.allocPixels(alloc, ctable)) {
721 return false;
722 }
723
reed@android.com8a1c16f2008-12-17 15:59:43 +0000724 SkAutoLockPixels srclock(*this);
725 SkAutoLockPixels dstlock(tmp);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000726
reed@android.com8a1c16f2008-12-17 15:59:43 +0000727 if (!this->readyToDraw() || !tmp.readyToDraw()) {
728 // allocator/lock failed
729 return false;
730 }
731
weita@google.comf9ab99a2009-05-03 18:23:30 +0000732 /* do memcpy for the sameConfigs cases and
733 re-draw for the !sameConfigs cases
734 */
735 if (sameConfigs) {
736 memcpy(tmp.getPixels(), this->getPixels(), this->getSize());
737 } else {
738 // if the src has alpha, we have to clear the dst first
739 if (!this->isOpaque()) {
740 tmp.eraseColor(0);
741 }
742
743 SkCanvas canvas(tmp);
744 SkPaint paint;
745
746 paint.setDither(true);
747 canvas.drawBitmap(*this, 0, 0, &paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000748 }
749
reed@android.com8a1c16f2008-12-17 15:59:43 +0000750 dst->swap(tmp);
751 return true;
752}
753
754///////////////////////////////////////////////////////////////////////////////
755///////////////////////////////////////////////////////////////////////////////
756
757static void downsampleby2_proc32(SkBitmap* dst, int x, int y,
758 const SkBitmap& src) {
759 x <<= 1;
760 y <<= 1;
761 const SkPMColor* p = src.getAddr32(x, y);
762 SkPMColor c, ag, rb;
763
764 c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF;
765 if (x < src.width() - 1) {
766 p += 1;
767 }
768 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
769
770 if (y < src.height() - 1) {
771 p = src.getAddr32(x, y + 1);
772 }
773 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
774 if (x < src.width() - 1) {
775 p += 1;
776 }
777 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
778
779 *dst->getAddr32(x >> 1, y >> 1) =
780 ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00);
781}
782
783static inline uint32_t expand16(U16CPU c) {
784 return (c & ~SK_G16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16);
785}
786
787// returns dirt in the top 16bits, but we don't care, since we only
788// store the low 16bits.
789static inline U16CPU pack16(uint32_t c) {
790 return (c & ~SK_G16_MASK_IN_PLACE) | ((c >> 16) & SK_G16_MASK_IN_PLACE);
791}
792
793static void downsampleby2_proc16(SkBitmap* dst, int x, int y,
794 const SkBitmap& src) {
795 x <<= 1;
796 y <<= 1;
797 const uint16_t* p = src.getAddr16(x, y);
798 SkPMColor c;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000799
reed@android.com8a1c16f2008-12-17 15:59:43 +0000800 c = expand16(*p);
801 if (x < (int)src.width() - 1) {
802 p += 1;
803 }
804 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000805
reed@android.com8a1c16f2008-12-17 15:59:43 +0000806 if (y < (int)src.height() - 1) {
807 p = src.getAddr16(x, y + 1);
808 }
809 c += expand16(*p);
810 if (x < (int)src.width() - 1) {
811 p += 1;
812 }
813 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000814
reed@android.com8a1c16f2008-12-17 15:59:43 +0000815 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)pack16(c >> 2);
816}
817
818static uint32_t expand4444(U16CPU c) {
819 return (c & 0xF0F) | ((c & ~0xF0F) << 12);
820}
821
822static U16CPU collaps4444(uint32_t c) {
823 return (c & 0xF0F) | ((c >> 12) & ~0xF0F);
824}
825
826static void downsampleby2_proc4444(SkBitmap* dst, int x, int y,
827 const SkBitmap& src) {
828 x <<= 1;
829 y <<= 1;
830 const uint16_t* p = src.getAddr16(x, y);
831 uint32_t c;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000832
reed@android.com8a1c16f2008-12-17 15:59:43 +0000833 c = expand4444(*p);
834 if (x < src.width() - 1) {
835 p += 1;
836 }
837 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000838
reed@android.com8a1c16f2008-12-17 15:59:43 +0000839 if (y < src.height() - 1) {
840 p = src.getAddr16(x, y + 1);
841 }
842 c += expand4444(*p);
843 if (x < src.width() - 1) {
844 p += 1;
845 }
846 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000847
reed@android.com8a1c16f2008-12-17 15:59:43 +0000848 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)collaps4444(c >> 2);
849}
850
851void SkBitmap::buildMipMap(bool forceRebuild) {
852#ifdef SK_SUPPORT_MIPMAP
853 if (forceRebuild)
854 this->freeMipMap();
855 else if (fMipMap)
856 return; // we're already built
857
858 SkASSERT(NULL == fMipMap);
859
860 void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src);
861
862 const SkBitmap::Config config = this->getConfig();
863
864 switch (config) {
865 case kARGB_8888_Config:
866 proc = downsampleby2_proc32;
867 break;
868 case kRGB_565_Config:
869 proc = downsampleby2_proc16;
870 break;
871 case kARGB_4444_Config:
872 proc = downsampleby2_proc4444;
873 break;
874 case kIndex8_Config:
875 case kA8_Config:
876 default:
877 return; // don't build mipmaps for these configs
878 }
879
880 // whip through our loop to compute the exact size needed
881 size_t size = 0;
882 int maxLevels = 0;
883 {
884 unsigned width = this->width();
885 unsigned height = this->height();
886 for (;;) {
887 width >>= 1;
888 height >>= 1;
889 if (0 == width || 0 == height) {
890 break;
891 }
892 size += ComputeRowBytes(config, width) * height;
893 maxLevels += 1;
894 }
895 }
896 if (0 == maxLevels) {
897 return;
898 }
899
900 MipMap* mm = MipMap::Alloc(maxLevels, size);
901 MipLevel* level = mm->levels();
902 uint8_t* addr = (uint8_t*)mm->pixels();
903
904 unsigned width = this->width();
905 unsigned height = this->height();
906 unsigned rowBytes = this->rowBytes();
907 SkBitmap srcBM(*this), dstBM;
908
909 srcBM.lockPixels();
910
911 for (int i = 0; i < maxLevels; i++) {
912 width >>= 1;
913 height >>= 1;
914 rowBytes = ComputeRowBytes(config, width);
915
916 level[i].fPixels = addr;
reed@android.comf459a492009-03-27 12:33:50 +0000917 level[i].fWidth = width;
918 level[i].fHeight = height;
reed@android.com49f0ff22009-03-19 21:52:42 +0000919 level[i].fRowBytes = rowBytes;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000920
921 dstBM.setConfig(config, width, height, rowBytes);
922 dstBM.setPixels(addr);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000923
reed@android.com8a1c16f2008-12-17 15:59:43 +0000924 for (unsigned y = 0; y < height; y++) {
925 for (unsigned x = 0; x < width; x++) {
926 proc(&dstBM, x, y, srcBM);
927 }
928 }
929
930 srcBM = dstBM;
931 addr += height * rowBytes;
932 }
933 SkASSERT(addr == (uint8_t*)mm->pixels() + size);
934 fMipMap = mm;
935#endif
936}
937
938bool SkBitmap::hasMipMap() const {
939#ifdef SK_SUPPORT_MIPMAP
940 return fMipMap != NULL;
941#else
942 return false;
943#endif
944}
945
946int SkBitmap::extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy) {
947#ifdef SK_SUPPORT_MIPMAP
948 if (NULL == fMipMap)
949 return 0;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000950
reed@android.com8a1c16f2008-12-17 15:59:43 +0000951 int level = ComputeMipLevel(sx, sy) >> 16;
952 SkASSERT(level >= 0);
953 if (level <= 0) {
954 return 0;
955 }
956
957 if (level >= fMipMap->fLevelCount) {
958 level = fMipMap->fLevelCount - 1;
959 }
960 if (dst) {
961 const MipLevel& mip = fMipMap->levels()[level - 1];
962 dst->setConfig((SkBitmap::Config)this->config(),
963 mip.fWidth, mip.fHeight, mip.fRowBytes);
964 dst->setPixels(mip.fPixels);
965 }
966 return level;
967#else
968 return 0;
969#endif
970}
971
972SkFixed SkBitmap::ComputeMipLevel(SkFixed sx, SkFixed sy) {
973#ifdef SK_SUPPORT_MIPMAP
974 sx = SkAbs32(sx);
975 sy = SkAbs32(sy);
976 if (sx < sy) {
977 sx = sy;
978 }
979 if (sx < SK_Fixed1) {
980 return 0;
981 }
982 int clz = SkCLZ(sx);
983 SkASSERT(clz >= 1 && clz <= 15);
984 return SkIntToFixed(15 - clz) + ((unsigned)(sx << (clz + 1)) >> 16);
985#else
986 return 0;
987#endif
988}
989
990///////////////////////////////////////////////////////////////////////////////
991
992static void GetBitmapAlpha(const SkBitmap& src, uint8_t SK_RESTRICT alpha[],
993 int alphaRowBytes) {
994 SkASSERT(alpha != NULL);
995 SkASSERT(alphaRowBytes >= src.width());
996
997 SkBitmap::Config config = src.getConfig();
998 int w = src.width();
999 int h = src.height();
1000 int rb = src.rowBytes();
1001
1002 if (SkBitmap::kA8_Config == config && !src.isOpaque()) {
1003 const uint8_t* s = src.getAddr8(0, 0);
1004 while (--h >= 0) {
1005 memcpy(alpha, s, w);
1006 s += rb;
1007 alpha += alphaRowBytes;
1008 }
1009 } else if (SkBitmap::kARGB_8888_Config == config && !src.isOpaque()) {
1010 const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0);
1011 while (--h >= 0) {
1012 for (int x = 0; x < w; x++) {
1013 alpha[x] = SkGetPackedA32(s[x]);
1014 }
1015 s = (const SkPMColor*)((const char*)s + rb);
1016 alpha += alphaRowBytes;
1017 }
1018 } else if (SkBitmap::kARGB_4444_Config == config && !src.isOpaque()) {
1019 const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0);
1020 while (--h >= 0) {
1021 for (int x = 0; x < w; x++) {
1022 alpha[x] = SkPacked4444ToA32(s[x]);
1023 }
1024 s = (const SkPMColor16*)((const char*)s + rb);
1025 alpha += alphaRowBytes;
1026 }
1027 } else if (SkBitmap::kIndex8_Config == config && !src.isOpaque()) {
1028 SkColorTable* ct = src.getColorTable();
1029 if (ct) {
1030 const SkPMColor* SK_RESTRICT table = ct->lockColors();
1031 const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0);
1032 while (--h >= 0) {
1033 for (int x = 0; x < w; x++) {
1034 alpha[x] = SkGetPackedA32(table[s[x]]);
1035 }
1036 s += rb;
1037 alpha += alphaRowBytes;
1038 }
1039 ct->unlockColors(false);
1040 }
1041 } else { // src is opaque, so just fill alpha[] with 0xFF
1042 memset(alpha, 0xFF, h * alphaRowBytes);
1043 }
1044}
1045
1046#include "SkPaint.h"
1047#include "SkMaskFilter.h"
1048#include "SkMatrix.h"
1049
1050void SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
1051 SkIPoint* offset) const {
1052 SkDEBUGCODE(this->validate();)
1053
1054 SkMatrix identity;
1055 SkMask srcM, dstM;
1056
1057 srcM.fBounds.set(0, 0, this->width(), this->height());
1058 srcM.fRowBytes = SkAlign4(this->width());
1059 srcM.fFormat = SkMask::kA8_Format;
1060
1061 SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL;
1062
1063 // compute our (larger?) dst bounds if we have a filter
1064 if (NULL != filter) {
1065 identity.reset();
1066 srcM.fImage = NULL;
1067 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1068 goto NO_FILTER_CASE;
1069 }
1070 dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
1071 } else {
1072 NO_FILTER_CASE:
1073 dst->setConfig(SkBitmap::kA8_Config, this->width(), this->height(),
1074 srcM.fRowBytes);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001075 dst->allocPixels();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001076 GetBitmapAlpha(*this, dst->getAddr8(0, 0), srcM.fRowBytes);
1077 if (offset) {
1078 offset->set(0, 0);
1079 }
1080 return;
1081 }
1082
1083 SkAutoMaskImage srcCleanup(&srcM, true);
1084
1085 GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
1086 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1087 goto NO_FILTER_CASE;
1088 }
1089
1090 SkAutoMaskImage dstCleanup(&dstM, false);
1091
1092 dst->setConfig(SkBitmap::kA8_Config, dstM.fBounds.width(),
1093 dstM.fBounds.height(), dstM.fRowBytes);
1094 dst->allocPixels();
1095 memcpy(dst->getPixels(), dstM.fImage, dstM.computeImageSize());
1096 if (offset) {
1097 offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
1098 }
1099 SkDEBUGCODE(dst->validate();)
1100}
1101
1102///////////////////////////////////////////////////////////////////////////////
1103
1104enum {
1105 SERIALIZE_PIXELTYPE_NONE,
1106 SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE,
1107 SERIALIZE_PIXELTYPE_RAW_NO_CTABLE,
1108 SERIALIZE_PIXELTYPE_REF_DATA,
1109 SERIALIZE_PIXELTYPE_REF_PTR,
1110};
1111
1112static void writeString(SkFlattenableWriteBuffer& buffer, const char str[]) {
1113 size_t len = strlen(str);
1114 buffer.write32(len);
1115 buffer.writePad(str, len);
1116}
1117
1118static SkPixelRef::Factory deserialize_factory(SkFlattenableReadBuffer& buffer) {
1119 size_t len = buffer.readInt();
1120 SkAutoSMalloc<256> storage(len + 1);
1121 char* str = (char*)storage.get();
1122 buffer.read(str, len);
1123 str[len] = 0;
1124 return SkPixelRef::NameToFactory(str);
1125}
1126
1127/*
1128 It is tricky to know how much to flatten. If we don't have a pixelref (i.e.
1129 we just have pixels, then we can only flatten the pixels, or write out an
1130 empty bitmap.
weita@google.comf9ab99a2009-05-03 18:23:30 +00001131
reed@android.com8a1c16f2008-12-17 15:59:43 +00001132 With a pixelref, we still have the question of recognizing when two sitings
1133 of the same pixelref are the same, and when they are different. Perhaps we
1134 should look at the generationID and keep a record of that in some dictionary
1135 associated with the buffer. SkGLTextureCache does this sort of thing to know
1136 when to create a new texture.
1137*/
1138void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
1139 buffer.write32(fWidth);
1140 buffer.write32(fHeight);
1141 buffer.write32(fRowBytes);
1142 buffer.write8(fConfig);
1143 buffer.writeBool(this->isOpaque());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001144
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145 /* If we are called in this mode, then it is up to the caller to manage
1146 the owner-counts on the pixelref, as we just record the ptr itself.
1147 */
1148 if (!buffer.persistBitmapPixels()) {
1149 if (fPixelRef) {
1150 buffer.write8(SERIALIZE_PIXELTYPE_REF_PTR);
1151 buffer.write32(fPixelRefOffset);
1152 buffer.writeRefCnt(fPixelRef);
1153 return;
1154 } else {
1155 // we ignore the non-persist request, since we don't have a ref
1156 // ... or we could just write an empty bitmap...
1157 // (true) will write an empty bitmap, (false) will flatten the pix
1158 if (true) {
1159 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1160 return;
1161 }
1162 }
1163 }
1164
1165 if (fPixelRef) {
1166 SkPixelRef::Factory fact = fPixelRef->getFactory();
1167 if (fact) {
1168 const char* name = SkPixelRef::FactoryToName(fact);
1169 if (name && *name) {
1170 buffer.write8(SERIALIZE_PIXELTYPE_REF_DATA);
1171 buffer.write32(fPixelRefOffset);
1172 writeString(buffer, name);
1173 fPixelRef->flatten(buffer);
1174 return;
1175 }
1176 }
1177 // if we get here, we can't record the pixels
1178 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1179 } else if (fPixels) {
1180 if (fColorTable) {
1181 buffer.write8(SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE);
1182 fColorTable->flatten(buffer);
1183 } else {
1184 buffer.write8(SERIALIZE_PIXELTYPE_RAW_NO_CTABLE);
1185 }
1186 buffer.writePad(fPixels, this->getSize());
1187 } else {
1188 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1189 }
1190}
1191
1192void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) {
1193 this->reset();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001194
reed@android.com8a1c16f2008-12-17 15:59:43 +00001195 int width = buffer.readInt();
1196 int height = buffer.readInt();
1197 int rowBytes = buffer.readInt();
1198 int config = buffer.readU8();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001199
reed@android.com8a1c16f2008-12-17 15:59:43 +00001200 this->setConfig((Config)config, width, height, rowBytes);
1201 this->setIsOpaque(buffer.readBool());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001202
reed@android.com8a1c16f2008-12-17 15:59:43 +00001203 size_t size = this->getSize();
1204 int reftype = buffer.readU8();
1205 switch (reftype) {
1206 case SERIALIZE_PIXELTYPE_REF_PTR: {
1207 size_t offset = buffer.readU32();
1208 SkPixelRef* pr = (SkPixelRef*)buffer.readRefCnt();
1209 this->setPixelRef(pr, offset);
1210 break;
1211 }
1212 case SERIALIZE_PIXELTYPE_REF_DATA: {
1213 size_t offset = buffer.readU32();
1214 SkPixelRef::Factory fact = deserialize_factory(buffer);
1215 SkPixelRef* pr = fact(buffer);
1216 this->setPixelRef(pr, offset)->safeUnref();
1217 break;
1218 }
1219 case SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE:
1220 case SERIALIZE_PIXELTYPE_RAW_NO_CTABLE: {
1221 SkColorTable* ctable = NULL;
1222 if (SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE == reftype) {
1223 ctable = SkNEW_ARGS(SkColorTable, (buffer));
1224 }
1225 if (this->allocPixels(ctable)) {
1226 this->lockPixels();
1227 buffer.read(this->getPixels(), size);
1228 this->unlockPixels();
1229 } else {
1230 buffer.skip(size);
1231 }
1232 ctable->safeUnref();
1233 break;
1234 }
1235 case SERIALIZE_PIXELTYPE_NONE:
1236 break;
1237 default:
1238 SkASSERT(!"unrecognized pixeltype in serialized data");
1239 sk_throw();
1240 }
1241}
1242
1243///////////////////////////////////////////////////////////////////////////////
1244
1245SkBitmap::RLEPixels::RLEPixels(int width, int height) {
1246 fHeight = height;
1247 fYPtrs = (uint8_t**)sk_malloc_throw(height * sizeof(uint8_t*));
1248 bzero(fYPtrs, height * sizeof(uint8_t*));
1249}
1250
1251SkBitmap::RLEPixels::~RLEPixels() {
1252 sk_free(fYPtrs);
1253}
1254
1255///////////////////////////////////////////////////////////////////////////////
1256
1257#ifdef SK_DEBUG
1258void SkBitmap::validate() const {
1259 SkASSERT(fConfig < kConfigCount);
1260 SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
1261 SkASSERT(fFlags <= kImageIsOpaque_Flag);
1262 SkASSERT(fPixelLockCount >= 0);
1263 SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000);
1264 SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
1265
1266#if 0 // these asserts are not thread-correct, so disable for now
1267 if (fPixelRef) {
1268 if (fPixelLockCount > 0) {
1269 SkASSERT(fPixelRef->getLockCount() > 0);
1270 } else {
1271 SkASSERT(NULL == fPixels);
1272 SkASSERT(NULL == fColorTable);
1273 }
1274 }
1275#endif
1276}
1277#endif
1278