| Index: src/record/SkRecordDraw.cpp | 
| diff --git a/src/record/SkRecordDraw.cpp b/src/record/SkRecordDraw.cpp | 
| index 21b2c7a82b33a630e4769554dfa444cc05c13890..74549db66f50ecbfbab1c0af41cf65db1d59aadd 100644 | 
| --- a/src/record/SkRecordDraw.cpp | 
| +++ b/src/record/SkRecordDraw.cpp | 
| @@ -7,8 +7,16 @@ | 
|  | 
| #include "SkRecordDraw.h" | 
|  | 
| +#include "SkRecordTraits.h" | 
| + | 
| namespace { | 
|  | 
| +// All clip commands, Restore, and SaveLayer may change the clip. | 
| +template <typename T> struct ChangesClip { static const bool value = SkRecords::IsClip<T>::value; }; | 
| +template <> struct ChangesClip<SkRecords::Restore> { static const bool value = true; }; | 
| +template <> struct ChangesClip<SkRecords::SaveLayer> { static const bool value = true; }; | 
| + | 
| + | 
| // This is an SkRecord visitor that will draw that SkRecord to an SkCanvas. | 
| class Draw : SkNoncopyable { | 
| public: | 
| @@ -25,60 +33,44 @@ public: | 
| } | 
|  | 
| private: | 
| -    // Return true if we can skip this command, false if not. | 
| -    // Update fIndex here directly to skip more than just this one command. | 
| -    template <typename T> bool skip(const T&) { | 
| -        // We can skip most commands if the clip is empty.  Exceptions are specialized below. | 
| -        return fClipEmpty; | 
| -    } | 
| - | 
| // No base case, so we'll be compile-time checked that we implemented all possibilities below. | 
| template <typename T> void draw(const T&); | 
|  | 
| -    // Update fClipEmpty if necessary. | 
| -    template <typename T> void updateClip() { | 
| -        // Most commands don't change the clip.  Exceptions are specialized below. | 
| +    // skip() returns true if we can skip this command, false if not. | 
| +    // Update fIndex directly to skip more than just this one command. | 
| + | 
| +    // If we're drawing into an empty clip, we can skip it.  Otherwise, run the command. | 
| +    template <typename T> | 
| +    SK_WHEN(SkRecords::IsDraw<T>, bool) skip(const T&) { return fClipEmpty; } | 
| + | 
| +    template <typename T> | 
| +    SK_WHEN(!SkRecords::IsDraw<T>, bool) skip(const T&) { return false; } | 
| + | 
| +    // Special versions for commands added by optimizations. | 
| +    bool skip(const SkRecords::PairedPushCull& r) { | 
| +        if (fCanvas->quickReject(r.base->rect)) { | 
| +            fIndex += r.skip; | 
| +            return true; | 
| +        } | 
| +        return this->skip(*r.base); | 
| +    } | 
| + | 
| +    bool skip(const SkRecords::BoundedDrawPosTextH& r) { | 
| +        return this->skip(*r.base) || fCanvas->quickRejectY(r.minY, r.maxY); | 
| } | 
|  | 
| +    // If we might have changed the clip, update it, else do nothing. | 
| +    template <typename T> | 
| +    SK_WHEN(ChangesClip<T>, void) updateClip() { fClipEmpty = fCanvas->isClipEmpty(); } | 
| +    template <typename T> | 
| +    SK_WHEN(!ChangesClip<T>, void) updateClip() {} | 
| + | 
| SkCanvas* fCanvas; | 
| unsigned fIndex; | 
| bool fClipEmpty; | 
| }; | 
|  | 
| -// TODO(mtklein): do this specialization with template traits instead of macros | 
| - | 
| -// These commands may change the clip. | 
| -#define UPDATE_CLIP(T) template <> void Draw::updateClip<SkRecords::T>() \ | 
| -    { fClipEmpty = fCanvas->isClipEmpty(); } | 
| -UPDATE_CLIP(Restore); | 
| -UPDATE_CLIP(SaveLayer); | 
| -UPDATE_CLIP(ClipPath); | 
| -UPDATE_CLIP(ClipRRect); | 
| -UPDATE_CLIP(ClipRect); | 
| -UPDATE_CLIP(ClipRegion); | 
| -#undef UPDATE_CLIP | 
| - | 
| -// These commands must always run. | 
| -#define SKIP(T) template <> bool Draw::skip(const SkRecords::T&) { return false; } | 
| -SKIP(Restore); | 
| -SKIP(Save); | 
| -SKIP(SaveLayer); | 
| -SKIP(Clear); | 
| -SKIP(PushCull); | 
| -SKIP(PopCull); | 
| -#undef SKIP | 
| - | 
| -// We can skip these commands if they're intersecting with a clip that's already empty. | 
| -#define SKIP(T) template <> bool Draw::skip(const SkRecords::T& r) \ | 
| -    { return fClipEmpty && SkRegion::kIntersect_Op == r.op; } | 
| -SKIP(ClipPath); | 
| -SKIP(ClipRRect); | 
| -SKIP(ClipRect); | 
| -SKIP(ClipRegion); | 
| -#undef SKIP | 
| - | 
| -// NoOps can always be skipped and draw nothing. | 
| -template <> bool Draw::skip(const SkRecords::NoOp&) { return true; } | 
| +// NoOps draw nothing. | 
| template <> void Draw::draw(const SkRecords::NoOp&) {} | 
|  | 
| #define DRAW(T, call) template <> void Draw::draw(const SkRecords::T& r) { fCanvas->call; } | 
| @@ -116,25 +108,8 @@ DRAW(DrawVertices, drawVertices(r.vmode, r.vertexCount, r.vertices, r.texs, r.co | 
| r.xmode.get(), r.indices, r.indexCount, r.paint)); | 
| #undef DRAW | 
|  | 
| -// Added by SkRecordAnnotateCullingPairs. | 
| -template <> bool Draw::skip(const SkRecords::PairedPushCull& r) { | 
| -    if (fCanvas->quickReject(r.base->rect)) { | 
| -        fIndex += r.skip; | 
| -        return true; | 
| -    } | 
| -    return false; | 
| -} | 
| - | 
| -// Added by SkRecordBoundDrawPosTextH | 
| -template <> bool Draw::skip(const SkRecords::BoundedDrawPosTextH& r) { | 
| -    return fClipEmpty || fCanvas->quickRejectY(r.minY, r.maxY); | 
| -} | 
| - | 
| -// These draw by proxying to the commands they wrap.  (All the optimization is for skip().) | 
| -#define DRAW(T) template <> void Draw::draw(const SkRecords::T& r) { this->draw(*r.base); } | 
| -DRAW(PairedPushCull); | 
| -DRAW(BoundedDrawPosTextH); | 
| -#undef DRAW | 
| +template <> void Draw::draw(const SkRecords::PairedPushCull& r) { this->draw(*r.base); } | 
| +template <> void Draw::draw(const SkRecords::BoundedDrawPosTextH& r) { this->draw(*r.base); } | 
|  | 
| }  // namespace | 
|  | 
|  |