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