Index: src/core/SkCanvas.cpp |
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp |
index 2fe2fd58e8856c3ac0cf79011a7df8c276bdfac0..0b0434dab895963f0132fcbabe7ca99d93b3e8a2 100644 |
--- a/src/core/SkCanvas.cpp |
+++ b/src/core/SkCanvas.cpp |
@@ -22,7 +22,6 @@ |
#include "SkLatticeIter.h" |
#include "SkMatrixUtils.h" |
#include "SkMetaData.h" |
-#include "SkNx.h" |
#include "SkPaintPriv.h" |
#include "SkPatchUtils.h" |
#include "SkPicture.h" |
@@ -631,24 +630,16 @@ |
//////////////////////////////////////////////////////////////////////////// |
-static inline SkRect qr_clip_bounds(const SkIRect& bounds) { |
- // Expand bounds out by 1 in case we are anti-aliasing. We store the |
- // bounds as floats to enable a faster quick reject implementation. |
- SkRect dst; |
- SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft); |
- return dst; |
-} |
- |
void SkCanvas::resetForNextPicture(const SkIRect& bounds) { |
this->restoreToCount(1); |
+ fCachedLocalClipBounds.setEmpty(); |
+ fCachedLocalClipBoundsDirty = true; |
fClipStack->reset(); |
fMCRec->reset(bounds); |
// We're peering through a lot of structs here. Only at this scope do we |
// know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice). |
static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size()); |
- fDeviceClipBounds = qr_clip_bounds(bounds); |
- fConservativeIsScaleTranslate = true; |
} |
SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) { |
@@ -659,6 +650,8 @@ |
// const-cast. |
*const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag); |
+ fCachedLocalClipBounds.setEmpty(); |
+ fCachedLocalClipBoundsDirty = true; |
fAllowSoftClip = true; |
fAllowSimplifyClip = false; |
fDeviceCMDirty = true; |
@@ -687,10 +680,7 @@ |
SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry()); |
fMCRec->fLayer->fDevice = SkRef(device); |
fMCRec->fRasterClip.setRect(device->getGlobalBounds()); |
- fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds()); |
- fConservativeIsScaleTranslate = true; |
- } |
- |
+ } |
return device; |
} |
@@ -1105,8 +1095,8 @@ |
// early exit if the layer's bounds are clipped out |
if (!ir.intersect(clipBounds)) { |
if (BoundsAffectsClip(saveLayerFlags)) { |
+ fCachedLocalClipBoundsDirty = true; |
fMCRec->fRasterClip.setEmpty(); |
- fDeviceClipBounds.setEmpty(); |
} |
return false; |
} |
@@ -1117,9 +1107,9 @@ |
if (BoundsAffectsClip(saveLayerFlags)) { |
// Simplify the current clips since they will be applied properly during restore() |
+ fCachedLocalClipBoundsDirty = true; |
fClipStack->clipDevRect(ir, SkRegion::kReplace_Op); |
fMCRec->fRasterClip.setRect(ir); |
- fDeviceClipBounds = qr_clip_bounds(ir); |
} |
if (intersection) { |
@@ -1311,6 +1301,7 @@ |
SkASSERT(fMCStack.count() != 0); |
fDeviceCMDirty = true; |
+ fCachedLocalClipBoundsDirty = true; |
fClipStack->restore(); |
@@ -1344,11 +1335,6 @@ |
// no need to update fMCRec, 'cause we're killing the canvas |
} |
} |
- |
- if (fMCRec) { |
- fConservativeIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate(); |
- fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds()); |
- } |
} |
sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) { |
@@ -1503,20 +1489,21 @@ |
this->checkForDeferredSave(); |
fDeviceCMDirty = true; |
+ fCachedLocalClipBoundsDirty = true; |
fMCRec->fMatrix.preConcat(matrix); |
- fConservativeIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate(); |
+ |
this->didConcat(matrix); |
} |
void SkCanvas::internalSetMatrix(const SkMatrix& matrix) { |
fDeviceCMDirty = true; |
+ fCachedLocalClipBoundsDirty = true; |
fMCRec->fMatrix = matrix; |
} |
void SkCanvas::setMatrix(const SkMatrix& matrix) { |
this->checkForDeferredSave(); |
this->internalSetMatrix(matrix); |
- fConservativeIsScaleTranslate = matrix.isScaleTranslate(); |
this->didSetMatrix(matrix); |
} |
@@ -1588,7 +1575,6 @@ |
fClipStack->clipEmpty(); |
(void)fMCRec->fRasterClip.setEmpty(); |
- fDeviceClipBounds.setEmpty(); |
return; |
} |
} |
@@ -1618,6 +1604,7 @@ |
AutoValidateClip avc(this); |
fDeviceCMDirty = true; |
+ fCachedLocalClipBoundsDirty = true; |
if (isScaleTrans) { |
const bool isAA = kSoft_ClipEdgeStyle == edgeStyle; |
@@ -1633,8 +1620,6 @@ |
path.addRect(rect); |
this->SkCanvas::onClipPath(path, op, edgeStyle); |
} |
- |
- fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds()); |
} |
void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) { |
@@ -1653,6 +1638,7 @@ |
AutoValidateClip avc(this); |
fDeviceCMDirty = true; |
+ fCachedLocalClipBoundsDirty = true; |
if (!fAllowSoftClip) { |
edgeStyle = kHard_ClipEdgeStyle; |
} |
@@ -1661,7 +1647,6 @@ |
fMCRec->fRasterClip.op(transformedRRect, this->getTopLayerBounds(), op, |
kSoft_ClipEdgeStyle == edgeStyle); |
- fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds()); |
return; |
} |
@@ -1709,7 +1694,6 @@ |
fClipStack->clipEmpty(); |
(void)fMCRec->fRasterClip.setEmpty(); |
- fDeviceClipBounds.setEmpty(); |
return; |
} |
} |
@@ -1718,6 +1702,7 @@ |
AutoValidateClip avc(this); |
fDeviceCMDirty = true; |
+ fCachedLocalClipBoundsDirty = true; |
if (!fAllowSoftClip) { |
edgeStyle = kHard_ClipEdgeStyle; |
} |
@@ -1748,7 +1733,6 @@ |
} |
fMCRec->fRasterClip.op(devPath, this->getTopLayerBounds(), op, edgeStyle); |
- fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds()); |
} |
void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) { |
@@ -1760,13 +1744,13 @@ |
AutoValidateClip avc(this); |
fDeviceCMDirty = true; |
+ fCachedLocalClipBoundsDirty = true; |
// todo: signal fClipStack that we have a region, and therefore (I guess) |
// we have to ignore it, and use the region directly? |
fClipStack->clipDevRect(rgn.getBounds(), op); |
fMCRec->fRasterClip.op(rgn, op); |
- fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds()); |
} |
#ifdef SK_DEBUG |
@@ -1823,74 +1807,31 @@ |
return fMCRec->fRasterClip.isRect(); |
} |
-static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) { |
-#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 |
- __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec); |
- __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec); |
- __m128 mask = _mm_cmplt_ps(lLtT, RrBb); |
- return 0xF != _mm_movemask_ps(mask); |
-#elif defined(SK_ARM_HAS_NEON) |
- float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0]; |
- float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1]; |
- uint32x4_t mask = vcltq_f32(lLtT, RrBb); |
- return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask); |
-#else |
- SkRect devRectAsRect; |
- SkRect devClipAsRect; |
- devRect.store(&devRectAsRect.fLeft); |
- devClip.store(&devClipAsRect.fLeft); |
- return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect); |
-#endif |
-} |
- |
-// It's important for this function to not be inlined. Otherwise the compiler will share code |
-// between the fast path and the slow path, resulting in two slow paths. |
-static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip, |
- const SkMatrix& matrix) { |
- SkRect deviceRect; |
- matrix.mapRect(&deviceRect, src); |
- return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip); |
-} |
- |
-bool SkCanvas::quickReject(const SkRect& src) const { |
-#ifdef SK_DEBUG |
- // Verify that fDeviceClipBounds are set properly. |
- SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds()); |
+bool SkCanvas::quickReject(const SkRect& rect) const { |
+ if (!rect.isFinite()) |
+ return true; |
+ |
if (fMCRec->fRasterClip.isEmpty()) { |
- SkASSERT(fDeviceClipBounds.isEmpty() || tmp == fDeviceClipBounds); |
+ return true; |
+ } |
+ |
+ if (fMCRec->fMatrix.hasPerspective()) { |
+ SkRect dst; |
+ fMCRec->fMatrix.mapRect(&dst, rect); |
+ return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds()); |
} else { |
- SkASSERT(tmp == fDeviceClipBounds); |
- } |
- |
- // Verify that fConservativeIsScaleTranslate is set properly. |
- SkASSERT(!fConservativeIsScaleTranslate || fMCRec->fMatrix.isScaleTranslate()); |
-#endif |
- |
- if (!fConservativeIsScaleTranslate) { |
- return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix); |
- } |
- |
- // We inline the implementation of mapScaleTranslate() for the fast path. |
- float sx = fMCRec->fMatrix.getScaleX(); |
- float sy = fMCRec->fMatrix.getScaleY(); |
- float tx = fMCRec->fMatrix.getTranslateX(); |
- float ty = fMCRec->fMatrix.getTranslateY(); |
- Sk4f scale(sx, sy, sx, sy); |
- Sk4f trans(tx, ty, tx, ty); |
- |
- // Apply matrix. |
- Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans; |
- |
- // Make sure left < right, top < bottom. |
- Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]); |
- Sk4f min = Sk4f::Min(ltrb, rblt); |
- Sk4f max = Sk4f::Max(ltrb, rblt); |
- // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on |
- // ARM this sequence generates the fastest (a single instruction). |
- Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]); |
- |
- // Check if the device rect is NaN or outside the clip. |
- return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft)); |
+ const SkRect& clipR = this->getLocalClipBounds(); |
+ |
+ // for speed, do the most likely reject compares first |
+ // TODO: should we use | instead, or compare all 4 at once? |
+ if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) { |
+ return true; |
+ } |
+ if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) { |
+ return true; |
+ } |
+ return false; |
+ } |
} |
bool SkCanvas::quickReject(const SkPath& path) const { |