#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:
}
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; }
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
#include "SkRecordOpts.h"
+#include "SkRecordTraits.h"
#include "SkRecords.h"
#include "SkTDArray.h"
explicit SaveRestoreNooper(SkRecord* record)
: Common(record), fSave(kInactive), fChanged(false) {}
- // Most drawing commands reset to inactive state without nooping anything.
+ // Drawing commands reset state to inactive without nooping.
template <typename T>
- void operator()(T*) { fSave = kInactive; }
+ SK_WHEN(SkRecords::IsDraw<T>, void) operator()(T*) { fSave = kInactive; }
+
+ // Most non-drawing commands can be ignored.
+ template <typename T>
+ SK_WHEN(!SkRecords::IsDraw<T>, void) operator()(T*) {}
+
+ void operator()(SkRecords::Save* r) {
+ fSave = SkCanvas::kMatrixClip_SaveFlag == r->flags ? this->index() : kInactive;
+ }
+
+ void operator()(SkRecords::Restore* r) {
+ if (fSave != kInactive) {
+ // Remove everything between the save and restore, inclusive on both sides.
+ fChanged = true;
+ for (unsigned i = fSave; i <= this->index(); i++) {
+ fRecord->replace<SkRecords::NoOp>(i);
+ }
+ fSave = kInactive;
+ }
+ }
bool changed() const { return fChanged; }
bool fChanged;
};
-// If the command doesn't draw anything, that doesn't reset the state back to inactive.
-// TODO(mtklein): do this with some sort of template-based trait mechanism instead of macros
-#define DOESNT_DRAW(T) template <> void SaveRestoreNooper::operator()(SkRecords::T*) {}
-DOESNT_DRAW(NoOp)
-DOESNT_DRAW(Concat)
-DOESNT_DRAW(SetMatrix)
-DOESNT_DRAW(ClipRect)
-DOESNT_DRAW(ClipRRect)
-DOESNT_DRAW(ClipPath)
-DOESNT_DRAW(ClipRegion)
-DOESNT_DRAW(PairedPushCull)
-DOESNT_DRAW(PushCull)
-DOESNT_DRAW(PopCull)
-#undef DOESNT_DRAW
-
-template <>
-void SaveRestoreNooper::operator()(SkRecords::Save* r) {
- fSave = SkCanvas::kMatrixClip_SaveFlag == r->flags ? this->index() : kInactive;
-}
-
-template <>
-void SaveRestoreNooper::operator()(SkRecords::Restore* r) {
- if (fSave != kInactive) {
- // Remove everything between the save and restore, inclusive on both sides.
- fChanged = true;
- for (unsigned i = fSave; i <= this->index(); i++) {
- fRecord->replace<SkRecords::NoOp>(i);
- }
- fSave = kInactive;
- }
-}
-
-
// Tries to replace PushCull with PairedPushCull, which lets us skip to the paired PopCull
// when the canvas can quickReject the cull rect.
class CullAnnotator : public Common {
explicit CullAnnotator(SkRecord* record) : Common(record) {}
// Do nothing to most ops.
- template <typename T>
- void operator()(T*) {}
+ template <typename T> void operator()(T*) {}
+
+ void operator()(SkRecords::PushCull* push) {
+ Pair pair = { this->index(), push };
+ fPushStack.push(pair);
+ }
+
+ void operator()(SkRecords::PopCull* pop) {
+ Pair push = fPushStack.top();
+ fPushStack.pop();
+
+ SkASSERT(this->index() > push.index);
+ unsigned skip = this->index() - push.index;
+
+ SkRecords::Adopted<SkRecords::PushCull> adopted(push.command);
+ SkNEW_PLACEMENT_ARGS(fRecord->replace<SkRecords::PairedPushCull>(push.index, adopted),
+ SkRecords::PairedPushCull, (&adopted, skip));
+ }
private:
struct Pair {
SkTDArray<Pair> fPushStack;
};
-template <>
-void CullAnnotator::operator()(SkRecords::PushCull* push) {
- Pair pair = { this->index(), push };
- fPushStack.push(pair);
-}
-
-template <>
-void CullAnnotator::operator()(SkRecords::PopCull* pop) {
- Pair push = fPushStack.top();
- fPushStack.pop();
-
- SkASSERT(this->index() > push.index);
- unsigned skip = this->index() - push.index;
-
- SkRecords::Adopted<SkRecords::PushCull> adopted(push.command);
- SkNEW_PLACEMENT_ARGS(fRecord->replace<SkRecords::PairedPushCull>(push.index, adopted),
- SkRecords::PairedPushCull, (&adopted, skip));
-}
-
// Replaces DrawPosText with DrawPosTextH when all Y coordinates are equal.
class StrengthReducer : public Common {
public:
explicit StrengthReducer(SkRecord* record) : Common(record) {}
// Do nothing to most ops.
- template <typename T>
- void operator()(T*) {}
-};
+ template <typename T> void operator()(T*) {}
-template <>
-void StrengthReducer::operator()(SkRecords::DrawPosText* r) {
- const unsigned points = r->paint.countText(r->text, r->byteLength);
- if (points == 0) {
- // No point (ha!).
- return;
- }
-
- const SkScalar firstY = r->pos[0].fY;
- for (unsigned i = 1; i < points; i++) {
- if (r->pos[i].fY != firstY) {
- // Needs the full strength of DrawPosText.
+ void operator()(SkRecords::DrawPosText* r) {
+ const unsigned points = r->paint.countText(r->text, r->byteLength);
+ if (points == 0) {
+ // No point (ha!).
return;
}
- }
- // All ys are the same. We can replace DrawPosText with DrawPosTextH.
-
- // r->pos is points SkPoints, [(x,y),(x,y),(x,y),(x,y), ... ].
- // We're going to squint and look at that as 2*points SkScalars, [x,y,x,y,x,y,x,y, ...].
- // Then we'll rearrange things so all the xs are in order up front, clobbering the ys.
- SK_COMPILE_ASSERT(sizeof(SkPoint) == 2 * sizeof(SkScalar), SquintingIsNotSafe);
- SkScalar* scalars = &r->pos[0].fX;
- for (unsigned i = 0; i < 2*points; i += 2) {
- scalars[i/2] = scalars[i];
- }
- // Extend lifetime of r to the end of the method so we can copy its parts.
- SkRecords::Adopted<SkRecords::DrawPosText> adopted(r);
- SkNEW_PLACEMENT_ARGS(fRecord->replace<SkRecords::DrawPosTextH>(this->index(), adopted),
- SkRecords::DrawPosTextH,
- (r->text, r->byteLength, scalars, firstY, r->paint));
-}
+ const SkScalar firstY = r->pos[0].fY;
+ for (unsigned i = 1; i < points; i++) {
+ if (r->pos[i].fY != firstY) {
+ // Needs the full strength of DrawPosText.
+ return;
+ }
+ }
+ // All ys are the same. We can replace DrawPosText with DrawPosTextH.
+
+ // r->pos is points SkPoints, [(x,y),(x,y),(x,y),(x,y), ... ].
+ // We're going to squint and look at that as 2*points SkScalars, [x,y,x,y,x,y,x,y, ...].
+ // Then we'll rearrange things so all the xs are in order up front, clobbering the ys.
+ SK_COMPILE_ASSERT(sizeof(SkPoint) == 2 * sizeof(SkScalar), SquintingIsNotSafe);
+ SkScalar* scalars = &r->pos[0].fX;
+ for (unsigned i = 0; i < 2*points; i += 2) {
+ scalars[i/2] = scalars[i];
+ }
+ // Extend lifetime of r to the end of the method so we can copy its parts.
+ SkRecords::Adopted<SkRecords::DrawPosText> adopted(r);
+ SkNEW_PLACEMENT_ARGS(fRecord->replace<SkRecords::DrawPosTextH>(this->index(), adopted),
+ SkRecords::DrawPosTextH,
+ (r->text, r->byteLength, scalars, firstY, r->paint));
+ }
+};
// Tries to replace DrawPosTextH with BoundedDrawPosTextH, which knows conservative upper and lower
// bounds to use with SkCanvas::quickRejectY.
explicit TextBounder(SkRecord* record) : Common(record) {}
// Do nothing to most ops.
- template <typename T>
- void operator()(T*) {}
-};
+ template <typename T> void operator()(T*) {}
-template <>
-void TextBounder::operator()(SkRecords::DrawPosTextH* r) {
- // If we're drawing vertical text, none of the checks we're about to do make any sense.
- // We'll need to call SkPaint::computeFastBounds() later, so bail if that's not possible.
- if (r->paint.isVerticalText() || !r->paint.canComputeFastBounds()) {
- return;
+ void operator()(SkRecords::DrawPosTextH* r) {
+ // If we're drawing vertical text, none of the checks we're about to do make any sense.
+ // We'll need to call SkPaint::computeFastBounds() later, so bail if that's not possible.
+ if (r->paint.isVerticalText() || !r->paint.canComputeFastBounds()) {
+ return;
+ }
+
+ // Rather than checking the top and bottom font metrics, we guess. Actually looking up the
+ // top and bottom metrics is slow, and this overapproximation should be good enough.
+ const SkScalar buffer = r->paint.getTextSize() * 1.5f;
+ SkDEBUGCODE(SkPaint::FontMetrics metrics;)
+ SkDEBUGCODE(r->paint.getFontMetrics(&metrics);)
+ SkASSERT(-buffer <= metrics.fTop);
+ SkASSERT(+buffer >= metrics.fBottom);
+
+ // Let the paint adjust the text bounds. We don't care about left and right here, so we use
+ // 0 and 1 respectively just so the bounds rectangle isn't empty.
+ SkRect bounds;
+ bounds.set(0, r->y - buffer, SK_Scalar1, r->y + buffer);
+ SkRect adjusted = r->paint.computeFastBounds(bounds, &bounds);
+
+ SkRecords::Adopted<SkRecords::DrawPosTextH> adopted(r);
+ SkNEW_PLACEMENT_ARGS(
+ fRecord->replace<SkRecords::BoundedDrawPosTextH>(this->index(), adopted),
+ SkRecords::BoundedDrawPosTextH,
+ (&adopted, adjusted.fTop, adjusted.fBottom));
}
+};
- // Rather than checking the top and bottom font metrics, we guess. Actually looking up the
- // top and bottom metrics is slow, and this overapproximation should be good enough.
- const SkScalar buffer = r->paint.getTextSize() * 1.5f;
- SkDEBUGCODE(SkPaint::FontMetrics metrics;)
- SkDEBUGCODE(r->paint.getFontMetrics(&metrics);)
- SkASSERT(-buffer <= metrics.fTop);
- SkASSERT(+buffer >= metrics.fBottom);
-
- // Let the paint adjust the text bounds. We don't care about left and right here, so we use
- // 0 and 1 respectively just so the bounds rectangle isn't empty.
- SkRect bounds;
- bounds.set(0, r->y - buffer, SK_Scalar1, r->y + buffer);
- SkRect adjusted = r->paint.computeFastBounds(bounds, &bounds);
-
- SkRecords::Adopted<SkRecords::DrawPosTextH> adopted(r);
- SkNEW_PLACEMENT_ARGS(fRecord->replace<SkRecords::BoundedDrawPosTextH>(this->index(), adopted),
- SkRecords::BoundedDrawPosTextH,
- (&adopted, adjusted.fTop, adjusted.fBottom));
-}
template <typename Pass>
static void run_pass(Pass& pass, SkRecord* record) {