blob: f56150e2a8dbc130a1444e727aedd126742feef3 [file] [log] [blame]
// 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