blob: 686c8d3f25c535ecd799745a8408fd434bcec482 [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.
#pragma once
#include <functional>
#include <memory>
#include <vector>
#include "flutter/fml/macros.h"
#include "impeller/core/formats.h"
#include "impeller/geometry/path.h"
#include "impeller/geometry/point.h"
#include "impeller/geometry/trig.h"
struct TESStesselator;
namespace impeller {
void DestroyTessellator(TESStesselator* tessellator);
using CTessellator =
std::unique_ptr<TESStesselator, decltype(&DestroyTessellator)>;
enum class WindingOrder {
kClockwise,
kCounterClockwise,
};
//------------------------------------------------------------------------------
/// @brief A utility that generates triangles of the specified fill type
/// given a polyline. This happens on the CPU.
///
/// This object is not thread safe, and its methods must not be
/// called from multiple threads.
///
class Tessellator {
private:
/// Essentially just a vector of Trig objects, but supports storing a
/// reference to either a cached vector or a locally generated vector.
/// The constructor will fill the vector with quarter circular samples
/// for the indicated number of equal divisions if the vector is new.
class Trigs {
public:
explicit Trigs(std::vector<Trig>& trigs, size_t divisions) : trigs_(trigs) {
init(divisions);
FML_DCHECK(trigs_.size() == divisions + 1);
}
explicit Trigs(size_t divisions)
: local_storage_(std::make_unique<std::vector<Trig>>()),
trigs_(*local_storage_) {
init(divisions);
FML_DCHECK(trigs_.size() == divisions + 1);
}
// Utility forwards of the indicated vector methods.
auto inline size() const { return trigs_.size(); }
auto inline begin() const { return trigs_.begin(); }
auto inline end() const { return trigs_.end(); }
private:
// nullptr if a cached vector is used, otherwise the actual storage
std::unique_ptr<std::vector<Trig>> local_storage_;
// Whether or not a cached vector or the local storage is used, this
// this reference will always be valid
std::vector<Trig>& trigs_;
// Fill the vector with the indicated number of equal divisions of
// trigonometric values if it is empty.
void init(size_t divisions);
};
public:
enum class Result {
kSuccess,
kInputError,
kTessellationError,
};
/// @brief A callback function for a |VertexGenerator| to deliver
/// the vertices it computes as |Point| objects.
using TessellatedVertexProc = std::function<void(const Point& p)>;
/// @brief An object which produces a list of vertices as |Point|s that
/// tessellate a previously provided shape and delivers the vertices
/// through a |TessellatedVertexProc| callback.
///
/// The object can also provide advance information on how many
/// vertices it will generate.
///
/// @see |Tessellator::FilledCircle|
/// @see |Tessellator::StrokedCircle|
/// @see |Tessellator::RoundCapLine|
/// @see |Tessellator::FilledEllipse|
class VertexGenerator {
public:
/// @brief Returns the |PrimitiveType| that describes the relationship
/// among the list of vertices produced by the |GenerateVertices|
/// method.
///
/// Most generators will deliver |kTriangleStrip| triangles
virtual PrimitiveType GetTriangleType() const = 0;
/// @brief Returns the number of vertices that the generator plans to
/// produce, if known.
///
/// This value is advisory only and can be used to reserve space
/// where the vertices will be placed, but the count may be an
/// estimate.
///
/// Implementations are encouraged to avoid overestimating
/// the count by too large a number and to provide a best
/// guess so as to minimize potential buffer reallocations
/// as the vertices are delivered.
virtual size_t GetVertexCount() const = 0;
/// @brief Generate the vertices and deliver them in the necessary
/// order (as required by the PrimitiveType) to the given
/// callback function.
virtual void GenerateVertices(const TessellatedVertexProc& proc) const = 0;
};
/// @brief The |VertexGenerator| implementation common to all shapes
/// that are based on a polygonal representation of an ellipse.
class EllipticalVertexGenerator : public virtual VertexGenerator {
public:
/// |VertexGenerator|
PrimitiveType GetTriangleType() const override {
return PrimitiveType::kTriangleStrip;
}
/// |VertexGenerator|
size_t GetVertexCount() const override {
return trigs_.size() * vertices_per_trig_;
}
/// |VertexGenerator|
void GenerateVertices(const TessellatedVertexProc& proc) const override {
impl_(trigs_, data_, proc);
}
private:
friend class Tessellator;
struct Data {
// Circles and Ellipses only use one of these points.
// RoundCapLines use both as the endpoints of the unexpanded line.
// A round rect can specify its interior rectangle by using the
// 2 points as opposing corners.
const Point reference_centers[2];
// Circular shapes have the same value in radii.width and radii.height
const Size radii;
// half_width is only used in cases where the generator will be
// generating 2 different outlines, such as StrokedCircle
const Scalar half_width;
};
typedef void GeneratorProc(const Trigs& trigs,
const Data& data,
const TessellatedVertexProc& proc);
GeneratorProc& impl_;
const Trigs trigs_;
const Data data_;
const size_t vertices_per_trig_;
EllipticalVertexGenerator(GeneratorProc& generator,
Trigs&& trigs,
PrimitiveType triangle_type,
size_t vertices_per_trig,
Data&& data);
};
Tessellator();
~Tessellator();
/// @brief A callback that returns the results of the tessellation.
///
/// The index buffer may not be populated, in which case [indices] will
/// be nullptr and indices_count will be 0.
using BuilderCallback = std::function<bool(const float* vertices,
size_t vertices_count,
const uint16_t* indices,
size_t indices_count)>;
//----------------------------------------------------------------------------
/// @brief Generates filled triangles from the path. A callback is
/// invoked once for the entire tessellation.
///
/// @param[in] path The path to tessellate.
/// @param[in] tolerance The tolerance value for conversion of the path to
/// a polyline. This value is often derived from the
/// Matrix::GetMaxBasisLength of the CTM applied to the
/// path for rendering.
/// @param[in] callback The callback, return false to indicate failure.
///
/// @return The result status of the tessellation.
///
Tessellator::Result Tessellate(const Path& path,
Scalar tolerance,
const BuilderCallback& callback);
//----------------------------------------------------------------------------
/// @brief Given a convex path, create a triangle fan structure.
///
/// @param[in] path The path to tessellate.
/// @param[in] tolerance The tolerance value for conversion of the path to
/// a polyline. This value is often derived from the
/// Matrix::GetMaxBasisLength of the CTM applied to the
/// path for rendering.
///
/// @return A point vector containing the vertices in triangle strip format.
///
std::vector<Point> TessellateConvex(const Path& path, Scalar tolerance);
/// @brief The pixel tolerance used by the algorighm to determine how
/// many divisions to create for a circle.
///
/// No point on the polygon of vertices should deviate from the
/// true circle by more than this tolerance.
static constexpr Scalar kCircleTolerance = 0.1f;
/// @brief Create a |VertexGenerator| that can produce vertices for
/// a filled circle of the given radius around the given center
/// with enough polygon sub-divisions to provide reasonable
/// fidelity when viewed under the given view transform.
///
/// Note that the view transform is only used to choose the
/// number of sample points to use per quarter circle and the
/// returned points are not transformed by it, instead they are
/// relative to the coordinate space of the center point.
EllipticalVertexGenerator FilledCircle(const Matrix& view_transform,
const Point& center,
Scalar radius);
/// @brief Create a |VertexGenerator| that can produce vertices for
/// a stroked circle of the given radius and half_width around
/// the given shared center with enough polygon sub-divisions
/// to provide reasonable fidelity when viewed under the given
/// view transform. The outer edge of the stroked circle is
/// generated at (radius + half_width) and the inner edge is
/// generated at (radius - half_width).
///
/// Note that the view transform is only used to choose the
/// number of sample points to use per quarter circle and the
/// returned points are not transformed by it, instead they are
/// relative to the coordinate space of the center point.
EllipticalVertexGenerator StrokedCircle(const Matrix& view_transform,
const Point& center,
Scalar radius,
Scalar half_width);
/// @brief Create a |VertexGenerator| that can produce vertices for
/// a line with round end caps of the given radius with enough
/// polygon sub-divisions to provide reasonable fidelity when
/// viewed under the given view transform.
///
/// Note that the view transform is only used to choose the
/// number of sample points to use per quarter circle and the
/// returned points are not transformed by it, instead they are
/// relative to the coordinate space of the two points.
EllipticalVertexGenerator RoundCapLine(const Matrix& view_transform,
const Point& p0,
const Point& p1,
Scalar radius);
/// @brief Create a |VertexGenerator| that can produce vertices for
/// a filled ellipse inscribed within the given bounds with
/// enough polygon sub-divisions to provide reasonable
/// fidelity when viewed under the given view transform.
///
/// Note that the view transform is only used to choose the
/// number of sample points to use per quarter circle and the
/// returned points are not transformed by it, instead they are
/// relative to the coordinate space of the bounds.
EllipticalVertexGenerator FilledEllipse(const Matrix& view_transform,
const Rect& bounds);
private:
/// Used for polyline generation.
std::unique_ptr<std::vector<Point>> point_buffer_;
CTessellator c_tessellator_;
// Data for variouos Circle/EllipseGenerator classes, cached per
// Tessellator instance which is usually the foreground life of an app
// if not longer.
static constexpr size_t kCachedTrigCount = 300;
std::vector<Trig> precomputed_trigs_[kCachedTrigCount];
Trigs GetTrigsForDivisions(size_t divisions);
static void GenerateFilledCircle(const Trigs& trigs,
const EllipticalVertexGenerator::Data& data,
const TessellatedVertexProc& proc);
static void GenerateStrokedCircle(const Trigs& trigs,
const EllipticalVertexGenerator::Data& data,
const TessellatedVertexProc& proc);
static void GenerateRoundCapLine(const Trigs& trigs,
const EllipticalVertexGenerator::Data& data,
const TessellatedVertexProc& proc);
static void GenerateFilledEllipse(const Trigs& trigs,
const EllipticalVertexGenerator::Data& data,
const TessellatedVertexProc& proc);
Tessellator(const Tessellator&) = delete;
Tessellator& operator=(const Tessellator&) = delete;
};
} // namespace impeller