|  | // Copyright 2013 The Flutter Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #ifndef FLUTTER_FLOW_EMBEDDED_VIEWS_H_ | 
|  | #define FLUTTER_FLOW_EMBEDDED_VIEWS_H_ | 
|  |  | 
|  | #include <vector> | 
|  |  | 
|  | #include "flutter/display_list/display_list_builder.h" | 
|  | #include "flutter/flow/rtree.h" | 
|  | #include "flutter/flow/surface_frame.h" | 
|  | #include "flutter/fml/memory/ref_counted.h" | 
|  | #include "flutter/fml/raster_thread_merger.h" | 
|  | #include "third_party/skia/include/core/SkCanvas.h" | 
|  | #include "third_party/skia/include/core/SkPath.h" | 
|  | #include "third_party/skia/include/core/SkPictureRecorder.h" | 
|  | #include "third_party/skia/include/core/SkPoint.h" | 
|  | #include "third_party/skia/include/core/SkRRect.h" | 
|  | #include "third_party/skia/include/core/SkRect.h" | 
|  | #include "third_party/skia/include/core/SkSize.h" | 
|  | #include "third_party/skia/include/core/SkSurface.h" | 
|  |  | 
|  | namespace flutter { | 
|  |  | 
|  | enum MutatorType { | 
|  | kClipRect, | 
|  | kClipRRect, | 
|  | kClipPath, | 
|  | kTransform, | 
|  | kOpacity, | 
|  | kBackdropFilter | 
|  | }; | 
|  |  | 
|  | // Represents an image filter mutation. | 
|  | // | 
|  | // Should be used for image_filter_layer and backdrop_filter_layer. | 
|  | // TODO(cyanglaz): Refactor this into a ImageFilterMutator class. | 
|  | // https://github.com/flutter/flutter/issues/108470 | 
|  | class ImageFilterMutation { | 
|  | public: | 
|  | ImageFilterMutation(std::shared_ptr<const DlImageFilter> filter, | 
|  | const SkRect& filter_rect) | 
|  | : filter_(filter), filter_rect_(filter_rect) {} | 
|  |  | 
|  | const DlImageFilter& GetFilter() const { return *filter_; } | 
|  | const SkRect& GetFilterRect() const { return filter_rect_; } | 
|  |  | 
|  | bool operator==(const ImageFilterMutation& other) const { | 
|  | return *filter_ == *other.filter_ && filter_rect_ == other.filter_rect_; | 
|  | } | 
|  |  | 
|  | bool operator!=(const ImageFilterMutation& other) const { | 
|  | return !operator==(other); | 
|  | } | 
|  |  | 
|  | private: | 
|  | std::shared_ptr<const DlImageFilter> filter_; | 
|  | const SkRect filter_rect_; | 
|  | }; | 
|  |  | 
|  | // Stores mutation information like clipping or kTransform. | 
|  | // | 
|  | // The `type` indicates the type of the mutation: kClipRect, kTransform and etc. | 
|  | // Each `type` is paired with an object that supports the mutation. For example, | 
|  | // if the `type` is kClipRect, `rect()` is used the represent the rect to be | 
|  | // clipped. One mutation object must only contain one type of mutation. | 
|  | class Mutator { | 
|  | public: | 
|  | Mutator(const Mutator& other) { | 
|  | type_ = other.type_; | 
|  | switch (other.type_) { | 
|  | case kClipRect: | 
|  | rect_ = other.rect_; | 
|  | break; | 
|  | case kClipRRect: | 
|  | rrect_ = other.rrect_; | 
|  | break; | 
|  | case kClipPath: | 
|  | path_ = new SkPath(*other.path_); | 
|  | break; | 
|  | case kTransform: | 
|  | matrix_ = other.matrix_; | 
|  | break; | 
|  | case kOpacity: | 
|  | alpha_ = other.alpha_; | 
|  | break; | 
|  | case kBackdropFilter: | 
|  | filter_mutation_ = other.filter_mutation_; | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | explicit Mutator(const SkRect& rect) : type_(kClipRect), rect_(rect) {} | 
|  | explicit Mutator(const SkRRect& rrect) : type_(kClipRRect), rrect_(rrect) {} | 
|  | explicit Mutator(const SkPath& path) | 
|  | : type_(kClipPath), path_(new SkPath(path)) {} | 
|  | explicit Mutator(const SkMatrix& matrix) | 
|  | : type_(kTransform), matrix_(matrix) {} | 
|  | explicit Mutator(const int& alpha) : type_(kOpacity), alpha_(alpha) {} | 
|  | explicit Mutator(std::shared_ptr<const DlImageFilter> filter, | 
|  | const SkRect& filter_rect) | 
|  | : type_(kBackdropFilter), | 
|  | filter_mutation_( | 
|  | std::make_shared<ImageFilterMutation>(filter, filter_rect)) {} | 
|  |  | 
|  | const MutatorType& GetType() const { return type_; } | 
|  | const SkRect& GetRect() const { return rect_; } | 
|  | const SkRRect& GetRRect() const { return rrect_; } | 
|  | const SkPath& GetPath() const { return *path_; } | 
|  | const SkMatrix& GetMatrix() const { return matrix_; } | 
|  | const ImageFilterMutation& GetFilterMutation() const { | 
|  | return *filter_mutation_; | 
|  | } | 
|  | const int& GetAlpha() const { return alpha_; } | 
|  | float GetAlphaFloat() const { return (alpha_ / 255.0); } | 
|  |  | 
|  | bool operator==(const Mutator& other) const { | 
|  | if (type_ != other.type_) { | 
|  | return false; | 
|  | } | 
|  | switch (type_) { | 
|  | case kClipRect: | 
|  | return rect_ == other.rect_; | 
|  | case kClipRRect: | 
|  | return rrect_ == other.rrect_; | 
|  | case kClipPath: | 
|  | return *path_ == *other.path_; | 
|  | case kTransform: | 
|  | return matrix_ == other.matrix_; | 
|  | case kOpacity: | 
|  | return alpha_ == other.alpha_; | 
|  | case kBackdropFilter: | 
|  | return *filter_mutation_ == *other.filter_mutation_; | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool operator!=(const Mutator& other) const { return !operator==(other); } | 
|  |  | 
|  | bool IsClipType() { | 
|  | return type_ == kClipRect || type_ == kClipRRect || type_ == kClipPath; | 
|  | } | 
|  |  | 
|  | ~Mutator() { | 
|  | if (type_ == kClipPath) { | 
|  | delete path_; | 
|  | } | 
|  | }; | 
|  |  | 
|  | private: | 
|  | MutatorType type_; | 
|  |  | 
|  | // TODO(cyanglaz): Remove union. | 
|  | //  https://github.com/flutter/flutter/issues/108470 | 
|  | union { | 
|  | SkRect rect_; | 
|  | SkRRect rrect_; | 
|  | SkMatrix matrix_; | 
|  | SkPath* path_; | 
|  | int alpha_; | 
|  | }; | 
|  |  | 
|  | std::shared_ptr<ImageFilterMutation> filter_mutation_; | 
|  | };  // Mutator | 
|  |  | 
|  | // A stack of mutators that can be applied to an embedded platform view. | 
|  | // | 
|  | // The stack may include mutators like transforms and clips, each mutator | 
|  | // applies to all the mutators that are below it in the stack and to the | 
|  | // embedded view. | 
|  | // | 
|  | // For example consider the following stack: [T1, T2, T3], where T1 is the top | 
|  | // of the stack and T3 is the bottom of the stack. Applying this mutators stack | 
|  | // to a platform view P1 will result in T1(T2(T3(P1))). | 
|  | class MutatorsStack { | 
|  | public: | 
|  | MutatorsStack() = default; | 
|  |  | 
|  | void PushClipRect(const SkRect& rect); | 
|  | void PushClipRRect(const SkRRect& rrect); | 
|  | void PushClipPath(const SkPath& path); | 
|  | void PushTransform(const SkMatrix& matrix); | 
|  | void PushOpacity(const int& alpha); | 
|  | void PushBackdropFilter(const std::shared_ptr<const DlImageFilter>& filter, | 
|  | const SkRect& filter_rect); | 
|  |  | 
|  | // Removes the `Mutator` on the top of the stack | 
|  | // and destroys it. | 
|  | void Pop(); | 
|  |  | 
|  | // Returns a reverse iterator pointing to the top of the stack, which is the | 
|  | // mutator that is furtherest from the leaf node. | 
|  | const std::vector<std::shared_ptr<Mutator>>::const_reverse_iterator Top() | 
|  | const; | 
|  | // Returns a reverse iterator pointing to the bottom of the stack, which is | 
|  | // the mutator that is closeset from the leaf node. | 
|  | const std::vector<std::shared_ptr<Mutator>>::const_reverse_iterator Bottom() | 
|  | const; | 
|  |  | 
|  | // Returns an iterator pointing to the beginning of the mutator vector, which | 
|  | // is the mutator that is furtherest from the leaf node. | 
|  | const std::vector<std::shared_ptr<Mutator>>::const_iterator Begin() const; | 
|  |  | 
|  | // Returns an iterator pointing to the end of the mutator vector, which is the | 
|  | // mutator that is closest from the leaf node. | 
|  | const std::vector<std::shared_ptr<Mutator>>::const_iterator End() const; | 
|  |  | 
|  | bool is_empty() const { return vector_.empty(); } | 
|  |  | 
|  | bool operator==(const MutatorsStack& other) const { | 
|  | if (vector_.size() != other.vector_.size()) { | 
|  | return false; | 
|  | } | 
|  | for (size_t i = 0; i < vector_.size(); i++) { | 
|  | if (*vector_[i] != *other.vector_[i]) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool operator==(const std::vector<Mutator>& other) const { | 
|  | if (vector_.size() != other.size()) { | 
|  | return false; | 
|  | } | 
|  | for (size_t i = 0; i < vector_.size(); i++) { | 
|  | if (*vector_[i] != other[i]) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool operator!=(const MutatorsStack& other) const { | 
|  | return !operator==(other); | 
|  | } | 
|  |  | 
|  | bool operator!=(const std::vector<Mutator>& other) const { | 
|  | return !operator==(other); | 
|  | } | 
|  |  | 
|  | private: | 
|  | std::vector<std::shared_ptr<Mutator>> vector_; | 
|  | };  // MutatorsStack | 
|  |  | 
|  | class EmbeddedViewParams { | 
|  | public: | 
|  | EmbeddedViewParams() = default; | 
|  |  | 
|  | EmbeddedViewParams(SkMatrix matrix, | 
|  | SkSize size_points, | 
|  | MutatorsStack mutators_stack, | 
|  | bool display_list_enabled = false) | 
|  | : matrix_(matrix), | 
|  | size_points_(size_points), | 
|  | mutators_stack_(mutators_stack), | 
|  | display_list_enabled_(display_list_enabled) { | 
|  | SkPath path; | 
|  | SkRect starting_rect = SkRect::MakeSize(size_points); | 
|  | path.addRect(starting_rect); | 
|  | path.transform(matrix); | 
|  | final_bounding_rect_ = path.getBounds(); | 
|  | } | 
|  |  | 
|  | // The transformation Matrix corresponding to the sum of all the | 
|  | // transformations in the platform view's mutator stack. | 
|  | const SkMatrix& transformMatrix() const { return matrix_; }; | 
|  | // The original size of the platform view before any mutation matrix is | 
|  | // applied. | 
|  | const SkSize& sizePoints() const { return size_points_; }; | 
|  | // The mutators stack contains the detailed step by step mutations for this | 
|  | // platform view. | 
|  | const MutatorsStack& mutatorsStack() const { return mutators_stack_; }; | 
|  | // The bounding rect of the platform view after applying all the mutations. | 
|  | // | 
|  | // Clippings are ignored. | 
|  | const SkRect& finalBoundingRect() const { return final_bounding_rect_; } | 
|  |  | 
|  | // Pushes the stored DlImageFilter object to the mutators stack. | 
|  | void PushImageFilter(std::shared_ptr<const DlImageFilter> filter, | 
|  | const SkRect& filter_rect) { | 
|  | mutators_stack_.PushBackdropFilter(filter, filter_rect); | 
|  | } | 
|  |  | 
|  | // Whether the embedder should construct DisplayList objects to hold the | 
|  | // rendering commands for each between-view slice of the layer tree. | 
|  | bool display_list_enabled() const { return display_list_enabled_; } | 
|  |  | 
|  | bool operator==(const EmbeddedViewParams& other) const { | 
|  | return size_points_ == other.size_points_ && | 
|  | mutators_stack_ == other.mutators_stack_ && | 
|  | final_bounding_rect_ == other.final_bounding_rect_ && | 
|  | matrix_ == other.matrix_; | 
|  | } | 
|  |  | 
|  | private: | 
|  | SkMatrix matrix_; | 
|  | SkSize size_points_; | 
|  | MutatorsStack mutators_stack_; | 
|  | SkRect final_bounding_rect_; | 
|  | bool display_list_enabled_; | 
|  | }; | 
|  |  | 
|  | enum class PostPrerollResult { | 
|  | // Frame has successfully rasterized. | 
|  | kSuccess, | 
|  | // Frame is submitted twice. This is currently only used when | 
|  | // thread configuration change occurs. | 
|  | kResubmitFrame, | 
|  | // Frame is dropped and a new frame with the same layer tree is | 
|  | // attempted. This is currently only used when thread configuration | 
|  | // change occurs. | 
|  | kSkipAndRetryFrame | 
|  | }; | 
|  |  | 
|  | // The |PlatformViewLayer| calls |CompositeEmbeddedView| in its |Paint| | 
|  | // method to replace the leaf_nodes_canvas and leaf_nodes_builder in its | 
|  | // |PaintContext| for subsequent layers in the frame to render into. | 
|  | // The builder value will only be supplied if the associated ScopedFrame | 
|  | // is being rendered to DisplayLists. The |EmbedderPaintContext| struct | 
|  | // allows the method to return both values. | 
|  | struct EmbedderPaintContext { | 
|  | SkCanvas* canvas; | 
|  | DisplayListBuilder* builder; | 
|  | }; | 
|  |  | 
|  | // The |EmbedderViewSlice| represents the details of recording all of | 
|  | // the layer tree rendering operations that appear between before, after | 
|  | // and between the embedded views. The Slice may be recorded into an | 
|  | // SkPicture or a DisplayListBuilder depending on the ScopedFrame. | 
|  | class EmbedderViewSlice { | 
|  | public: | 
|  | virtual ~EmbedderViewSlice() = default; | 
|  | virtual SkCanvas* canvas() = 0; | 
|  | virtual DisplayListBuilder* builder() = 0; | 
|  | virtual void end_recording() = 0; | 
|  | virtual std::list<SkRect> searchNonOverlappingDrawnRects( | 
|  | const SkRect& query) const = 0; | 
|  | virtual void render_into(SkCanvas* canvas) = 0; | 
|  | virtual void render_into(DisplayListBuilder* builder) = 0; | 
|  | }; | 
|  |  | 
|  | class SkPictureEmbedderViewSlice : public EmbedderViewSlice { | 
|  | public: | 
|  | SkPictureEmbedderViewSlice(SkRect view_bounds); | 
|  | ~SkPictureEmbedderViewSlice() override = default; | 
|  |  | 
|  | SkCanvas* canvas() override; | 
|  | DisplayListBuilder* builder() override; | 
|  | void end_recording() override; | 
|  | std::list<SkRect> searchNonOverlappingDrawnRects( | 
|  | const SkRect& query) const override; | 
|  | void render_into(SkCanvas* canvas) override; | 
|  | void render_into(DisplayListBuilder* builder) override; | 
|  |  | 
|  | private: | 
|  | std::unique_ptr<SkPictureRecorder> recorder_; | 
|  | sk_sp<RTree> rtree_; | 
|  | sk_sp<SkPicture> picture_; | 
|  | }; | 
|  |  | 
|  | class DisplayListEmbedderViewSlice : public EmbedderViewSlice { | 
|  | public: | 
|  | DisplayListEmbedderViewSlice(SkRect view_bounds); | 
|  | ~DisplayListEmbedderViewSlice() override = default; | 
|  |  | 
|  | SkCanvas* canvas() override; | 
|  | DisplayListBuilder* builder() override; | 
|  | void end_recording() override; | 
|  | std::list<SkRect> searchNonOverlappingDrawnRects( | 
|  | const SkRect& query) const override; | 
|  | void render_into(SkCanvas* canvas) override; | 
|  | void render_into(DisplayListBuilder* builder) override; | 
|  |  | 
|  | private: | 
|  | std::unique_ptr<DisplayListCanvasRecorder> recorder_; | 
|  | sk_sp<DisplayList> display_list_; | 
|  | }; | 
|  |  | 
|  | // Facilitates embedding of platform views within the flow layer tree. | 
|  | // | 
|  | // Used on iOS, Android (hybrid composite mode), and on embedded platforms | 
|  | // that provide a system compositor as part of the project arguments. | 
|  | class ExternalViewEmbedder { | 
|  | // TODO(cyanglaz): Make embedder own the `EmbeddedViewParams`. | 
|  |  | 
|  | public: | 
|  | ExternalViewEmbedder() = default; | 
|  |  | 
|  | virtual ~ExternalViewEmbedder() = default; | 
|  |  | 
|  | // Usually, the root canvas is not owned by the view embedder. However, if | 
|  | // the view embedder wants to provide a canvas to the rasterizer, it may | 
|  | // return one here. This canvas takes priority over the canvas materialized | 
|  | // from the on-screen render target. | 
|  | virtual SkCanvas* GetRootCanvas() = 0; | 
|  |  | 
|  | // Call this in-lieu of |SubmitFrame| to clear pre-roll state and | 
|  | // sets the stage for the next pre-roll. | 
|  | virtual void CancelFrame() = 0; | 
|  |  | 
|  | // Indicates the beginning of a frame. | 
|  | // | 
|  | // The `raster_thread_merger` will be null if |SupportsDynamicThreadMerging| | 
|  | // returns false. | 
|  | virtual void BeginFrame( | 
|  | SkISize frame_size, | 
|  | GrDirectContext* context, | 
|  | double device_pixel_ratio, | 
|  | fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) = 0; | 
|  |  | 
|  | virtual void PrerollCompositeEmbeddedView( | 
|  | int view_id, | 
|  | std::unique_ptr<EmbeddedViewParams> params) = 0; | 
|  |  | 
|  | // This needs to get called after |Preroll| finishes on the layer tree. | 
|  | // Returns kResubmitFrame if the frame needs to be processed again, this is | 
|  | // after it does any requisite tasks needed to bring itself to a valid state. | 
|  | // Returns kSuccess if the view embedder is already in a valid state. | 
|  | virtual PostPrerollResult PostPrerollAction( | 
|  | fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) { | 
|  | return PostPrerollResult::kSuccess; | 
|  | } | 
|  |  | 
|  | virtual std::vector<SkCanvas*> GetCurrentCanvases() = 0; | 
|  | virtual std::vector<DisplayListBuilder*> GetCurrentBuilders() = 0; | 
|  |  | 
|  | // Must be called on the UI thread. | 
|  | virtual EmbedderPaintContext CompositeEmbeddedView(int view_id) = 0; | 
|  |  | 
|  | // Implementers must submit the frame by calling frame.Submit(). | 
|  | // | 
|  | // This method can mutate the root Skia canvas before submitting the frame. | 
|  | // | 
|  | // It can also allocate frames for overlay surfaces to compose hybrid views. | 
|  | virtual void SubmitFrame(GrDirectContext* context, | 
|  | std::unique_ptr<SurfaceFrame> frame); | 
|  |  | 
|  | // This method provides the embedder a way to do additional tasks after | 
|  | // |SubmitFrame|. For example, merge task runners if `should_resubmit_frame` | 
|  | // is true. | 
|  | // | 
|  | // For example on the iOS embedder, threads are merged in this call. | 
|  | // A new frame on the platform thread starts immediately. If the GPU thread | 
|  | // still has some task running, there could be two frames being rendered | 
|  | // concurrently, which causes undefined behaviors. | 
|  | // | 
|  | // The `raster_thread_merger` will be null if |SupportsDynamicThreadMerging| | 
|  | // returns false. | 
|  | virtual void EndFrame( | 
|  | bool should_resubmit_frame, | 
|  | fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {} | 
|  |  | 
|  | // Whether the embedder should support dynamic thread merging. | 
|  | // | 
|  | // Returning `true` results a |RasterThreadMerger| instance to be created. | 
|  | // * See also |BegineFrame| and |EndFrame| for getting the | 
|  | // |RasterThreadMerger| instance. | 
|  | virtual bool SupportsDynamicThreadMerging(); | 
|  |  | 
|  | // Called when the rasterizer is being torn down. | 
|  | // This method provides a way to release resources associated with the current | 
|  | // embedder. | 
|  | virtual void Teardown(); | 
|  |  | 
|  | // Change the flag about whether it is used in this frame, it will be set to | 
|  | // true when 'BeginFrame' and false when 'EndFrame'. | 
|  | void SetUsedThisFrame(bool used_this_frame) { | 
|  | used_this_frame_ = used_this_frame; | 
|  | } | 
|  |  | 
|  | // Whether it is used in this frame, returns true between 'BeginFrame' and | 
|  | // 'EndFrame', otherwise returns false. | 
|  | bool GetUsedThisFrame() const { return used_this_frame_; } | 
|  |  | 
|  | // Pushes the platform view id of a visited platform view to a list of | 
|  | // visited platform views. | 
|  | virtual void PushVisitedPlatformView(int64_t view_id) {} | 
|  |  | 
|  | // Pushes a DlImageFilter object to each platform view within a list of | 
|  | // visited platform views. | 
|  | // | 
|  | // See also: |PushVisitedPlatformView| for pushing platform view ids to the | 
|  | // visited platform views list. | 
|  | virtual void PushFilterToVisitedPlatformViews( | 
|  | std::shared_ptr<const DlImageFilter> filter, | 
|  | const SkRect& filter_rect) {} | 
|  |  | 
|  | private: | 
|  | bool used_this_frame_ = false; | 
|  |  | 
|  | FML_DISALLOW_COPY_AND_ASSIGN(ExternalViewEmbedder); | 
|  |  | 
|  | };  // ExternalViewEmbedder | 
|  |  | 
|  | }  // namespace flutter | 
|  |  | 
|  | #endif  // FLUTTER_FLOW_EMBEDDED_VIEWS_H_ |