18d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/*
28d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Copyright 2014 Google Inc.
3c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt *
48d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Use of this source code is governed by a BSD-style license that can be
5c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt * found in the LICENSE file.
6c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt */
78d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
88d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "SkRecordOpts.h"
98d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "SkRecordPattern.h"
118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "SkRecords.h"
128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "SkTDArray.h"
138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtusing namespace SkRecords;
158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid SkRecordOptimize(SkRecord* record) {
178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    // TODO(mtklein): fuse independent optimizations to reduce number of passes?
188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    SkRecordNoopCulls(record);
198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    SkRecordNoopSaveRestores(record);
208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    // TODO(mtklein): figure out why we draw differently and reenable
218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    //SkRecordNoopSaveLayerDrawRestores(record);
228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
23b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt    SkRecordAnnotateCullingPairs(record);
248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    SkRecordReduceDrawPosTextStrength(record);  // Helpful to run this before BoundDrawPosTextH.
258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    SkRecordBoundDrawPosTextH(record);
268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// Most of the optimizations in this file are pattern-based.  These are all defined as structs with:
298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt//   - a Pattern typedef
308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt//   - a bool onMatch(SkRceord*, Pattern*, unsigned begin, unsigned end) method,
318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt//     which returns true if it made changes and false if not.
328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
33ea69e84a6f4455c59348485895d3d5e3af77a65bDmitry Shmidt// Run a pattern-based optimization once across the SkRecord, returning true if it made any changes.
34ea69e84a6f4455c59348485895d3d5e3af77a65bDmitry Shmidt// It looks for spans which match Pass::Pattern, and when found calls onMatch() with the pattern,
35ea69e84a6f4455c59348485895d3d5e3af77a65bDmitry Shmidt// record, and [begin,end) span of the commands that matched.
36ea69e84a6f4455c59348485895d3d5e3af77a65bDmitry Shmidttemplate <typename Pass>
37ea69e84a6f4455c59348485895d3d5e3af77a65bDmitry Shmidtstatic bool apply(Pass* pass, SkRecord* record) {
38ea69e84a6f4455c59348485895d3d5e3af77a65bDmitry Shmidt    typename Pass::Pattern pattern;
398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    bool changed = false;
408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    unsigned begin, end = 0;
418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    while (pattern.search(record, &begin, &end)) {
438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        changed |= pass->onMatch(record, &pattern, begin, end);
448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    }
458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    return changed;
468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstruct CullNooper {
498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    typedef Pattern3<Is<PushCull>, Star<Is<NoOp> >, Is<PopCull> > Pattern;
508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
51d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt    bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) {
528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        record->replace<NoOp>(begin);  // PushCull
538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        record->replace<NoOp>(end-1);  // PopCull
548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        return true;
558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    }
568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt};
578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid SkRecordNoopCulls(SkRecord* record) {
59b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt    CullNooper pass;
608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    while (apply(&pass, record));
618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
62b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt
63b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt// Turns the logical NoOp Save and Restore in Save-Draw*-Restore patterns into actual NoOps.
64b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidtstruct SaveOnlyDrawsRestoreNooper {
65b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt    typedef Pattern3<Is<Save>,
66b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt                     Star<Or<Is<NoOp>, IsDraw> >,
67b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt                     Is<Restore> >
68b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt        Pattern;
69b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt
70b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt    bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) {
71b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt        record->replace<NoOp>(begin);  // Save
72b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt        record->replace<NoOp>(end-1);  // Restore
738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        return true;
748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    }
751f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt};
76c2ebb4b85d69b65f552fee71ac68f44e8d87b39eDmitry Shmidt// Turns logical no-op Save-[non-drawing command]*-Restore patterns into actual no-ops.
771f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidtstruct SaveNoDrawsRestoreNooper {
788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    // Star matches greedily, so we also have to exclude Save and Restore.
798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    typedef Pattern3<Is<Save>,
808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                     Star<Not<Or3<Is<Save>,
818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                                  Is<Restore>,
828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                                  IsDraw> > >,
838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                     Is<Restore> >
848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        Pattern;
858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) {
878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        // If restore doesn't revert both matrix and clip, this isn't safe to noop away.
888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        if (pattern->first<Save>()->flags != SkCanvas::kMatrixClip_SaveFlag) {
898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt            return false;
908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        }
918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        // The entire span between Save and Restore (inclusively) does nothing.
938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        for (unsigned i = begin; i < end; i++) {
948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt            record->replace<NoOp>(i);
958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        }
968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        return true;
978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    }
988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt};
998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid SkRecordNoopSaveRestores(SkRecord* record) {
1008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    SaveOnlyDrawsRestoreNooper onlyDraws;
1018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    SaveNoDrawsRestoreNooper noDraws;
1028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    // Run until they stop changing things.
1048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    while (apply(&onlyDraws, record) || apply(&noDraws, record));
1058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// For some SaveLayer-[drawing command]-Restore patterns, merge the SaveLayer's alpha into the
1088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// draw, and no-op the SaveLayer and Restore.
1098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstruct SaveLayerDrawRestoreNooper {
1108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    typedef Pattern3<Is<SaveLayer>, IsDraw, Is<Restore> > Pattern;
1118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) {
1138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        SaveLayer* saveLayer = pattern->first<SaveLayer>();
1148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        if (saveLayer->bounds != NULL) {
1158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt            // SaveLayer with bounds is too tricky for us.
1168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt            return false;
1178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        }
1188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        SkPaint* layerPaint = saveLayer->paint;
1208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        if (NULL == layerPaint) {
1211f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt            // There wasn't really any point to this SaveLayer at all.
12204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt            return KillSaveLayerAndRestore(record, begin);
12304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt        }
12404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
12504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt        SkPaint* drawPaint = pattern->second<SkPaint>();
12604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt        if (drawPaint == NULL) {
12704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt            // We can just give the draw the SaveLayer's paint.
12804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt            // TODO(mtklein): figure out how to do this clearly
12904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt            return false;
13004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt        }
13104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
1321f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt        const uint32_t layerColor = layerPaint->getColor();
1338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        const uint32_t  drawColor =  drawPaint->getColor();
1348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        if (!IsOnlyAlpha(layerColor)  || !IsOpaque(drawColor) ||
1358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt            HasAnyEffect(*layerPaint) || HasAnyEffect(*drawPaint)) {
1368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt            // Too fancy for us.  Actually, as long as layerColor is just an alpha
1378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt            // we can blend it into drawColor's alpha; drawColor doesn't strictly have to be opaque.
1388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt            return false;
1398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        }
1408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        drawPaint->setColor(SkColorSetA(drawColor, SkColorGetA(layerColor)));
1428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        return KillSaveLayerAndRestore(record, begin);
1438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    }
144f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt
145f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt    static bool KillSaveLayerAndRestore(SkRecord* record, unsigned saveLayerIndex) {
146f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt        record->replace<NoOp>(saveLayerIndex);    // SaveLayer
147f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt        record->replace<NoOp>(saveLayerIndex+2);  // Restore
148f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt        return true;
149f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt    }
150f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt
1518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    static bool HasAnyEffect(const SkPaint& paint) {
1528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        return paint.getPathEffect()  ||
1538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt               paint.getShader()      ||
1548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt               paint.getXfermode()    ||
1558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt               paint.getMaskFilter()  ||
1568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt               paint.getColorFilter() ||
1578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt               paint.getRasterizer()  ||
1588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt               paint.getLooper()      ||
1598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt               paint.getImageFilter();
1608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    }
1618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    static bool IsOpaque(SkColor color) {
1638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        return SkColorGetA(color) == SK_AlphaOPAQUE;
1648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    }
1658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    static bool IsOnlyAlpha(SkColor color) {
1668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        return SK_ColorTRANSPARENT == SkColorSetA(color, SK_AlphaTRANSPARENT);
1678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    }
1688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt};
1698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid SkRecordNoopSaveLayerDrawRestores(SkRecord* record) {
1708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    SaveLayerDrawRestoreNooper pass;
1718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    apply(&pass, record);
1728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// Replaces DrawPosText with DrawPosTextH when all Y coordinates are equal.
1768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstruct StrengthReducer {
1778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    typedef Pattern1<Is<DrawPosText> > Pattern;
1788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) {
1808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        SkASSERT(end == begin + 1);
1818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        DrawPosText* draw = pattern->first<DrawPosText>();
1828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        const unsigned points = draw->paint.countText(draw->text, draw->byteLength);
1848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        if (points == 0) {
1858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt            return false;  // No point (ha!).
1868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        }
1878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        const SkScalar firstY = draw->pos[0].fY;
1898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        for (unsigned i = 1; i < points; i++) {
1908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt            if (draw->pos[i].fY != firstY) {
1918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                return false;  // Needs full power of DrawPosText.
1928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt            }
1938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        }
1948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        // All ys are the same.  We can replace DrawPosText with DrawPosTextH.
1958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        // draw->pos is points SkPoints, [(x,y),(x,y),(x,y),(x,y), ... ].
1978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        // We're going to squint and look at that as 2*points SkScalars, [x,y,x,y,x,y,x,y, ...].
1988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        // Then we'll rearrange things so all the xs are in order up front, clobbering the ys.
1998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        SK_COMPILE_ASSERT(sizeof(SkPoint) == 2 * sizeof(SkScalar), SquintingIsNotSafe);
2008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        SkScalar* scalars = &draw->pos[0].fX;
2018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        for (unsigned i = 0; i < 2*points; i += 2) {
2028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt            scalars[i/2] = scalars[i];
2038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        }
2048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        // Extend lifetime of draw to the end of the loop so we can copy its paint.
2068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        Adopted<DrawPosText> adopted(draw);
2078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        SkNEW_PLACEMENT_ARGS(record->replace<DrawPosTextH>(begin, adopted),
2088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                             DrawPosTextH,
209f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt                             (draw->paint, draw->text, draw->byteLength, scalars, firstY));
2108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        return true;
2118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    }
2128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt};
2138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid SkRecordReduceDrawPosTextStrength(SkRecord* record) {
214f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt    StrengthReducer pass;
2158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    apply(&pass, record);
2168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
2178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// Tries to replace DrawPosTextH with BoundedDrawPosTextH, which knows conservative upper and lower
2198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// bounds to use with SkCanvas::quickRejectY.
2208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstruct TextBounder {
2218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    typedef Pattern1<Is<DrawPosTextH> > Pattern;
2228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) {
2248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        SkASSERT(end == begin + 1);
2258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        DrawPosTextH* draw = pattern->first<DrawPosTextH>();
2268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        // If we're drawing vertical text, none of the checks we're about to do make any sense.
2288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        // We'll need to call SkPaint::computeFastBounds() later, so bail if that's not possible.
2298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        if (draw->paint.isVerticalText() || !draw->paint.canComputeFastBounds()) {
2308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt            return false;
2318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        }
2328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        // Rather than checking the top and bottom font metrics, we guess.  Actually looking up the
2341f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt        // top and bottom metrics is slow, and this overapproximation should be good enough.
2358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        const SkScalar buffer = draw->paint.getTextSize() * 1.5f;
2368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        SkDEBUGCODE(SkPaint::FontMetrics metrics;)
2378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        SkDEBUGCODE(draw->paint.getFontMetrics(&metrics);)
2388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        SkASSERT(-buffer <= metrics.fTop);
2398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        SkASSERT(+buffer >= metrics.fBottom);
2408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        // Let the paint adjust the text bounds.  We don't care about left and right here, so we use
2428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        // 0 and 1 respectively just so the bounds rectangle isn't empty.
2438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        SkRect bounds;
2448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        bounds.set(0, draw->y - buffer, SK_Scalar1, draw->y + buffer);
2458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        SkRect adjusted = draw->paint.computeFastBounds(bounds, &bounds);
2468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        Adopted<DrawPosTextH> adopted(draw);
2488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        SkNEW_PLACEMENT_ARGS(record->replace<BoundedDrawPosTextH>(begin, adopted),
2498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                             BoundedDrawPosTextH,
2508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                             (&adopted, adjusted.fTop, adjusted.fBottom));
2518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        return true;
2528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    }
2538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt};
2548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid SkRecordBoundDrawPosTextH(SkRecord* record) {
2558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    TextBounder pass;
2568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    apply(&pass, record);
2578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
2588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// Replaces PushCull with PairedPushCull, which lets us skip to the paired PopCull when the canvas
2608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// can quickReject the cull rect.
2618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// There's no efficient way (yet?) to express this one as a pattern, so we write a custom pass.
2628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtclass CullAnnotator {
2638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtpublic:
2648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    // Do nothing to most ops.
2658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    template <typename T> void operator()(T*) {}
2668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    void operator()(PushCull* push) {
2688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        Pair pair = { fIndex, push };
2698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        fPushStack.push(pair);
2708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    }
2718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    void operator()(PopCull* pop) {
2738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        Pair push = fPushStack.top();
2748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        fPushStack.pop();
2758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        SkASSERT(fIndex > push.index);
2778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        unsigned skip = fIndex - push.index;
2788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        Adopted<PushCull> adopted(push.command);
2808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        SkNEW_PLACEMENT_ARGS(fRecord->replace<PairedPushCull>(push.index, adopted),
2818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                             PairedPushCull, (&adopted, skip));
2828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    }
2838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    void apply(SkRecord* record) {
2858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        for (fRecord = record, fIndex = 0; fIndex < record->count(); fIndex++) {
2868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt            fRecord->mutate<void>(fIndex, *this);
2878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        }
2888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    }
2898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtprivate:
2918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    struct Pair {
2928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        unsigned index;
2938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        PushCull* command;
2948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    };
2958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    SkTDArray<Pair> fPushStack;
2978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    SkRecord* fRecord;
2988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    unsigned fIndex;
2998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt};
3008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid SkRecordAnnotateCullingPairs(SkRecord* record) {
3018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    CullAnnotator pass;
3028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    pass.apply(record);
3031f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt}
3041f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt