diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index 537ebf96f2109..583c65faa42c4 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -39,11 +39,13 @@ ../../../flutter/display_list/effects/dl_image_filter_unittests.cc ../../../flutter/display_list/effects/dl_mask_filter_unittests.cc ../../../flutter/display_list/effects/dl_path_effect_unittests.cc +../../../flutter/display_list/geometry/dl_geometry_types_unittests.cc ../../../flutter/display_list/geometry/dl_region_unittests.cc ../../../flutter/display_list/geometry/dl_rtree_unittests.cc ../../../flutter/display_list/skia/dl_sk_conversions_unittests.cc ../../../flutter/display_list/skia/dl_sk_paint_dispatcher_unittests.cc ../../../flutter/display_list/testing +../../../flutter/display_list/utils/dl_accumulation_rect_unittests.cc ../../../flutter/display_list/utils/dl_matrix_clip_tracker_unittests.cc ../../../flutter/docs ../../../flutter/examples diff --git a/display_list/BUILD.gn b/display_list/BUILD.gn index 614ff1260f65b..a886af8f2c7bc 100644 --- a/display_list/BUILD.gn +++ b/display_list/BUILD.gn @@ -120,10 +120,12 @@ if (enable_unittests) { "effects/dl_image_filter_unittests.cc", "effects/dl_mask_filter_unittests.cc", "effects/dl_path_effect_unittests.cc", + "geometry/dl_geometry_types_unittests.cc", "geometry/dl_region_unittests.cc", "geometry/dl_rtree_unittests.cc", "skia/dl_sk_conversions_unittests.cc", "skia/dl_sk_paint_dispatcher_unittests.cc", + "utils/dl_accumulation_rect_unittests.cc", "utils/dl_matrix_clip_tracker_unittests.cc", ] diff --git a/display_list/dl_builder.cc b/display_list/dl_builder.cc index bacacb2454945..ef030f74256c0 100644 --- a/display_list/dl_builder.cc +++ b/display_list/dl_builder.cc @@ -74,11 +74,11 @@ sk_sp DisplayListBuilder::Build() { size_t nested_bytes = nested_bytes_; int nested_count = nested_op_count_; uint32_t total_depth = depth_; - bool compatible = current_info().is_group_opacity_compatible(); + bool opacity_compatible = current_layer().is_group_opacity_compatible(); bool is_safe = is_ui_thread_safe_; - bool affects_transparency = current_info().affects_transparent_layer; - bool root_has_backdrop_filter = current_info().contains_backdrop_filter; - DlBlendMode max_root_blend_mode = current_info().max_blend_mode; + bool affects_transparency = current_layer().affects_transparent_layer; + bool root_has_backdrop_filter = current_layer().contains_backdrop_filter; + DlBlendMode max_root_blend_mode = current_layer().max_blend_mode; sk_sp rtree; SkRect bounds; @@ -93,7 +93,7 @@ sk_sp DisplayListBuilder::Build() { bounds = rtree->bounds(); rtree_data_.reset(); } else { - bounds = current_info().global_space_accumulator->bounds(); + bounds = current_layer().global_space_accumulator.bounds(); } used_ = allocated_ = render_op_count_ = op_index_ = 0; @@ -105,18 +105,12 @@ sk_sp DisplayListBuilder::Build() { current_ = DlPaint(); save_stack_.pop_back(); - save_stack_.emplace_back(original_cull_rect_); - current_info().is_nop = original_cull_rect_.IsEmpty(); - if (rtree) { - rtree_data_.emplace(); - } else { - current_info().global_space_accumulator.reset(new AccumulationRect()); - } + Init(rtree != nullptr); storage_.realloc(bytes); return sk_sp(new DisplayList( std::move(storage_), bytes, count, nested_bytes, nested_count, - total_depth, bounds, compatible, is_safe, affects_transparency, + total_depth, bounds, opacity_compatible, is_safe, affects_transparency, max_root_blend_mode, root_has_backdrop_filter, std::move(rtree))); } @@ -130,12 +124,17 @@ static const DlRect& ProtectEmpty(const SkRect& rect) { DisplayListBuilder::DisplayListBuilder(const SkRect& cull_rect, bool prepare_rtree) : original_cull_rect_(ProtectEmpty(cull_rect)) { + Init(prepare_rtree); +} + +void DisplayListBuilder::Init(bool prepare_rtree) { + FML_DCHECK(save_stack_.empty()); + FML_DCHECK(!rtree_data_.has_value()); + save_stack_.emplace_back(original_cull_rect_); current_info().is_nop = original_cull_rect_.IsEmpty(); if (prepare_rtree) { rtree_data_.emplace(); - } else { - current_info().global_space_accumulator.reset(new AccumulationRect()); } } @@ -454,7 +453,7 @@ void DisplayListBuilder::saveLayer(const SkRect& bounds, } if (backdrop != nullptr) { - current_info().contains_backdrop_filter = true; + current_layer().contains_backdrop_filter = true; } // Snapshot these values before we do any work as we need the values @@ -503,24 +502,12 @@ void DisplayListBuilder::saveLayer(const SkRect& bounds, rtree_data_.has_value() ? rtree_data_->rects.size() : 0u; save_stack_.emplace_back(¤t_info(), filter, rtree_index); + FML_DCHECK(current_info().is_save_layer); FML_DCHECK(!current_info().is_nop); FML_DCHECK(!current_info().has_deferred_save_op); current_info().save_offset = save_offset; current_info().save_depth = save_depth; - if (filter && !rtree_data_.has_value()) { - // By default the new SaveInfo shares the global accumulation rect with - // the parent layer and will only have one if the rtree_data is not - // being accumulated. - // - // But, if we have a filter and we are not accumulating rtree data, - // then we'll need to adjust all of the bounds accumulated via this - // new layer by the filter so we need to use a separate global - // accumulation rect for this layer and adjust it during RestoreLayer() - // before accumulating it into the parent layer. - current_info().global_space_accumulator.reset(new AccumulationRect()); - } - // If we inherit some culling bounds and we have a filter then we need // to adjust them so that we cull for the correct input space for the // output of the filter. @@ -602,75 +589,45 @@ void DisplayListBuilder::Restore() { return; } - { - // The current_info and parent_info will have a lifetime that does not - // extend past the pop_back() method below. - const auto& current_info = this->current_info(); - auto& parent_info = this->parent_info(); - - if (!current_info.has_deferred_save_op) { - SaveOpBase* op = reinterpret_cast(storage_.get() + - current_info.save_offset); - FML_CHECK(op->type == DisplayListOpType::kSave || - op->type == DisplayListOpType::kSaveLayer || - op->type == DisplayListOpType::kSaveLayerBackdrop); - - op->restore_index = op_index_; - op->total_content_depth = depth_ - current_info.save_depth; - } + if (!current_info().has_deferred_save_op) { + SaveOpBase* op = reinterpret_cast(storage_.get() + + current_info().save_offset); + FML_CHECK(op->type == DisplayListOpType::kSave || + op->type == DisplayListOpType::kSaveLayer || + op->type == DisplayListOpType::kSaveLayerBackdrop); - if (current_info.is_save_layer) { - RestoreLayer(current_info, parent_info); - } else { - // No need to propagate bounds as we do with layers... - - // global accumulator is either the same object or both nullptr - FML_DCHECK(current_info.global_space_accumulator.get() == - parent_info.global_space_accumulator.get()); - - // layer accumulators are both the same object - FML_DCHECK(current_info.layer_local_accumulator.get() == - parent_info.layer_local_accumulator.get()); - FML_DCHECK(current_info.layer_local_accumulator.get() != nullptr); + op->restore_index = op_index_; + op->total_content_depth = depth_ - current_info().save_depth; - // We only propagate these values through a regular save() - if (current_info.opacity_incompatible_op_detected) { - parent_info.opacity_incompatible_op_detected = true; - } - - if (current_info.contains_backdrop_filter) { - parent_info.contains_backdrop_filter = true; - } - parent_info.update_blend_mode(current_info.max_blend_mode); + if (current_info().is_save_layer) { + RestoreLayer(); } // Wait until all outgoing bounds information for the saveLayer is // recorded before pushing the record to the buffer so that any rtree // bounds will be attributed to the op_index of the restore op. - if (!current_info.has_deferred_save_op) { - Push(0); - } else { - FML_DCHECK(!current_info.is_save_layer); - } + Push(0); + } else { + FML_DCHECK(!current_info().is_save_layer); } save_stack_.pop_back(); } -void DisplayListBuilder::RestoreLayer(const SaveInfo& current_info, - SaveInfo& parent_info) { +void DisplayListBuilder::RestoreLayer() { FML_DCHECK(save_stack_.size() > 1); - FML_DCHECK(!current_info.has_deferred_save_op); + FML_DCHECK(current_info().is_save_layer); + FML_DCHECK(!current_info().has_deferred_save_op); // A saveLayer will usually do a final copy to the main buffer in // addition to its content, but that is accounted for outside of // the total content depth computed above in Restore. depth_ += render_op_depth_cost_; - SkRect content_bounds = current_info.layer_local_accumulator->bounds(); + SkRect content_bounds = current_layer().layer_local_accumulator.bounds(); SaveLayerOpBase* layer_op = reinterpret_cast( - storage_.get() + current_info.save_offset); + storage_.get() + current_info().save_offset); FML_CHECK(layer_op->type == DisplayListOpType::kSaveLayer || layer_op->type == DisplayListOpType::kSaveLayerBackdrop); @@ -681,20 +638,20 @@ void DisplayListBuilder::RestoreLayer(const SaveInfo& current_info, } } layer_op->rect = content_bounds; - layer_op->max_blend_mode = current_info.max_blend_mode; + layer_op->max_blend_mode = current_layer().max_blend_mode; - if (current_info.contains_backdrop_filter) { + if (current_layer().contains_backdrop_filter) { layer_op->options = layer_op->options.with_contains_backdrop_filter(); } - if (current_info.is_group_opacity_compatible()) { + if (current_layer().is_group_opacity_compatible()) { layer_op->options = layer_op->options.with_can_distribute_opacity(); } // Ensure that the bounds transferred in the following call will be // attributed to the index of the restore op. FML_DCHECK(layer_op->restore_index == op_index_); - TransferLayerBounds(current_info, parent_info, content_bounds); + TransferLayerBounds(content_bounds); } // There are a few different conditions and corresponding operations to @@ -728,26 +685,18 @@ void DisplayListBuilder::RestoreLayer(const SaveInfo& current_info, // // Finally, we will have to adjust the layer's content bounds by the filter // and accumulate those into the parent layer's local bounds. -void DisplayListBuilder::TransferLayerBounds(const SaveInfo& current_info, - SaveInfo& parent_info, - const SkRect& content_bounds) { - auto& filter = current_info.filter; +void DisplayListBuilder::TransferLayerBounds(const SkRect& content_bounds) { + auto& filter = current_layer().filter; if (!filter) { - // One or the other of the rtree data or the global space accumulator - // must be non-null, and the other must be null. - FML_DCHECK(rtree_data_.has_value() != - static_cast(current_info.global_space_accumulator)); - - // The current and parent global space accumulators either must both be - // null, or they must both point to the same accumulator. - FML_DCHECK(current_info.global_space_accumulator.get() == - parent_info.global_space_accumulator.get()); - - // If we have no filter then the global bounds were already accumulated - // into the parent's global accumulator, but we need to update the local - // bounds of the parent for the results of the saveLayer call. - parent_info.AccumulateBoundsLocal(content_bounds); + // We either accumulate global bounds into the rtree_data if there + // is one, or into the global_space_accumulator, but not both. + FML_DCHECK(!rtree_data_.has_value() || + current_layer().global_space_accumulator.is_empty()); + + parent_info().AccumulateBoundsLocal(content_bounds); + parent_layer().global_space_accumulator.accumulate( + current_layer().global_space_accumulator); return; } @@ -759,13 +708,14 @@ void DisplayListBuilder::TransferLayerBounds(const SaveInfo& current_info, // Matrix and Clip for the filter adjustment are the global values from // just before our saveLayer and should still be the current values // present in the parent layer. - const SkRect clip = parent_info.global_state.device_cull_rect(); - const SkMatrix matrix = parent_info.global_state.matrix_3x3(); + const SkRect clip = parent_info().global_state.device_cull_rect(); + const SkMatrix matrix = parent_info().global_state.matrix_3x3(); if (rtree_data_.has_value()) { - // Neither current or parent layer should have a global space accumulator - FML_DCHECK(!current_info.global_space_accumulator); - FML_DCHECK(!parent_info.global_space_accumulator); + // Neither current or parent layer should have any global bounds in + // their accumulator + FML_DCHECK(current_layer().global_space_accumulator.is_empty()); + FML_DCHECK(parent_layer().global_space_accumulator.is_empty()); // The rtree rects were accumulated without the bounds modification of // the filter applied to the layer so they may fail to trigger on a @@ -777,24 +727,12 @@ void DisplayListBuilder::TransferLayerBounds(const SaveInfo& current_info, // revisit all of the RTree rects accumulated during the current layer // (indicated by rtree_rects_start_index) and expand them by the filter. - // Starting rect index was snapshotted to this layer's data during - // saveLayer. - auto rect_start_index = current_info.rtree_rects_start_index; - if (AdjustRTreeRects(rtree_data_.value(), *filter, matrix, clip, - rect_start_index)) { + current_layer().rtree_rects_start_index)) { parent_is_flooded = true; } } else { - // Both current and parent layer should have a global space accumulator - FML_DCHECK(current_info.global_space_accumulator); - FML_DCHECK(parent_info.global_space_accumulator); - - // And they should not be the same accumulator - FML_DCHECK(current_info.global_space_accumulator.get() != - parent_info.global_space_accumulator.get()); - - SkRect global_bounds = current_info.global_space_accumulator->bounds(); + SkRect global_bounds = current_layer().global_space_accumulator.bounds(); if (!global_bounds.isEmpty()) { SkIRect global_ibounds = global_bounds.roundOut(); if (!filter->map_device_bounds(global_ibounds, matrix, global_ibounds)) { @@ -802,7 +740,7 @@ void DisplayListBuilder::TransferLayerBounds(const SaveInfo& current_info, } else { global_bounds.set(global_ibounds); if (global_bounds.intersect(clip)) { - parent_info.global_space_accumulator->accumulate(global_bounds); + parent_layer().global_space_accumulator.accumulate(global_bounds); } } } @@ -834,9 +772,9 @@ void DisplayListBuilder::TransferLayerBounds(const SaveInfo& current_info, // rare case that there are no rendering ops in it, or somehow none // of them were chosen by the rtree search (unlikely). The saveLayer // must be processed for the parent flood to happen. - AccumulateUnbounded(parent_info); + AccumulateUnbounded(parent_info()); } else { - parent_info.AccumulateBoundsLocal(bounds_for_parent); + parent_info().AccumulateBoundsLocal(bounds_for_parent); } } @@ -1330,7 +1268,7 @@ void DisplayListBuilder::drawPoints(PointMode mode, // distribution of group opacity without analyzing the mode and the // bounds of every sub-primitive. // See: https://fiddle.skia.org/c/228459001d2de8db117ce25ef5cedb0c - current_info().layer_local_accumulator->record_overlapping_bounds(); + current_layer().layer_local_accumulator.record_overlapping_bounds(); // Even though we've eliminated the possibility of opacity peephole // optimizations above, we still set the appropriate flags based on // the rendering attributes in case we solve the overlapping points @@ -1367,7 +1305,7 @@ void DisplayListBuilder::drawVertices(const DlVertices* vertices, // colors above - both conditions must be analyzed sufficiently // and implemented accordingly before drawVertices is compatible with // opacity peephole optimizations. - current_info().layer_local_accumulator->record_overlapping_bounds(); + current_layer().layer_local_accumulator.record_overlapping_bounds(); } } void DisplayListBuilder::DrawVertices(const DlVertices* vertices, @@ -1514,7 +1452,7 @@ void DisplayListBuilder::drawAtlas(const sk_sp atlas, // independently it might expand the bounds on one corner and then flag // the condition when the next corner is added. if (accumulator.overlap_detected()) { - current_info().layer_local_accumulator->record_overlapping_bounds(); + current_layer().layer_local_accumulator.record_overlapping_bounds(); } int bytes = count * (sizeof(SkRSXform) + sizeof(SkRect)); @@ -1633,7 +1571,7 @@ void DisplayListBuilder::DrawDisplayList(const sk_sp display_list, : OpResult::kPreservesTransparency, display_list->max_root_blend_mode()); if (display_list->root_has_backdrop_filter()) { - current_info().contains_backdrop_filter = true; + current_layer().contains_backdrop_filter = true; } } void DisplayListBuilder::drawTextBlob(const sk_sp blob, @@ -1801,22 +1739,20 @@ bool DisplayListBuilder::AdjustBoundsForPaint(SkRect& bounds, return true; } -bool DisplayListBuilder::AccumulateUnbounded(SaveInfo& layer) { - SkRect global_clip = layer.global_state.device_cull_rect(); - SkRect layer_clip = layer.global_state.local_cull_rect(); - if (global_clip.isEmpty() || !layer.layer_state.mapAndClipRect(&layer_clip)) { +bool DisplayListBuilder::AccumulateUnbounded(const SaveInfo& save) { + SkRect global_clip = save.global_state.device_cull_rect(); + SkRect layer_clip = save.global_state.local_cull_rect(); + if (global_clip.isEmpty() || !save.layer_state.mapAndClipRect(&layer_clip)) { return false; } if (rtree_data_.has_value()) { - FML_DCHECK(!layer.global_space_accumulator); + FML_DCHECK(save.layer_info->global_space_accumulator.is_empty()); rtree_data_->rects.push_back(global_clip); rtree_data_->indices.push_back(op_index_); } else { - FML_DCHECK(layer.global_space_accumulator); - layer.global_space_accumulator->accumulate(global_clip); + save.layer_info->global_space_accumulator.accumulate(global_clip); } - layer.layer_local_accumulator->accumulate(layer_clip); - layer.is_unbounded = true; + save.layer_info->layer_local_accumulator.accumulate(layer_clip); return true; } @@ -1842,16 +1778,15 @@ bool DisplayListBuilder::AccumulateBounds(const SkRect& bounds, return false; } if (rtree_data_.has_value()) { - FML_DCHECK(!layer.global_space_accumulator); + FML_DCHECK(layer.layer_info->global_space_accumulator.is_empty()); if (id >= 0) { rtree_data_->rects.push_back(global_bounds); rtree_data_->indices.push_back(id); } } else { - FML_DCHECK(layer.global_space_accumulator); - layer.global_space_accumulator->accumulate(global_bounds); + layer.layer_info->global_space_accumulator.accumulate(global_bounds); } - layer.layer_local_accumulator->accumulate(layer_bounds); + layer.layer_info->layer_local_accumulator.accumulate(layer_bounds); return true; } @@ -1863,7 +1798,7 @@ bool DisplayListBuilder::SaveInfo::AccumulateBoundsLocal(const SkRect& bounds) { if (!layer_state.mapAndClipRect(bounds, &local_bounds)) { return false; } - layer_local_accumulator->accumulate(local_bounds); + layer_info->layer_local_accumulator.accumulate(local_bounds); return true; } diff --git a/display_list/dl_builder.h b/display_list/dl_builder.h index 9c6b54920664f..f492680003a5e 100644 --- a/display_list/dl_builder.h +++ b/display_list/dl_builder.h @@ -248,6 +248,8 @@ class DisplayListBuilder final : public virtual DlCanvas, sk_sp Build(); private: + void Init(bool prepare_rtree); + // This method exposes the internal stateful DlOpReceiver implementation // of the DisplayListBuilder, primarily for testing purposes. Its use // is obsolete and forbidden in every other case and is only shared to a @@ -512,7 +514,49 @@ class DisplayListBuilder final : public virtual DlCanvas, std::vector indices; }; - // The SaveInfo class stores internal data for both Save and SaveLayer calls + struct LayerInfo { + LayerInfo(const std::shared_ptr& filter, + size_t rtree_rects_start_index) + : filter(filter), + rtree_rects_start_index(rtree_rects_start_index) {} + + // The filter that will be applied to the contents of the saveLayer + // when it is restored into the parent layer. + const std::shared_ptr filter; + + // The index of the rtree rects when the saveLayer was called, used + // only in the case that the saveLayer has a filter so that the + // accumulated rects can be updated in the corresponding restore call. + const size_t rtree_rects_start_index = 0; + + // The bounds accumulator for the entire DisplayList, relative to its root + // (not used when accumulating rects for an rtree, though) + AccumulationRect global_space_accumulator; + + // The bounds accumulator to set/verify the bounds of the most recently + // invoked saveLayer call, relative to the root of that saveLayer + AccumulationRect layer_local_accumulator; + + DlBlendMode max_blend_mode = DlBlendMode::kClear; + + bool opacity_incompatible_op_detected = false; + bool affects_transparent_layer = false; + bool contains_backdrop_filter = false; + + bool is_group_opacity_compatible() const { + return !opacity_incompatible_op_detected && + !layer_local_accumulator.overlap_detected(); + } + + void update_blend_mode(DlBlendMode mode) { + if (max_blend_mode < mode) { + max_blend_mode = mode; + } + } + }; + + // The SaveInfo class stores internal data common to both Save and + // SaveLayer calls class SaveInfo { public: // For vector reallocation calls to copy vector data @@ -521,78 +565,43 @@ class DisplayListBuilder final : public virtual DlCanvas, // For constructor (root layer) initialization explicit SaveInfo(const DlRect& cull_rect) - : is_root_layer(true), - is_save_layer(true), + : is_save_layer(true), global_state(cull_rect), layer_state(cull_rect), - layer_local_accumulator(new AccumulationRect()) {} + layer_info(new LayerInfo(nullptr, 0u)) {} // For regular save calls: // Passing a pointer to the parent_info so as to distinguish this - // call from the copy constructor used above in vector reallocations + // call from the copy constructors used during vector reallocations explicit SaveInfo(const SaveInfo* parent_info) - : is_root_layer(false), - is_save_layer(false), + : is_save_layer(false), has_deferred_save_op(true), global_state(parent_info->global_state), layer_state(parent_info->layer_state), - global_space_accumulator(parent_info->global_space_accumulator), - layer_local_accumulator(parent_info->layer_local_accumulator) {} + layer_info(parent_info->layer_info) {} // For saveLayer calls: explicit SaveInfo(const SaveInfo* parent_info, const std::shared_ptr& filter, int rtree_rect_index) - : is_root_layer(false), - is_save_layer(true), - rtree_rects_start_index(rtree_rect_index), + : is_save_layer(true), global_state(parent_info->global_state), layer_state(kMaxCullRect), - global_space_accumulator(parent_info->global_space_accumulator), - layer_local_accumulator(new AccumulationRect()), - filter(filter) {} - - bool is_group_opacity_compatible() const { - return !opacity_incompatible_op_detected && - !layer_local_accumulator->overlap_detected(); - } - - // Records the given bounds after transforming by the global and - // layer matrices. - bool AccumulateBoundsLocal(const SkRect& bounds); + layer_info(new LayerInfo(filter, rtree_rect_index)) {} - // Simply transfers the local bounds to the parent - void TransferBoundsToParent(const SaveInfo& parent); - - void update_blend_mode(DlBlendMode mode) { - if (max_blend_mode < mode) { - max_blend_mode = mode; - } - } - - const bool is_root_layer; const bool is_save_layer; bool has_deferred_save_op = false; bool is_nop = false; - bool opacity_incompatible_op_detected = false; - bool is_unbounded = false; - bool affects_transparent_layer = false; - bool contains_backdrop_filter = false; - - DlBlendMode max_blend_mode = DlBlendMode::kClear; - // The offset into the buffer where the associated save op is recorded - // (which is not necessarily the same as when the Save() method is called) - size_t save_offset = 0; // The depth when the save call is recorded, used to compute the total // depth of its content when the associated restore is called. uint32_t save_depth = 0; - // The index of the rtree rects when the saveLayer was called, used - // only in the case that the saveLayer has a filter so that the - // accumulated rects can be updated in the corresponding restore call. - const size_t rtree_rects_start_index = 0; + // The offset into the buffer where the associated save op is recorded + // (which is not necessarily the same as when the Save() method is called + // due to deferred saves) + size_t save_offset = 0; // The transform and clip accumulated since the root of the DisplayList DisplayListMatrixClipState global_state; @@ -601,30 +610,14 @@ class DisplayListBuilder final : public virtual DlCanvas, // used to compute and update its bounds when the restore is called. DisplayListMatrixClipState layer_state; - // Not every layer needs its own accumulator(s). In particular, the - // global accumulator is only used if we are not construting an rtree. - // Regular save calls will share both accumulators with their parent. - // Additionally, a saveLayer will separate its global accumulator from - // its parent (if not constructing an rtree) when it has a filter which - // requires it to post-adjust the bounds accumulated while recording - // its content. Finally, every saveLayer has its own local accumulator. - // - // All accumulations could occur in the local layer space, and then be - // transformed and accumulated into the parent as each layer is restored, - // but that technique would compound the bounds errors that happen when - // a list of transforms is performed serially on a rectangle (mainly - // when multiple rotation or skew transforms are involved). - - // The bounds accumulator for the entire DisplayList, relative to its root - std::shared_ptr global_space_accumulator; + std::shared_ptr layer_info; - // The bounds accumulator to set/verify the bounds of the most recently - // invoked saveLayer call, relative to the root of that saveLayer - std::shared_ptr layer_local_accumulator; + // Records the given bounds after transforming by the global and + // layer matrices. + bool AccumulateBoundsLocal(const SkRect& bounds); - // The filter that will be applied to the contents of the saveLayer - // when it is restored into the parent layer. - const std::shared_ptr filter; + // Simply transfers the local bounds to the parent + void TransferBoundsToParent(const SaveInfo& parent); }; const DlRect original_cull_rect_; @@ -634,7 +627,7 @@ class DisplayListBuilder final : public virtual DlCanvas, DlPaint current_; // Returns a reference to the SaveInfo structure at the top of the current - // save_stack state. Note that the clip and matrix state can be accessed + // save_stack vector. Note that the clip and matrix state can be accessed // more directly through global_state() and layer_state(). SaveInfo& current_info() { return save_stack_.back(); } const SaveInfo& current_info() const { return save_stack_.back(); } @@ -646,6 +639,23 @@ class DisplayListBuilder final : public virtual DlCanvas, return *std::prev(save_stack_.end(), 2); } + // Returns a reference to the LayerInfo structure at the top of the current + // save_stack vector. Note that the clip and matrix state can be accessed + // more directly through global_state() and layer_state(). + LayerInfo& current_layer() { return *save_stack_.back().layer_info; } + const LayerInfo& current_layer() const { + return *save_stack_.back().layer_info; + } + + // Returns a reference to the LayerInfo structure just below the top + // of the current save_stack state. + LayerInfo& parent_layer() { + return *std::prev(save_stack_.end(), 2)->layer_info; + } + const LayerInfo& parent_layer() const { + return *std::prev(save_stack_.end(), 2)->layer_info; + } + // Returns a reference to the matrix and clip state for the entire // DisplayList. The initial transform of this state is identity and // the initial cull_rect is the root original_cull_rect supplied @@ -673,11 +683,8 @@ class DisplayListBuilder final : public virtual DlCanvas, return current_info().layer_state; } - void RestoreLayer(const SaveInfo& current_info, - SaveInfo& parent_info); - void TransferLayerBounds(const SaveInfo& current_info, - SaveInfo& parent_info, - const SkRect& content_bounds); + void RestoreLayer(); + void TransferLayerBounds(const SkRect& content_bounds); bool AdjustRTreeRects(RTreeData& data, const DlImageFilter& filter, const SkMatrix& matrix, @@ -709,7 +716,7 @@ class DisplayListBuilder final : public virtual DlCanvas, // that has determined its compatibility as indicated by |compatible|. void UpdateLayerOpacityCompatibility(bool compatible) { if (!compatible) { - current_info().opacity_incompatible_op_detected = true; + current_layer().opacity_incompatible_op_detected = true; } } @@ -770,10 +777,10 @@ class DisplayListBuilder final : public virtual DlCanvas, case OpResult::kPreservesTransparency: break; case OpResult::kAffectsAll: - current_info().affects_transparent_layer = true; + current_layer().affects_transparent_layer = true; break; } - current_info().update_blend_mode(mode); + current_layer().update_blend_mode(mode); } void UpdateLayerResult(OpResult result, bool uses_attributes = true) { UpdateLayerResult(result, uses_attributes ? current_.getBlendMode() @@ -794,7 +801,7 @@ class DisplayListBuilder final : public virtual DlCanvas, // Records the fact that we encountered an op that either could not // estimate its bounds or that fills all of the destination space. - bool AccumulateUnbounded(SaveInfo& layer); + bool AccumulateUnbounded(const SaveInfo& save); bool AccumulateUnbounded() { return AccumulateUnbounded(current_info()); } diff --git a/display_list/geometry/dl_geometry_types.h b/display_list/geometry/dl_geometry_types.h index 34b431cde4c62..4d7132e3b7d62 100644 --- a/display_list/geometry/dl_geometry_types.h +++ b/display_list/geometry/dl_geometry_types.h @@ -20,18 +20,33 @@ using DlScalar = impeller::Scalar; using DlDegrees = impeller::Degrees; using DlRadians = impeller::Radians; -using DlISize = impeller::ISize32; +using DlPoint = impeller::Point; +using DlIPoint = impeller::IPoint32; using DlSize = impeller::Size; +using DlISize = impeller::ISize32; using DlRect = impeller::Rect; using DlIRect = impeller::IRect32; using DlMatrix = impeller::Matrix; +static_assert(sizeof(SkPoint) == sizeof(DlPoint)); +static_assert(sizeof(SkIPoint) == sizeof(DlIPoint)); +static_assert(sizeof(SkSize) == sizeof(DlSize)); +static_assert(sizeof(SkISize) == sizeof(DlISize)); static_assert(sizeof(SkRect) == sizeof(DlRect)); +static_assert(sizeof(SkIRect) == sizeof(DlIRect)); + +inline const DlPoint& ToDlPoint(const SkPoint& point) { + return *reinterpret_cast(&point); +} inline const DlRect& ToDlRect(const SkRect& rect) { return *reinterpret_cast(&rect); } +inline const DlISize& ToDlISize(const SkISize& size) { + return *reinterpret_cast(&size); +} + inline constexpr DlMatrix ToDlMatrix(const SkMatrix& matrix) { // clang-format off return DlMatrix::MakeColumn( @@ -49,6 +64,10 @@ inline constexpr DlMatrix ToDlMatrix(const SkM44& matrix) { return dl_matrix; } +inline const SkPoint& ToSkPoint(const DlPoint& point) { + return *reinterpret_cast(&point); +} + inline const SkRect& ToSkRect(const DlRect& rect) { return *reinterpret_cast(&rect); } diff --git a/display_list/geometry/dl_geometry_types_unittests.cc b/display_list/geometry/dl_geometry_types_unittests.cc new file mode 100644 index 0000000000000..739052017c684 --- /dev/null +++ b/display_list/geometry/dl_geometry_types_unittests.cc @@ -0,0 +1,54 @@ +// 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. + +#include "flutter/display_list/geometry/dl_geometry_types.h" +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +TEST(DisplayListGeometryTypes, PointConversion) { + SkPoint sk_p = SkPoint::Make(1.0f, 2.0f); + DlPoint dl_p = DlPoint(1.0f, 2.0f); + + EXPECT_EQ(sk_p, ToSkPoint(dl_p)); + EXPECT_EQ(ToDlPoint(sk_p), dl_p); + + sk_p = SkPoint::Make(1.0f, 2.0f); + dl_p = DlPoint(1.0f, 3.0f); + + EXPECT_NE(sk_p, ToSkPoint(dl_p)); + EXPECT_NE(ToDlPoint(sk_p), dl_p); +} + +TEST(DisplayListGeometryTypes, RectConversion) { + SkRect sk_r = SkRect::MakeLTRB(1.0f, 2.0f, 3.0f, 4.0f); + DlRect dl_r = DlRect::MakeLTRB(1.0f, 2.0f, 3.0f, 4.0f); + + EXPECT_EQ(sk_r, ToSkRect(dl_r)); + EXPECT_EQ(ToDlRect(sk_r), dl_r); + + sk_r = SkRect::MakeLTRB(1.0f, 2.0f, 3.0f, 4.0f); + dl_r = DlRect::MakeLTRB(1.0f, 2.0f, 3.0f, 5.0f); + + EXPECT_NE(sk_r, ToSkRect(dl_r)); + EXPECT_NE(ToDlRect(sk_r), dl_r); +} + +TEST(DisplayListGeometryTypes, ISizeConversion) { + SkISize sk_s = SkISize::Make(1.0f, 2.0f); + DlISize dl_s = DlISize(1.0f, 2.0f); + + EXPECT_EQ(sk_s, ToSkISize(dl_s)); + EXPECT_EQ(ToDlISize(sk_s), dl_s); + + sk_s = SkISize::Make(1.0f, 2.0f); + dl_s = DlISize(1.0f, 3.0f); + + EXPECT_NE(sk_s, ToSkISize(dl_s)); + EXPECT_NE(ToDlISize(sk_s), dl_s); +} + +} // namespace testing +} // namespace flutter diff --git a/display_list/utils/dl_accumulation_rect.cc b/display_list/utils/dl_accumulation_rect.cc index cd882895d7fed..c0b6a48829f3d 100644 --- a/display_list/utils/dl_accumulation_rect.cc +++ b/display_list/utils/dl_accumulation_rect.cc @@ -50,6 +50,28 @@ void AccumulationRect::accumulate(SkRect r) { } } +void AccumulationRect::accumulate(AccumulationRect& ar) { + if (ar.is_empty()) { + return; + } + if (ar.min_x_ < max_x_ && ar.max_x_ > min_x_ && // + ar.min_y_ < max_y_ && ar.max_y_ > min_y_) { + record_overlapping_bounds(); + } + if (min_x_ > ar.min_x_) { + min_x_ = ar.min_x_; + } + if (min_y_ > ar.min_y_) { + min_y_ = ar.min_y_; + } + if (max_x_ < ar.max_x_) { + max_x_ = ar.max_x_; + } + if (max_y_ < ar.max_y_) { + max_y_ = ar.max_y_; + } +} + SkRect AccumulationRect::bounds() const { return (max_x_ >= min_x_ && max_y_ >= min_y_) ? SkRect::MakeLTRB(min_x_, min_y_, max_x_, max_y_) diff --git a/display_list/utils/dl_accumulation_rect.h b/display_list/utils/dl_accumulation_rect.h index ca492c17e9d06..09ed6adf16068 100644 --- a/display_list/utils/dl_accumulation_rect.h +++ b/display_list/utils/dl_accumulation_rect.h @@ -28,8 +28,10 @@ class AccumulationRect { void accumulate(SkScalar x, SkScalar y); void accumulate(SkPoint p) { accumulate(p.fX, p.fY); } + void accumulate(DlPoint p) { accumulate(p.x, p.y); } void accumulate(SkRect r); void accumulate(DlRect r) { accumulate(ToSkRect(r)); } + void accumulate(AccumulationRect& ar); bool is_empty() const { return min_x_ >= max_x_ || min_y_ >= max_y_; } bool is_not_empty() const { return min_x_ < max_x_ && min_y_ < max_y_; } diff --git a/display_list/utils/dl_accumulation_rect_unittests.cc b/display_list/utils/dl_accumulation_rect_unittests.cc new file mode 100644 index 0000000000000..0031acdbc096d --- /dev/null +++ b/display_list/utils/dl_accumulation_rect_unittests.cc @@ -0,0 +1,273 @@ +// 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. + +#include "flutter/display_list/utils/dl_accumulation_rect.h" +#include "flutter/testing/assertions_skia.h" +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +TEST(DisplayListAccumulationRect, Constructor) { + AccumulationRect accumulator; + + EXPECT_TRUE(accumulator.is_empty()); + EXPECT_TRUE(accumulator.bounds().isEmpty()); + EXPECT_FALSE(accumulator.overlap_detected()); +} + +TEST(DisplayListAccumulationRect, OnePoint) { + AccumulationRect accumulator; + accumulator.accumulate(10.0f, 10.0f); + + EXPECT_TRUE(accumulator.is_empty()); + EXPECT_TRUE(accumulator.bounds().isEmpty()); + EXPECT_FALSE(accumulator.overlap_detected()); +} + +TEST(DisplayListAccumulationRect, TwoPoints) { + auto test = [](DlScalar x1, DlScalar y1, // + DlScalar x2, DlScalar y2, // + SkRect bounds, // + bool should_be_empty, bool should_overlap, + const std::string& label) { + { + AccumulationRect accumulator; + accumulator.accumulate(x1, y1); + accumulator.accumulate(x2, y2); + + EXPECT_EQ(accumulator.is_empty(), should_be_empty) << label; + EXPECT_EQ(accumulator.bounds().isEmpty(), should_be_empty) << label; + EXPECT_EQ(accumulator.bounds(), bounds) << label; + EXPECT_EQ(accumulator.overlap_detected(), should_overlap) << label; + } + + { + AccumulationRect accumulator; + accumulator.accumulate(SkPoint::Make(x1, y1)); + accumulator.accumulate(SkPoint::Make(x2, y2)); + + EXPECT_EQ(accumulator.is_empty(), should_be_empty) << label; + EXPECT_EQ(accumulator.bounds().isEmpty(), should_be_empty) << label; + EXPECT_EQ(accumulator.bounds(), bounds) << label; + EXPECT_EQ(accumulator.overlap_detected(), should_overlap) << label; + } + + { + AccumulationRect accumulator; + accumulator.accumulate(DlPoint(x1, y1)); + accumulator.accumulate(DlPoint(x2, y2)); + + EXPECT_EQ(accumulator.is_empty(), should_be_empty) << label; + EXPECT_EQ(accumulator.bounds().isEmpty(), should_be_empty) << label; + EXPECT_EQ(accumulator.bounds(), bounds) << label; + EXPECT_EQ(accumulator.overlap_detected(), should_overlap) << label; + } + }; + + test(10.0f, 10.0f, 10.0f, 10.0f, SkRect::MakeLTRB(10.0f, 10.0f, 10.0f, 10.0f), + true, false, "Same"); + test(10.0f, 10.0f, 20.0f, 10.0f, SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 10.0f), + true, false, "Horizontal"); + test(10.0f, 10.0f, 10.0f, 20.0f, SkRect::MakeLTRB(10.0f, 10.0f, 10.0f, 20.0f), + true, false, "Vertical"); + test(10.0f, 10.0f, 20.0f, 20.0f, SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f), + false, false, "Diagonal"); +} + +TEST(DisplayListAccumulationRect, ThreePoints) { + auto test = [](DlScalar x1, DlScalar y1, // + DlScalar x2, DlScalar y2, // + DlScalar x3, DlScalar y3, // + SkRect bounds, // + bool should_be_empty, bool should_overlap, + const std::string& label) { + { + AccumulationRect accumulator; + accumulator.accumulate(x1, y1); + accumulator.accumulate(x2, y2); + accumulator.accumulate(x3, y3); + + EXPECT_EQ(accumulator.is_empty(), should_be_empty) << label; + EXPECT_EQ(accumulator.bounds().isEmpty(), should_be_empty) << label; + EXPECT_EQ(accumulator.bounds(), bounds) << label; + EXPECT_EQ(accumulator.overlap_detected(), should_overlap) << label; + } + + { + AccumulationRect accumulator; + accumulator.accumulate(SkPoint::Make(x1, y1)); + accumulator.accumulate(SkPoint::Make(x2, y2)); + accumulator.accumulate(SkPoint::Make(x3, y3)); + + EXPECT_EQ(accumulator.is_empty(), should_be_empty) << label; + EXPECT_EQ(accumulator.bounds().isEmpty(), should_be_empty) << label; + EXPECT_EQ(accumulator.bounds(), bounds) << label; + EXPECT_EQ(accumulator.overlap_detected(), should_overlap) << label; + } + + { + AccumulationRect accumulator; + accumulator.accumulate(DlPoint(x1, y1)); + accumulator.accumulate(DlPoint(x2, y2)); + accumulator.accumulate(DlPoint(x3, y3)); + + EXPECT_EQ(accumulator.is_empty(), should_be_empty) << label; + EXPECT_EQ(accumulator.bounds().isEmpty(), should_be_empty) << label; + EXPECT_EQ(accumulator.bounds(), bounds) << label; + EXPECT_EQ(accumulator.overlap_detected(), should_overlap) << label; + } + }; + + test(10.0f, 10.0f, 10.0f, 10.0f, 10.0f, 10.0f, + SkRect::MakeLTRB(10.0f, 10.0f, 10.0f, 10.0f), true, false, "Same"); + test(10.0f, 10.0f, 20.0f, 10.0f, 15.0f, 10.0f, + SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 10.0f), true, false, "Horizontal"); + test(10.0f, 10.0f, 10.0f, 20.0f, 10.0f, 15.0f, + SkRect::MakeLTRB(10.0f, 10.0f, 10.0f, 20.0f), true, false, "Vertical"); + test(10.0f, 10.0f, 20.0f, 20.0f, 25.0f, 15.0f, + SkRect::MakeLTRB(10.0f, 10.0f, 25.0f, 20.0f), false, false, "Disjoint"); + test(10.0f, 10.0f, 20.0f, 20.0f, 15.0f, 15.0f, + SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f), false, true, "Inside"); +} + +TEST(DisplayListAccumulationRect, EmptyRect) { + auto test = [](DlScalar l, DlScalar t, DlScalar r, DlScalar b, // + SkRect bounds, // + bool should_be_empty, bool should_overlap, + const std::string& label) { + { + AccumulationRect accumulator; + accumulator.accumulate(SkRect::MakeLTRB(l, t, r, b)); + + EXPECT_EQ(accumulator.is_empty(), should_be_empty) << label; + EXPECT_EQ(accumulator.bounds().isEmpty(), should_be_empty) << label; + EXPECT_EQ(accumulator.bounds(), bounds) << label; + EXPECT_EQ(accumulator.overlap_detected(), should_overlap) << label; + } + + { + AccumulationRect accumulator; + accumulator.accumulate(DlRect::MakeLTRB(l, t, r, b)); + + EXPECT_EQ(accumulator.is_empty(), should_be_empty) << label; + EXPECT_EQ(accumulator.bounds().isEmpty(), should_be_empty) << label; + EXPECT_EQ(accumulator.bounds(), bounds) << label; + EXPECT_EQ(accumulator.overlap_detected(), should_overlap) << label; + } + + { + AccumulationRect content; + content.accumulate(l, t); + content.accumulate(r, b); + EXPECT_EQ(content.is_empty(), should_be_empty) << label; + EXPECT_EQ(content.bounds().isEmpty(), should_be_empty) << label; + // bounds for an accumulation by points may be different than the + // bounds for an accumulation by the rect they produce because + // construction by points has no "empty rejection" case. + if (!should_be_empty) { + EXPECT_EQ(content.bounds(), bounds) << label; + } + EXPECT_EQ(content.overlap_detected(), should_overlap) << label; + + AccumulationRect accumulator; + accumulator.accumulate(content); + + EXPECT_EQ(accumulator.is_empty(), should_be_empty) << label; + EXPECT_EQ(accumulator.bounds().isEmpty(), should_be_empty) << label; + EXPECT_EQ(accumulator.bounds(), bounds) << label; + EXPECT_EQ(accumulator.overlap_detected(), should_overlap) << label; + } + }; + + test(10.0f, 10.0f, 10.0f, 10.0f, SkRect::MakeLTRB(0.0f, 0.0f, 0.0f, 0.0f), + true, false, "Singular"); + test(10.0f, 10.0f, 20.0f, 10.0f, SkRect::MakeLTRB(0.0f, 0.0f, 0.0f, 0.0f), + true, false, "Horizontal Empty"); + test(10.0f, 10.0f, 10.0f, 20.0f, SkRect::MakeLTRB(0.0f, 0.0f, 0.0f, 0.0f), + true, false, "Vertical Empty"); + test(10.0f, 10.0f, 20.0f, 20.0f, SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f), + false, false, "Non-Empty"); +} + +TEST(DisplayListAccumulationRect, TwoRects) { + auto test = [](DlScalar l1, DlScalar t1, DlScalar r1, DlScalar b1, // + DlScalar l2, DlScalar t2, DlScalar r2, DlScalar b2, // + SkRect bounds, // + bool should_be_empty, bool should_overlap, + const std::string& label) { + { + AccumulationRect accumulator; + accumulator.accumulate(SkRect::MakeLTRB(l1, t1, r1, b1)); + accumulator.accumulate(SkRect::MakeLTRB(l2, t2, r2, b2)); + + EXPECT_EQ(accumulator.is_empty(), should_be_empty) << label; + EXPECT_EQ(accumulator.bounds().isEmpty(), should_be_empty) << label; + EXPECT_EQ(accumulator.bounds(), bounds) << label; + EXPECT_EQ(accumulator.overlap_detected(), should_overlap) << label; + } + + { + AccumulationRect accumulator; + accumulator.accumulate(DlRect::MakeLTRB(l1, t1, r1, b1)); + accumulator.accumulate(DlRect::MakeLTRB(l2, t2, r2, b2)); + + EXPECT_EQ(accumulator.is_empty(), should_be_empty) << label; + EXPECT_EQ(accumulator.bounds().isEmpty(), should_be_empty) << label; + EXPECT_EQ(accumulator.bounds(), bounds) << label; + EXPECT_EQ(accumulator.overlap_detected(), should_overlap) << label; + } + + { + AccumulationRect content1; + content1.accumulate(l1, t1); + content1.accumulate(r1, b1); + + AccumulationRect content2; + content2.accumulate(l2, t2); + content2.accumulate(r2, b2); + + AccumulationRect accumulator; + accumulator.accumulate(content1); + accumulator.accumulate(content2); + + EXPECT_EQ(accumulator.is_empty(), should_be_empty) << label; + EXPECT_EQ(accumulator.bounds().isEmpty(), should_be_empty) << label; + EXPECT_EQ(accumulator.bounds(), bounds) << label; + EXPECT_EQ(accumulator.overlap_detected(), should_overlap) << label; + } + }; + + test(10.0f, 10.0f, 10.0f, 10.0f, // + 20.0f, 20.0f, 20.0f, 20.0f, // + SkRect::MakeLTRB(0.0f, 0.0f, 0.0f, 0.0f), // + true, false, "Empty + Empty"); + test(10.0f, 10.0f, 20.0f, 10.0f, // + 10.0f, 10.0f, 10.0f, 20.0f, // + SkRect::MakeLTRB(0.0f, 0.0f, 0.0f, 0.0f), // + true, false, "Horizontal + Vertical"); + test(10.0f, 10.0f, 10.0f, 10.0f, // + 15.0f, 15.0f, 20.0f, 20.0f, // + SkRect::MakeLTRB(15.0f, 15.0f, 20.0f, 20.0f), // + false, false, "Empty + Non-Empty"); + test(10.0f, 10.0f, 15.0f, 15.0f, // + 20.0f, 20.0f, 20.0f, 20.0f, // + SkRect::MakeLTRB(10.0f, 10.0f, 15.0f, 15.0f), // + false, false, "Non-Empty + Empty"); + test(10.0f, 10.0f, 15.0f, 15.0f, // + 15.0f, 15.0f, 20.0f, 20.0f, // + SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f), // + false, false, "Abutting"); + test(10.0f, 10.0f, 15.0f, 15.0f, // + 16.0f, 16.0f, 20.0f, 20.0f, // + SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f), // + false, false, "Disjoint"); + test(10.0f, 10.0f, 16.0f, 16.0f, // + 15.0f, 15.0f, 20.0f, 20.0f, // + SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f), // + false, true, "Overlapping"); +} + +} // namespace testing +} // namespace flutter diff --git a/display_list/utils/dl_matrix_clip_tracker_unittests.cc b/display_list/utils/dl_matrix_clip_tracker_unittests.cc index 2e379900a8173..1904cc0ba0aea 100644 --- a/display_list/utils/dl_matrix_clip_tracker_unittests.cc +++ b/display_list/utils/dl_matrix_clip_tracker_unittests.cc @@ -9,10 +9,6 @@ namespace flutter { namespace testing { -using DlRect = impeller::Rect; -using DlMatrix = impeller::Matrix; -using Degrees = impeller::Degrees; - TEST(DisplayListMatrixClipTracker, Constructor) { const SkRect cull_rect = SkRect::MakeLTRB(20, 40, 60, 80); const DlRect dl_cull_rect = DlRect::MakeLTRB(20, 40, 60, 80); @@ -602,7 +598,7 @@ TEST(DisplayListMatrixClipTracker, Rotate) { SkMatrix::Concat(matrix, SkMatrix::RotateDeg(90)); const SkM44 rotated_m44 = SkM44(rotated_matrix); const DlMatrix rotated_dl_matrix = - dl_matrix * DlMatrix::MakeRotationZ(Degrees(90)); + dl_matrix * DlMatrix::MakeRotationZ(DlDegrees(90)); const SkRect local_cull_rect = SkRect::MakeLTRB(10, -15, 20, -5); DisplayListMatrixClipTracker tracker1(cull_rect, matrix); @@ -644,7 +640,7 @@ TEST(DisplayListMatrixClipState, Rotate) { SkMatrix::Concat(matrix, SkMatrix::RotateDeg(90)); const SkM44 rotated_m44 = SkM44(rotated_matrix); const DlMatrix rotated_dl_matrix = - dl_matrix * DlMatrix::MakeRotationZ(Degrees(90)); + dl_matrix * DlMatrix::MakeRotationZ(DlDegrees(90)); const SkRect local_cull_rect = SkRect::MakeLTRB(10, -15, 20, -5); DisplayListMatrixClipState state1(cull_rect, matrix); diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h index 919141c160ce1..4f7bbba55f7ff 100644 --- a/impeller/geometry/rect.h +++ b/impeller/geometry/rect.h @@ -185,6 +185,10 @@ struct TRect { bottom_ == r.bottom_; } + [[nodiscard]] constexpr bool operator!=(const TRect& r) const { + return !(*this == r); + } + [[nodiscard]] constexpr TRect Scale(Type scale) const { return TRect(left_ * scale, // top_ * scale, //