1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "flutter/flow/layers/image_filter_layer.h"
6
7namespace flutter {
8
9ImageFilterLayer::ImageFilterLayer(sk_sp<SkImageFilter> filter)
10 : filter_(std::move(filter)),
11 transformed_filter_(nullptr),
12 render_count_(1) {}
13
14void ImageFilterLayer::Preroll(PrerollContext* context,
15 const SkMatrix& matrix) {
16 TRACE_EVENT0("flutter", "ImageFilterLayer::Preroll");
17
18 Layer::AutoPrerollSaveLayerState save =
19 Layer::AutoPrerollSaveLayerState::Create(context);
20
21 SkRect child_bounds = SkRect::MakeEmpty();
22 PrerollChildren(context, matrix, &child_bounds);
23 if (filter_) {
24 const SkIRect filter_input_bounds = child_bounds.roundOut();
25 SkIRect filter_output_bounds =
26 filter_->filterBounds(filter_input_bounds, SkMatrix::I(),
27 SkImageFilter::kForward_MapDirection);
28 child_bounds = SkRect::Make(filter_output_bounds);
29 }
30 set_paint_bounds(child_bounds);
31
32 transformed_filter_ = nullptr;
33 if (render_count_ >= kMinimumRendersBeforeCachingFilterLayer) {
34 // We have rendered this same ImageFilterLayer object enough
35 // times to consider its properties and children to be stable
36 // from frame to frame so we try to cache the layer itself
37 // for maximum performance.
38 TryToPrepareRasterCache(context, this, matrix);
39 } else {
40 // This ImageFilterLayer is not yet considered stable so we
41 // increment the count to measure how many times it has been
42 // seen from frame to frame.
43 render_count_++;
44
45 // Now we will try to pre-render the children into the cache.
46 // To apply the filter to pre-rendered children, we must first
47 // modify the filter to be aware of the transform under which
48 // the cached bitmap was produced. Some SkImageFilter
49 // instances can do this operation on some transforms and some
50 // (filters or transforms) cannot. We can only cache the children
51 // and apply the filter on the fly if this operation succeeds.
52 transformed_filter_ = filter_->makeWithLocalMatrix(matrix);
53 if (transformed_filter_) {
54 // With a modified SkImageFilter we can now try to cache the
55 // children to avoid their rendering costs if they remain
56 // stable between frames and also avoiding a rendering surface
57 // switch during the Paint phase even if they are not stable.
58 // This benefit is seen most during animations.
59 TryToPrepareRasterCache(context, GetCacheableChild(), matrix);
60 }
61 }
62}
63
64void ImageFilterLayer::Paint(PaintContext& context) const {
65 TRACE_EVENT0("flutter", "ImageFilterLayer::Paint");
66 FML_DCHECK(needs_painting());
67
68 if (context.raster_cache) {
69 if (context.raster_cache->Draw(this, *context.leaf_nodes_canvas)) {
70 return;
71 }
72 if (transformed_filter_) {
73 SkPaint paint;
74 paint.setImageFilter(transformed_filter_);
75
76 if (context.raster_cache->Draw(GetCacheableChild(),
77 *context.leaf_nodes_canvas, &paint)) {
78 return;
79 }
80 }
81 }
82
83 SkPaint paint;
84 paint.setImageFilter(filter_);
85
86 // Normally a save_layer is sized to the current layer bounds, but in this
87 // case the bounds of the child may not be the same as the filtered version
88 // so we use the bounds of the child container which do not include any
89 // modifications that the filter might apply.
90 Layer::AutoSaveLayer save_layer = Layer::AutoSaveLayer::Create(
91 context, GetChildContainer()->paint_bounds(), &paint);
92 PaintChildren(context);
93}
94
95} // namespace flutter
96