SkBitmap.cpp revision 848250415eddc54075f7eb8795e8db79e749c6ab
1
2/*
3 * Copyright 2008 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10#include "SkBitmap.h"
11#include "SkColorPriv.h"
12#include "SkDither.h"
13#include "SkFlattenable.h"
14#include "SkImagePriv.h"
15#include "SkMallocPixelRef.h"
16#include "SkMask.h"
17#include "SkReadBuffer.h"
18#include "SkWriteBuffer.h"
19#include "SkPixelRef.h"
20#include "SkThread.h"
21#include "SkUnPreMultiply.h"
22#include "SkUtils.h"
23#include "SkValidationUtils.h"
24#include "SkPackBits.h"
25#include <new>
26
27static bool reset_return_false(SkBitmap* bm) {
28    bm->reset();
29    return false;
30}
31
32SkBitmap::SkBitmap() {
33    sk_bzero(this, sizeof(*this));
34}
35
36SkBitmap::SkBitmap(const SkBitmap& src) {
37    SkDEBUGCODE(src.validate();)
38    sk_bzero(this, sizeof(*this));
39    *this = src;
40    SkDEBUGCODE(this->validate();)
41}
42
43SkBitmap::~SkBitmap() {
44    SkDEBUGCODE(this->validate();)
45    this->freePixels();
46}
47
48SkBitmap& SkBitmap::operator=(const SkBitmap& src) {
49    if (this != &src) {
50        this->freePixels();
51        memcpy(this, &src, sizeof(src));
52
53        // inc src reference counts
54        SkSafeRef(src.fPixelRef);
55
56        // we reset our locks if we get blown away
57        fPixelLockCount = 0;
58
59        if (fPixelRef) {
60            // ignore the values from the memcpy
61            fPixels = NULL;
62            fColorTable = NULL;
63            // Note that what to for genID is somewhat arbitrary. We have no
64            // way to track changes to raw pixels across multiple SkBitmaps.
65            // Would benefit from an SkRawPixelRef type created by
66            // setPixels.
67            // Just leave the memcpy'ed one but they'll get out of sync
68            // as soon either is modified.
69        }
70    }
71
72    SkDEBUGCODE(this->validate();)
73    return *this;
74}
75
76void SkBitmap::swap(SkBitmap& other) {
77    SkTSwap(fColorTable, other.fColorTable);
78    SkTSwap(fPixelRef, other.fPixelRef);
79    SkTSwap(fPixelRefOrigin, other.fPixelRefOrigin);
80    SkTSwap(fPixelLockCount, other.fPixelLockCount);
81    SkTSwap(fPixels, other.fPixels);
82    SkTSwap(fInfo, other.fInfo);
83    SkTSwap(fRowBytes, other.fRowBytes);
84    SkTSwap(fFlags, other.fFlags);
85
86    SkDEBUGCODE(this->validate();)
87}
88
89void SkBitmap::reset() {
90    this->freePixels();
91    sk_bzero(this, sizeof(*this));
92}
93
94void SkBitmap::getBounds(SkRect* bounds) const {
95    SkASSERT(bounds);
96    bounds->set(0, 0,
97                SkIntToScalar(fInfo.fWidth), SkIntToScalar(fInfo.fHeight));
98}
99
100void SkBitmap::getBounds(SkIRect* bounds) const {
101    SkASSERT(bounds);
102    bounds->set(0, 0, fInfo.fWidth, fInfo.fHeight);
103}
104
105///////////////////////////////////////////////////////////////////////////////
106
107bool SkBitmap::setInfo(const SkImageInfo& origInfo, size_t rowBytes) {
108    SkImageInfo info = origInfo;
109
110    if (!SkColorTypeValidateAlphaType(info.fColorType, info.fAlphaType,
111                                      &info.fAlphaType)) {
112        return reset_return_false(this);
113    }
114
115    // require that rowBytes fit in 31bits
116    int64_t mrb = info.minRowBytes64();
117    if ((int32_t)mrb != mrb) {
118        return reset_return_false(this);
119    }
120    if ((int64_t)rowBytes != (int32_t)rowBytes) {
121        return reset_return_false(this);
122    }
123
124    if (info.width() < 0 || info.height() < 0) {
125        return reset_return_false(this);
126    }
127
128    if (kUnknown_SkColorType == info.colorType()) {
129        rowBytes = 0;
130    } else if (0 == rowBytes) {
131        rowBytes = (size_t)mrb;
132    } else if (!info.validRowBytes(rowBytes)) {
133        return reset_return_false(this);
134    }
135
136    this->freePixels();
137
138    fInfo = info;
139    fRowBytes = SkToU32(rowBytes);
140    return true;
141}
142
143bool SkBitmap::setAlphaType(SkAlphaType alphaType) {
144    if (!SkColorTypeValidateAlphaType(fInfo.fColorType, alphaType, &alphaType)) {
145        return false;
146    }
147    if (fInfo.fAlphaType != alphaType) {
148        fInfo.fAlphaType = alphaType;
149        if (fPixelRef) {
150            fPixelRef->changeAlphaType(alphaType);
151        }
152    }
153    return true;
154}
155
156void SkBitmap::updatePixelsFromRef() const {
157    if (NULL != fPixelRef) {
158        if (fPixelLockCount > 0) {
159            SkASSERT(fPixelRef->isLocked());
160
161            void* p = fPixelRef->pixels();
162            if (NULL != p) {
163                p = (char*)p
164                    + fPixelRefOrigin.fY * fRowBytes
165                    + fPixelRefOrigin.fX * fInfo.bytesPerPixel();
166            }
167            fPixels = p;
168            fColorTable = fPixelRef->colorTable();
169        } else {
170            SkASSERT(0 == fPixelLockCount);
171            fPixels = NULL;
172            fColorTable = NULL;
173        }
174    }
175}
176
177SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, int dx, int dy) {
178#ifdef SK_DEBUG
179    if (pr) {
180        if (kUnknown_SkColorType != fInfo.colorType()) {
181            const SkImageInfo& prInfo = pr->info();
182            SkASSERT(fInfo.fWidth <= prInfo.fWidth);
183            SkASSERT(fInfo.fHeight <= prInfo.fHeight);
184            SkASSERT(fInfo.fColorType == prInfo.fColorType);
185            switch (prInfo.fAlphaType) {
186                case kIgnore_SkAlphaType:
187                    SkASSERT(fInfo.fAlphaType == kIgnore_SkAlphaType);
188                    break;
189                case kOpaque_SkAlphaType:
190                case kPremul_SkAlphaType:
191                    SkASSERT(fInfo.fAlphaType == kOpaque_SkAlphaType ||
192                             fInfo.fAlphaType == kPremul_SkAlphaType);
193                    break;
194                case kUnpremul_SkAlphaType:
195                    SkASSERT(fInfo.fAlphaType == kOpaque_SkAlphaType ||
196                             fInfo.fAlphaType == kUnpremul_SkAlphaType);
197                    break;
198            }
199        }
200    }
201#endif
202
203    if (pr) {
204        const SkImageInfo& info = pr->info();
205        fPixelRefOrigin.set(SkPin32(dx, 0, info.fWidth),
206                            SkPin32(dy, 0, info.fHeight));
207    } else {
208        // ignore dx,dy if there is no pixelref
209        fPixelRefOrigin.setZero();
210    }
211
212    if (fPixelRef != pr) {
213        this->freePixels();
214        SkASSERT(NULL == fPixelRef);
215
216        SkSafeRef(pr);
217        fPixelRef = pr;
218        this->updatePixelsFromRef();
219    }
220
221    SkDEBUGCODE(this->validate();)
222    return pr;
223}
224
225void SkBitmap::lockPixels() const {
226    if (NULL != fPixelRef && 0 == sk_atomic_inc(&fPixelLockCount)) {
227        fPixelRef->lockPixels();
228        this->updatePixelsFromRef();
229    }
230    SkDEBUGCODE(this->validate();)
231}
232
233void SkBitmap::unlockPixels() const {
234    SkASSERT(NULL == fPixelRef || fPixelLockCount > 0);
235
236    if (NULL != fPixelRef && 1 == sk_atomic_dec(&fPixelLockCount)) {
237        fPixelRef->unlockPixels();
238        this->updatePixelsFromRef();
239    }
240    SkDEBUGCODE(this->validate();)
241}
242
243bool SkBitmap::lockPixelsAreWritable() const {
244    return (fPixelRef) ? fPixelRef->lockPixelsAreWritable() : false;
245}
246
247void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
248    if (NULL == p) {
249        this->setPixelRef(NULL);
250        return;
251    }
252
253    if (kUnknown_SkColorType == fInfo.colorType()) {
254        this->setPixelRef(NULL);
255        return;
256    }
257
258    SkPixelRef* pr = SkMallocPixelRef::NewDirect(fInfo, p, fRowBytes, ctable);
259    if (NULL == pr) {
260        this->setPixelRef(NULL);
261        return;
262    }
263
264    this->setPixelRef(pr)->unref();
265
266    // since we're already allocated, we lockPixels right away
267    this->lockPixels();
268    SkDEBUGCODE(this->validate();)
269}
270
271bool SkBitmap::tryAllocPixels(Allocator* allocator, SkColorTable* ctable) {
272    HeapAllocator stdalloc;
273
274    if (NULL == allocator) {
275        allocator = &stdalloc;
276    }
277    return allocator->allocPixelRef(this, ctable);
278}
279
280///////////////////////////////////////////////////////////////////////////////
281
282bool SkBitmap::tryAllocPixels(const SkImageInfo& requestedInfo, size_t rowBytes) {
283    if (kIndex_8_SkColorType == requestedInfo.colorType()) {
284        return reset_return_false(this);
285    }
286    if (!this->setInfo(requestedInfo, rowBytes)) {
287        return reset_return_false(this);
288    }
289
290    // setInfo may have corrected info (e.g. 565 is always opaque).
291    const SkImageInfo& correctedInfo = this->info();
292    // setInfo may have computed a valid rowbytes if 0 were passed in
293    rowBytes = this->rowBytes();
294
295    SkMallocPixelRef::PRFactory defaultFactory;
296
297    SkPixelRef* pr = defaultFactory.create(correctedInfo, rowBytes, NULL);
298    if (NULL == pr) {
299        return reset_return_false(this);
300    }
301    this->setPixelRef(pr)->unref();
302
303    // TODO: lockPixels could/should return bool or void*/NULL
304    this->lockPixels();
305    if (NULL == this->getPixels()) {
306        return reset_return_false(this);
307    }
308    return true;
309}
310
311bool SkBitmap::tryAllocPixels(const SkImageInfo& requestedInfo, SkPixelRefFactory* factory,
312                                SkColorTable* ctable) {
313    if (kIndex_8_SkColorType == requestedInfo.fColorType && NULL == ctable) {
314        return reset_return_false(this);
315    }
316    if (!this->setInfo(requestedInfo)) {
317        return reset_return_false(this);
318    }
319
320    // setInfo may have corrected info (e.g. 565 is always opaque).
321    const SkImageInfo& correctedInfo = this->info();
322
323    SkMallocPixelRef::PRFactory defaultFactory;
324    if (NULL == factory) {
325        factory = &defaultFactory;
326    }
327
328    SkPixelRef* pr = factory->create(correctedInfo, correctedInfo.minRowBytes(), ctable);
329    if (NULL == pr) {
330        return reset_return_false(this);
331    }
332    this->setPixelRef(pr)->unref();
333
334    // TODO: lockPixels could/should return bool or void*/NULL
335    this->lockPixels();
336    if (NULL == this->getPixels()) {
337        return reset_return_false(this);
338    }
339    return true;
340}
341
342bool SkBitmap::installPixels(const SkImageInfo& requestedInfo, void* pixels, size_t rb,
343                             SkColorTable* ct, void (*releaseProc)(void* addr, void* context),
344                             void* context) {
345    if (!this->setInfo(requestedInfo, rb)) {
346        this->reset();
347        return false;
348    }
349
350    // setInfo may have corrected info (e.g. 565 is always opaque).
351    const SkImageInfo& correctedInfo = this->info();
352
353    SkPixelRef* pr = SkMallocPixelRef::NewWithProc(correctedInfo, rb, ct, pixels, releaseProc,
354                                                   context);
355    if (!pr) {
356        this->reset();
357        return false;
358    }
359
360    this->setPixelRef(pr)->unref();
361
362    // since we're already allocated, we lockPixels right away
363    this->lockPixels();
364    SkDEBUGCODE(this->validate();)
365    return true;
366}
367
368bool SkBitmap::installMaskPixels(const SkMask& mask) {
369    if (SkMask::kA8_Format != mask.fFormat) {
370        this->reset();
371        return false;
372    }
373    return this->installPixels(SkImageInfo::MakeA8(mask.fBounds.width(),
374                                                   mask.fBounds.height()),
375                               mask.fImage, mask.fRowBytes);
376}
377
378///////////////////////////////////////////////////////////////////////////////
379
380void SkBitmap::freePixels() {
381    if (NULL != fPixelRef) {
382        if (fPixelLockCount > 0) {
383            fPixelRef->unlockPixels();
384        }
385        fPixelRef->unref();
386        fPixelRef = NULL;
387        fPixelRefOrigin.setZero();
388    }
389    fPixelLockCount = 0;
390    fPixels = NULL;
391    fColorTable = NULL;
392}
393
394uint32_t SkBitmap::getGenerationID() const {
395    return (fPixelRef) ? fPixelRef->getGenerationID() : 0;
396}
397
398void SkBitmap::notifyPixelsChanged() const {
399    SkASSERT(!this->isImmutable());
400    if (fPixelRef) {
401        fPixelRef->notifyPixelsChanged();
402    }
403}
404
405GrTexture* SkBitmap::getTexture() const {
406    return fPixelRef ? fPixelRef->getTexture() : NULL;
407}
408
409///////////////////////////////////////////////////////////////////////////////
410
411/** We explicitly use the same allocator for our pixels that SkMask does,
412 so that we can freely assign memory allocated by one class to the other.
413 */
414bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
415                                            SkColorTable* ctable) {
416    const SkImageInfo info = dst->info();
417    if (kUnknown_SkColorType == info.colorType()) {
418//        SkDebugf("unsupported config for info %d\n", dst->config());
419        return false;
420    }
421
422    SkPixelRef* pr = SkMallocPixelRef::NewAllocate(info, dst->rowBytes(), ctable);
423    if (NULL == pr) {
424        return false;
425    }
426
427    dst->setPixelRef(pr)->unref();
428    // since we're already allocated, we lockPixels right away
429    dst->lockPixels();
430    return true;
431}
432
433///////////////////////////////////////////////////////////////////////////////
434
435bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize,
436                            size_t dstRowBytes, bool preserveDstPad) const {
437
438    if (0 == dstRowBytes) {
439        dstRowBytes = fRowBytes;
440    }
441
442    if (dstRowBytes < fInfo.minRowBytes() ||
443        dst == NULL || (getPixels() == NULL && pixelRef() == NULL)) {
444        return false;
445    }
446
447    if (!preserveDstPad && static_cast<uint32_t>(dstRowBytes) == fRowBytes) {
448        size_t safeSize = this->getSafeSize();
449        if (safeSize > dstSize || safeSize == 0)
450            return false;
451        else {
452            SkAutoLockPixels lock(*this);
453            // This implementation will write bytes beyond the end of each row,
454            // excluding the last row, if the bitmap's stride is greater than
455            // strictly required by the current config.
456            memcpy(dst, getPixels(), safeSize);
457
458            return true;
459        }
460    } else {
461        // If destination has different stride than us, then copy line by line.
462        if (fInfo.getSafeSize(dstRowBytes) > dstSize) {
463            return false;
464        } else {
465            // Just copy what we need on each line.
466            size_t rowBytes = fInfo.minRowBytes();
467            SkAutoLockPixels lock(*this);
468            const uint8_t* srcP = reinterpret_cast<const uint8_t*>(getPixels());
469            uint8_t* dstP = reinterpret_cast<uint8_t*>(dst);
470            for (int row = 0; row < fInfo.fHeight;
471                 row++, srcP += fRowBytes, dstP += dstRowBytes) {
472                memcpy(dstP, srcP, rowBytes);
473            }
474
475            return true;
476        }
477    }
478}
479
480///////////////////////////////////////////////////////////////////////////////
481
482bool SkBitmap::isImmutable() const {
483    return fPixelRef ? fPixelRef->isImmutable() : false;
484}
485
486void SkBitmap::setImmutable() {
487    if (fPixelRef) {
488        fPixelRef->setImmutable();
489    }
490}
491
492bool SkBitmap::isVolatile() const {
493    return (fFlags & kImageIsVolatile_Flag) != 0;
494}
495
496void SkBitmap::setIsVolatile(bool isVolatile) {
497    if (isVolatile) {
498        fFlags |= kImageIsVolatile_Flag;
499    } else {
500        fFlags &= ~kImageIsVolatile_Flag;
501    }
502}
503
504void* SkBitmap::getAddr(int x, int y) const {
505    SkASSERT((unsigned)x < (unsigned)this->width());
506    SkASSERT((unsigned)y < (unsigned)this->height());
507
508    char* base = (char*)this->getPixels();
509    if (base) {
510        base += y * this->rowBytes();
511        switch (this->colorType()) {
512            case kRGBA_8888_SkColorType:
513            case kBGRA_8888_SkColorType:
514                base += x << 2;
515                break;
516            case kARGB_4444_SkColorType:
517            case kRGB_565_SkColorType:
518                base += x << 1;
519                break;
520            case kAlpha_8_SkColorType:
521            case kIndex_8_SkColorType:
522                base += x;
523                break;
524            default:
525                SkDEBUGFAIL("Can't return addr for config");
526                base = NULL;
527                break;
528        }
529    }
530    return base;
531}
532
533SkColor SkBitmap::getColor(int x, int y) const {
534    SkASSERT((unsigned)x < (unsigned)this->width());
535    SkASSERT((unsigned)y < (unsigned)this->height());
536
537    switch (this->colorType()) {
538        case kAlpha_8_SkColorType: {
539            uint8_t* addr = this->getAddr8(x, y);
540            return SkColorSetA(0, addr[0]);
541        }
542        case kIndex_8_SkColorType: {
543            SkPMColor c = this->getIndex8Color(x, y);
544            return SkUnPreMultiply::PMColorToColor(c);
545        }
546        case kRGB_565_SkColorType: {
547            uint16_t* addr = this->getAddr16(x, y);
548            return SkPixel16ToColor(addr[0]);
549        }
550        case kARGB_4444_SkColorType: {
551            uint16_t* addr = this->getAddr16(x, y);
552            SkPMColor c = SkPixel4444ToPixel32(addr[0]);
553            return SkUnPreMultiply::PMColorToColor(c);
554        }
555        case kBGRA_8888_SkColorType:
556        case kRGBA_8888_SkColorType: {
557            uint32_t* addr = this->getAddr32(x, y);
558            return SkUnPreMultiply::PMColorToColor(addr[0]);
559        }
560        default:
561            SkASSERT(false);
562            return 0;
563    }
564    SkASSERT(false);  // Not reached.
565    return 0;
566}
567
568bool SkBitmap::ComputeIsOpaque(const SkBitmap& bm) {
569    SkAutoLockPixels alp(bm);
570    if (!bm.getPixels()) {
571        return false;
572    }
573
574    const int height = bm.height();
575    const int width = bm.width();
576
577    switch (bm.colorType()) {
578        case kAlpha_8_SkColorType: {
579            unsigned a = 0xFF;
580            for (int y = 0; y < height; ++y) {
581                const uint8_t* row = bm.getAddr8(0, y);
582                for (int x = 0; x < width; ++x) {
583                    a &= row[x];
584                }
585                if (0xFF != a) {
586                    return false;
587                }
588            }
589            return true;
590        } break;
591        case kIndex_8_SkColorType: {
592            SkAutoLockColors alc(bm);
593            const SkPMColor* table = alc.colors();
594            if (!table) {
595                return false;
596            }
597            SkPMColor c = (SkPMColor)~0;
598            for (int i = bm.getColorTable()->count() - 1; i >= 0; --i) {
599                c &= table[i];
600            }
601            return 0xFF == SkGetPackedA32(c);
602        } break;
603        case kRGB_565_SkColorType:
604            return true;
605            break;
606        case kARGB_4444_SkColorType: {
607            unsigned c = 0xFFFF;
608            for (int y = 0; y < height; ++y) {
609                const SkPMColor16* row = bm.getAddr16(0, y);
610                for (int x = 0; x < width; ++x) {
611                    c &= row[x];
612                }
613                if (0xF != SkGetPackedA4444(c)) {
614                    return false;
615                }
616            }
617            return true;
618        } break;
619        case kBGRA_8888_SkColorType:
620        case kRGBA_8888_SkColorType: {
621            SkPMColor c = (SkPMColor)~0;
622            for (int y = 0; y < height; ++y) {
623                const SkPMColor* row = bm.getAddr32(0, y);
624                for (int x = 0; x < width; ++x) {
625                    c &= row[x];
626                }
627                if (0xFF != SkGetPackedA32(c)) {
628                    return false;
629                }
630            }
631            return true;
632        }
633        default:
634            break;
635    }
636    return false;
637}
638
639
640///////////////////////////////////////////////////////////////////////////////
641///////////////////////////////////////////////////////////////////////////////
642
643static uint16_t pack_8888_to_4444(unsigned a, unsigned r, unsigned g, unsigned b) {
644    unsigned pixel = (SkA32To4444(a) << SK_A4444_SHIFT) |
645                     (SkR32To4444(r) << SK_R4444_SHIFT) |
646                     (SkG32To4444(g) << SK_G4444_SHIFT) |
647                     (SkB32To4444(b) << SK_B4444_SHIFT);
648    return SkToU16(pixel);
649}
650
651void SkBitmap::internalErase(const SkIRect& area,
652                             U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
653#ifdef SK_DEBUG
654    SkDEBUGCODE(this->validate();)
655    SkASSERT(!area.isEmpty());
656    {
657        SkIRect total = { 0, 0, this->width(), this->height() };
658        SkASSERT(total.contains(area));
659    }
660#endif
661
662    switch (fInfo.colorType()) {
663        case kUnknown_SkColorType:
664        case kIndex_8_SkColorType:
665            return; // can't erase. Should we bzero so the memory is not uninitialized?
666        default:
667            break;
668    }
669
670    SkAutoLockPixels alp(*this);
671    // perform this check after the lock call
672    if (!this->readyToDraw()) {
673        return;
674    }
675
676    int height = area.height();
677    const int width = area.width();
678    const int rowBytes = fRowBytes;
679
680    switch (this->colorType()) {
681        case kAlpha_8_SkColorType: {
682            uint8_t* p = this->getAddr8(area.fLeft, area.fTop);
683            while (--height >= 0) {
684                memset(p, a, width);
685                p += rowBytes;
686            }
687            break;
688        }
689        case kARGB_4444_SkColorType:
690        case kRGB_565_SkColorType: {
691            uint16_t* p = this->getAddr16(area.fLeft, area.fTop);;
692            uint16_t v;
693
694            // make rgb premultiplied
695            if (255 != a) {
696                r = SkAlphaMul(r, a);
697                g = SkAlphaMul(g, a);
698                b = SkAlphaMul(b, a);
699            }
700
701            if (kARGB_4444_SkColorType == this->colorType()) {
702                v = pack_8888_to_4444(a, r, g, b);
703            } else {
704                v = SkPackRGB16(r >> (8 - SK_R16_BITS),
705                                g >> (8 - SK_G16_BITS),
706                                b >> (8 - SK_B16_BITS));
707            }
708            while (--height >= 0) {
709                sk_memset16(p, v, width);
710                p = (uint16_t*)((char*)p + rowBytes);
711            }
712            break;
713        }
714        case kBGRA_8888_SkColorType:
715        case kRGBA_8888_SkColorType: {
716            uint32_t* p = this->getAddr32(area.fLeft, area.fTop);
717
718            if (255 != a && kPremul_SkAlphaType == this->alphaType()) {
719                r = SkAlphaMul(r, a);
720                g = SkAlphaMul(g, a);
721                b = SkAlphaMul(b, a);
722            }
723            uint32_t v = kRGBA_8888_SkColorType == this->colorType() ?
724                         SkPackARGB_as_RGBA(a, r, g, b) : SkPackARGB_as_BGRA(a, r, g, b);
725
726            while (--height >= 0) {
727                sk_memset32(p, v, width);
728                p = (uint32_t*)((char*)p + rowBytes);
729            }
730            break;
731        }
732        default:
733            return; // no change, so don't call notifyPixelsChanged()
734    }
735
736    this->notifyPixelsChanged();
737}
738
739void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
740    SkIRect area = { 0, 0, this->width(), this->height() };
741    if (!area.isEmpty()) {
742        this->internalErase(area, a, r, g, b);
743    }
744}
745
746void SkBitmap::eraseArea(const SkIRect& rect, SkColor c) const {
747    SkIRect area = { 0, 0, this->width(), this->height() };
748    if (area.intersect(rect)) {
749        this->internalErase(area, SkColorGetA(c), SkColorGetR(c),
750                            SkColorGetG(c), SkColorGetB(c));
751    }
752}
753
754//////////////////////////////////////////////////////////////////////////////////////
755//////////////////////////////////////////////////////////////////////////////////////
756
757bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
758    SkDEBUGCODE(this->validate();)
759
760    if (NULL == result || NULL == fPixelRef) {
761        return false;   // no src pixels
762    }
763
764    SkIRect srcRect, r;
765    srcRect.set(0, 0, this->width(), this->height());
766    if (!r.intersect(srcRect, subset)) {
767        return false;   // r is empty (i.e. no intersection)
768    }
769
770    if (fPixelRef->getTexture() != NULL) {
771        // Do a deep copy
772        SkPixelRef* pixelRef = fPixelRef->deepCopy(this->colorType(), &subset);
773        if (pixelRef != NULL) {
774            SkBitmap dst;
775            dst.setInfo(SkImageInfo::Make(subset.width(), subset.height(),
776                                          this->colorType(), this->alphaType()));
777            dst.setIsVolatile(this->isVolatile());
778            dst.setPixelRef(pixelRef)->unref();
779            SkDEBUGCODE(dst.validate());
780            result->swap(dst);
781            return true;
782        }
783    }
784
785    // If the upper left of the rectangle was outside the bounds of this SkBitmap, we should have
786    // exited above.
787    SkASSERT(static_cast<unsigned>(r.fLeft) < static_cast<unsigned>(this->width()));
788    SkASSERT(static_cast<unsigned>(r.fTop) < static_cast<unsigned>(this->height()));
789
790    SkBitmap dst;
791    dst.setInfo(SkImageInfo::Make(r.width(), r.height(), this->colorType(), this->alphaType()),
792                this->rowBytes());
793    dst.setIsVolatile(this->isVolatile());
794
795    if (fPixelRef) {
796        SkIPoint origin = fPixelRefOrigin;
797        origin.fX += r.fLeft;
798        origin.fY += r.fTop;
799        // share the pixelref with a custom offset
800        dst.setPixelRef(fPixelRef, origin);
801    }
802    SkDEBUGCODE(dst.validate();)
803
804    // we know we're good, so commit to result
805    result->swap(dst);
806    return true;
807}
808
809///////////////////////////////////////////////////////////////////////////////
810
811#include "SkCanvas.h"
812#include "SkPaint.h"
813
814bool SkBitmap::canCopyTo(SkColorType dstColorType) const {
815    const SkColorType srcCT = this->colorType();
816
817    if (srcCT == kUnknown_SkColorType) {
818        return false;
819    }
820
821    bool sameConfigs = (srcCT == dstColorType);
822    switch (dstColorType) {
823        case kAlpha_8_SkColorType:
824        case kRGB_565_SkColorType:
825        case kRGBA_8888_SkColorType:
826        case kBGRA_8888_SkColorType:
827            break;
828        case kIndex_8_SkColorType:
829            if (!sameConfigs) {
830                return false;
831            }
832            break;
833        case kARGB_4444_SkColorType:
834            return sameConfigs || kN32_SkColorType == srcCT || kIndex_8_SkColorType == srcCT;
835        default:
836            return false;
837    }
838    return true;
839}
840
841#include "SkConfig8888.h"
842
843bool SkBitmap::readPixels(const SkImageInfo& requestedDstInfo, void* dstPixels, size_t dstRB,
844                          int x, int y) const {
845    if (kUnknown_SkColorType == requestedDstInfo.colorType()) {
846        return false;
847    }
848    if (NULL == dstPixels || dstRB < requestedDstInfo.minRowBytes()) {
849        return false;
850    }
851    if (0 == requestedDstInfo.width() || 0 == requestedDstInfo.height()) {
852        return false;
853    }
854
855    SkIRect srcR = SkIRect::MakeXYWH(x, y, requestedDstInfo.width(), requestedDstInfo.height());
856    if (!srcR.intersect(0, 0, this->width(), this->height())) {
857        return false;
858    }
859
860    SkImageInfo dstInfo = requestedDstInfo;
861    // the intersect may have shrunk info's logical size
862    dstInfo.fWidth = srcR.width();
863    dstInfo.fHeight = srcR.height();
864
865    // if x or y are negative, then we have to adjust pixels
866    if (x > 0) {
867        x = 0;
868    }
869    if (y > 0) {
870        y = 0;
871    }
872    // here x,y are either 0 or negative
873    dstPixels = ((char*)dstPixels - y * dstRB - x * dstInfo.bytesPerPixel());
874
875    //////////////
876
877    SkAutoLockPixels alp(*this);
878
879    // since we don't stop creating un-pixeled devices yet, check for no pixels here
880    if (NULL == this->getPixels()) {
881        return false;
882    }
883
884    SkImageInfo srcInfo = this->info();
885    srcInfo.fWidth = dstInfo.width();
886    srcInfo.fHeight = dstInfo.height();
887
888    const void* srcPixels = this->getAddr(srcR.x(), srcR.y());
889    return SkPixelInfo::CopyPixels(dstInfo, dstPixels, dstRB, srcInfo, srcPixels, this->rowBytes(),
890                                   this->getColorTable());
891}
892
893bool SkBitmap::copyTo(SkBitmap* dst, SkColorType dstColorType, Allocator* alloc) const {
894    if (!this->canCopyTo(dstColorType)) {
895        return false;
896    }
897
898    // if we have a texture, first get those pixels
899    SkBitmap tmpSrc;
900    const SkBitmap* src = this;
901
902    if (fPixelRef) {
903        SkIRect subset;
904        subset.setXYWH(fPixelRefOrigin.fX, fPixelRefOrigin.fY,
905                       fInfo.width(), fInfo.height());
906        if (fPixelRef->readPixels(&tmpSrc, &subset)) {
907            if (fPixelRef->info().alphaType() == kUnpremul_SkAlphaType) {
908                // FIXME: The only meaningful implementation of readPixels
909                // (GrPixelRef) assumes premultiplied pixels.
910                return false;
911            }
912            SkASSERT(tmpSrc.width() == this->width());
913            SkASSERT(tmpSrc.height() == this->height());
914
915            // did we get lucky and we can just return tmpSrc?
916            if (tmpSrc.colorType() == dstColorType && NULL == alloc) {
917                dst->swap(tmpSrc);
918                // If the result is an exact copy, clone the gen ID.
919                if (dst->pixelRef() && dst->pixelRef()->info() == fPixelRef->info()) {
920                    dst->pixelRef()->cloneGenID(*fPixelRef);
921                }
922                return true;
923            }
924
925            // fall through to the raster case
926            src = &tmpSrc;
927        }
928    }
929
930    // we lock this now, since we may need its colortable
931    SkAutoLockPixels srclock(*src);
932    if (!src->readyToDraw()) {
933        return false;
934    }
935
936    // The only way to be readyToDraw is if fPixelRef is non NULL.
937    SkASSERT(fPixelRef != NULL);
938
939    SkImageInfo dstInfo = src->info();
940    dstInfo.fColorType = dstColorType;
941
942    SkBitmap tmpDst;
943    if (!tmpDst.setInfo(dstInfo)) {
944        return false;
945    }
946
947    // allocate colortable if srcConfig == kIndex8_Config
948    SkAutoTUnref<SkColorTable> ctable;
949    if (dstColorType == kIndex_8_SkColorType) {
950        // TODO: can we just ref() the src colortable? Is it reentrant-safe?
951        ctable.reset(SkNEW_ARGS(SkColorTable, (*src->getColorTable())));
952    }
953    if (!tmpDst.tryAllocPixels(alloc, ctable)) {
954        return false;
955    }
956
957    if (!tmpDst.readyToDraw()) {
958        // allocator/lock failed
959        return false;
960    }
961
962    // pixelRef must be non NULL or tmpDst.readyToDraw() would have
963    // returned false.
964    SkASSERT(tmpDst.pixelRef() != NULL);
965
966    if (!src->readPixels(tmpDst.info(), tmpDst.getPixels(), tmpDst.rowBytes(), 0, 0)) {
967        return false;
968    }
969
970    //  (for BitmapHeap) Clone the pixelref genID even though we have a new pixelref.
971    //  The old copyTo impl did this, so we continue it for now.
972    //
973    //  TODO: should we ignore rowbytes (i.e. getSize)? Then it could just be
974    //      if (src_pixelref->info == dst_pixelref->info)
975    //
976    if (src->colorType() == dstColorType && tmpDst.getSize() == src->getSize()) {
977        SkPixelRef* dstPixelRef = tmpDst.pixelRef();
978        if (dstPixelRef->info() == fPixelRef->info()) {
979            dstPixelRef->cloneGenID(*fPixelRef);
980        }
981    }
982
983    dst->swap(tmpDst);
984    return true;
985}
986
987bool SkBitmap::deepCopyTo(SkBitmap* dst) const {
988    const SkColorType dstCT = this->colorType();
989
990    if (!this->canCopyTo(dstCT)) {
991        return false;
992    }
993
994    // If we have a PixelRef, and it supports deep copy, use it.
995    // Currently supported only by texture-backed bitmaps.
996    if (fPixelRef) {
997        SkPixelRef* pixelRef = fPixelRef->deepCopy(dstCT, NULL);
998        if (pixelRef) {
999            uint32_t rowBytes;
1000            if (this->colorType() == dstCT) {
1001                // Since there is no subset to pass to deepCopy, and deepCopy
1002                // succeeded, the new pixel ref must be identical.
1003                SkASSERT(fPixelRef->info() == pixelRef->info());
1004                pixelRef->cloneGenID(*fPixelRef);
1005                // Use the same rowBytes as the original.
1006                rowBytes = fRowBytes;
1007            } else {
1008                // With the new config, an appropriate fRowBytes will be computed by setInfo.
1009                rowBytes = 0;
1010            }
1011
1012            SkImageInfo info = fInfo;
1013            info.fColorType = dstCT;
1014            if (!dst->setInfo(info, rowBytes)) {
1015                return false;
1016            }
1017            dst->setPixelRef(pixelRef, fPixelRefOrigin)->unref();
1018            return true;
1019        }
1020    }
1021
1022    if (this->getTexture()) {
1023        return false;
1024    } else {
1025        return this->copyTo(dst, dstCT, NULL);
1026    }
1027}
1028
1029///////////////////////////////////////////////////////////////////////////////
1030
1031static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha,
1032                           int alphaRowBytes) {
1033    SkASSERT(alpha != NULL);
1034    SkASSERT(alphaRowBytes >= src.width());
1035
1036    SkColorType colorType = src.colorType();
1037    int         w = src.width();
1038    int         h = src.height();
1039    size_t      rb = src.rowBytes();
1040
1041    SkAutoLockPixels alp(src);
1042    if (!src.readyToDraw()) {
1043        // zero out the alpha buffer and return
1044        while (--h >= 0) {
1045            memset(alpha, 0, w);
1046            alpha += alphaRowBytes;
1047        }
1048        return false;
1049    }
1050
1051    if (kAlpha_8_SkColorType == colorType && !src.isOpaque()) {
1052        const uint8_t* s = src.getAddr8(0, 0);
1053        while (--h >= 0) {
1054            memcpy(alpha, s, w);
1055            s += rb;
1056            alpha += alphaRowBytes;
1057        }
1058    } else if (kN32_SkColorType == colorType && !src.isOpaque()) {
1059        const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0);
1060        while (--h >= 0) {
1061            for (int x = 0; x < w; x++) {
1062                alpha[x] = SkGetPackedA32(s[x]);
1063            }
1064            s = (const SkPMColor*)((const char*)s + rb);
1065            alpha += alphaRowBytes;
1066        }
1067    } else if (kARGB_4444_SkColorType == colorType && !src.isOpaque()) {
1068        const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0);
1069        while (--h >= 0) {
1070            for (int x = 0; x < w; x++) {
1071                alpha[x] = SkPacked4444ToA32(s[x]);
1072            }
1073            s = (const SkPMColor16*)((const char*)s + rb);
1074            alpha += alphaRowBytes;
1075        }
1076    } else if (kIndex_8_SkColorType == colorType && !src.isOpaque()) {
1077        SkColorTable* ct = src.getColorTable();
1078        if (ct) {
1079            const SkPMColor* SK_RESTRICT table = ct->lockColors();
1080            const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0);
1081            while (--h >= 0) {
1082                for (int x = 0; x < w; x++) {
1083                    alpha[x] = SkGetPackedA32(table[s[x]]);
1084                }
1085                s += rb;
1086                alpha += alphaRowBytes;
1087            }
1088            ct->unlockColors();
1089        }
1090    } else {    // src is opaque, so just fill alpha[] with 0xFF
1091        memset(alpha, 0xFF, h * alphaRowBytes);
1092    }
1093    return true;
1094}
1095
1096#include "SkPaint.h"
1097#include "SkMaskFilter.h"
1098#include "SkMatrix.h"
1099
1100bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
1101                            Allocator *allocator, SkIPoint* offset) const {
1102    SkDEBUGCODE(this->validate();)
1103
1104    SkBitmap    tmpBitmap;
1105    SkMatrix    identity;
1106    SkMask      srcM, dstM;
1107
1108    srcM.fBounds.set(0, 0, this->width(), this->height());
1109    srcM.fRowBytes = SkAlign4(this->width());
1110    srcM.fFormat = SkMask::kA8_Format;
1111
1112    SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL;
1113
1114    // compute our (larger?) dst bounds if we have a filter
1115    if (NULL != filter) {
1116        identity.reset();
1117        srcM.fImage = NULL;
1118        if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1119            goto NO_FILTER_CASE;
1120        }
1121        dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
1122    } else {
1123    NO_FILTER_CASE:
1124        tmpBitmap.setInfo(SkImageInfo::MakeA8(this->width(), this->height()), srcM.fRowBytes);
1125        if (!tmpBitmap.tryAllocPixels(allocator, NULL)) {
1126            // Allocation of pixels for alpha bitmap failed.
1127            SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1128                    tmpBitmap.width(), tmpBitmap.height());
1129            return false;
1130        }
1131        GetBitmapAlpha(*this, tmpBitmap.getAddr8(0, 0), srcM.fRowBytes);
1132        if (offset) {
1133            offset->set(0, 0);
1134        }
1135        tmpBitmap.swap(*dst);
1136        return true;
1137    }
1138    srcM.fImage = SkMask::AllocImage(srcM.computeImageSize());
1139    SkAutoMaskFreeImage srcCleanup(srcM.fImage);
1140
1141    GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
1142    if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1143        goto NO_FILTER_CASE;
1144    }
1145    SkAutoMaskFreeImage dstCleanup(dstM.fImage);
1146
1147    tmpBitmap.setInfo(SkImageInfo::MakeA8(dstM.fBounds.width(), dstM.fBounds.height()),
1148                      dstM.fRowBytes);
1149    if (!tmpBitmap.tryAllocPixels(allocator, NULL)) {
1150        // Allocation of pixels for alpha bitmap failed.
1151        SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1152                tmpBitmap.width(), tmpBitmap.height());
1153        return false;
1154    }
1155    memcpy(tmpBitmap.getPixels(), dstM.fImage, dstM.computeImageSize());
1156    if (offset) {
1157        offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
1158    }
1159    SkDEBUGCODE(tmpBitmap.validate();)
1160
1161    tmpBitmap.swap(*dst);
1162    return true;
1163}
1164
1165///////////////////////////////////////////////////////////////////////////////
1166
1167void SkBitmap::WriteRawPixels(SkWriteBuffer* buffer, const SkBitmap& bitmap) {
1168    const SkImageInfo info = bitmap.info();
1169    SkAutoLockPixels alp(bitmap);
1170    if (0 == info.width() || 0 == info.height() || NULL == bitmap.getPixels()) {
1171        buffer->writeUInt(0); // instead of snugRB, signaling no pixels
1172        return;
1173    }
1174
1175    const size_t snugRB = info.width() * info.bytesPerPixel();
1176    const char* src = (const char*)bitmap.getPixels();
1177    const size_t ramRB = bitmap.rowBytes();
1178
1179    buffer->write32(SkToU32(snugRB));
1180    info.flatten(*buffer);
1181
1182    const size_t size = snugRB * info.height();
1183    SkAutoMalloc storage(size);
1184    char* dst = (char*)storage.get();
1185    for (int y = 0; y < info.height(); ++y) {
1186        memcpy(dst, src, snugRB);
1187        dst += snugRB;
1188        src += ramRB;
1189    }
1190    buffer->writeByteArray(storage.get(), size);
1191
1192    SkColorTable* ct = bitmap.getColorTable();
1193    if (kIndex_8_SkColorType == info.colorType() && ct) {
1194        buffer->writeBool(true);
1195        ct->writeToBuffer(*buffer);
1196    } else {
1197        buffer->writeBool(false);
1198    }
1199}
1200
1201bool SkBitmap::ReadRawPixels(SkReadBuffer* buffer, SkBitmap* bitmap) {
1202    const size_t snugRB = buffer->readUInt();
1203    if (0 == snugRB) {  // no pixels
1204        return false;
1205    }
1206
1207    SkImageInfo info;
1208    info.unflatten(*buffer);
1209
1210    // If there was an error reading "info", don't use it to compute minRowBytes()
1211    if (!buffer->validate(true)) {
1212        return false;
1213    }
1214
1215    const size_t ramRB = info.minRowBytes();
1216    const int height = info.height();
1217    const size_t snugSize = snugRB * height;
1218    const size_t ramSize = ramRB * height;
1219    if (!buffer->validate(snugSize <= ramSize)) {
1220        return false;
1221    }
1222
1223    char* dst = (char*)sk_malloc_throw(ramSize);
1224    buffer->readByteArray(dst, snugSize);
1225    SkAutoDataUnref data(SkData::NewFromMalloc(dst, ramSize));
1226
1227    if (snugSize != ramSize) {
1228        const char* srcRow = dst + snugRB * (height - 1);
1229        char* dstRow = dst + ramRB * (height - 1);
1230        for (int y = height - 1; y >= 1; --y) {
1231            memmove(dstRow, srcRow, snugRB);
1232            srcRow -= snugRB;
1233            dstRow -= ramRB;
1234        }
1235        SkASSERT(srcRow == dstRow); // first row does not need to be moved
1236    }
1237
1238    SkAutoTUnref<SkColorTable> ctable;
1239    if (buffer->readBool()) {
1240        ctable.reset(SkNEW_ARGS(SkColorTable, (*buffer)));
1241    }
1242
1243    SkAutoTUnref<SkPixelRef> pr(SkMallocPixelRef::NewWithData(info, info.minRowBytes(),
1244                                                              ctable.get(), data.get()));
1245    bitmap->setInfo(pr->info());
1246    bitmap->setPixelRef(pr, 0, 0);
1247    return true;
1248}
1249
1250enum {
1251    SERIALIZE_PIXELTYPE_NONE,
1252    SERIALIZE_PIXELTYPE_REF_DATA
1253};
1254
1255void SkBitmap::legacyUnflatten(SkReadBuffer& buffer) {
1256#ifdef SK_SUPPORT_LEGACY_PIXELREF_UNFLATTENABLE
1257    this->reset();
1258
1259    SkImageInfo info;
1260    info.unflatten(buffer);
1261    size_t rowBytes = buffer.readInt();
1262    if (!buffer.validate((info.width() >= 0) && (info.height() >= 0) &&
1263                         SkColorTypeIsValid(info.fColorType) &&
1264                         SkAlphaTypeIsValid(info.fAlphaType) &&
1265                         SkColorTypeValidateAlphaType(info.fColorType, info.fAlphaType) &&
1266                         info.validRowBytes(rowBytes))) {
1267        return;
1268    }
1269
1270    bool configIsValid = this->setInfo(info, rowBytes);
1271    buffer.validate(configIsValid);
1272
1273    int reftype = buffer.readInt();
1274    if (buffer.validate((SERIALIZE_PIXELTYPE_REF_DATA == reftype) ||
1275                        (SERIALIZE_PIXELTYPE_NONE == reftype))) {
1276        switch (reftype) {
1277            case SERIALIZE_PIXELTYPE_REF_DATA: {
1278                SkIPoint origin;
1279                origin.fX = buffer.readInt();
1280                origin.fY = buffer.readInt();
1281                size_t offset = origin.fY * rowBytes + origin.fX * info.bytesPerPixel();
1282                SkPixelRef* pr = buffer.readFlattenable<SkPixelRef>();
1283                if (!buffer.validate((NULL == pr) ||
1284                       (pr->getAllocatedSizeInBytes() >= (offset + this->getSafeSize())))) {
1285                    origin.setZero();
1286                }
1287                SkSafeUnref(this->setPixelRef(pr, origin));
1288                break;
1289            }
1290            case SERIALIZE_PIXELTYPE_NONE:
1291                break;
1292            default:
1293                SkDEBUGFAIL("unrecognized pixeltype in serialized data");
1294                sk_throw();
1295        }
1296    }
1297#else
1298    sk_throw();
1299#endif
1300}
1301
1302///////////////////////////////////////////////////////////////////////////////
1303
1304SkBitmap::RLEPixels::RLEPixels(int width, int height) {
1305    fHeight = height;
1306    fYPtrs = (uint8_t**)sk_calloc_throw(height * sizeof(uint8_t*));
1307}
1308
1309SkBitmap::RLEPixels::~RLEPixels() {
1310    sk_free(fYPtrs);
1311}
1312
1313///////////////////////////////////////////////////////////////////////////////
1314
1315#ifdef SK_DEBUG
1316void SkBitmap::validate() const {
1317    fInfo.validate();
1318
1319    // ImageInfo may not require this, but Bitmap ensures that opaque-only
1320    // colorTypes report opaque for their alphatype
1321    if (kRGB_565_SkColorType == fInfo.colorType()) {
1322        SkASSERT(kOpaque_SkAlphaType == fInfo.alphaType());
1323    }
1324
1325    SkASSERT(fInfo.validRowBytes(fRowBytes));
1326    uint8_t allFlags = kImageIsVolatile_Flag;
1327#ifdef SK_BUILD_FOR_ANDROID
1328    allFlags |= kHasHardwareMipMap_Flag;
1329#endif
1330    SkASSERT((~allFlags & fFlags) == 0);
1331    SkASSERT(fPixelLockCount >= 0);
1332
1333    if (fPixels) {
1334        SkASSERT(fPixelRef);
1335        SkASSERT(fPixelLockCount > 0);
1336        SkASSERT(fPixelRef->isLocked());
1337        SkASSERT(fPixelRef->rowBytes() == fRowBytes);
1338        SkASSERT(fPixelRefOrigin.fX >= 0);
1339        SkASSERT(fPixelRefOrigin.fY >= 0);
1340        SkASSERT(fPixelRef->info().width() >= (int)this->width() + fPixelRefOrigin.fX);
1341        SkASSERT(fPixelRef->info().fHeight >= (int)this->height() + fPixelRefOrigin.fY);
1342        SkASSERT(fPixelRef->rowBytes() >= fInfo.minRowBytes());
1343    } else {
1344        SkASSERT(NULL == fColorTable);
1345    }
1346}
1347#endif
1348
1349#ifndef SK_IGNORE_TO_STRING
1350void SkBitmap::toString(SkString* str) const {
1351
1352    static const char* gColorTypeNames[kLastEnum_SkColorType + 1] = {
1353        "UNKNOWN", "A8", "565", "4444", "RGBA", "BGRA", "INDEX8",
1354    };
1355
1356    str->appendf("bitmap: ((%d, %d) %s", this->width(), this->height(),
1357                 gColorTypeNames[this->colorType()]);
1358
1359    str->append(" (");
1360    if (this->isOpaque()) {
1361        str->append("opaque");
1362    } else {
1363        str->append("transparent");
1364    }
1365    if (this->isImmutable()) {
1366        str->append(", immutable");
1367    } else {
1368        str->append(", not-immutable");
1369    }
1370    str->append(")");
1371
1372    SkPixelRef* pr = this->pixelRef();
1373    if (NULL == pr) {
1374        // show null or the explicit pixel address (rare)
1375        str->appendf(" pixels:%p", this->getPixels());
1376    } else {
1377        const char* uri = pr->getURI();
1378        if (NULL != uri) {
1379            str->appendf(" uri:\"%s\"", uri);
1380        } else {
1381            str->appendf(" pixelref:%p", pr);
1382        }
1383    }
1384
1385    str->append(")");
1386}
1387#endif
1388
1389///////////////////////////////////////////////////////////////////////////////
1390
1391#ifdef SK_DEBUG
1392void SkImageInfo::validate() const {
1393    SkASSERT(fWidth >= 0);
1394    SkASSERT(fHeight >= 0);
1395    SkASSERT(SkColorTypeIsValid(fColorType));
1396    SkASSERT(SkAlphaTypeIsValid(fAlphaType));
1397}
1398#endif
1399