| // 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 <type_traits> | 
 |  | 
 | #include "flutter/display_list/display_list.h" | 
 | #include "flutter/display_list/display_list_canvas_dispatcher.h" | 
 | #include "flutter/display_list/display_list_ops.h" | 
 | #include "flutter/display_list/display_list_utils.h" | 
 | #include "flutter/fml/trace_event.h" | 
 |  | 
 | namespace flutter { | 
 |  | 
 | const SaveLayerOptions SaveLayerOptions::kNoAttributes = SaveLayerOptions(); | 
 | const SaveLayerOptions SaveLayerOptions::kWithAttributes = | 
 |     kNoAttributes.with_renders_with_attributes(); | 
 |  | 
 | DisplayList::DisplayList() | 
 |     : byte_count_(0), | 
 |       op_count_(0), | 
 |       nested_byte_count_(0), | 
 |       nested_op_count_(0), | 
 |       unique_id_(0), | 
 |       bounds_({0, 0, 0, 0}), | 
 |       bounds_cull_({0, 0, 0, 0}), | 
 |       can_apply_group_opacity_(true) {} | 
 |  | 
 | DisplayList::DisplayList(uint8_t* ptr, | 
 |                          size_t byte_count, | 
 |                          unsigned int op_count, | 
 |                          size_t nested_byte_count, | 
 |                          unsigned int nested_op_count, | 
 |                          const SkRect& cull_rect, | 
 |                          bool can_apply_group_opacity) | 
 |     : storage_(ptr), | 
 |       byte_count_(byte_count), | 
 |       op_count_(op_count), | 
 |       nested_byte_count_(nested_byte_count), | 
 |       nested_op_count_(nested_op_count), | 
 |       bounds_({0, 0, -1, -1}), | 
 |       bounds_cull_(cull_rect), | 
 |       can_apply_group_opacity_(can_apply_group_opacity) { | 
 |   static std::atomic<uint32_t> nextID{1}; | 
 |   do { | 
 |     unique_id_ = nextID.fetch_add(+1, std::memory_order_relaxed); | 
 |   } while (unique_id_ == 0); | 
 | } | 
 |  | 
 | DisplayList::~DisplayList() { | 
 |   uint8_t* ptr = storage_.get(); | 
 |   DisposeOps(ptr, ptr + byte_count_); | 
 | } | 
 |  | 
 | void DisplayList::ComputeBounds() { | 
 |   DisplayListBoundsCalculator calculator(&bounds_cull_); | 
 |   Dispatch(calculator); | 
 |   bounds_ = calculator.bounds(); | 
 | } | 
 |  | 
 | void DisplayList::Dispatch(Dispatcher& dispatcher, | 
 |                            uint8_t* ptr, | 
 |                            uint8_t* end) const { | 
 |   TRACE_EVENT0("flutter", "DisplayList::Dispatch"); | 
 |   while (ptr < end) { | 
 |     auto op = reinterpret_cast<const DLOp*>(ptr); | 
 |     ptr += op->size; | 
 |     FML_DCHECK(ptr <= end); | 
 |     switch (op->type) { | 
 | #define DL_OP_DISPATCH(name)                                \ | 
 |   case DisplayListOpType::k##name:                          \ | 
 |     static_cast<const name##Op*>(op)->dispatch(dispatcher); \ | 
 |     break; | 
 |  | 
 |       FOR_EACH_DISPLAY_LIST_OP(DL_OP_DISPATCH) | 
 |  | 
 | #undef DL_OP_DISPATCH | 
 |  | 
 |       default: | 
 |         FML_DCHECK(false); | 
 |         return; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void DisplayList::DisposeOps(uint8_t* ptr, uint8_t* end) { | 
 |   while (ptr < end) { | 
 |     auto op = reinterpret_cast<const DLOp*>(ptr); | 
 |     ptr += op->size; | 
 |     FML_DCHECK(ptr <= end); | 
 |     switch (op->type) { | 
 | #define DL_OP_DISPOSE(name)                            \ | 
 |   case DisplayListOpType::k##name:                     \ | 
 |     if (!std::is_trivially_destructible_v<name##Op>) { \ | 
 |       static_cast<const name##Op*>(op)->~name##Op();   \ | 
 |     }                                                  \ | 
 |     break; | 
 |  | 
 |       FOR_EACH_DISPLAY_LIST_OP(DL_OP_DISPOSE) | 
 |  | 
 | #undef DL_OP_DISPOSE | 
 |  | 
 |       default: | 
 |         FML_DCHECK(false); | 
 |         return; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | static bool CompareOps(uint8_t* ptrA, | 
 |                        uint8_t* endA, | 
 |                        uint8_t* ptrB, | 
 |                        uint8_t* endB) { | 
 |   // These conditions are checked by the caller... | 
 |   FML_DCHECK((endA - ptrA) == (endB - ptrB)); | 
 |   FML_DCHECK(ptrA != ptrB); | 
 |   uint8_t* bulkStartA = ptrA; | 
 |   uint8_t* bulkStartB = ptrB; | 
 |   while (ptrA < endA && ptrB < endB) { | 
 |     auto opA = reinterpret_cast<const DLOp*>(ptrA); | 
 |     auto opB = reinterpret_cast<const DLOp*>(ptrB); | 
 |     if (opA->type != opB->type || opA->size != opB->size) { | 
 |       return false; | 
 |     } | 
 |     ptrA += opA->size; | 
 |     ptrB += opB->size; | 
 |     FML_DCHECK(ptrA <= endA); | 
 |     FML_DCHECK(ptrB <= endB); | 
 |     DisplayListCompare result; | 
 |     switch (opA->type) { | 
 | #define DL_OP_EQUALS(name)                              \ | 
 |   case DisplayListOpType::k##name:                      \ | 
 |     result = static_cast<const name##Op*>(opA)->equals( \ | 
 |         static_cast<const name##Op*>(opB));             \ | 
 |     break; | 
 |  | 
 |       FOR_EACH_DISPLAY_LIST_OP(DL_OP_EQUALS) | 
 |  | 
 | #undef DL_OP_EQUALS | 
 |  | 
 |       default: | 
 |         FML_DCHECK(false); | 
 |         return false; | 
 |     } | 
 |     switch (result) { | 
 |       case DisplayListCompare::kNotEqual: | 
 |         return false; | 
 |       case DisplayListCompare::kUseBulkCompare: | 
 |         break; | 
 |       case DisplayListCompare::kEqual: | 
 |         // Check if we have a backlog of bytes to bulk compare and then | 
 |         // reset the bulk compare pointers to the address following this op | 
 |         auto bulkBytes = reinterpret_cast<const uint8_t*>(opA) - bulkStartA; | 
 |         if (bulkBytes > 0) { | 
 |           if (memcmp(bulkStartA, bulkStartB, bulkBytes) != 0) { | 
 |             return false; | 
 |           } | 
 |         } | 
 |         bulkStartA = ptrA; | 
 |         bulkStartB = ptrB; | 
 |         break; | 
 |     } | 
 |   } | 
 |   if (ptrA != endA || ptrB != endB) { | 
 |     return false; | 
 |   } | 
 |   if (bulkStartA < ptrA) { | 
 |     // Perform a final bulk compare if we have remaining bytes waiting | 
 |     if (memcmp(bulkStartA, bulkStartB, ptrA - bulkStartA) != 0) { | 
 |       return false; | 
 |     } | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | void DisplayList::RenderTo(DisplayListBuilder* builder, | 
 |                            SkScalar opacity) const { | 
 |   // TODO(100983): Opacity is not respected and attributes are not reset. | 
 |   if (!builder) { | 
 |     return; | 
 |   } | 
 |   Dispatch(*builder); | 
 | } | 
 |  | 
 | void DisplayList::RenderTo(SkCanvas* canvas, SkScalar opacity) const { | 
 |   DisplayListCanvasDispatcher dispatcher(canvas, opacity); | 
 |   Dispatch(dispatcher); | 
 | } | 
 |  | 
 | bool DisplayList::Equals(const DisplayList* other) const { | 
 |   if (this == other) { | 
 |     return true; | 
 |   } | 
 |   if (byte_count_ != other->byte_count_ || op_count_ != other->op_count_) { | 
 |     return false; | 
 |   } | 
 |   uint8_t* ptr = storage_.get(); | 
 |   uint8_t* o_ptr = other->storage_.get(); | 
 |   if (ptr == o_ptr) { | 
 |     return true; | 
 |   } | 
 |   return CompareOps(ptr, ptr + byte_count_, o_ptr, o_ptr + other->byte_count_); | 
 | } | 
 |  | 
 | }  // namespace flutter |