@@ -230,6 +230,7 @@ class SkCanvas::MCRec {
230
230
*/
231
231
DeviceCM* fTopLayer ;
232
232
std::unique_ptr<BackImage> fBackImage ;
233
+ SkConservativeClip fRasterClip ;
233
234
SkM44 fMatrix ;
234
235
int fDeferredSaveCount ;
235
236
@@ -242,7 +243,7 @@ class SkCanvas::MCRec {
242
243
// don't bother initializing fNext
243
244
inc_rec ();
244
245
}
245
- MCRec (const MCRec& prev) : fMatrix (prev.fMatrix ) {
246
+ MCRec (const MCRec& prev) : fRasterClip (prev. fRasterClip ), fMatrix (prev.fMatrix ) {
246
247
fLayer = nullptr ;
247
248
fTopLayer = prev.fTopLayer ;
248
249
fDeferredSaveCount = 0 ;
@@ -255,12 +256,13 @@ class SkCanvas::MCRec {
255
256
dec_rec ();
256
257
}
257
258
258
- void reset () {
259
+ void reset (const SkIRect& bounds ) {
259
260
SkASSERT (fLayer );
260
261
SkASSERT (fDeferredSaveCount == 0 );
261
262
fLayer ->validate ();
262
263
263
264
fMatrix .setIdentity ();
265
+ fRasterClip .setRect (bounds);
264
266
}
265
267
};
266
268
@@ -469,35 +471,37 @@ class AutoLayerForImageFilter {
469
471
470
472
#define DRAW_END }
471
473
472
- #define UPDATE_DEVICE_CLIP (code ) \
474
+ // TODO: conservativeUpdate is temporary to stage moving bounds tracking into SkNoPixelsDevice
475
+ #define UPDATE_DEVICE_CLIP (code, conservativeUpdate ) \
473
476
do { \
474
477
AutoValidateClip avc (this ); \
475
478
FOR_EACH_TOP_DEVICE (code); \
476
- fQuickRejectBounds = qr_clip_bounds (this ->computeDeviceClipBounds ()); \
479
+ fMCRec ->fRasterClip .conservativeUpdate ; \
480
+ fQuickRejectBounds = qr_clip_bounds (fMCRec ->fRasterClip .getBounds ()); \
477
481
} while (0 )
478
482
479
483
// //////////////////////////////////////////////////////////////////////////
480
484
481
- static inline SkRect qr_clip_bounds (const SkRect & bounds) {
485
+ static inline SkRect qr_clip_bounds (const SkIRect & bounds) {
482
486
if (bounds.isEmpty ()) {
483
487
return SkRect::MakeEmpty ();
484
488
}
485
489
486
490
// Expand bounds out by 1 in case we are anti-aliasing. We store the
487
491
// bounds as floats to enable a faster quick reject implementation.
488
492
SkRect dst;
489
- ( Sk4f ::Load (&bounds.fLeft ) + Sk4f (-1 . f , - 1 . f , 1 . f , 1 . f )).store (&dst.fLeft );
493
+ SkNx_cast< float >( Sk4i ::Load (&bounds.fLeft ) + Sk4i (-1 ,- 1 , 1 , 1 )).store (&dst.fLeft );
490
494
return dst;
491
495
}
492
496
493
497
void SkCanvas::resetForNextPicture (const SkIRect& bounds) {
494
498
this ->restoreToCount (1 );
495
- fMCRec ->reset ();
499
+ fMCRec ->reset (bounds );
496
500
497
501
// We're peering through a lot of structs here. Only at this scope do we
498
502
// know that the device is a SkNoPixelsDevice.
499
503
static_cast <SkNoPixelsDevice*>(fMCRec ->fLayer ->fDevice .get ())->resetForNextPicture (bounds);
500
- fQuickRejectBounds = qr_clip_bounds (SkRect::Make ( bounds) );
504
+ fQuickRejectBounds = qr_clip_bounds (bounds);
501
505
fIsScaleTranslate = true ;
502
506
}
503
507
@@ -515,6 +519,7 @@ void SkCanvas::init(sk_sp<SkBaseDevice> device) {
515
519
516
520
fMCRec = (MCRec*)fMCStack .push_back ();
517
521
new (fMCRec ) MCRec;
522
+ fMCRec ->fRasterClip .setDeviceClipRestriction (&fClipRestrictionRect );
518
523
fIsScaleTranslate = true ;
519
524
520
525
fMCRec ->fLayer = (DeviceCM*)fDeviceCMStorage ;
@@ -523,15 +528,19 @@ void SkCanvas::init(sk_sp<SkBaseDevice> device) {
523
528
fMCRec ->fTopLayer = fMCRec ->fLayer ;
524
529
525
530
fSurfaceBase = nullptr ;
531
+ fQuickRejectBounds = {0 , 0 , 0 , 0 };
526
532
527
533
if (device) {
528
534
// The root device and the canvas should always have the same pixel geometry
529
535
SkASSERT (fProps .pixelGeometry () == device->surfaceProps ().pixelGeometry ());
536
+ fMCRec ->fRasterClip .setRect (device->getGlobalBounds ());
537
+ fQuickRejectBounds = qr_clip_bounds (device->getGlobalBounds ());
538
+
530
539
device->androidFramework_setDeviceClipRestriction (&fClipRestrictionRect );
540
+
531
541
device->setMarkerStack (fMarkerStack .get ());
532
542
}
533
543
534
- fQuickRejectBounds = qr_clip_bounds (this ->computeDeviceClipBounds ());
535
544
fScratchGlyphRunBuilder = std::make_unique<SkGlyphRunBuilder>();
536
545
}
537
546
@@ -1158,13 +1167,15 @@ void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy stra
1158
1167
// replaceClip() simulates what a new top-level device's canvas would be in
1159
1168
// the non-recording scenario. This allows the canvas to report the expanding
1160
1169
// effects of image filters on the temporary clip bounds.
1161
- UPDATE_DEVICE_CLIP (device->replaceClip (ir));
1170
+ UPDATE_DEVICE_CLIP (device->replaceClip (ir),
1171
+ setRect (ir));
1162
1172
} else {
1163
1173
// else the layer device failed to be created, so the saveLayer() effectively
1164
1174
// becomes just a save(). The clipRegion() explicitly applies the bounds of the
1165
1175
// failed layer, without resetting the clip of the prior device that all subsequent
1166
1176
// nested draw calls need to respect.
1167
- UPDATE_DEVICE_CLIP (device->clipRegion (SkRegion (ir), SkClipOp::kIntersect ));
1177
+ UPDATE_DEVICE_CLIP (device->clipRegion (SkRegion (ir), SkClipOp::kIntersect ),
1178
+ opRegion (SkRegion (ir), SkRegion::kIntersect_Op ));
1168
1179
}
1169
1180
}
1170
1181
return ;
@@ -1185,6 +1196,10 @@ void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy stra
1185
1196
1186
1197
newDevice->setOrigin (fMCRec ->fMatrix , ir.fLeft , ir.fTop );
1187
1198
newDevice->androidFramework_setDeviceClipRestriction (&fClipRestrictionRect );
1199
+ if (boundsAffectsClip) {
1200
+ fMCRec ->fRasterClip .setRect (ir);
1201
+ fQuickRejectBounds = qr_clip_bounds (ir);
1202
+ }
1188
1203
1189
1204
if (layer->fNext ) {
1190
1205
// need to punch a hole in the previous device, so we don't draw there, given that
@@ -1195,8 +1210,6 @@ void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy stra
1195
1210
layer->fDevice ->clipRegion (hole, SkClipOp::kDifference );
1196
1211
} while (layer->fNext );
1197
1212
}
1198
-
1199
- fQuickRejectBounds = qr_clip_bounds (this ->computeDeviceClipBounds ());
1200
1213
}
1201
1214
1202
1215
int SkCanvas::saveLayerAlpha (const SkRect* bounds, U8CPU alpha) {
@@ -1304,10 +1317,7 @@ void SkCanvas::internalRestore() {
1304
1317
1305
1318
if (fMCRec ) {
1306
1319
fIsScaleTranslate = SkMatrixPriv::IsScaleTranslateAsM33 (fMCRec ->fMatrix );
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 ();
1320
+ fQuickRejectBounds = qr_clip_bounds (fMCRec ->fRasterClip .getBounds ());
1311
1321
}
1312
1322
}
1313
1323
@@ -1576,7 +1586,8 @@ void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
1576
1586
void SkCanvas::onClipRect (const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
1577
1587
SkASSERT (rect.isSorted ());
1578
1588
const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1579
- UPDATE_DEVICE_CLIP (device->clipRect (rect, op, isAA));
1589
+ UPDATE_DEVICE_CLIP (device->clipRect (rect, op, isAA), \
1590
+ opRect (rect, fMCRec ->fMatrix .asM33 (), this ->getTopLayerBounds (), (SkRegion::Op) op, isAA));
1580
1591
}
1581
1592
1582
1593
void SkCanvas::androidFramework_setDeviceClipRestriction (const SkIRect& rect) {
@@ -1586,12 +1597,14 @@ void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
1586
1597
// removing it (i.e. rect is empty).
1587
1598
this ->checkForDeferredSave ();
1588
1599
}
1589
- UPDATE_DEVICE_CLIP (device->androidFramework_setDeviceClipRestriction (&fClipRestrictionRect ));
1600
+ UPDATE_DEVICE_CLIP (device->androidFramework_setDeviceClipRestriction (&fClipRestrictionRect ),\
1601
+ opIRect (fClipRestrictionRect , SkRegion::kIntersect_Op ));
1590
1602
}
1591
1603
1592
1604
void SkCanvas::androidFramework_replaceClip (const SkIRect& rect) {
1593
1605
this ->checkForDeferredSave ();
1594
- UPDATE_DEVICE_CLIP (device->replaceClip (rect));
1606
+ UPDATE_DEVICE_CLIP (device->replaceClip (rect), \
1607
+ setRect (rect));
1595
1608
}
1596
1609
1597
1610
void SkCanvas::clipRRect (const SkRRect& rrect, SkClipOp op, bool doAA) {
@@ -1606,7 +1619,8 @@ void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
1606
1619
1607
1620
void SkCanvas::onClipRRect (const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
1608
1621
bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1609
- UPDATE_DEVICE_CLIP (device->clipRRect (rrect, op, isAA));
1622
+ UPDATE_DEVICE_CLIP (device->clipRRect (rrect, op, isAA), \
1623
+ opRRect (rrect, fMCRec ->fMatrix .asM33 (), this ->getTopLayerBounds (), (SkRegion::Op)op, isAA));
1610
1624
}
1611
1625
1612
1626
void SkCanvas::clipPath (const SkPath& path, SkClipOp op, bool doAA) {
@@ -1636,7 +1650,8 @@ void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
1636
1650
1637
1651
void SkCanvas::onClipPath (const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
1638
1652
bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1639
- UPDATE_DEVICE_CLIP (device->clipPath (path, op, isAA));
1653
+ UPDATE_DEVICE_CLIP (device->clipPath (path, op, isAA), \
1654
+ opPath (path, fMCRec ->fMatrix .asM33 (), this ->getTopLayerBounds (), (SkRegion::Op)op, isAA));
1640
1655
}
1641
1656
1642
1657
void SkCanvas::clipShader (sk_sp<SkShader> sh, SkClipOp op) {
@@ -1657,7 +1672,8 @@ void SkCanvas::clipShader(sk_sp<SkShader> sh, SkClipOp op) {
1657
1672
}
1658
1673
1659
1674
void SkCanvas::onClipShader (sk_sp<SkShader> sh, SkClipOp op) {
1660
- UPDATE_DEVICE_CLIP (device->clipShader (sh, op));
1675
+ UPDATE_DEVICE_CLIP (device->clipShader (sh, op),
1676
+ opShader (sh));
1661
1677
}
1662
1678
1663
1679
void SkCanvas::clipRegion (const SkRegion& rgn, SkClipOp op) {
@@ -1666,12 +1682,13 @@ void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
1666
1682
}
1667
1683
1668
1684
void SkCanvas::onClipRegion (const SkRegion& rgn, SkClipOp op) {
1669
- UPDATE_DEVICE_CLIP (device->clipRegion (rgn, op));
1685
+ UPDATE_DEVICE_CLIP (device->clipRegion (rgn, op),
1686
+ opRegion (rgn, (SkRegion::Op) op));
1670
1687
}
1671
1688
1672
1689
void SkCanvas::validateClip () const {
1673
1690
#ifdef SK_DEBUG
1674
- SkRect tmp = qr_clip_bounds (this ->computeDeviceClipBounds ());
1691
+ SkRect tmp = qr_clip_bounds (this ->fMCRec -> fRasterClip . getBounds ());
1675
1692
if (this ->isClipEmpty ()) {
1676
1693
SkASSERT (fQuickRejectBounds .isEmpty ());
1677
1694
} else {
@@ -1718,9 +1735,14 @@ void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) {
1718
1735
// /////////////////////////////////////////////////////////////////////////////
1719
1736
1720
1737
bool 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
1721
1742
SkBaseDevice* dev = this->getTopDevice();
1722
1743
// if no device we return true
1723
- return !dev || dev->onGetClipType () == SkBaseDevice::ClipType::kEmpty ;
1744
+ return !dev || dev->onGetClipType() == SkBaseDevice::kEmpty_ClipType;
1745
+ #endif
1724
1746
}
1725
1747
1726
1748
bool SkCanvas::isClipRect () const {
@@ -1819,28 +1841,7 @@ SkRect SkCanvas::getLocalClipBounds() const {
1819
1841
}
1820
1842
1821
1843
SkIRect SkCanvas::getDeviceClipBounds () const {
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;
1844
+ return fMCRec ->fRasterClip .getBounds ();
1844
1845
}
1845
1846
1846
1847
// /////////////////////////////////////////////////////////////////////
@@ -2452,10 +2453,7 @@ bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const
2452
2453
SkPoint pt;
2453
2454
ctm.mapXY (x, y, &pt);
2454
2455
SkIRect ir = SkIRect::MakeXYWH (SkScalarRoundToInt (pt.x ()), SkScalarRoundToInt (pt.y ()), w, h);
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 );
2456
+ return ir.contains (fMCRec ->fRasterClip .getBounds ());
2459
2457
}
2460
2458
2461
2459
// Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null)
0 commit comments