Home | History | Annotate | Download | only in core
      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 "SkCanvasPriv.h"
     11 #include "SkRecordPattern.h"
     12 #include "SkRecords.h"
     13 #include "SkTDArray.h"
     14 
     15 using namespace SkRecords;
     16 
     17 // Most of the optimizations in this file are pattern-based.  These are all defined as structs with:
     18 //   - a Match typedef
     19 //   - a bool onMatch(SkRceord*, Match*, int begin, int end) method,
     20 //     which returns true if it made changes and false if not.
     21 
     22 // Run a pattern-based optimization once across the SkRecord, returning true if it made any changes.
     23 // It looks for spans which match Pass::Match, and when found calls onMatch() with that pattern,
     24 // record, and [begin,end) span of the commands that matched.
     25 template <typename Pass>
     26 static bool apply(Pass* pass, SkRecord* record) {
     27     typename Pass::Match match;
     28     bool changed = false;
     29     int begin, end = 0;
     30 
     31     while (match.search(record, &begin, &end)) {
     32         changed |= pass->onMatch(record, &match, begin, end);
     33     }
     34     return changed;
     35 }
     36 
     37 ///////////////////////////////////////////////////////////////////////////////////////////////////
     38 
     39 static void multiple_set_matrices(SkRecord* record) {
     40     struct {
     41         typedef Pattern<Is<SetMatrix>,
     42                         Greedy<Is<NoOp>>,
     43                         Is<SetMatrix> >
     44             Match;
     45 
     46         bool onMatch(SkRecord* record, Match* pattern, int begin, int end) {
     47             record->replace<NoOp>(begin);  // first SetMatrix
     48             return true;
     49         }
     50     } pass;
     51     while (apply(&pass, record));
     52 }
     53 
     54 ///////////////////////////////////////////////////////////////////////////////////////////////////
     55 
     56 #if 0   // experimental, but needs knowledge of previous matrix to operate correctly
     57 static void apply_matrix_to_draw_params(SkRecord* record) {
     58     struct {
     59         typedef Pattern<Is<SetMatrix>,
     60                         Greedy<Is<NoOp>>,
     61                         Is<SetMatrix> >
     62             Pattern;
     63 
     64         bool onMatch(SkRecord* record, Pattern* pattern, int begin, int end) {
     65             record->replace<NoOp>(begin);  // first SetMatrix
     66             return true;
     67         }
     68     } pass;
     69     // No need to loop, as we never "open up" opportunities for more of this type of optimization.
     70     apply(&pass, record);
     71 }
     72 #endif
     73 
     74 ///////////////////////////////////////////////////////////////////////////////////////////////////
     75 
     76 // Turns the logical NoOp Save and Restore in Save-Draw*-Restore patterns into actual NoOps.
     77 struct SaveOnlyDrawsRestoreNooper {
     78     typedef Pattern<Is<Save>,
     79                     Greedy<Or<Is<NoOp>, IsDraw>>,
     80                     Is<Restore>>
     81         Match;
     82 
     83     bool onMatch(SkRecord* record, Match*, int begin, int end) {
     84         record->replace<NoOp>(begin);  // Save
     85         record->replace<NoOp>(end-1);  // Restore
     86         return true;
     87     }
     88 };
     89 
     90 static bool fold_opacity_layer_color_to_paint(const SkPaint* layerPaint,
     91                                               bool isSaveLayer,
     92                                               SkPaint* paint) {
     93     // We assume layerPaint is always from a saveLayer.  If isSaveLayer is
     94     // true, we assume paint is too.
     95 
     96     // The alpha folding can proceed if the filter layer paint does not have properties which cause
     97     // the resulting filter layer to be "blended" in complex ways to the parent layer. For example,
     98     // looper drawing unmodulated filter layer twice and then modulating the result produces
     99     // different image to drawing modulated filter layer twice.
    100     // TODO: most likely the looper and only some xfer modes are the hard constraints
    101     if (!paint->isSrcOver() || paint->getLooper()) {
    102         return false;
    103     }
    104 
    105     if (!isSaveLayer && paint->getImageFilter()) {
    106         // For normal draws, the paint color is used as one input for the color for the draw. Image
    107         // filter will operate on the result, and thus we can not change the input.
    108         // For layer saves, the image filter is applied to the layer contents. The layer is then
    109         // modulated with the paint color, so it's fine to proceed with the fold for saveLayer
    110         // paints with image filters.
    111         return false;
    112     }
    113 
    114     if (paint->getColorFilter()) {
    115         // Filter input depends on the paint color.
    116 
    117         // Here we could filter the color if we knew the draw is going to be uniform color.  This
    118         // should be detectable as drawPath/drawRect/.. without a shader being uniform, while
    119         // drawBitmap/drawSprite or a shader being non-uniform. However, current matchers don't
    120         // give the type out easily, so just do not optimize that at the moment.
    121         return false;
    122     }
    123 
    124     if (layerPaint) {
    125         const uint32_t layerColor = layerPaint->getColor();
    126         // The layer paint color must have only alpha component.
    127         if (SK_ColorTRANSPARENT != SkColorSetA(layerColor, SK_AlphaTRANSPARENT)) {
    128             return false;
    129         }
    130 
    131         // The layer paint can not have any effects.
    132         if (layerPaint->getPathEffect()  ||
    133             layerPaint->getShader()      ||
    134             !layerPaint->isSrcOver()     ||
    135             layerPaint->getMaskFilter()  ||
    136             layerPaint->getColorFilter() ||
    137             layerPaint->getLooper()      ||
    138             layerPaint->getImageFilter()) {
    139             return false;
    140         }
    141         paint->setAlpha(SkMulDiv255Round(paint->getAlpha(), SkColorGetA(layerColor)));
    142     }
    143 
    144     return true;
    145 }
    146 
    147 // Turns logical no-op Save-[non-drawing command]*-Restore patterns into actual no-ops.
    148 struct SaveNoDrawsRestoreNooper {
    149     // Greedy matches greedily, so we also have to exclude Save and Restore.
    150     // Nested SaveLayers need to be excluded, or we'll match their Restore!
    151     typedef Pattern<Is<Save>,
    152                     Greedy<Not<Or<Is<Save>,
    153                                   Is<SaveLayer>,
    154                                   Is<Restore>,
    155                                   IsDraw>>>,
    156                     Is<Restore>>
    157         Match;
    158 
    159     bool onMatch(SkRecord* record, Match*, int begin, int end) {
    160         // The entire span between Save and Restore (inclusively) does nothing.
    161         for (int i = begin; i < end; i++) {
    162             record->replace<NoOp>(i);
    163         }
    164         return true;
    165     }
    166 };
    167 void SkRecordNoopSaveRestores(SkRecord* record) {
    168     SaveOnlyDrawsRestoreNooper onlyDraws;
    169     SaveNoDrawsRestoreNooper noDraws;
    170 
    171     // Run until they stop changing things.
    172     while (apply(&onlyDraws, record) || apply(&noDraws, record));
    173 }
    174 
    175 #ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
    176 static bool effectively_srcover(const SkPaint* paint) {
    177     if (!paint || paint->isSrcOver()) {
    178         return true;
    179     }
    180     // src-mode with opaque and no effects (which might change opaqueness) is ok too.
    181     return !paint->getShader() && !paint->getColorFilter() && !paint->getImageFilter() &&
    182            0xFF == paint->getAlpha() && paint->getBlendMode() == SkBlendMode::kSrc;
    183 }
    184 
    185 // For some SaveLayer-[drawing command]-Restore patterns, merge the SaveLayer's alpha into the
    186 // draw, and no-op the SaveLayer and Restore.
    187 struct SaveLayerDrawRestoreNooper {
    188     typedef Pattern<Is<SaveLayer>, IsDraw, Is<Restore>> Match;
    189 
    190     bool onMatch(SkRecord* record, Match* match, int begin, int end) {
    191         if (match->first<SaveLayer>()->backdrop || match->first<SaveLayer>()->clipMask) {
    192             // can't throw away the layer if we have a backdrop or clip mask
    193             return false;
    194         }
    195 
    196         if (match->first<SaveLayer>()->saveLayerFlags &
    197                 SkCanvasPriv::kDontClipToLayer_SaveLayerFlag) {
    198             // can't throw away the layer if set
    199             return false;
    200         }
    201 
    202         // A SaveLayer's bounds field is just a hint, so we should be free to ignore it.
    203         SkPaint* layerPaint = match->first<SaveLayer>()->paint;
    204         SkPaint* drawPaint = match->second<SkPaint>();
    205 
    206         if (nullptr == layerPaint && effectively_srcover(drawPaint)) {
    207             // There wasn't really any point to this SaveLayer at all.
    208             return KillSaveLayerAndRestore(record, begin);
    209         }
    210 
    211         if (drawPaint == nullptr) {
    212             // We can just give the draw the SaveLayer's paint.
    213             // TODO(mtklein): figure out how to do this clearly
    214             return false;
    215         }
    216 
    217         if (!fold_opacity_layer_color_to_paint(layerPaint, false /*isSaveLayer*/, drawPaint)) {
    218             return false;
    219         }
    220 
    221         return KillSaveLayerAndRestore(record, begin);
    222     }
    223 
    224     static bool KillSaveLayerAndRestore(SkRecord* record, int saveLayerIndex) {
    225         record->replace<NoOp>(saveLayerIndex);    // SaveLayer
    226         record->replace<NoOp>(saveLayerIndex+2);  // Restore
    227         return true;
    228     }
    229 };
    230 void SkRecordNoopSaveLayerDrawRestores(SkRecord* record) {
    231     SaveLayerDrawRestoreNooper pass;
    232     apply(&pass, record);
    233 }
    234 #endif
    235 
    236 /* For SVG generated:
    237   SaveLayer (non-opaque, typically for CSS opacity)
    238     Save
    239       ClipRect
    240       SaveLayer (typically for SVG filter)
    241       Restore
    242     Restore
    243   Restore
    244 */
    245 struct SvgOpacityAndFilterLayerMergePass {
    246     typedef Pattern<Is<SaveLayer>, Is<Save>, Is<ClipRect>, Is<SaveLayer>,
    247                     Is<Restore>, Is<Restore>, Is<Restore>> Match;
    248 
    249     bool onMatch(SkRecord* record, Match* match, int begin, int end) {
    250         if (match->first<SaveLayer>()->backdrop) {
    251             // can't throw away the layer if we have a backdrop
    252             return false;
    253         }
    254 
    255         SkPaint* opacityPaint = match->first<SaveLayer>()->paint;
    256         if (nullptr == opacityPaint) {
    257             // There wasn't really any point to this SaveLayer at all.
    258             return KillSaveLayerAndRestore(record, begin);
    259         }
    260 
    261         // This layer typically contains a filter, but this should work for layers with for other
    262         // purposes too.
    263         SkPaint* filterLayerPaint = match->fourth<SaveLayer>()->paint;
    264         if (filterLayerPaint == nullptr) {
    265             // We can just give the inner SaveLayer the paint of the outer SaveLayer.
    266             // TODO(mtklein): figure out how to do this clearly
    267             return false;
    268         }
    269 
    270         if (!fold_opacity_layer_color_to_paint(opacityPaint, true /*isSaveLayer*/,
    271                                                filterLayerPaint)) {
    272             return false;
    273         }
    274 
    275         return KillSaveLayerAndRestore(record, begin);
    276     }
    277 
    278     static bool KillSaveLayerAndRestore(SkRecord* record, int saveLayerIndex) {
    279         record->replace<NoOp>(saveLayerIndex);     // SaveLayer
    280         record->replace<NoOp>(saveLayerIndex + 6); // Restore
    281         return true;
    282     }
    283 };
    284 
    285 void SkRecordMergeSvgOpacityAndFilterLayers(SkRecord* record) {
    286     SvgOpacityAndFilterLayerMergePass pass;
    287     apply(&pass, record);
    288 }
    289 
    290 ///////////////////////////////////////////////////////////////////////////////////////////////////
    291 
    292 void SkRecordOptimize(SkRecord* record) {
    293     // This might be useful  as a first pass in the future if we want to weed
    294     // out junk for other optimization passes.  Right now, nothing needs it,
    295     // and the bounding box hierarchy will do the work of skipping no-op
    296     // Save-NoDraw-Restore sequences better than we can here.
    297     // As there is a known problem with this peephole and drawAnnotation, disable this.
    298     // If we want to enable this we must first fix this bug:
    299     //     https://bugs.chromium.org/p/skia/issues/detail?id=5548
    300 //    SkRecordNoopSaveRestores(record);
    301 
    302     // Turn off this optimization completely for Android framework
    303     // because it makes the following Android CTS test fail:
    304     // android.uirendering.cts.testclasses.LayerTests#testSaveLayerClippedWithAlpha
    305 #ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
    306     SkRecordNoopSaveLayerDrawRestores(record);
    307 #endif
    308     SkRecordMergeSvgOpacityAndFilterLayers(record);
    309 
    310     record->defrag();
    311 }
    312 
    313 void SkRecordOptimize2(SkRecord* record) {
    314     multiple_set_matrices(record);
    315     SkRecordNoopSaveRestores(record);
    316     // See why we turn this off in SkRecordOptimize above.
    317 #ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
    318     SkRecordNoopSaveLayerDrawRestores(record);
    319 #endif
    320     SkRecordMergeSvgOpacityAndFilterLayers(record);
    321 
    322     record->defrag();
    323 }
    324