1 /* 2 * Copyright 2014 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "SkRecordOpts.h" 9 10 #include "SkRecordPattern.h" 11 #include "SkRecords.h" 12 #include "SkTDArray.h" 13 14 using namespace SkRecords; 15 16 void SkRecordOptimize(SkRecord* record) { 17 // This might be useful as a first pass in the future if we want to weed 18 // out junk for other optimization passes. Right now, nothing needs it, 19 // and the bounding box hierarchy will do the work of skipping no-op 20 // Save-NoDraw-Restore sequences better than we can here. 21 //SkRecordNoopSaveRestores(record); 22 23 SkRecordNoopSaveLayerDrawRestores(record); 24 } 25 26 // Most of the optimizations in this file are pattern-based. These are all defined as structs with: 27 // - a Pattern typedef 28 // - a bool onMatch(SkRceord*, Pattern*, unsigned begin, unsigned end) method, 29 // which returns true if it made changes and false if not. 30 31 // Run a pattern-based optimization once across the SkRecord, returning true if it made any changes. 32 // It looks for spans which match Pass::Pattern, and when found calls onMatch() with the pattern, 33 // record, and [begin,end) span of the commands that matched. 34 template <typename Pass> 35 static bool apply(Pass* pass, SkRecord* record) { 36 typename Pass::Pattern pattern; 37 bool changed = false; 38 unsigned begin, end = 0; 39 40 while (pattern.search(record, &begin, &end)) { 41 changed |= pass->onMatch(record, &pattern, begin, end); 42 } 43 return changed; 44 } 45 46 // Turns the logical NoOp Save and Restore in Save-Draw*-Restore patterns into actual NoOps. 47 struct SaveOnlyDrawsRestoreNooper { 48 typedef Pattern3<Is<Save>, 49 Star<Or<Is<NoOp>, IsDraw> >, 50 Is<Restore> > 51 Pattern; 52 53 bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) { 54 record->replace<NoOp>(begin); // Save 55 record->replace<NoOp>(end-1); // Restore 56 return true; 57 } 58 }; 59 // Turns logical no-op Save-[non-drawing command]*-Restore patterns into actual no-ops. 60 struct SaveNoDrawsRestoreNooper { 61 // Star matches greedily, so we also have to exclude Save and Restore. 62 // Nested SaveLayers need to be excluded, or we'll match their Restore! 63 typedef Pattern3<Is<Save>, 64 Star<Not<Or4<Is<Save>, 65 Is<SaveLayer>, 66 Is<Restore>, 67 IsDraw> > >, 68 Is<Restore> > 69 Pattern; 70 71 bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) { 72 // The entire span between Save and Restore (inclusively) does nothing. 73 for (unsigned i = begin; i < end; i++) { 74 record->replace<NoOp>(i); 75 } 76 return true; 77 } 78 }; 79 void SkRecordNoopSaveRestores(SkRecord* record) { 80 SaveOnlyDrawsRestoreNooper onlyDraws; 81 SaveNoDrawsRestoreNooper noDraws; 82 83 // Run until they stop changing things. 84 while (apply(&onlyDraws, record) || apply(&noDraws, record)); 85 } 86 87 // For some SaveLayer-[drawing command]-Restore patterns, merge the SaveLayer's alpha into the 88 // draw, and no-op the SaveLayer and Restore. 89 struct SaveLayerDrawRestoreNooper { 90 typedef Pattern3<Is<SaveLayer>, IsDraw, Is<Restore> > Pattern; 91 92 bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) { 93 SaveLayer* saveLayer = pattern->first<SaveLayer>(); 94 if (saveLayer->bounds != NULL) { 95 // SaveLayer with bounds is too tricky for us. 96 return false; 97 } 98 99 SkPaint* layerPaint = saveLayer->paint; 100 if (NULL == layerPaint) { 101 // There wasn't really any point to this SaveLayer at all. 102 return KillSaveLayerAndRestore(record, begin); 103 } 104 105 SkPaint* drawPaint = pattern->second<SkPaint>(); 106 if (drawPaint == NULL) { 107 // We can just give the draw the SaveLayer's paint. 108 // TODO(mtklein): figure out how to do this clearly 109 return false; 110 } 111 112 const uint32_t layerColor = layerPaint->getColor(); 113 const uint32_t drawColor = drawPaint->getColor(); 114 if (!IsOnlyAlpha(layerColor) || !IsOpaque(drawColor) || 115 HasAnyEffect(*layerPaint) || HasAnyEffect(*drawPaint)) { 116 // Too fancy for us. Actually, as long as layerColor is just an alpha 117 // we can blend it into drawColor's alpha; drawColor doesn't strictly have to be opaque. 118 return false; 119 } 120 121 drawPaint->setColor(SkColorSetA(drawColor, SkColorGetA(layerColor))); 122 return KillSaveLayerAndRestore(record, begin); 123 } 124 125 static bool KillSaveLayerAndRestore(SkRecord* record, unsigned saveLayerIndex) { 126 record->replace<NoOp>(saveLayerIndex); // SaveLayer 127 record->replace<NoOp>(saveLayerIndex+2); // Restore 128 return true; 129 } 130 131 static bool HasAnyEffect(const SkPaint& paint) { 132 return paint.getPathEffect() || 133 paint.getShader() || 134 paint.getXfermode() || 135 paint.getMaskFilter() || 136 paint.getColorFilter() || 137 paint.getRasterizer() || 138 paint.getLooper() || 139 paint.getImageFilter(); 140 } 141 142 static bool IsOpaque(SkColor color) { 143 return SkColorGetA(color) == SK_AlphaOPAQUE; 144 } 145 static bool IsOnlyAlpha(SkColor color) { 146 return SK_ColorTRANSPARENT == SkColorSetA(color, SK_AlphaTRANSPARENT); 147 } 148 }; 149 void SkRecordNoopSaveLayerDrawRestores(SkRecord* record) { 150 SaveLayerDrawRestoreNooper pass; 151 apply(&pass, record); 152 } 153 154