1/*
2 * Copyright 2008 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkAtomics.h"
9#include "SkBitmap.h"
10#include "SkColorData.h"
11#include "SkColorTable.h"
12#include "SkConvertPixels.h"
13#include "SkData.h"
14#include "SkFilterQuality.h"
15#include "SkHalf.h"
16#include "SkImageInfoPriv.h"
17#include "SkMallocPixelRef.h"
18#include "SkMask.h"
19#include "SkMaskFilterBase.h"
20#include "SkMath.h"
21#include "SkPixelRef.h"
22#include "SkPixmapPriv.h"
23#include "SkReadBuffer.h"
24#include "SkRect.h"
25#include "SkScalar.h"
26#include "SkTemplates.h"
27#include "SkUnPreMultiply.h"
28#include "SkWriteBuffer.h"
29#include "SkWritePixelsRec.h"
30
31#include <string.h>
32
33static bool reset_return_false(SkBitmap* bm) {
34    bm->reset();
35    return false;
36}
37
38SkBitmap::SkBitmap() : fFlags(0) {}
39
40SkBitmap::SkBitmap(const SkBitmap& src)
41    : fPixelRef      (src.fPixelRef)
42    , fPixmap        (src.fPixmap)
43    , fFlags         (src.fFlags)
44{
45    SkDEBUGCODE(src.validate();)
46    SkDEBUGCODE(this->validate();)
47}
48
49SkBitmap::SkBitmap(SkBitmap&& other)
50    : fPixelRef      (std::move(other.fPixelRef))
51    , fPixmap        (std::move(other.fPixmap))
52    , fFlags                   (other.fFlags)
53{
54    SkASSERT(!other.fPixelRef);
55    other.fPixmap.reset();
56    other.fFlags          = 0;
57}
58
59SkBitmap::~SkBitmap() {}
60
61SkBitmap& SkBitmap::operator=(const SkBitmap& src) {
62    if (this != &src) {
63        fPixelRef       = src.fPixelRef;
64        fPixmap         = src.fPixmap;
65        fFlags          = src.fFlags;
66    }
67    SkDEBUGCODE(this->validate();)
68    return *this;
69}
70
71SkBitmap& SkBitmap::operator=(SkBitmap&& other) {
72    if (this != &other) {
73        fPixelRef       = std::move(other.fPixelRef);
74        fPixmap         = std::move(other.fPixmap);
75        fFlags          = other.fFlags;
76        SkASSERT(!other.fPixelRef);
77        other.fPixmap.reset();
78        other.fFlags          = 0;
79    }
80    return *this;
81}
82
83void SkBitmap::swap(SkBitmap& other) {
84    SkTSwap(*this, other);
85    SkDEBUGCODE(this->validate();)
86}
87
88void SkBitmap::reset() {
89    fPixelRef = nullptr;  // Free pixels.
90    fPixmap.reset();
91    fFlags = 0;
92}
93
94void SkBitmap::getBounds(SkRect* bounds) const {
95    SkASSERT(bounds);
96    *bounds = SkRect::Make(this->dimensions());
97}
98
99void SkBitmap::getBounds(SkIRect* bounds) const {
100    SkASSERT(bounds);
101    *bounds = fPixmap.bounds();
102}
103
104///////////////////////////////////////////////////////////////////////////////
105
106bool SkBitmap::setInfo(const SkImageInfo& info, size_t rowBytes) {
107    SkAlphaType newAT = info.alphaType();
108    if (!SkColorTypeValidateAlphaType(info.colorType(), info.alphaType(), &newAT)) {
109        return reset_return_false(this);
110    }
111    // don't look at info.alphaType(), since newAT is the real value...
112
113    // require that rowBytes fit in 31bits
114    int64_t mrb = info.minRowBytes64();
115    if ((int32_t)mrb != mrb) {
116        return reset_return_false(this);
117    }
118    if ((int64_t)rowBytes != (int32_t)rowBytes) {
119        return reset_return_false(this);
120    }
121
122    if (info.width() < 0 || info.height() < 0) {
123        return reset_return_false(this);
124    }
125
126    if (kUnknown_SkColorType == info.colorType()) {
127        rowBytes = 0;
128    } else if (0 == rowBytes) {
129        rowBytes = (size_t)mrb;
130    } else if (!info.validRowBytes(rowBytes)) {
131        return reset_return_false(this);
132    }
133
134    fPixelRef = nullptr;  // Free pixels.
135    fPixmap.reset(info.makeAlphaType(newAT), nullptr, SkToU32(rowBytes));
136    SkDEBUGCODE(this->validate();)
137    return true;
138}
139
140
141
142bool SkBitmap::setAlphaType(SkAlphaType newAlphaType) {
143    if (!SkColorTypeValidateAlphaType(this->colorType(), newAlphaType, &newAlphaType)) {
144        return false;
145    }
146    if (this->alphaType() != newAlphaType) {
147        auto newInfo = fPixmap.info().makeAlphaType(newAlphaType);
148        fPixmap.reset(std::move(newInfo), fPixmap.addr(), fPixmap.rowBytes());
149    }
150    SkDEBUGCODE(this->validate();)
151    return true;
152}
153
154SkIPoint SkBitmap::pixelRefOrigin() const {
155    const char* addr = (const char*)fPixmap.addr();
156    const char* pix = (const char*)(fPixelRef ? fPixelRef->pixels() : nullptr);
157    size_t rb = this->rowBytes();
158    if (!pix || 0 == rb) {
159        return {0, 0};
160    }
161    SkASSERT(this->bytesPerPixel() > 0);
162    SkASSERT(this->bytesPerPixel() == (1 << this->shiftPerPixel()));
163    SkASSERT(addr >= pix);
164    size_t off = addr - pix;
165    return {SkToS32((off % rb) >> this->shiftPerPixel()), SkToS32(off / rb)};
166}
167
168void SkBitmap::setPixelRef(sk_sp<SkPixelRef> pr, int dx, int dy) {
169#ifdef SK_DEBUG
170    if (pr) {
171        if (kUnknown_SkColorType != this->colorType()) {
172            SkASSERT(dx >= 0 && this->width() + dx <= pr->width());
173            SkASSERT(dy >= 0 && this->height() + dy <= pr->height());
174        }
175    }
176#endif
177    fPixelRef = kUnknown_SkColorType != this->colorType() ? std::move(pr) : nullptr;
178    void* p = nullptr;
179    size_t rowBytes = this->rowBytes();
180    // ignore dx,dy if there is no pixelref
181    if (fPixelRef) {
182        rowBytes = fPixelRef->rowBytes();
183        // TODO(reed):  Enforce that PixelRefs must have non-null pixels.
184        p = fPixelRef->pixels();
185        if (p) {
186            p = (char*)p + dy * rowBytes + dx * this->bytesPerPixel();
187        }
188    }
189    SkPixmapPriv::ResetPixmapKeepInfo(&fPixmap, p, rowBytes);
190    SkDEBUGCODE(this->validate();)
191}
192
193void SkBitmap::setPixels(void* p) {
194    if (nullptr == p) {
195        this->setPixelRef(nullptr, 0, 0);
196        return;
197    }
198
199    if (kUnknown_SkColorType == this->colorType()) {
200        this->setPixelRef(nullptr, 0, 0);
201        return;
202    }
203
204    this->setPixelRef(SkMallocPixelRef::MakeDirect(this->info(), p, this->rowBytes()), 0, 0);
205    if (!fPixelRef) {
206        return;
207    }
208    SkDEBUGCODE(this->validate();)
209}
210
211bool SkBitmap::tryAllocPixels(Allocator* allocator) {
212    HeapAllocator stdalloc;
213
214    if (nullptr == allocator) {
215        allocator = &stdalloc;
216    }
217    return allocator->allocPixelRef(this);
218}
219
220///////////////////////////////////////////////////////////////////////////////
221
222bool SkBitmap::tryAllocPixels(const SkImageInfo& requestedInfo, size_t rowBytes) {
223    if (!this->setInfo(requestedInfo, rowBytes)) {
224        return reset_return_false(this);
225    }
226
227    // setInfo may have corrected info (e.g. 565 is always opaque).
228    const SkImageInfo& correctedInfo = this->info();
229    if (kUnknown_SkColorType == correctedInfo.colorType()) {
230        return true;
231    }
232    // setInfo may have computed a valid rowbytes if 0 were passed in
233    rowBytes = this->rowBytes();
234
235    sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeAllocate(correctedInfo, rowBytes);
236    if (!pr) {
237        return reset_return_false(this);
238    }
239    this->setPixelRef(std::move(pr), 0, 0);
240    if (nullptr == this->getPixels()) {
241        return reset_return_false(this);
242    }
243    SkDEBUGCODE(this->validate();)
244    return true;
245}
246
247bool SkBitmap::tryAllocPixelsFlags(const SkImageInfo& requestedInfo, uint32_t allocFlags) {
248    if (!this->setInfo(requestedInfo)) {
249        return reset_return_false(this);
250    }
251
252    // setInfo may have corrected info (e.g. 565 is always opaque).
253    const SkImageInfo& correctedInfo = this->info();
254
255    sk_sp<SkPixelRef> pr = (allocFlags & kZeroPixels_AllocFlag) ?
256        SkMallocPixelRef::MakeZeroed(correctedInfo, correctedInfo.minRowBytes()) :
257        SkMallocPixelRef::MakeAllocate(correctedInfo, correctedInfo.minRowBytes());
258    if (!pr) {
259        return reset_return_false(this);
260    }
261    this->setPixelRef(std::move(pr), 0, 0);
262    if (nullptr == this->getPixels()) {
263        return reset_return_false(this);
264    }
265    SkDEBUGCODE(this->validate();)
266    return true;
267}
268
269static void invoke_release_proc(void (*proc)(void* pixels, void* ctx), void* pixels, void* ctx) {
270    if (proc) {
271        proc(pixels, ctx);
272    }
273}
274
275bool SkBitmap::installPixels(const SkImageInfo& requestedInfo, void* pixels, size_t rb,
276                             void (*releaseProc)(void* addr, void* context), void* context) {
277    if (!this->setInfo(requestedInfo, rb)) {
278        invoke_release_proc(releaseProc, pixels, context);
279        this->reset();
280        return false;
281    }
282    if (nullptr == pixels) {
283        invoke_release_proc(releaseProc, pixels, context);
284        return true;    // we behaved as if they called setInfo()
285    }
286
287    // setInfo may have corrected info (e.g. 565 is always opaque).
288    const SkImageInfo& correctedInfo = this->info();
289
290    sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeWithProc(correctedInfo, rb, pixels,
291                                                          releaseProc, context);
292    if (!pr) {
293        this->reset();
294        return false;
295    }
296
297    this->setPixelRef(std::move(pr), 0, 0);
298    SkDEBUGCODE(this->validate();)
299    return true;
300}
301
302bool SkBitmap::installPixels(const SkPixmap& pixmap) {
303    return this->installPixels(pixmap.info(), pixmap.writable_addr(), pixmap.rowBytes(),
304                               nullptr, nullptr);
305}
306
307bool SkBitmap::installMaskPixels(const SkMask& mask) {
308    if (SkMask::kA8_Format != mask.fFormat) {
309        this->reset();
310        return false;
311    }
312    return this->installPixels(SkImageInfo::MakeA8(mask.fBounds.width(),
313                                                   mask.fBounds.height()),
314                               mask.fImage, mask.fRowBytes);
315}
316
317///////////////////////////////////////////////////////////////////////////////
318
319uint32_t SkBitmap::getGenerationID() const {
320    return fPixelRef ? fPixelRef->getGenerationID() : 0;
321}
322
323void SkBitmap::notifyPixelsChanged() const {
324    SkASSERT(!this->isImmutable());
325    if (fPixelRef) {
326        fPixelRef->notifyPixelsChanged();
327    }
328}
329
330///////////////////////////////////////////////////////////////////////////////
331
332/** We explicitly use the same allocator for our pixels that SkMask does,
333 so that we can freely assign memory allocated by one class to the other.
334 */
335bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst) {
336    const SkImageInfo info = dst->info();
337    if (kUnknown_SkColorType == info.colorType()) {
338//        SkDebugf("unsupported config for info %d\n", dst->config());
339        return false;
340    }
341
342    sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeAllocate(info, dst->rowBytes());
343    if (!pr) {
344        return false;
345    }
346
347    dst->setPixelRef(std::move(pr), 0, 0);
348    SkDEBUGCODE(dst->validate();)
349    return true;
350}
351
352///////////////////////////////////////////////////////////////////////////////
353
354bool SkBitmap::isImmutable() const {
355    return fPixelRef ? fPixelRef->isImmutable() : false;
356}
357
358void SkBitmap::setImmutable() {
359    if (fPixelRef) {
360        fPixelRef->setImmutable();
361    }
362}
363
364bool SkBitmap::isVolatile() const {
365    return (fFlags & kImageIsVolatile_Flag) != 0;
366}
367
368void SkBitmap::setIsVolatile(bool isVolatile) {
369    if (isVolatile) {
370        fFlags |= kImageIsVolatile_Flag;
371    } else {
372        fFlags &= ~kImageIsVolatile_Flag;
373    }
374}
375
376void* SkBitmap::getAddr(int x, int y) const {
377    SkASSERT((unsigned)x < (unsigned)this->width());
378    SkASSERT((unsigned)y < (unsigned)this->height());
379
380    char* base = (char*)this->getPixels();
381    if (base) {
382        base += y * this->rowBytes();
383        switch (this->colorType()) {
384            case kRGBA_F16_SkColorType:
385                base += x << 3;
386                break;
387            case kRGBA_8888_SkColorType:
388            case kBGRA_8888_SkColorType:
389                base += x << 2;
390                break;
391            case kARGB_4444_SkColorType:
392            case kRGB_565_SkColorType:
393                base += x << 1;
394                break;
395            case kAlpha_8_SkColorType:
396            case kGray_8_SkColorType:
397                base += x;
398                break;
399            default:
400                SkDEBUGFAIL("Can't return addr for config");
401                base = nullptr;
402                break;
403        }
404    }
405    return base;
406}
407
408///////////////////////////////////////////////////////////////////////////////
409///////////////////////////////////////////////////////////////////////////////
410
411void SkBitmap::erase(SkColor c, const SkIRect& area) const {
412    SkDEBUGCODE(this->validate();)
413
414    switch (this->colorType()) {
415        case kUnknown_SkColorType:
416            // TODO: can we ASSERT that we never get here?
417            return; // can't erase. Should we bzero so the memory is not uninitialized?
418        default:
419            break;
420    }
421
422    SkPixmap result;
423    if (!this->peekPixels(&result)) {
424        return;
425    }
426
427    if (result.erase(c, area)) {
428        this->notifyPixelsChanged();
429    }
430}
431
432void SkBitmap::eraseColor(SkColor c) const {
433    this->erase(c, SkIRect::MakeWH(this->width(), this->height()));
434}
435
436//////////////////////////////////////////////////////////////////////////////////////
437//////////////////////////////////////////////////////////////////////////////////////
438
439bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
440    SkDEBUGCODE(this->validate();)
441
442    if (nullptr == result || !fPixelRef) {
443        return false;   // no src pixels
444    }
445
446    SkIRect srcRect, r;
447    srcRect.set(0, 0, this->width(), this->height());
448    if (!r.intersect(srcRect, subset)) {
449        return false;   // r is empty (i.e. no intersection)
450    }
451
452    // If the upper left of the rectangle was outside the bounds of this SkBitmap, we should have
453    // exited above.
454    SkASSERT(static_cast<unsigned>(r.fLeft) < static_cast<unsigned>(this->width()));
455    SkASSERT(static_cast<unsigned>(r.fTop) < static_cast<unsigned>(this->height()));
456
457    SkBitmap dst;
458    dst.setInfo(this->info().makeWH(r.width(), r.height()), this->rowBytes());
459    dst.setIsVolatile(this->isVolatile());
460
461    if (fPixelRef) {
462        SkIPoint origin = this->pixelRefOrigin();
463        // share the pixelref with a custom offset
464        dst.setPixelRef(fPixelRef, origin.x() + r.fLeft, origin.y() + r.fTop);
465    }
466    SkDEBUGCODE(dst.validate();)
467
468    // we know we're good, so commit to result
469    result->swap(dst);
470    return true;
471}
472
473///////////////////////////////////////////////////////////////////////////////
474
475bool SkBitmap::readPixels(const SkImageInfo& requestedDstInfo, void* dstPixels, size_t dstRB,
476                          int x, int y, SkTransferFunctionBehavior behavior) const {
477    SkPixmap src;
478    if (!this->peekPixels(&src)) {
479        return false;
480    }
481    return src.readPixels(requestedDstInfo, dstPixels, dstRB, x, y, behavior);
482}
483
484bool SkBitmap::readPixels(const SkPixmap& dst, int srcX, int srcY) const {
485    return this->readPixels(dst.info(), dst.writable_addr(), dst.rowBytes(), srcX, srcY);
486}
487
488bool SkBitmap::writePixels(const SkPixmap& src, int dstX, int dstY,
489                           SkTransferFunctionBehavior behavior) {
490    if (!SkImageInfoValidConversion(this->info(), src.info())) {
491        return false;
492    }
493
494    SkWritePixelsRec rec(src.info(), src.addr(), src.rowBytes(), dstX, dstY);
495    if (!rec.trim(this->width(), this->height())) {
496        return false;
497    }
498
499    void* dstPixels = this->getAddr(rec.fX, rec.fY);
500    const SkImageInfo dstInfo = this->info().makeWH(rec.fInfo.width(), rec.fInfo.height());
501    SkConvertPixels(dstInfo, dstPixels, this->rowBytes(), rec.fInfo, rec.fPixels, rec.fRowBytes,
502                    nullptr, behavior);
503    return true;
504}
505
506///////////////////////////////////////////////////////////////////////////////
507
508static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha, int alphaRowBytes) {
509    SkASSERT(alpha != nullptr);
510    SkASSERT(alphaRowBytes >= src.width());
511
512    SkPixmap pmap;
513    if (!src.peekPixels(&pmap)) {
514        for (int y = 0; y < src.height(); ++y) {
515            memset(alpha, 0, src.width());
516            alpha += alphaRowBytes;
517        }
518        return false;
519    }
520    SkConvertPixels(SkImageInfo::MakeA8(pmap.width(), pmap.height()), alpha, alphaRowBytes,
521                    pmap.info(), pmap.addr(), pmap.rowBytes(), nullptr,
522                    SkTransferFunctionBehavior::kRespect);
523    return true;
524}
525
526#include "SkPaint.h"
527#include "SkMaskFilter.h"
528#include "SkMatrix.h"
529
530bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
531                            Allocator *allocator, SkIPoint* offset) const {
532    SkDEBUGCODE(this->validate();)
533
534    SkBitmap    tmpBitmap;
535    SkMatrix    identity;
536    SkMask      srcM, dstM;
537
538    srcM.fBounds.set(0, 0, this->width(), this->height());
539    srcM.fRowBytes = SkAlign4(this->width());
540    srcM.fFormat = SkMask::kA8_Format;
541
542    SkMaskFilter* filter = paint ? paint->getMaskFilter() : nullptr;
543
544    // compute our (larger?) dst bounds if we have a filter
545    if (filter) {
546        identity.reset();
547        if (!as_MFB(filter)->filterMask(&dstM, srcM, identity, nullptr)) {
548            goto NO_FILTER_CASE;
549        }
550        dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
551    } else {
552    NO_FILTER_CASE:
553        tmpBitmap.setInfo(SkImageInfo::MakeA8(this->width(), this->height()), srcM.fRowBytes);
554        if (!tmpBitmap.tryAllocPixels(allocator)) {
555            // Allocation of pixels for alpha bitmap failed.
556            SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
557                    tmpBitmap.width(), tmpBitmap.height());
558            return false;
559        }
560        GetBitmapAlpha(*this, tmpBitmap.getAddr8(0, 0), srcM.fRowBytes);
561        if (offset) {
562            offset->set(0, 0);
563        }
564        tmpBitmap.swap(*dst);
565        return true;
566    }
567    srcM.fImage = SkMask::AllocImage(srcM.computeImageSize());
568    SkAutoMaskFreeImage srcCleanup(srcM.fImage);
569
570    GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
571    if (!as_MFB(filter)->filterMask(&dstM, srcM, identity, nullptr)) {
572        goto NO_FILTER_CASE;
573    }
574    SkAutoMaskFreeImage dstCleanup(dstM.fImage);
575
576    tmpBitmap.setInfo(SkImageInfo::MakeA8(dstM.fBounds.width(), dstM.fBounds.height()),
577                      dstM.fRowBytes);
578    if (!tmpBitmap.tryAllocPixels(allocator)) {
579        // Allocation of pixels for alpha bitmap failed.
580        SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
581                tmpBitmap.width(), tmpBitmap.height());
582        return false;
583    }
584    memcpy(tmpBitmap.getPixels(), dstM.fImage, dstM.computeImageSize());
585    if (offset) {
586        offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
587    }
588    SkDEBUGCODE(tmpBitmap.validate();)
589
590    tmpBitmap.swap(*dst);
591    return true;
592}
593
594///////////////////////////////////////////////////////////////////////////////
595
596#ifdef SK_DEBUG
597void SkBitmap::validate() const {
598    this->info().validate();
599
600    // ImageInfo may not require this, but Bitmap ensures that opaque-only
601    // colorTypes report opaque for their alphatype
602    if (kRGB_565_SkColorType == this->colorType()) {
603        SkASSERT(kOpaque_SkAlphaType == this->alphaType());
604    }
605
606    SkASSERT(this->info().validRowBytes(this->rowBytes()));
607    uint8_t allFlags = kImageIsVolatile_Flag;
608#ifdef SK_BUILD_FOR_ANDROID
609    allFlags |= kHasHardwareMipMap_Flag;
610#endif
611    SkASSERT((~allFlags & fFlags) == 0);
612
613    if (fPixelRef && fPixelRef->pixels()) {
614        SkASSERT(this->getPixels());
615    } else {
616        SkASSERT(!this->getPixels());
617    }
618
619    if (this->getPixels()) {
620        SkASSERT(fPixelRef);
621        SkASSERT(fPixelRef->rowBytes() == this->rowBytes());
622        SkIPoint origin = this->pixelRefOrigin();
623        SkASSERT(origin.fX >= 0);
624        SkASSERT(origin.fY >= 0);
625        SkASSERT(fPixelRef->width() >= (int)this->width() + origin.fX);
626        SkASSERT(fPixelRef->height() >= (int)this->height() + origin.fY);
627        SkASSERT(fPixelRef->rowBytes() >= this->info().minRowBytes());
628    }
629}
630#endif
631
632#ifndef SK_IGNORE_TO_STRING
633#include "SkString.h"
634void SkBitmap::toString(SkString* str) const {
635
636    static const char* gColorTypeNames[kLastEnum_SkColorType + 1] = {
637        "UNKNOWN", "A8", "565", "4444", "RGBA", "BGRA", "INDEX8",
638    };
639
640    str->appendf("bitmap: ((%d, %d) %s", this->width(), this->height(),
641                 gColorTypeNames[this->colorType()]);
642
643    str->append(" (");
644    if (this->isOpaque()) {
645        str->append("opaque");
646    } else {
647        str->append("transparent");
648    }
649    if (this->isImmutable()) {
650        str->append(", immutable");
651    } else {
652        str->append(", not-immutable");
653    }
654    str->append(")");
655
656    str->appendf(" pixelref:%p", this->pixelRef());
657    str->append(")");
658}
659#endif
660
661///////////////////////////////////////////////////////////////////////////////
662
663bool SkBitmap::peekPixels(SkPixmap* pmap) const {
664    if (this->getPixels()) {
665        if (pmap) {
666            *pmap = fPixmap;
667        }
668        return true;
669    }
670    return false;
671}
672
673///////////////////////////////////////////////////////////////////////////////
674
675#ifdef SK_DEBUG
676void SkImageInfo::validate() const {
677    SkASSERT(fWidth >= 0);
678    SkASSERT(fHeight >= 0);
679    SkASSERT(SkColorTypeIsValid(fColorType));
680    SkASSERT(SkAlphaTypeIsValid(fAlphaType));
681}
682#endif
683