| // 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/dl_vertices.h" | |
| #include "flutter/display_list/utils/dl_bounds_accumulator.h" | |
| #include "flutter/fml/logging.h" | |
| namespace flutter { | |
| using Flags = DlVertices::Builder::Flags; | |
| static void DlVerticesDeleter(void* p) { | |
| // Some of our target environments would prefer a sized delete, | |
| // but other target environments do not have that operator. | |
| // Use an unsized delete until we get better agreement in the | |
| // environments. | |
| // See https://github.com/flutter/flutter/issues/100327 | |
| ::operator delete(p); | |
| } | |
| static size_t bytes_needed(int vertex_count, Flags flags, int index_count) { | |
| int needed = sizeof(DlVertices); | |
| // We always have vertices | |
| needed += vertex_count * sizeof(SkPoint); | |
| if (flags.has_texture_coordinates) { | |
| needed += vertex_count * sizeof(SkPoint); | |
| } | |
| if (flags.has_colors) { | |
| needed += vertex_count * sizeof(DlColor); | |
| } | |
| if (index_count > 0) { | |
| needed += index_count * sizeof(uint16_t); | |
| } | |
| return needed; | |
| } | |
| std::shared_ptr<DlVertices> DlVertices::Make( | |
| DlVertexMode mode, | |
| int vertex_count, | |
| const SkPoint vertices[], | |
| const SkPoint texture_coordinates[], | |
| const DlColor colors[], | |
| int index_count, | |
| const uint16_t indices[]) { | |
| if (!vertices || vertex_count <= 0) { | |
| vertex_count = 0; | |
| texture_coordinates = nullptr; | |
| colors = nullptr; | |
| } | |
| if (!indices || index_count <= 0) { | |
| index_count = 0; | |
| indices = nullptr; | |
| } | |
| Flags flags; | |
| FML_DCHECK(!flags.has_texture_coordinates); | |
| FML_DCHECK(!flags.has_colors); | |
| if (texture_coordinates) { | |
| flags |= Builder::kHasTextureCoordinates; | |
| } | |
| if (colors) { | |
| flags |= Builder::kHasColors; | |
| } | |
| Builder builder(mode, vertex_count, flags, index_count); | |
| builder.store_vertices(vertices); | |
| if (texture_coordinates) { | |
| builder.store_texture_coordinates(texture_coordinates); | |
| } | |
| if (colors) { | |
| builder.store_colors(colors); | |
| } | |
| if (indices) { | |
| builder.store_indices(indices); | |
| } | |
| return builder.build(); | |
| } | |
| size_t DlVertices::size() const { | |
| return bytes_needed(vertex_count_, | |
| {{texture_coordinates_offset_ > 0, colors_offset_ > 0}}, | |
| index_count_); | |
| } | |
| static SkRect compute_bounds(const SkPoint* points, int count) { | |
| RectBoundsAccumulator accumulator; | |
| for (int i = 0; i < count; i++) { | |
| accumulator.accumulate(points[i]); | |
| } | |
| return accumulator.bounds(); | |
| } | |
| DlVertices::DlVertices(DlVertexMode mode, | |
| int unchecked_vertex_count, | |
| const SkPoint* vertices, | |
| const SkPoint* texture_coordinates, | |
| const DlColor* colors, | |
| int unchecked_index_count, | |
| const uint16_t* indices, | |
| const SkRect* bounds) | |
| : mode_(mode), | |
| vertex_count_(std::max(unchecked_vertex_count, 0)), | |
| index_count_(indices ? std::max(unchecked_index_count, 0) : 0) { | |
| bounds_ = bounds ? *bounds : compute_bounds(vertices, vertex_count_); | |
| char* pod = reinterpret_cast<char*>(this); | |
| size_t offset = sizeof(DlVertices); | |
| auto advance = [pod, &offset](auto* src, int count) { | |
| if (src != nullptr && count > 0) { | |
| size_t bytes = count * sizeof(*src); | |
| memcpy(pod + offset, src, bytes); | |
| size_t ret = offset; | |
| offset += bytes; | |
| return ret; | |
| } else { | |
| return static_cast<size_t>(0); | |
| } | |
| }; | |
| vertices_offset_ = advance(vertices, vertex_count_); | |
| texture_coordinates_offset_ = advance(texture_coordinates, vertex_count_); | |
| colors_offset_ = advance(colors, vertex_count_); | |
| indices_offset_ = advance(indices, index_count_); | |
| FML_DCHECK(offset == bytes_needed(vertex_count_, | |
| {{!!texture_coordinates, !!colors}}, | |
| index_count_)); | |
| } | |
| DlVertices::DlVertices(const DlVertices* other) | |
| : DlVertices(other->mode_, | |
| other->vertex_count_, | |
| other->vertices(), | |
| other->texture_coordinates(), | |
| other->colors(), | |
| other->index_count_, | |
| other->indices(), | |
| &other->bounds_) {} | |
| DlVertices::DlVertices(DlVertexMode mode, | |
| int unchecked_vertex_count, | |
| Flags flags, | |
| int unchecked_index_count) | |
| : mode_(mode), | |
| vertex_count_(std::max(unchecked_vertex_count, 0)), | |
| index_count_(std::max(unchecked_index_count, 0)) { | |
| char* pod = reinterpret_cast<char*>(this); | |
| size_t offset = sizeof(DlVertices); | |
| auto advance = [pod, &offset](size_t size, int count) { | |
| if (count > 0) { | |
| size_t bytes = count * size; | |
| memset(pod + offset, 0, bytes); | |
| size_t ret = offset; | |
| offset += bytes; | |
| return ret; | |
| } else { | |
| return static_cast<size_t>(0); | |
| } | |
| }; | |
| vertices_offset_ = advance(sizeof(SkPoint), vertex_count_); | |
| texture_coordinates_offset_ = advance( | |
| sizeof(SkPoint), flags.has_texture_coordinates ? vertex_count_ : 0); | |
| colors_offset_ = | |
| advance(sizeof(DlColor), flags.has_colors ? vertex_count_ : 0); | |
| indices_offset_ = advance(sizeof(uint16_t), index_count_); | |
| FML_DCHECK(offset == bytes_needed(vertex_count_, flags, index_count_)); | |
| FML_DCHECK((vertex_count_ != 0) == (vertices() != nullptr)); | |
| FML_DCHECK((vertex_count_ != 0 && flags.has_texture_coordinates) == | |
| (texture_coordinates() != nullptr)); | |
| FML_DCHECK((vertex_count_ != 0 && flags.has_colors) == (colors() != nullptr)); | |
| FML_DCHECK((index_count_ != 0) == (indices() != nullptr)); | |
| } | |
| bool DlVertices::operator==(DlVertices const& other) const { | |
| auto lists_equal = [](auto* a, auto* b, int count) { | |
| if (a == nullptr || b == nullptr) { | |
| return a == b; | |
| } | |
| for (int i = 0; i < count; i++) { | |
| if (a[i] != b[i]) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| }; | |
| return // | |
| mode_ == other.mode_ && // | |
| vertex_count_ == other.vertex_count_ && // | |
| lists_equal(vertices(), other.vertices(), vertex_count_) && // | |
| lists_equal(texture_coordinates(), other.texture_coordinates(), // | |
| vertex_count_) && // | |
| lists_equal(colors(), other.colors(), vertex_count_) && // | |
| index_count_ == other.index_count_ && // | |
| lists_equal(indices(), other.indices(), index_count_); | |
| } | |
| DlVertices::Builder::Builder(DlVertexMode mode, | |
| int vertex_count, | |
| Flags flags, | |
| int index_count) | |
| : needs_vertices_(true), | |
| needs_texture_coords_(flags.has_texture_coordinates), | |
| needs_colors_(flags.has_colors), | |
| needs_indices_(index_count > 0) { | |
| vertex_count = std::max(vertex_count, 0); | |
| index_count = std::max(index_count, 0); | |
| void* storage = | |
| ::operator new(bytes_needed(vertex_count, flags, index_count)); | |
| vertices_.reset(new (storage) | |
| DlVertices(mode, vertex_count, flags, index_count), | |
| DlVerticesDeleter); | |
| } | |
| static void store_points(char* dst, int offset, const float* src, int count) { | |
| SkPoint* points = reinterpret_cast<SkPoint*>(dst + offset); | |
| for (int i = 0; i < count; i++) { | |
| points[i] = SkPoint::Make(src[i * 2], src[i * 2 + 1]); | |
| } | |
| } | |
| void DlVertices::Builder::store_vertices(const SkPoint vertices[]) { | |
| FML_CHECK(is_valid()); | |
| FML_CHECK(needs_vertices_); | |
| char* pod = reinterpret_cast<char*>(vertices_.get()); | |
| size_t bytes = vertices_->vertex_count_ * sizeof(vertices[0]); | |
| memcpy(pod + vertices_->vertices_offset_, vertices, bytes); | |
| needs_vertices_ = false; | |
| } | |
| void DlVertices::Builder::store_vertices(const float vertices[]) { | |
| FML_CHECK(is_valid()); | |
| FML_CHECK(needs_vertices_); | |
| char* pod = reinterpret_cast<char*>(vertices_.get()); | |
| store_points(pod, vertices_->vertices_offset_, vertices, | |
| vertices_->vertex_count_); | |
| needs_vertices_ = false; | |
| } | |
| void DlVertices::Builder::store_texture_coordinates(const SkPoint coords[]) { | |
| FML_CHECK(is_valid()); | |
| FML_CHECK(needs_texture_coords_); | |
| char* pod = reinterpret_cast<char*>(vertices_.get()); | |
| size_t bytes = vertices_->vertex_count_ * sizeof(coords[0]); | |
| memcpy(pod + vertices_->texture_coordinates_offset_, coords, bytes); | |
| needs_texture_coords_ = false; | |
| } | |
| void DlVertices::Builder::store_texture_coordinates(const float coords[]) { | |
| FML_CHECK(is_valid()); | |
| FML_CHECK(needs_texture_coords_); | |
| char* pod = reinterpret_cast<char*>(vertices_.get()); | |
| store_points(pod, vertices_->texture_coordinates_offset_, coords, | |
| vertices_->vertex_count_); | |
| needs_texture_coords_ = false; | |
| } | |
| void DlVertices::Builder::store_colors(const DlColor colors[]) { | |
| FML_CHECK(is_valid()); | |
| FML_CHECK(needs_colors_); | |
| char* pod = reinterpret_cast<char*>(vertices_.get()); | |
| size_t bytes = vertices_->vertex_count_ * sizeof(colors[0]); | |
| memcpy(pod + vertices_->colors_offset_, colors, bytes); | |
| needs_colors_ = false; | |
| } | |
| void DlVertices::Builder::store_indices(const uint16_t indices[]) { | |
| FML_CHECK(is_valid()); | |
| FML_CHECK(needs_indices_); | |
| char* pod = reinterpret_cast<char*>(vertices_.get()); | |
| size_t bytes = vertices_->index_count_ * sizeof(indices[0]); | |
| memcpy(pod + vertices_->indices_offset_, indices, bytes); | |
| needs_indices_ = false; | |
| } | |
| std::shared_ptr<DlVertices> DlVertices::Builder::build() { | |
| FML_CHECK(is_valid()); | |
| if (vertices_->vertex_count() <= 0) { | |
| // We set this to true in the constructor to make sure that they | |
| // call store_vertices() only once, but if there are no vertices | |
| // then we will not object to them never having stored any vertices | |
| needs_vertices_ = false; | |
| } | |
| FML_CHECK(!needs_vertices_); | |
| FML_CHECK(!needs_texture_coords_); | |
| FML_CHECK(!needs_colors_); | |
| FML_CHECK(!needs_indices_); | |
| vertices_->bounds_ = | |
| compute_bounds(vertices_->vertices(), vertices_->vertex_count_); | |
| return std::move(vertices_); | |
| } | |
| } // namespace flutter |