@@ -230,7 +230,6 @@ class SkCanvas::MCRec {
230230 */
231231 DeviceCM* fTopLayer ;
232232 std::unique_ptr<BackImage> fBackImage ;
233- SkConservativeClip fRasterClip ;
234233 SkM44 fMatrix ;
235234 int fDeferredSaveCount ;
236235
@@ -243,7 +242,7 @@ class SkCanvas::MCRec {
243242 // don't bother initializing fNext
244243 inc_rec ();
245244 }
246- MCRec (const MCRec& prev) : fRasterClip (prev. fRasterClip ), fMatrix (prev.fMatrix ) {
245+ MCRec (const MCRec& prev) : fMatrix (prev.fMatrix ) {
247246 fLayer = nullptr ;
248247 fTopLayer = prev.fTopLayer ;
249248 fDeferredSaveCount = 0 ;
@@ -256,13 +255,12 @@ class SkCanvas::MCRec {
256255 dec_rec ();
257256 }
258257
259- void reset (const SkIRect& bounds ) {
258+ void reset () {
260259 SkASSERT (fLayer );
261260 SkASSERT (fDeferredSaveCount == 0 );
262261 fLayer ->validate ();
263262
264263 fMatrix .setIdentity ();
265- fRasterClip .setRect (bounds);
266264 }
267265};
268266
@@ -471,37 +469,35 @@ class AutoLayerForImageFilter {
471469
472470#define DRAW_END }
473471
474- // TODO: conservativeUpdate is temporary to stage moving bounds tracking into SkNoPixelsDevice
475- #define UPDATE_DEVICE_CLIP (code, conservativeUpdate ) \
472+ #define UPDATE_DEVICE_CLIP (code ) \
476473 do { \
477474 AutoValidateClip avc (this ); \
478475 FOR_EACH_TOP_DEVICE (code); \
479- fMCRec ->fRasterClip .conservativeUpdate ; \
480- fQuickRejectBounds = qr_clip_bounds (fMCRec ->fRasterClip .getBounds ()); \
476+ fQuickRejectBounds = qr_clip_bounds (this ->computeDeviceClipBounds ()); \
481477 } while (0 )
482478
483479// //////////////////////////////////////////////////////////////////////////
484480
485- static inline SkRect qr_clip_bounds (const SkIRect & bounds) {
481+ static inline SkRect qr_clip_bounds (const SkRect & bounds) {
486482 if (bounds.isEmpty ()) {
487483 return SkRect::MakeEmpty ();
488484 }
489485
490486 // Expand bounds out by 1 in case we are anti-aliasing. We store the
491487 // bounds as floats to enable a faster quick reject implementation.
492488 SkRect dst;
493- SkNx_cast< float >( Sk4i ::Load (&bounds.fLeft ) + Sk4i (-1 ,- 1 , 1 , 1 )).store (&dst.fLeft );
489+ ( Sk4f ::Load (&bounds.fLeft ) + Sk4f (-1 . f , - 1 . f , 1 . f , 1 . f )).store (&dst.fLeft );
494490 return dst;
495491}
496492
497493void SkCanvas::resetForNextPicture (const SkIRect& bounds) {
498494 this ->restoreToCount (1 );
499- fMCRec ->reset (bounds );
495+ fMCRec ->reset ();
500496
501497 // We're peering through a lot of structs here. Only at this scope do we
502498 // know that the device is a SkNoPixelsDevice.
503499 static_cast <SkNoPixelsDevice*>(fMCRec ->fLayer ->fDevice .get ())->resetForNextPicture (bounds);
504- fQuickRejectBounds = qr_clip_bounds (bounds );
500+ fQuickRejectBounds = qr_clip_bounds (this -> computeDeviceClipBounds () );
505501 fIsScaleTranslate = true ;
506502}
507503
@@ -519,7 +515,6 @@ void SkCanvas::init(sk_sp<SkBaseDevice> device) {
519515
520516 fMCRec = (MCRec*)fMCStack .push_back ();
521517 new (fMCRec ) MCRec;
522- fMCRec ->fRasterClip .setDeviceClipRestriction (&fClipRestrictionRect );
523518 fIsScaleTranslate = true ;
524519
525520 fMCRec ->fLayer = (DeviceCM*)fDeviceCMStorage ;
@@ -528,19 +523,15 @@ void SkCanvas::init(sk_sp<SkBaseDevice> device) {
528523 fMCRec ->fTopLayer = fMCRec ->fLayer ;
529524
530525 fSurfaceBase = nullptr ;
531- fQuickRejectBounds = {0 , 0 , 0 , 0 };
532526
533527 if (device) {
534528 // The root device and the canvas should always have the same pixel geometry
535529 SkASSERT (fProps .pixelGeometry () == device->surfaceProps ().pixelGeometry ());
536- fMCRec ->fRasterClip .setRect (device->getGlobalBounds ());
537- fQuickRejectBounds = qr_clip_bounds (device->getGlobalBounds ());
538-
539530 device->androidFramework_setDeviceClipRestriction (&fClipRestrictionRect );
540-
541531 device->setMarkerStack (fMarkerStack .get ());
542532 }
543533
534+ fQuickRejectBounds = qr_clip_bounds (this ->computeDeviceClipBounds ());
544535 fScratchGlyphRunBuilder = std::make_unique<SkGlyphRunBuilder>();
545536}
546537
@@ -1167,15 +1158,13 @@ void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy stra
11671158 // replaceClip() simulates what a new top-level device's canvas would be in
11681159 // the non-recording scenario. This allows the canvas to report the expanding
11691160 // effects of image filters on the temporary clip bounds.
1170- UPDATE_DEVICE_CLIP (device->replaceClip (ir),
1171- setRect (ir));
1161+ UPDATE_DEVICE_CLIP (device->replaceClip (ir));
11721162 } else {
11731163 // else the layer device failed to be created, so the saveLayer() effectively
11741164 // becomes just a save(). The clipRegion() explicitly applies the bounds of the
11751165 // failed layer, without resetting the clip of the prior device that all subsequent
11761166 // nested draw calls need to respect.
1177- UPDATE_DEVICE_CLIP (device->clipRegion (SkRegion (ir), SkClipOp::kIntersect ),
1178- opRegion (SkRegion (ir), SkRegion::kIntersect_Op ));
1167+ UPDATE_DEVICE_CLIP (device->clipRegion (SkRegion (ir), SkClipOp::kIntersect ));
11791168 }
11801169 }
11811170 return ;
@@ -1196,10 +1185,6 @@ void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy stra
11961185
11971186 newDevice->setOrigin (fMCRec ->fMatrix , ir.fLeft , ir.fTop );
11981187 newDevice->androidFramework_setDeviceClipRestriction (&fClipRestrictionRect );
1199- if (boundsAffectsClip) {
1200- fMCRec ->fRasterClip .setRect (ir);
1201- fQuickRejectBounds = qr_clip_bounds (ir);
1202- }
12031188
12041189 if (layer->fNext ) {
12051190 // need to punch a hole in the previous device, so we don't draw there, given that
@@ -1210,6 +1195,8 @@ void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy stra
12101195 layer->fDevice ->clipRegion (hole, SkClipOp::kDifference );
12111196 } while (layer->fNext );
12121197 }
1198+
1199+ fQuickRejectBounds = qr_clip_bounds (this ->computeDeviceClipBounds ());
12131200}
12141201
12151202int SkCanvas::saveLayerAlpha (const SkRect* bounds, U8CPU alpha) {
@@ -1317,7 +1304,10 @@ void SkCanvas::internalRestore() {
13171304
13181305 if (fMCRec ) {
13191306 fIsScaleTranslate = SkMatrixPriv::IsScaleTranslateAsM33 (fMCRec ->fMatrix );
1320- fQuickRejectBounds = qr_clip_bounds (fMCRec ->fRasterClip .getBounds ());
1307+ // Update the quick-reject bounds in case the restore changed the top device or the
1308+ // removed save record had included modifications to the clip stack.
1309+ fQuickRejectBounds = qr_clip_bounds (this ->computeDeviceClipBounds ());
1310+ this ->validateClip ();
13211311 }
13221312}
13231313
@@ -1586,8 +1576,7 @@ void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
15861576void SkCanvas::onClipRect (const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
15871577 SkASSERT (rect.isSorted ());
15881578 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1589- UPDATE_DEVICE_CLIP (device->clipRect (rect, op, isAA), \
1590- opRect (rect, fMCRec ->fMatrix .asM33 (), this ->getTopLayerBounds (), (SkRegion::Op) op, isAA));
1579+ UPDATE_DEVICE_CLIP (device->clipRect (rect, op, isAA));
15911580}
15921581
15931582void SkCanvas::androidFramework_setDeviceClipRestriction (const SkIRect& rect) {
@@ -1597,14 +1586,12 @@ void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
15971586 // removing it (i.e. rect is empty).
15981587 this ->checkForDeferredSave ();
15991588 }
1600- UPDATE_DEVICE_CLIP (device->androidFramework_setDeviceClipRestriction (&fClipRestrictionRect ),\
1601- opIRect (fClipRestrictionRect , SkRegion::kIntersect_Op ));
1589+ UPDATE_DEVICE_CLIP (device->androidFramework_setDeviceClipRestriction (&fClipRestrictionRect ));
16021590}
16031591
16041592void SkCanvas::androidFramework_replaceClip (const SkIRect& rect) {
16051593 this ->checkForDeferredSave ();
1606- UPDATE_DEVICE_CLIP (device->replaceClip (rect), \
1607- setRect (rect));
1594+ UPDATE_DEVICE_CLIP (device->replaceClip (rect));
16081595}
16091596
16101597void SkCanvas::clipRRect (const SkRRect& rrect, SkClipOp op, bool doAA) {
@@ -1619,8 +1606,7 @@ void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
16191606
16201607void SkCanvas::onClipRRect (const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
16211608 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1622- UPDATE_DEVICE_CLIP (device->clipRRect (rrect, op, isAA), \
1623- opRRect (rrect, fMCRec ->fMatrix .asM33 (), this ->getTopLayerBounds (), (SkRegion::Op)op, isAA));
1609+ UPDATE_DEVICE_CLIP (device->clipRRect (rrect, op, isAA));
16241610}
16251611
16261612void SkCanvas::clipPath (const SkPath& path, SkClipOp op, bool doAA) {
@@ -1650,8 +1636,7 @@ void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
16501636
16511637void SkCanvas::onClipPath (const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
16521638 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1653- UPDATE_DEVICE_CLIP (device->clipPath (path, op, isAA), \
1654- opPath (path, fMCRec ->fMatrix .asM33 (), this ->getTopLayerBounds (), (SkRegion::Op)op, isAA));
1639+ UPDATE_DEVICE_CLIP (device->clipPath (path, op, isAA));
16551640}
16561641
16571642void SkCanvas::clipShader (sk_sp<SkShader> sh, SkClipOp op) {
@@ -1672,8 +1657,7 @@ void SkCanvas::clipShader(sk_sp<SkShader> sh, SkClipOp op) {
16721657}
16731658
16741659void SkCanvas::onClipShader (sk_sp<SkShader> sh, SkClipOp op) {
1675- UPDATE_DEVICE_CLIP (device->clipShader (sh, op),
1676- opShader (sh));
1660+ UPDATE_DEVICE_CLIP (device->clipShader (sh, op));
16771661}
16781662
16791663void SkCanvas::clipRegion (const SkRegion& rgn, SkClipOp op) {
@@ -1682,13 +1666,12 @@ void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
16821666}
16831667
16841668void SkCanvas::onClipRegion (const SkRegion& rgn, SkClipOp op) {
1685- UPDATE_DEVICE_CLIP (device->clipRegion (rgn, op),
1686- opRegion (rgn, (SkRegion::Op) op));
1669+ UPDATE_DEVICE_CLIP (device->clipRegion (rgn, op));
16871670}
16881671
16891672void SkCanvas::validateClip () const {
16901673#ifdef SK_DEBUG
1691- SkRect tmp = qr_clip_bounds (this ->fMCRec -> fRasterClip . getBounds ());
1674+ SkRect tmp = qr_clip_bounds (this ->computeDeviceClipBounds ());
16921675 if (this ->isClipEmpty ()) {
16931676 SkASSERT (fQuickRejectBounds .isEmpty ());
16941677 } else {
@@ -1735,14 +1718,9 @@ void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) {
17351718// /////////////////////////////////////////////////////////////////////////////
17361719
17371720bool SkCanvas::isClipEmpty () const {
1738- return fMCRec ->fRasterClip .isEmpty ();
1739-
1740- // TODO: should we only use the conservative answer in a recording canvas?
1741- #if 0
17421721 SkBaseDevice* dev = this ->getTopDevice ();
17431722 // if no device we return true
1744- return !dev || dev->onGetClipType() == SkBaseDevice::kEmpty_ClipType;
1745- #endif
1723+ return !dev || dev->onGetClipType () == SkBaseDevice::ClipType::kEmpty ;
17461724}
17471725
17481726bool SkCanvas::isClipRect () const {
@@ -1841,7 +1819,28 @@ SkRect SkCanvas::getLocalClipBounds() const {
18411819}
18421820
18431821SkIRect SkCanvas::getDeviceClipBounds () const {
1844- return fMCRec ->fRasterClip .getBounds ();
1822+ return this ->computeDeviceClipBounds ().roundOut ();
1823+ }
1824+
1825+ SkRect SkCanvas::computeDeviceClipBounds () const {
1826+ if (this ->isClipEmpty ()) {
1827+ return SkRect::MakeEmpty ();
1828+ }
1829+
1830+ // Compute the union of all top devices' device bounds, mapped into the global coordinate system
1831+ DeviceCM* layer = fMCRec ->fTopLayer ;
1832+ SkRect totalBounds = SkRect::MakeEmpty ();
1833+ while (layer) {
1834+ if (layer->fDevice ) {
1835+ SkIRect devClipBounds = layer->fDevice ->devClipBounds ();
1836+ SkRect layerBounds =
1837+ layer->fDevice ->deviceToGlobal ().mapRect (SkRect::Make (devClipBounds));
1838+ totalBounds.join (layerBounds);
1839+ }
1840+ layer = layer->fNext ;
1841+ }
1842+
1843+ return totalBounds;
18451844}
18461845
18471846// /////////////////////////////////////////////////////////////////////
@@ -2453,7 +2452,10 @@ bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const
24532452 SkPoint pt;
24542453 ctm.mapXY (x, y, &pt);
24552454 SkIRect ir = SkIRect::MakeXYWH (SkScalarRoundToInt (pt.x ()), SkScalarRoundToInt (pt.y ()), w, h);
2456- return ir.contains (fMCRec ->fRasterClip .getBounds ());
2455+ // quick bounds have been outset by 1px compared to overall device bounds, so this makes the
2456+ // contains check equivalent to between ir and device bounds
2457+ ir.outset (1 , 1 );
2458+ return ir.contains (fQuickRejectBounds );
24572459}
24582460
24592461// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
0 commit comments