diff options
author | commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2014-04-14 20:35:12 +0000 |
---|---|---|
committer | commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2014-04-14 20:35:12 +0000 |
commit | 73b55eb7d789549ee9d6602ed96d2d9b74f3c6e3 (patch) | |
tree | 8984ad2f03a65900d4a8927be2be559a1c34f183 | |
parent | b17a24fedbec365827b92eb3b4021fd251ba9cb1 (diff) |
SkRecordDraw: use SkCanvas::quickRejectY for text draws
PS 1) factor apart into canSkip, draw, updateClip
PS 2) use quickRejectY for text
BUG=skia:2378
R=fmalita@chromium.org, reed@google.com, mtklein@google.com, tomhudson@google.com
Author: mtklein@chromium.org
Review URL: https://codereview.chromium.org/233493004
git-svn-id: http://skia.googlecode.com/svn/trunk@14192 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r-- | src/record/SkRecordDraw.cpp | 186 |
1 files changed, 126 insertions, 60 deletions
diff --git a/src/record/SkRecordDraw.cpp b/src/record/SkRecordDraw.cpp index 27eb7ae595..f0d3772e9f 100644 --- a/src/record/SkRecordDraw.cpp +++ b/src/record/SkRecordDraw.cpp @@ -17,79 +17,145 @@ public: unsigned index() const { return fIndex; } void next() { ++fIndex; } - // No base case, so we'll be compile-time checked that we implemented all possibilities below. - template <typename T> void operator()(const T&); + template <typename T> void operator()(const T& r) { + if (!this->canSkip(r)) { + this->draw(r); + this->updateClip<T>(); + } + } private: - // Must be called after any potential clip change. - void updateClip() { fClipEmpty = fCanvas->isClipEmpty(); } + // Can we skip this command right now? + template <typename T> bool canSkip(const T&) const { + // 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. + } SkCanvas* fCanvas; unsigned fIndex; bool fClipEmpty; }; -template <> void Draw::operator()(const SkRecords::PushCull& r) { - if (r.popOffset != SkRecords::kUnsetPopOffset && - fCanvas->quickReject(r.rect)) { - // We skip to the popCull, then the loop moves us just beyond it. - fIndex += r.popOffset; - } else { - fCanvas->pushCull(r.rect); +// 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 CAN_SKIP(T) template <> bool Draw::canSkip(const SkRecords::T&) const { return false; } +CAN_SKIP(Restore); +CAN_SKIP(Save); +CAN_SKIP(SaveLayer); +CAN_SKIP(Clear); +CAN_SKIP(PushCull); +CAN_SKIP(PopCull); +#undef CAN_SKIP + +// We can skip these commands if they're intersecting with a clip that's already empty. +#define CAN_SKIP(T) template <> bool Draw::canSkip(const SkRecords::T& r) const \ + { return fClipEmpty && SkRegion::kIntersect_Op == r.op; } +CAN_SKIP(ClipPath); +CAN_SKIP(ClipRRect); +CAN_SKIP(ClipRect); +CAN_SKIP(ClipRegion); +#undef CAN_SKIP + +static bool can_skip_text(const SkCanvas& c, const SkPaint& p, SkScalar minY, SkScalar maxY) { + // If we're drawing vertical text, none of the checks we're about to do make any sense. + // We use canComputeFastBounds as a proxy for "is this text going to be rectangular?". + if (p.isVerticalText() || !p.canComputeFastBounds()) { + return false; } + + // 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 = p.getTextSize() * 1.5f; + SkDEBUGCODE(SkPaint::FontMetrics metrics;) + SkDEBUGCODE(p.getFontMetrics(&metrics);) + SkASSERT(-buffer <= metrics.fTop); + SkASSERT(+buffer >= metrics.fBottom); + return c.quickRejectY(minY - buffer, maxY + buffer); +} + +template <> bool Draw::canSkip(const SkRecords::DrawPosTextH& r) const { + return fClipEmpty || can_skip_text(*fCanvas, r.paint, r.y, r.y); } -// These commands might change the clip. -#define CASE(T, call) \ - template <> void Draw::operator()(const SkRecords::T& r) { fCanvas->call; this->updateClip(); } -CASE(Restore, restore()); -CASE(SaveLayer, saveLayer(r.bounds, r.paint, r.flags)); -#undef CASE - -// These certainly do change the clip, -// but we can skip them if they're intersecting with a clip that's already empty. -#define CASE(T, call) template <> void Draw::operator()(const SkRecords::T& r) { \ - if (!(fClipEmpty && SkRegion::kIntersect_Op == r.op)) { fCanvas->call; this->updateClip(); } \ +template <> bool Draw::canSkip(const SkRecords::DrawPosText& r) const { + if (fClipEmpty) { + return true; + } + + // TODO(mtklein): may want to move this minY/maxY calculation into a one-time pass + const unsigned points = r.paint.countText(r.text, r.byteLength); + if (points == 0) { + return true; + } + SkScalar minY = SK_ScalarInfinity, maxY = SK_ScalarNegativeInfinity; + for (unsigned i = 0; i < points; i++) { + minY = SkTMin(minY, r.pos[i].fY); + maxY = SkTMax(maxY, r.pos[i].fY); + } + + return can_skip_text(*fCanvas, r.paint, minY, maxY); } -CASE(ClipPath, clipPath(r.path, r.op, r.doAA)); -CASE(ClipRRect, clipRRect(r.rrect, r.op, r.doAA)); -CASE(ClipRect, clipRect(r.rect, r.op, r.doAA)); -CASE(ClipRegion, clipRegion(r.region, r.op)); -#undef CASE - -// Commands which must run regardless of the clip, but don't change it themselves. -#define CASE(T, call) \ - template <> void Draw::operator()(const SkRecords::T& r) { fCanvas->call; } -CASE(Save, save(r.flags)); -CASE(Clear, clear(r.color)); -CASE(PopCull, popCull()); -#undef CASE - -// Nothing fancy below here. These commands respect and don't change the clip. -#define CASE(T, call) \ - template <> void Draw::operator()(const SkRecords::T& r) { if (!fClipEmpty) fCanvas->call; } -CASE(Concat, concat(r.matrix)); -CASE(SetMatrix, setMatrix(r.matrix)); - -CASE(DrawBitmap, drawBitmap(r.bitmap, r.left, r.top, r.paint)); -CASE(DrawBitmapMatrix, drawBitmapMatrix(r.bitmap, r.matrix, r.paint)); -CASE(DrawBitmapNine, drawBitmapNine(r.bitmap, r.center, r.dst, r.paint)); -CASE(DrawBitmapRectToRect, drawBitmapRectToRect(r.bitmap, r.src, r.dst, r.paint, r.flags)); -CASE(DrawDRRect, drawDRRect(r.outer, r.inner, r.paint)); -CASE(DrawOval, drawOval(r.oval, r.paint)); -CASE(DrawPaint, drawPaint(r.paint)); -CASE(DrawPath, drawPath(r.path, r.paint)); -CASE(DrawPoints, drawPoints(r.mode, r.count, r.pts, r.paint)); -CASE(DrawPosText, drawPosText(r.text, r.byteLength, r.pos, r.paint)); -CASE(DrawPosTextH, drawPosTextH(r.text, r.byteLength, r.xpos, r.y, r.paint)); -CASE(DrawRRect, drawRRect(r.rrect, r.paint)); -CASE(DrawRect, drawRect(r.rect, r.paint)); -CASE(DrawSprite, drawSprite(r.bitmap, r.left, r.top, r.paint)); -CASE(DrawText, drawText(r.text, r.byteLength, r.x, r.y, r.paint)); -CASE(DrawTextOnPath, drawTextOnPath(r.text, r.byteLength, r.path, r.matrix, r.paint)); -CASE(DrawVertices, drawVertices(r.vmode, r.vertexCount, r.vertices, r.texs, r.colors, + +#define DRAW(T, call) template <> void Draw::draw(const SkRecords::T& r) { fCanvas->call; } +DRAW(Restore, restore()); +DRAW(Save, save(r.flags)); +DRAW(SaveLayer, saveLayer(r.bounds, r.paint, r.flags)); +DRAW(PopCull, popCull()); +DRAW(Clear, clear(r.color)); +DRAW(Concat, concat(r.matrix)); +DRAW(SetMatrix, setMatrix(r.matrix)); + +DRAW(ClipPath, clipPath(r.path, r.op, r.doAA)); +DRAW(ClipRRect, clipRRect(r.rrect, r.op, r.doAA)); +DRAW(ClipRect, clipRect(r.rect, r.op, r.doAA)); +DRAW(ClipRegion, clipRegion(r.region, r.op)); + +DRAW(DrawBitmap, drawBitmap(r.bitmap, r.left, r.top, r.paint)); +DRAW(DrawBitmapMatrix, drawBitmapMatrix(r.bitmap, r.matrix, r.paint)); +DRAW(DrawBitmapNine, drawBitmapNine(r.bitmap, r.center, r.dst, r.paint)); +DRAW(DrawBitmapRectToRect, drawBitmapRectToRect(r.bitmap, r.src, r.dst, r.paint, r.flags)); +DRAW(DrawDRRect, drawDRRect(r.outer, r.inner, r.paint)); +DRAW(DrawOval, drawOval(r.oval, r.paint)); +DRAW(DrawPaint, drawPaint(r.paint)); +DRAW(DrawPath, drawPath(r.path, r.paint)); +DRAW(DrawPoints, drawPoints(r.mode, r.count, r.pts, r.paint)); +DRAW(DrawPosText, drawPosText(r.text, r.byteLength, r.pos, r.paint)); +DRAW(DrawPosTextH, drawPosTextH(r.text, r.byteLength, r.xpos, r.y, r.paint)); +DRAW(DrawRRect, drawRRect(r.rrect, r.paint)); +DRAW(DrawRect, drawRect(r.rect, r.paint)); +DRAW(DrawSprite, drawSprite(r.bitmap, r.left, r.top, r.paint)); +DRAW(DrawText, drawText(r.text, r.byteLength, r.x, r.y, r.paint)); +DRAW(DrawTextOnPath, drawTextOnPath(r.text, r.byteLength, r.path, r.matrix, r.paint)); +DRAW(DrawVertices, drawVertices(r.vmode, r.vertexCount, r.vertices, r.texs, r.colors, r.xmode.get(), r.indices, r.indexCount, r.paint)); -#undef CASE +#undef DRAW + +// PushCull is a bit of a oddball. We might be able to just skip until just past its popCull. +template <> void Draw::draw(const SkRecords::PushCull& r) { + if (r.popOffset != SkRecords::kUnsetPopOffset && fCanvas->quickReject(r.rect)) { + fIndex += r.popOffset; + } else { + fCanvas->pushCull(r.rect); + } +} } // namespace |