1/*
2 * Copyright 2013 Google Inc.
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 "SkBitmapDevice.h"
9#include "SkDraw.h"
10#include "SkImageFilter.h"
11#include "SkImageFilterCache.h"
12#include "SkMallocPixelRef.h"
13#include "SkMatrix.h"
14#include "SkPaint.h"
15#include "SkPath.h"
16#include "SkPixelRef.h"
17#include "SkPixmap.h"
18#include "SkRasterClip.h"
19#include "SkRasterHandleAllocator.h"
20#include "SkShader.h"
21#include "SkSpecialImage.h"
22#include "SkSurface.h"
23#include "SkTLazy.h"
24#include "SkVertices.h"
25
26class SkColorTable;
27
28static bool valid_for_bitmap_device(const SkImageInfo& info,
29                                    SkAlphaType* newAlphaType) {
30    if (info.width() < 0 || info.height() < 0) {
31        return false;
32    }
33
34    // TODO: can we stop supporting kUnknown in SkBitmkapDevice?
35    if (kUnknown_SkColorType == info.colorType()) {
36        if (newAlphaType) {
37            *newAlphaType = kUnknown_SkAlphaType;
38        }
39        return true;
40    }
41
42    SkAlphaType canonicalAlphaType = info.alphaType();
43
44    switch (info.colorType()) {
45        case kAlpha_8_SkColorType:
46        case kARGB_4444_SkColorType:
47        case kRGBA_8888_SkColorType:
48        case kBGRA_8888_SkColorType:
49        case kRGBA_1010102_SkColorType:
50        case kRGBA_F16_SkColorType:
51            break;
52        case kGray_8_SkColorType:
53        case kRGB_565_SkColorType:
54        case kRGB_888x_SkColorType:
55        case kRGB_101010x_SkColorType:
56            canonicalAlphaType = kOpaque_SkAlphaType;
57            break;
58        default:
59            return false;
60    }
61
62    if (newAlphaType) {
63        *newAlphaType = canonicalAlphaType;
64    }
65    return true;
66}
67
68SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap)
69    : INHERITED(bitmap.info(), SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType))
70    , fBitmap(bitmap)
71    , fRCStack(bitmap.width(), bitmap.height())
72{
73    SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr));
74}
75
76SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& info) {
77    return Create(info, SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType));
78}
79
80SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap, const SkSurfaceProps& surfaceProps,
81                               SkRasterHandleAllocator::Handle hndl)
82    : INHERITED(bitmap.info(), surfaceProps)
83    , fBitmap(bitmap)
84    , fRasterHandle(hndl)
85    , fRCStack(bitmap.width(), bitmap.height())
86{
87    SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr));
88}
89
90SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& origInfo,
91                                       const SkSurfaceProps& surfaceProps,
92                                       SkRasterHandleAllocator* allocator) {
93    SkAlphaType newAT = origInfo.alphaType();
94    if (!valid_for_bitmap_device(origInfo, &newAT)) {
95        return nullptr;
96    }
97
98    SkRasterHandleAllocator::Handle hndl = nullptr;
99    const SkImageInfo info = origInfo.makeAlphaType(newAT);
100    SkBitmap bitmap;
101
102    if (kUnknown_SkColorType == info.colorType()) {
103        if (!bitmap.setInfo(info)) {
104            return nullptr;
105        }
106    } else if (allocator) {
107        hndl = allocator->allocBitmap(info, &bitmap);
108        if (!hndl) {
109            return nullptr;
110        }
111    } else if (info.isOpaque()) {
112        // If this bitmap is opaque, we don't have any sensible default color,
113        // so we just return uninitialized pixels.
114        if (!bitmap.tryAllocPixels(info)) {
115            return nullptr;
116        }
117    } else {
118        // This bitmap has transparency, so we'll zero the pixels (to transparent).
119        // We use the flag as a faster alloc-then-eraseColor(SK_ColorTRANSPARENT).
120        if (!bitmap.tryAllocPixelsFlags(info, SkBitmap::kZeroPixels_AllocFlag)) {
121            return nullptr;
122        }
123    }
124
125    return new SkBitmapDevice(bitmap, surfaceProps, hndl);
126}
127
128void SkBitmapDevice::replaceBitmapBackendForRasterSurface(const SkBitmap& bm) {
129    SkASSERT(bm.width() == fBitmap.width());
130    SkASSERT(bm.height() == fBitmap.height());
131    fBitmap = bm;   // intent is to use bm's pixelRef (and rowbytes/config)
132    this->privateResize(fBitmap.info().width(), fBitmap.info().height());
133}
134
135SkBaseDevice* SkBitmapDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint*) {
136    const SkSurfaceProps surfaceProps(this->surfaceProps().flags(), cinfo.fPixelGeometry);
137    return SkBitmapDevice::Create(cinfo.fInfo, surfaceProps, cinfo.fAllocator);
138}
139
140bool SkBitmapDevice::onAccessPixels(SkPixmap* pmap) {
141    if (this->onPeekPixels(pmap)) {
142        fBitmap.notifyPixelsChanged();
143        return true;
144    }
145    return false;
146}
147
148bool SkBitmapDevice::onPeekPixels(SkPixmap* pmap) {
149    const SkImageInfo info = fBitmap.info();
150    if (fBitmap.getPixels() && (kUnknown_SkColorType != info.colorType())) {
151        pmap->reset(fBitmap.info(), fBitmap.getPixels(), fBitmap.rowBytes());
152        return true;
153    }
154    return false;
155}
156
157bool SkBitmapDevice::onWritePixels(const SkPixmap& pm, int x, int y) {
158    // since we don't stop creating un-pixeled devices yet, check for no pixels here
159    if (nullptr == fBitmap.getPixels()) {
160        return false;
161    }
162
163    if (fBitmap.writePixels(pm, x, y)) {
164        fBitmap.notifyPixelsChanged();
165        return true;
166    }
167    return false;
168}
169
170bool SkBitmapDevice::onReadPixels(const SkPixmap& pm, int x, int y) {
171    return fBitmap.readPixels(pm, x, y);
172}
173
174///////////////////////////////////////////////////////////////////////////////
175
176class SkBitmapDevice::BDDraw : public SkDraw {
177public:
178    BDDraw(SkBitmapDevice* dev) {
179        // we need fDst to be set, and if we're actually drawing, to dirty the genID
180        if (!dev->accessPixels(&fDst)) {
181            // NoDrawDevice uses us (why?) so we have to catch this case w/ no pixels
182            fDst.reset(dev->imageInfo(), nullptr, 0);
183        }
184        fMatrix = &dev->ctm();
185        fRC = &dev->fRCStack.rc();
186    }
187};
188
189void SkBitmapDevice::drawPaint(const SkPaint& paint) {
190    BDDraw(this).drawPaint(paint);
191}
192
193void SkBitmapDevice::drawPoints(SkCanvas::PointMode mode, size_t count,
194                                const SkPoint pts[], const SkPaint& paint) {
195    BDDraw(this).drawPoints(mode, count, pts, paint, nullptr);
196}
197
198void SkBitmapDevice::drawRect(const SkRect& r, const SkPaint& paint) {
199    BDDraw(this).drawRect(r, paint);
200}
201
202void SkBitmapDevice::drawOval(const SkRect& oval, const SkPaint& paint) {
203    SkPath path;
204    path.addOval(oval);
205    // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
206    // required to override drawOval.
207    this->drawPath(path, paint, nullptr, true);
208}
209
210void SkBitmapDevice::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
211#ifdef SK_IGNORE_BLURRED_RRECT_OPT
212    SkPath  path;
213
214    path.addRRect(rrect);
215    // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
216    // required to override drawRRect.
217    this->drawPath(path, paint, nullptr, true);
218#else
219    BDDraw(this).drawRRect(rrect, paint);
220#endif
221}
222
223void SkBitmapDevice::drawPath(const SkPath& path,
224                              const SkPaint& paint, const SkMatrix* prePathMatrix,
225                              bool pathIsMutable) {
226    BDDraw(this).drawPath(path, paint, prePathMatrix, pathIsMutable);
227}
228
229void SkBitmapDevice::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
230                                const SkPaint& paint) {
231    SkMatrix matrix = SkMatrix::MakeTrans(x, y);
232    LogDrawScaleFactor(SkMatrix::Concat(this->ctm(), matrix), paint.getFilterQuality());
233    BDDraw(this).drawBitmap(bitmap, matrix, nullptr, paint);
234}
235
236static inline bool CanApplyDstMatrixAsCTM(const SkMatrix& m, const SkPaint& paint) {
237    if (!paint.getMaskFilter()) {
238        return true;
239    }
240
241    // Some mask filters parameters (sigma) depend on the CTM/scale.
242    return m.getType() <= SkMatrix::kTranslate_Mask;
243}
244
245void SkBitmapDevice::drawBitmapRect(const SkBitmap& bitmap,
246                                    const SkRect* src, const SkRect& dst,
247                                    const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) {
248    SkMatrix    matrix;
249    SkRect      bitmapBounds, tmpSrc, tmpDst;
250    SkBitmap    tmpBitmap;
251
252    bitmapBounds.isetWH(bitmap.width(), bitmap.height());
253
254    // Compute matrix from the two rectangles
255    if (src) {
256        tmpSrc = *src;
257    } else {
258        tmpSrc = bitmapBounds;
259    }
260    matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
261
262    LogDrawScaleFactor(SkMatrix::Concat(this->ctm(), matrix), paint.getFilterQuality());
263
264    const SkRect* dstPtr = &dst;
265    const SkBitmap* bitmapPtr = &bitmap;
266
267    // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if
268    // needed (if the src was clipped). No check needed if src==null.
269    if (src) {
270        if (!bitmapBounds.contains(*src)) {
271            if (!tmpSrc.intersect(bitmapBounds)) {
272                return; // nothing to draw
273            }
274            // recompute dst, based on the smaller tmpSrc
275            matrix.mapRect(&tmpDst, tmpSrc);
276            dstPtr = &tmpDst;
277        }
278    }
279
280    if (src && !src->contains(bitmapBounds) &&
281        SkCanvas::kFast_SrcRectConstraint == constraint &&
282        paint.getFilterQuality() != kNone_SkFilterQuality) {
283        // src is smaller than the bounds of the bitmap, and we are filtering, so we don't know
284        // how much more of the bitmap we need, so we can't use extractSubset or drawBitmap,
285        // but we must use a shader w/ dst bounds (which can access all of the bitmap needed).
286        goto USE_SHADER;
287    }
288
289    if (src) {
290        // since we may need to clamp to the borders of the src rect within
291        // the bitmap, we extract a subset.
292        const SkIRect srcIR = tmpSrc.roundOut();
293        if (!bitmap.extractSubset(&tmpBitmap, srcIR)) {
294            return;
295        }
296        bitmapPtr = &tmpBitmap;
297
298        // Since we did an extract, we need to adjust the matrix accordingly
299        SkScalar dx = 0, dy = 0;
300        if (srcIR.fLeft > 0) {
301            dx = SkIntToScalar(srcIR.fLeft);
302        }
303        if (srcIR.fTop > 0) {
304            dy = SkIntToScalar(srcIR.fTop);
305        }
306        if (dx || dy) {
307            matrix.preTranslate(dx, dy);
308        }
309
310#ifdef SK_DRAWBITMAPRECT_FAST_OFFSET
311        SkRect extractedBitmapBounds = SkRect::MakeXYWH(dx, dy,
312                                                        SkIntToScalar(bitmapPtr->width()),
313                                                        SkIntToScalar(bitmapPtr->height()));
314#else
315        SkRect extractedBitmapBounds;
316        extractedBitmapBounds.isetWH(bitmapPtr->width(), bitmapPtr->height());
317#endif
318        if (extractedBitmapBounds == tmpSrc) {
319            // no fractional part in src, we can just call drawBitmap
320            goto USE_DRAWBITMAP;
321        }
322    } else {
323        USE_DRAWBITMAP:
324        // We can go faster by just calling drawBitmap, which will concat the
325        // matrix with the CTM, and try to call drawSprite if it can. If not,
326        // it will make a shader and call drawRect, as we do below.
327        if (CanApplyDstMatrixAsCTM(matrix, paint)) {
328            BDDraw(this).drawBitmap(*bitmapPtr, matrix, dstPtr, paint);
329            return;
330        }
331    }
332
333    USE_SHADER:
334
335    // TODO(herb): Move this over to SkArenaAlloc when arena alloc has a facility to return sk_sps.
336    // Since the shader need only live for our stack-frame, pass in a custom allocator. This
337    // can save malloc calls, and signals to SkMakeBitmapShader to not try to copy the bitmap
338    // if its mutable, since that precaution is not needed (give the short lifetime of the shader).
339
340    // construct a shader, so we can call drawRect with the dst
341    auto s = SkMakeBitmapShader(*bitmapPtr, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
342                                &matrix, kNever_SkCopyPixelsMode);
343    if (!s) {
344        return;
345    }
346
347    SkPaint paintWithShader(paint);
348    paintWithShader.setStyle(SkPaint::kFill_Style);
349    paintWithShader.setShader(s);
350
351    // Call ourself, in case the subclass wanted to share this setup code
352    // but handle the drawRect code themselves.
353    this->drawRect(*dstPtr, paintWithShader);
354}
355
356void SkBitmapDevice::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& paint) {
357    BDDraw(this).drawSprite(bitmap, x, y, paint);
358}
359
360void SkBitmapDevice::drawText(const void* text, size_t len,
361                              SkScalar x, SkScalar y, const SkPaint& paint) {
362    BDDraw(this).drawText((const char*)text, len, x, y, paint, &fSurfaceProps);
363}
364
365void SkBitmapDevice::drawPosText(const void* text, size_t len, const SkScalar xpos[],
366                                 int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) {
367    BDDraw(this).drawPosText((const char*)text, len, xpos, scalarsPerPos, offset, paint,
368                             &fSurfaceProps);
369}
370
371void SkBitmapDevice::drawVertices(const SkVertices* vertices, SkBlendMode bmode,
372                                  const SkPaint& paint) {
373    BDDraw(this).drawVertices(vertices->mode(), vertices->vertexCount(), vertices->positions(),
374                              vertices->texCoords(), vertices->colors(), bmode,
375                              vertices->indices(), vertices->indexCount(), paint);
376}
377
378void SkBitmapDevice::drawDevice(SkBaseDevice* device, int x, int y, const SkPaint& origPaint) {
379    SkASSERT(!origPaint.getImageFilter());
380
381    // todo: can we unify with similar adjustment in SkGpuDevice?
382    SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
383    if (paint->getMaskFilter()) {
384        paint.writable()->setMaskFilter(paint->getMaskFilter()->makeWithLocalMatrix(this->ctm()));
385    }
386
387    BDDraw(this).drawSprite(static_cast<SkBitmapDevice*>(device)->fBitmap, x, y, *paint);
388}
389
390///////////////////////////////////////////////////////////////////////////////
391
392namespace {
393
394class SkAutoDeviceClipRestore {
395public:
396    SkAutoDeviceClipRestore(SkBaseDevice* device, const SkIRect& clip)
397        : fDevice(device)
398        , fPrevCTM(device->ctm()) {
399        fDevice->save();
400        fDevice->setCTM(SkMatrix::I());
401        fDevice->clipRect(SkRect::Make(clip), SkClipOp::kIntersect, false);
402        fDevice->setCTM(fPrevCTM);
403    }
404
405    ~SkAutoDeviceClipRestore() {
406        fDevice->restore(fPrevCTM);
407    }
408
409private:
410    SkBaseDevice*  fDevice;
411    const SkMatrix fPrevCTM;
412};
413
414}  // anonymous ns
415
416void SkBitmapDevice::drawSpecial(SkSpecialImage* src, int x, int y, const SkPaint& origPaint,
417                                 SkImage* clipImage, const SkMatrix& clipMatrix) {
418    SkASSERT(!src->isTextureBacked());
419
420    sk_sp<SkSpecialImage> filteredImage;
421    SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
422
423    if (SkImageFilter* filter = paint->getImageFilter()) {
424        SkIPoint offset = SkIPoint::Make(0, 0);
425        const SkMatrix matrix = SkMatrix::Concat(
426            SkMatrix::MakeTrans(SkIntToScalar(-x), SkIntToScalar(-y)), this->ctm());
427        const SkIRect clipBounds = fRCStack.rc().getBounds().makeOffset(-x, -y);
428        sk_sp<SkImageFilterCache> cache(this->getImageFilterCache());
429        SkImageFilter::OutputProperties outputProperties(fBitmap.colorSpace());
430        SkImageFilter::Context ctx(matrix, clipBounds, cache.get(), outputProperties);
431
432        filteredImage = filter->filterImage(src, ctx, &offset);
433        if (!filteredImage) {
434            return;
435        }
436
437        src = filteredImage.get();
438        paint.writable()->setImageFilter(nullptr);
439        x += offset.x();
440        y += offset.y();
441    }
442
443    if (paint->getMaskFilter()) {
444        paint.writable()->setMaskFilter(paint->getMaskFilter()->makeWithLocalMatrix(this->ctm()));
445    }
446
447    if (!clipImage) {
448        SkBitmap resultBM;
449        if (src->getROPixels(&resultBM)) {
450            this->drawSprite(resultBM, x, y, *paint);
451        }
452        return;
453    }
454
455    // Clip image case.
456    sk_sp<SkImage> srcImage(src->asImage());
457    if (!srcImage) {
458        return;
459    }
460
461    const SkMatrix totalMatrix = SkMatrix::Concat(this->ctm(), clipMatrix);
462    SkRect clipBounds;
463    totalMatrix.mapRect(&clipBounds, SkRect::Make(clipImage->bounds()));
464    const SkIRect srcBounds = srcImage->bounds().makeOffset(x, y);
465
466    SkIRect maskBounds = fRCStack.rc().getBounds();
467    if (!maskBounds.intersect(clipBounds.roundOut()) || !maskBounds.intersect(srcBounds)) {
468        return;
469    }
470
471    sk_sp<SkImage> mask;
472    SkMatrix maskMatrix, shaderMatrix;
473    SkTLazy<SkAutoDeviceClipRestore> autoClipRestore;
474
475    SkMatrix totalInverse;
476    if (clipImage->isAlphaOnly() && totalMatrix.invert(&totalInverse)) {
477        // If the mask is already in A8 format, we can draw it directly
478        // (while compensating in the shader matrix).
479        mask = sk_ref_sp(clipImage);
480        maskMatrix = totalMatrix;
481        shaderMatrix = SkMatrix::Concat(totalInverse, SkMatrix::MakeTrans(x, y));
482
483        // If the mask is not fully contained within the src layer, we must clip.
484        if (!srcBounds.contains(clipBounds)) {
485            autoClipRestore.init(this, srcBounds);
486        }
487
488        maskBounds.offsetTo(0, 0);
489    } else {
490        // Otherwise, we convert the mask to A8 explicitly.
491        sk_sp<SkSurface> surf = SkSurface::MakeRaster(SkImageInfo::MakeA8(maskBounds.width(),
492                                                                          maskBounds.height()));
493        SkCanvas* canvas = surf->getCanvas();
494        canvas->translate(-maskBounds.x(), -maskBounds.y());
495        canvas->concat(totalMatrix);
496        canvas->drawImage(clipImage, 0, 0);
497
498        mask = surf->makeImageSnapshot();
499        maskMatrix = SkMatrix::I();
500        shaderMatrix = SkMatrix::MakeTrans(x - maskBounds.x(), y - maskBounds.y());
501    }
502
503    SkAutoDeviceCTMRestore adctmr(this, maskMatrix);
504    paint.writable()->setShader(srcImage->makeShader(&shaderMatrix));
505    this->drawImage(mask.get(), maskBounds.x(), maskBounds.y(), *paint);
506}
507
508sk_sp<SkSpecialImage> SkBitmapDevice::makeSpecial(const SkBitmap& bitmap) {
509    return SkSpecialImage::MakeFromRaster(bitmap.bounds(), bitmap);
510}
511
512sk_sp<SkSpecialImage> SkBitmapDevice::makeSpecial(const SkImage* image) {
513    return SkSpecialImage::MakeFromImage(SkIRect::MakeWH(image->width(), image->height()),
514                                         image->makeNonTextureImage(), fBitmap.colorSpace());
515}
516
517sk_sp<SkSpecialImage> SkBitmapDevice::snapSpecial() {
518    return this->makeSpecial(fBitmap);
519}
520
521///////////////////////////////////////////////////////////////////////////////
522
523sk_sp<SkSurface> SkBitmapDevice::makeSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
524    return SkSurface::MakeRaster(info, &props);
525}
526
527SkImageFilterCache* SkBitmapDevice::getImageFilterCache() {
528    SkImageFilterCache* cache = SkImageFilterCache::Get();
529    cache->ref();
530    return cache;
531}
532
533///////////////////////////////////////////////////////////////////////////////////////////////////
534
535bool SkBitmapDevice::onShouldDisableLCD(const SkPaint& paint) const {
536    if (kN32_SkColorType != fBitmap.colorType() ||
537        paint.getPathEffect() ||
538        paint.isFakeBoldText() ||
539        paint.getStyle() != SkPaint::kFill_Style ||
540        !paint.isSrcOver())
541    {
542        return true;
543    }
544    return false;
545}
546
547///////////////////////////////////////////////////////////////////////////////////////////////////
548
549void SkBitmapDevice::onSave() {
550    fRCStack.save();
551}
552
553void SkBitmapDevice::onRestore() {
554    fRCStack.restore();
555}
556
557void SkBitmapDevice::onClipRect(const SkRect& rect, SkClipOp op, bool aa) {
558    fRCStack.clipRect(this->ctm(), rect, op, aa);
559}
560
561void SkBitmapDevice::onClipRRect(const SkRRect& rrect, SkClipOp op, bool aa) {
562    fRCStack.clipRRect(this->ctm(), rrect, op, aa);
563}
564
565void SkBitmapDevice::onClipPath(const SkPath& path, SkClipOp op, bool aa) {
566    fRCStack.clipPath(this->ctm(), path, op, aa);
567}
568
569void SkBitmapDevice::onClipRegion(const SkRegion& rgn, SkClipOp op) {
570    SkIPoint origin = this->getOrigin();
571    SkRegion tmp;
572    const SkRegion* ptr = &rgn;
573    if (origin.fX | origin.fY) {
574        // translate from "global/canvas" coordinates to relative to this device
575        rgn.translate(-origin.fX, -origin.fY, &tmp);
576        ptr = &tmp;
577    }
578    fRCStack.clipRegion(*ptr, op);
579}
580
581void SkBitmapDevice::onSetDeviceClipRestriction(SkIRect* mutableClipRestriction) {
582    fRCStack.setDeviceClipRestriction(mutableClipRestriction);
583    if (!mutableClipRestriction->isEmpty()) {
584        SkRegion rgn(*mutableClipRestriction);
585        fRCStack.clipRegion(rgn, SkClipOp::kIntersect);
586    }
587}
588
589bool SkBitmapDevice::onClipIsAA() const {
590    const SkRasterClip& rc = fRCStack.rc();
591    return !rc.isEmpty() && rc.isAA();
592}
593
594void SkBitmapDevice::onAsRgnClip(SkRegion* rgn) const {
595    const SkRasterClip& rc = fRCStack.rc();
596    if (rc.isAA()) {
597        rgn->setRect(rc.getBounds());
598    } else {
599        *rgn = rc.bwRgn();
600    }
601}
602
603void SkBitmapDevice::validateDevBounds(const SkIRect& drawClipBounds) {
604#ifdef SK_DEBUG
605    const SkIRect& stackBounds = fRCStack.rc().getBounds();
606    SkASSERT(drawClipBounds == stackBounds);
607#endif
608}
609
610SkBaseDevice::ClipType SkBitmapDevice::onGetClipType() const {
611    const SkRasterClip& rc = fRCStack.rc();
612    if (rc.isEmpty()) {
613        return kEmpty_ClipType;
614    } else if (rc.isRect()) {
615        return kRect_ClipType;
616    } else {
617        return kComplex_ClipType;
618    }
619}
620