1c4b21e6c03a6cdb03e116b9f510eb10cf8daedb1commit-bot@chromium.org/*
2c4b21e6c03a6cdb03e116b9f510eb10cf8daedb1commit-bot@chromium.org * Copyright 2014 Google Inc.
3c4b21e6c03a6cdb03e116b9f510eb10cf8daedb1commit-bot@chromium.org *
4c4b21e6c03a6cdb03e116b9f510eb10cf8daedb1commit-bot@chromium.org * Use of this source code is governed by a BSD-style license that can be
5c4b21e6c03a6cdb03e116b9f510eb10cf8daedb1commit-bot@chromium.org * found in the LICENSE file.
6c4b21e6c03a6cdb03e116b9f510eb10cf8daedb1commit-bot@chromium.org */
7c4b21e6c03a6cdb03e116b9f510eb10cf8daedb1commit-bot@chromium.org
8ad8ce572f69633ffebe2fa486275d82a5e9a71fecommit-bot@chromium.org#include "SkRecordOpts.h"
9506db0b7d2905c6bedba9fc5d4aeaf231a9a34eacommit-bot@chromium.org
1073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org#include "SkRecordPattern.h"
11506db0b7d2905c6bedba9fc5d4aeaf231a9a34eacommit-bot@chromium.org#include "SkRecords.h"
12506db0b7d2905c6bedba9fc5d4aeaf231a9a34eacommit-bot@chromium.org#include "SkTDArray.h"
13506db0b7d2905c6bedba9fc5d4aeaf231a9a34eacommit-bot@chromium.org
1473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgusing namespace SkRecords;
1573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
16ad8ce572f69633ffebe2fa486275d82a5e9a71fecommit-bot@chromium.orgvoid SkRecordOptimize(SkRecord* record) {
171e78fc4ed2a1ef9f049311696ebd0a26e1c3782dmtklein    // This might be useful  as a first pass in the future if we want to weed
181e78fc4ed2a1ef9f049311696ebd0a26e1c3782dmtklein    // out junk for other optimization passes.  Right now, nothing needs it,
191e78fc4ed2a1ef9f049311696ebd0a26e1c3782dmtklein    // and the bounding box hierarchy will do the work of skipping no-op
201e78fc4ed2a1ef9f049311696ebd0a26e1c3782dmtklein    // Save-NoDraw-Restore sequences better than we can here.
211e78fc4ed2a1ef9f049311696ebd0a26e1c3782dmtklein    //SkRecordNoopSaveRestores(record);
221e78fc4ed2a1ef9f049311696ebd0a26e1c3782dmtklein
2393f52a69443f9be16f4e98c21d1f6cf760a65f00mtklein    SkRecordNoopSaveLayerDrawRestores(record);
24ad8ce572f69633ffebe2fa486275d82a5e9a71fecommit-bot@chromium.org}
25ad8ce572f69633ffebe2fa486275d82a5e9a71fecommit-bot@chromium.org
2673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// Most of the optimizations in this file are pattern-based.  These are all defined as structs with:
2773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org//   - a Pattern typedef
2873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org//   - a bool onMatch(SkRceord*, Pattern*, unsigned begin, unsigned end) method,
2973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org//     which returns true if it made changes and false if not.
302e0c32af0508a1e544c9953ea2fe128dbae7d429commit-bot@chromium.org
3173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// Run a pattern-based optimization once across the SkRecord, returning true if it made any changes.
3273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// It looks for spans which match Pass::Pattern, and when found calls onMatch() with the pattern,
3373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// record, and [begin,end) span of the commands that matched.
3473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgtemplate <typename Pass>
3573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgstatic bool apply(Pass* pass, SkRecord* record) {
3673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    typename Pass::Pattern pattern;
3773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    bool changed = false;
3873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    unsigned begin, end = 0;
392e0c32af0508a1e544c9953ea2fe128dbae7d429commit-bot@chromium.org
4073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    while (pattern.search(record, &begin, &end)) {
4173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        changed |= pass->onMatch(record, &pattern, begin, end);
422e0c32af0508a1e544c9953ea2fe128dbae7d429commit-bot@chromium.org    }
4373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    return changed;
4473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org}
452e0c32af0508a1e544c9953ea2fe128dbae7d429commit-bot@chromium.org
46467705adf05ba99bbd9ccdf6a40eb463484a6fbfcommit-bot@chromium.org// Turns the logical NoOp Save and Restore in Save-Draw*-Restore patterns into actual NoOps.
47467705adf05ba99bbd9ccdf6a40eb463484a6fbfcommit-bot@chromium.orgstruct SaveOnlyDrawsRestoreNooper {
48467705adf05ba99bbd9ccdf6a40eb463484a6fbfcommit-bot@chromium.org    typedef Pattern3<Is<Save>,
49467705adf05ba99bbd9ccdf6a40eb463484a6fbfcommit-bot@chromium.org                     Star<Or<Is<NoOp>, IsDraw> >,
50467705adf05ba99bbd9ccdf6a40eb463484a6fbfcommit-bot@chromium.org                     Is<Restore> >
51467705adf05ba99bbd9ccdf6a40eb463484a6fbfcommit-bot@chromium.org        Pattern;
52467705adf05ba99bbd9ccdf6a40eb463484a6fbfcommit-bot@chromium.org
53467705adf05ba99bbd9ccdf6a40eb463484a6fbfcommit-bot@chromium.org    bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) {
54467705adf05ba99bbd9ccdf6a40eb463484a6fbfcommit-bot@chromium.org        record->replace<NoOp>(begin);  // Save
55467705adf05ba99bbd9ccdf6a40eb463484a6fbfcommit-bot@chromium.org        record->replace<NoOp>(end-1);  // Restore
56467705adf05ba99bbd9ccdf6a40eb463484a6fbfcommit-bot@chromium.org        return true;
57467705adf05ba99bbd9ccdf6a40eb463484a6fbfcommit-bot@chromium.org    }
58467705adf05ba99bbd9ccdf6a40eb463484a6fbfcommit-bot@chromium.org};
5973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// Turns logical no-op Save-[non-drawing command]*-Restore patterns into actual no-ops.
60467705adf05ba99bbd9ccdf6a40eb463484a6fbfcommit-bot@chromium.orgstruct SaveNoDrawsRestoreNooper {
6173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    // Star matches greedily, so we also have to exclude Save and Restore.
6299d6a9ee8b3516de892d118c71aa5e6e5c865efdmtklein    // Nested SaveLayers need to be excluded, or we'll match their Restore!
6373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    typedef Pattern3<Is<Save>,
6499d6a9ee8b3516de892d118c71aa5e6e5c865efdmtklein                     Star<Not<Or4<Is<Save>,
6599d6a9ee8b3516de892d118c71aa5e6e5c865efdmtklein                                  Is<SaveLayer>,
6673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org                                  Is<Restore>,
6773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org                                  IsDraw> > >,
6873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org                     Is<Restore> >
6973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        Pattern;
7073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
7173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) {
7273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        // The entire span between Save and Restore (inclusively) does nothing.
7373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        for (unsigned i = begin; i < end; i++) {
7473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org            record->replace<NoOp>(i);
7573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        }
7673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        return true;
772e0c32af0508a1e544c9953ea2fe128dbae7d429commit-bot@chromium.org    }
7888c3e279ab79125e5741b0b0b3175291e2e2bbeecommit-bot@chromium.org};
7973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgvoid SkRecordNoopSaveRestores(SkRecord* record) {
80467705adf05ba99bbd9ccdf6a40eb463484a6fbfcommit-bot@chromium.org    SaveOnlyDrawsRestoreNooper onlyDraws;
81467705adf05ba99bbd9ccdf6a40eb463484a6fbfcommit-bot@chromium.org    SaveNoDrawsRestoreNooper noDraws;
82467705adf05ba99bbd9ccdf6a40eb463484a6fbfcommit-bot@chromium.org
83467705adf05ba99bbd9ccdf6a40eb463484a6fbfcommit-bot@chromium.org    // Run until they stop changing things.
84467705adf05ba99bbd9ccdf6a40eb463484a6fbfcommit-bot@chromium.org    while (apply(&onlyDraws, record) || apply(&noDraws, record));
8573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org}
8688c3e279ab79125e5741b0b0b3175291e2e2bbeecommit-bot@chromium.org
87f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org// For some SaveLayer-[drawing command]-Restore patterns, merge the SaveLayer's alpha into the
88f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org// draw, and no-op the SaveLayer and Restore.
89f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.orgstruct SaveLayerDrawRestoreNooper {
90f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org    typedef Pattern3<Is<SaveLayer>, IsDraw, Is<Restore> > Pattern;
91f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org
92f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org    bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) {
93f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org        SaveLayer* saveLayer = pattern->first<SaveLayer>();
94f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org        if (saveLayer->bounds != NULL) {
95f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org            // SaveLayer with bounds is too tricky for us.
96f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org            return false;
97f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org        }
98f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org
99f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org        SkPaint* layerPaint = saveLayer->paint;
100f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org        if (NULL == layerPaint) {
101f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org            // There wasn't really any point to this SaveLayer at all.
102f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org            return KillSaveLayerAndRestore(record, begin);
103f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org        }
104f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org
105f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org        SkPaint* drawPaint = pattern->second<SkPaint>();
106f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org        if (drawPaint == NULL) {
107f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org            // We can just give the draw the SaveLayer's paint.
108f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org            // TODO(mtklein): figure out how to do this clearly
109f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org            return false;
110f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org        }
111f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org
112f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org        const uint32_t layerColor = layerPaint->getColor();
113f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org        const uint32_t  drawColor =  drawPaint->getColor();
114ee7e23d13f5400716715c1823ad6e41d9a5904cacommit-bot@chromium.org        if (!IsOnlyAlpha(layerColor)  || !IsOpaque(drawColor) ||
115ee7e23d13f5400716715c1823ad6e41d9a5904cacommit-bot@chromium.org            HasAnyEffect(*layerPaint) || HasAnyEffect(*drawPaint)) {
116f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org            // Too fancy for us.  Actually, as long as layerColor is just an alpha
117f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org            // we can blend it into drawColor's alpha; drawColor doesn't strictly have to be opaque.
118f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org            return false;
119f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org        }
120f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org
121f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org        drawPaint->setColor(SkColorSetA(drawColor, SkColorGetA(layerColor)));
122f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org        return KillSaveLayerAndRestore(record, begin);
123f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org    }
124f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org
125f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org    static bool KillSaveLayerAndRestore(SkRecord* record, unsigned saveLayerIndex) {
126f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org        record->replace<NoOp>(saveLayerIndex);    // SaveLayer
127f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org        record->replace<NoOp>(saveLayerIndex+2);  // Restore
128f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org        return true;
129f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org    }
130f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org
131f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org    static bool HasAnyEffect(const SkPaint& paint) {
132f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org        return paint.getPathEffect()  ||
133f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org               paint.getShader()      ||
134f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org               paint.getXfermode()    ||
135f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org               paint.getMaskFilter()  ||
136f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org               paint.getColorFilter() ||
137f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org               paint.getRasterizer()  ||
138f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org               paint.getLooper()      ||
139f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org               paint.getImageFilter();
140f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org    }
141f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org
142f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org    static bool IsOpaque(SkColor color) {
143f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org        return SkColorGetA(color) == SK_AlphaOPAQUE;
144f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org    }
145f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org    static bool IsOnlyAlpha(SkColor color) {
146f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org        return SK_ColorTRANSPARENT == SkColorSetA(color, SK_AlphaTRANSPARENT);
147f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org    }
148f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org};
149f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.orgvoid SkRecordNoopSaveLayerDrawRestores(SkRecord* record) {
150f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org    SaveLayerDrawRestoreNooper pass;
151f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org    apply(&pass, record);
152f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org}
153f5bf3cf0257dc3d18932bde51f8eae33442e071fcommit-bot@chromium.org
154