This reverts commit db0bdfd117077a69f79ce0d4c03ab387a78ef7dd to fix a tree breakage.
This commit is contained in:
parent
39b4951f8f
commit
485c9105fc
@ -139,10 +139,6 @@ inline const SkPoint* ToSkPoints(const DlPoint* points) {
|
||||
return points == nullptr ? nullptr : reinterpret_cast<const SkPoint*>(points);
|
||||
}
|
||||
|
||||
inline SkPoint* ToSkPoints(DlPoint* points) {
|
||||
return points == nullptr ? nullptr : reinterpret_cast<SkPoint*>(points);
|
||||
}
|
||||
|
||||
inline const SkRect& ToSkRect(const DlRect& rect) {
|
||||
return *reinterpret_cast<const SkRect*>(&rect);
|
||||
}
|
||||
|
@ -265,46 +265,39 @@ SkPath DlPath::ConvertToSkiaPath(const Path& path, const DlPoint& shift) {
|
||||
}
|
||||
};
|
||||
|
||||
for (auto it = path.begin(), end = path.end(); it != end; ++it) {
|
||||
switch (it.type()) {
|
||||
size_t count = path.GetComponentCount();
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
switch (path.GetComponentTypeAtIndex(i)) {
|
||||
case ComponentType::kContour: {
|
||||
const impeller::ContourComponent* contour = it.contour();
|
||||
FML_DCHECK(contour != nullptr);
|
||||
impeller::ContourComponent contour;
|
||||
path.GetContourComponentAtIndex(i, contour);
|
||||
if (subpath_needs_close) {
|
||||
sk_path.close();
|
||||
}
|
||||
pending_moveto = contour->destination;
|
||||
subpath_needs_close = contour->IsClosed();
|
||||
pending_moveto = contour.destination;
|
||||
subpath_needs_close = contour.IsClosed();
|
||||
break;
|
||||
}
|
||||
case ComponentType::kLinear: {
|
||||
const impeller::LinearPathComponent* linear = it.linear();
|
||||
FML_DCHECK(linear != nullptr);
|
||||
impeller::LinearPathComponent linear;
|
||||
path.GetLinearComponentAtIndex(i, linear);
|
||||
resolve_moveto();
|
||||
sk_path.lineTo(ToSkPoint(linear->p2));
|
||||
sk_path.lineTo(ToSkPoint(linear.p2));
|
||||
break;
|
||||
}
|
||||
case ComponentType::kQuadratic: {
|
||||
const impeller::QuadraticPathComponent* quadratic = it.quadratic();
|
||||
FML_DCHECK(quadratic != nullptr);
|
||||
impeller::QuadraticPathComponent quadratic;
|
||||
path.GetQuadraticComponentAtIndex(i, quadratic);
|
||||
resolve_moveto();
|
||||
sk_path.quadTo(ToSkPoint(quadratic->cp), ToSkPoint(quadratic->p2));
|
||||
break;
|
||||
}
|
||||
case ComponentType::kConic: {
|
||||
const impeller::ConicPathComponent* conic = it.conic();
|
||||
FML_DCHECK(conic != nullptr);
|
||||
resolve_moveto();
|
||||
sk_path.conicTo(ToSkPoint(conic->cp), ToSkPoint(conic->p2),
|
||||
conic->weight.x);
|
||||
sk_path.quadTo(ToSkPoint(quadratic.cp), ToSkPoint(quadratic.p2));
|
||||
break;
|
||||
}
|
||||
case ComponentType::kCubic: {
|
||||
const impeller::CubicPathComponent* cubic = it.cubic();
|
||||
FML_DCHECK(cubic != nullptr);
|
||||
impeller::CubicPathComponent cubic;
|
||||
path.GetCubicComponentAtIndex(i, cubic);
|
||||
resolve_moveto();
|
||||
sk_path.cubicTo(ToSkPoint(cubic->cp1), ToSkPoint(cubic->cp2),
|
||||
ToSkPoint(cubic->p2));
|
||||
sk_path.cubicTo(ToSkPoint(cubic.cp1), ToSkPoint(cubic.cp2),
|
||||
ToSkPoint(cubic.p2));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -346,26 +339,27 @@ Path DlPath::ConvertToImpellerPath(const SkPath& path, const DlPoint& shift) {
|
||||
builder.QuadraticCurveTo(ToDlPoint(data.points[1]),
|
||||
ToDlPoint(data.points[2]));
|
||||
break;
|
||||
case SkPath::kConic_Verb:
|
||||
// We might eventually have conic conversion math that deals with
|
||||
// degenerate conics gracefully (or just handle them directly),
|
||||
// but until then, we will detect and ignore them.
|
||||
if (data.points[0] != data.points[1]) {
|
||||
if (data.points[1] != data.points[2]) {
|
||||
std::array<DlPoint, 5> points;
|
||||
impeller::ConicPathComponent conic(
|
||||
ToDlPoint(data.points[0]), ToDlPoint(data.points[1]),
|
||||
ToDlPoint(data.points[2]), iterator.conicWeight());
|
||||
conic.SubdivideToQuadraticPoints(points);
|
||||
builder.QuadraticCurveTo(points[1], points[2]);
|
||||
builder.QuadraticCurveTo(points[3], points[4]);
|
||||
} else {
|
||||
builder.LineTo(ToDlPoint(data.points[1]));
|
||||
}
|
||||
} else if (data.points[1] != data.points[2]) {
|
||||
builder.LineTo(ToDlPoint(data.points[2]));
|
||||
case SkPath::kConic_Verb: {
|
||||
constexpr auto kPow2 = 1; // Only works for sweeps up to 90 degrees.
|
||||
constexpr auto kQuadCount = 1 + (2 * (1 << kPow2));
|
||||
SkPoint points[kQuadCount];
|
||||
const auto curve_count =
|
||||
SkPath::ConvertConicToQuads(data.points[0], //
|
||||
data.points[1], //
|
||||
data.points[2], //
|
||||
iterator.conicWeight(), //
|
||||
points, //
|
||||
kPow2 //
|
||||
);
|
||||
|
||||
for (int curve_index = 0, point_index = 0; //
|
||||
curve_index < curve_count; //
|
||||
curve_index++, point_index += 2 //
|
||||
) {
|
||||
builder.QuadraticCurveTo(ToDlPoint(points[point_index + 1]),
|
||||
ToDlPoint(points[point_index + 2]));
|
||||
}
|
||||
break;
|
||||
} break;
|
||||
case SkPath::kCubic_Verb:
|
||||
builder.CubicCurveTo(ToDlPoint(data.points[1]),
|
||||
ToDlPoint(data.points[2]),
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "flutter/display_list/effects/dl_color_sources.h"
|
||||
#include "flutter/display_list/effects/dl_image_filters.h"
|
||||
#include "flutter/display_list/skia/dl_sk_conversions.h"
|
||||
#include "flutter/impeller/geometry/path_component.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "third_party/skia/include/core/SkColorSpace.h"
|
||||
#include "third_party/skia/include/core/SkSamplingOptions.h"
|
||||
@ -373,92 +372,5 @@ TEST(DisplayListSkConversions, ToSkRSTransform) {
|
||||
}
|
||||
}
|
||||
|
||||
// This tests the new conic subdivision code in the Impeller conic path
|
||||
// component object vs the code we used to rely on inside Skia
|
||||
TEST(DisplayListSkConversions, ConicToQuads) {
|
||||
SkScalar weights[4] = {
|
||||
0.02f,
|
||||
0.5f,
|
||||
SK_ScalarSqrt2 * 0.5f,
|
||||
1.0f,
|
||||
};
|
||||
|
||||
for (SkScalar weight : weights) {
|
||||
SkPoint sk_points[5];
|
||||
int ncurves = SkPath::ConvertConicToQuads(
|
||||
SkPoint::Make(10, 10), SkPoint::Make(20, 10), SkPoint::Make(20, 20),
|
||||
weight, sk_points, 1);
|
||||
ASSERT_EQ(ncurves, 2) << "weight: " << weight;
|
||||
|
||||
std::array<DlPoint, 5> i_points;
|
||||
impeller::ConicPathComponent i_conic(DlPoint(10, 10), DlPoint(20, 10),
|
||||
DlPoint(20, 20), weight);
|
||||
i_conic.SubdivideToQuadraticPoints(i_points);
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
EXPECT_FLOAT_EQ(sk_points[i].fX, i_points[i].x)
|
||||
<< "weight: " << weight << "point[" << i << "].x";
|
||||
EXPECT_FLOAT_EQ(sk_points[i].fY, i_points[i].y)
|
||||
<< "weight: " << weight << "point[" << i << "].y";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This tests the new conic subdivision code in the Impeller conic path
|
||||
// component object vs the code we used to rely on inside Skia
|
||||
TEST(DisplayListSkConversions, ConicPathToQuads) {
|
||||
// If we execute conicTo with a weight of exactly 1.0, SkPath will turn
|
||||
// it into a quadTo, so we avoid that by using 0.999
|
||||
SkScalar weights[4] = {
|
||||
0.02f,
|
||||
0.5f,
|
||||
SK_ScalarSqrt2 * 0.5f,
|
||||
1.0f - kEhCloseEnough,
|
||||
};
|
||||
|
||||
for (SkScalar weight : weights) {
|
||||
SkPath sk_path;
|
||||
sk_path.moveTo(10, 10);
|
||||
sk_path.conicTo(20, 10, 20, 20, weight);
|
||||
|
||||
DlPath dl_path(sk_path);
|
||||
impeller::Path i_path = dl_path.GetPath();
|
||||
|
||||
auto it = i_path.begin();
|
||||
ASSERT_EQ(it.type(), impeller::Path::ComponentType::kContour);
|
||||
++it;
|
||||
|
||||
ASSERT_EQ(it.type(), impeller::Path::ComponentType::kQuadratic);
|
||||
auto quad1 = it.quadratic();
|
||||
ASSERT_NE(quad1, nullptr);
|
||||
++it;
|
||||
|
||||
ASSERT_EQ(it.type(), impeller::Path::ComponentType::kQuadratic);
|
||||
auto quad2 = it.quadratic();
|
||||
ASSERT_NE(quad2, nullptr);
|
||||
++it;
|
||||
|
||||
SkPoint sk_points[5];
|
||||
int ncurves = SkPath::ConvertConicToQuads(
|
||||
SkPoint::Make(10, 10), SkPoint::Make(20, 10), SkPoint::Make(20, 20),
|
||||
weight, sk_points, 1);
|
||||
ASSERT_EQ(ncurves, 2);
|
||||
|
||||
EXPECT_FLOAT_EQ(sk_points[0].fX, quad1->p1.x) << "weight: " << weight;
|
||||
EXPECT_FLOAT_EQ(sk_points[0].fY, quad1->p1.y) << "weight: " << weight;
|
||||
EXPECT_FLOAT_EQ(sk_points[1].fX, quad1->cp.x) << "weight: " << weight;
|
||||
EXPECT_FLOAT_EQ(sk_points[1].fY, quad1->cp.y) << "weight: " << weight;
|
||||
EXPECT_FLOAT_EQ(sk_points[2].fX, quad1->p2.x) << "weight: " << weight;
|
||||
EXPECT_FLOAT_EQ(sk_points[2].fY, quad1->p2.y) << "weight: " << weight;
|
||||
|
||||
EXPECT_FLOAT_EQ(sk_points[2].fX, quad2->p1.x) << "weight: " << weight;
|
||||
EXPECT_FLOAT_EQ(sk_points[2].fY, quad2->p1.y) << "weight: " << weight;
|
||||
EXPECT_FLOAT_EQ(sk_points[3].fX, quad2->cp.x) << "weight: " << weight;
|
||||
EXPECT_FLOAT_EQ(sk_points[3].fY, quad2->cp.y) << "weight: " << weight;
|
||||
EXPECT_FLOAT_EQ(sk_points[4].fX, quad2->p2.x) << "weight: " << weight;
|
||||
EXPECT_FLOAT_EQ(sk_points[4].fY, quad2->p2.y) << "weight: " << weight;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
@ -46,9 +46,8 @@ constexpr float k2OverSqrtPi = 1.12837916709551257390f;
|
||||
// sqrt(2)
|
||||
constexpr float kSqrt2 = 1.41421356237309504880f;
|
||||
|
||||
// sqrt(2) / 2 == 1/sqrt(2)
|
||||
// 1/sqrt(2)
|
||||
constexpr float k1OverSqrt2 = 0.70710678118654752440f;
|
||||
constexpr float kSqrt2Over2 = 0.70710678118654752440f;
|
||||
|
||||
// phi
|
||||
constexpr float kPhi = 1.61803398874989484820f;
|
||||
|
@ -19,64 +19,6 @@ Path::Path(Data data) : data_(std::make_shared<Data>(std::move(data))) {}
|
||||
|
||||
Path::~Path() = default;
|
||||
|
||||
Path::ComponentType Path::ComponentIterator::type() const {
|
||||
return path_.data_->components[component_index_];
|
||||
}
|
||||
|
||||
#define CHECK_COMPONENT(type) \
|
||||
(component_index_ < path_.data_->components.size() && \
|
||||
path_.data_->components[component_index_] == type && \
|
||||
storage_offset_ + VerbToOffset(type) <= path_.data_->points.size())
|
||||
|
||||
const LinearPathComponent* Path::ComponentIterator::linear() const {
|
||||
if (!CHECK_COMPONENT(Path::ComponentType::kLinear)) {
|
||||
return nullptr;
|
||||
}
|
||||
const Point* points = &(path_.data_->points[storage_offset_]);
|
||||
return reinterpret_cast<const LinearPathComponent*>(points);
|
||||
}
|
||||
|
||||
const QuadraticPathComponent* Path::ComponentIterator::quadratic() const {
|
||||
if (!CHECK_COMPONENT(Path::ComponentType::kQuadratic)) {
|
||||
return nullptr;
|
||||
}
|
||||
const Point* points = &(path_.data_->points[storage_offset_]);
|
||||
return reinterpret_cast<const QuadraticPathComponent*>(points);
|
||||
}
|
||||
|
||||
const ConicPathComponent* Path::ComponentIterator::conic() const {
|
||||
if (!CHECK_COMPONENT(Path::ComponentType::kConic)) {
|
||||
return nullptr;
|
||||
}
|
||||
const Point* points = &(path_.data_->points[storage_offset_]);
|
||||
return reinterpret_cast<const ConicPathComponent*>(points);
|
||||
}
|
||||
|
||||
const CubicPathComponent* Path::ComponentIterator::cubic() const {
|
||||
if (!CHECK_COMPONENT(Path::ComponentType::kCubic)) {
|
||||
return nullptr;
|
||||
}
|
||||
const Point* points = &(path_.data_->points[storage_offset_]);
|
||||
return reinterpret_cast<const CubicPathComponent*>(points);
|
||||
}
|
||||
|
||||
const ContourComponent* Path::ComponentIterator::contour() const {
|
||||
if (!CHECK_COMPONENT(Path::ComponentType::kContour)) {
|
||||
return nullptr;
|
||||
}
|
||||
const Point* points = &(path_.data_->points[storage_offset_]);
|
||||
return reinterpret_cast<const ContourComponent*>(points);
|
||||
}
|
||||
|
||||
Path::ComponentIterator& Path::ComponentIterator::operator++() {
|
||||
auto components = path_.data_->components;
|
||||
if (component_index_ < components.size()) {
|
||||
storage_offset_ += VerbToOffset(path_.data_->components[component_index_]);
|
||||
component_index_++;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::tuple<size_t, size_t> Path::Polyline::GetContourPointBounds(
|
||||
size_t contour_index) const {
|
||||
if (contour_index >= contours.size()) {
|
||||
@ -145,13 +87,6 @@ std::pair<size_t, size_t> Path::CountStorage(Scalar scale) const {
|
||||
points += quad->CountLinearPathComponents(scale);
|
||||
break;
|
||||
}
|
||||
case ComponentType::kConic: {
|
||||
const ConicPathComponent* conic =
|
||||
reinterpret_cast<const ConicPathComponent*>(
|
||||
&path_points[storage_offset]);
|
||||
points += conic->CountLinearPathComponents(scale);
|
||||
break;
|
||||
}
|
||||
case ComponentType::kCubic: {
|
||||
const CubicPathComponent* cubic =
|
||||
reinterpret_cast<const CubicPathComponent*>(
|
||||
@ -200,17 +135,6 @@ void Path::WritePolyline(Scalar scale, VertexWriter& writer) const {
|
||||
quad->ToLinearPathComponents(scale, writer);
|
||||
break;
|
||||
}
|
||||
case ComponentType::kConic: {
|
||||
const ConicPathComponent* conic =
|
||||
reinterpret_cast<const ConicPathComponent*>(
|
||||
&path_points[storage_offset]);
|
||||
if (first_point) {
|
||||
writer.Write(conic->p1);
|
||||
first_point = false;
|
||||
}
|
||||
conic->ToLinearPathComponents(scale, writer);
|
||||
break;
|
||||
}
|
||||
case ComponentType::kCubic: {
|
||||
const CubicPathComponent* cubic =
|
||||
reinterpret_cast<const CubicPathComponent*>(
|
||||
@ -246,13 +170,86 @@ void Path::WritePolyline(Scalar scale, VertexWriter& writer) const {
|
||||
}
|
||||
}
|
||||
|
||||
Path::ComponentIterator Path::begin() const {
|
||||
return ComponentIterator(*this, 0u, 0u);
|
||||
Path::ComponentType Path::GetComponentTypeAtIndex(size_t index) const {
|
||||
auto& components = data_->components;
|
||||
return components[index];
|
||||
}
|
||||
|
||||
Path::ComponentIterator Path::end() const {
|
||||
return ComponentIterator(*this, data_->components.size(),
|
||||
data_->points.size());
|
||||
bool Path::GetLinearComponentAtIndex(size_t index,
|
||||
LinearPathComponent& linear) const {
|
||||
auto& components = data_->components;
|
||||
if (index >= components.size() ||
|
||||
components[index] != ComponentType::kLinear) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t storage_offset = 0u;
|
||||
for (auto i = 0u; i < index; i++) {
|
||||
storage_offset += VerbToOffset(components[i]);
|
||||
}
|
||||
auto& points = data_->points;
|
||||
linear =
|
||||
LinearPathComponent(points[storage_offset], points[storage_offset + 1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Path::GetQuadraticComponentAtIndex(
|
||||
size_t index,
|
||||
QuadraticPathComponent& quadratic) const {
|
||||
auto& components = data_->components;
|
||||
if (index >= components.size() ||
|
||||
components[index] != ComponentType::kQuadratic) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t storage_offset = 0u;
|
||||
for (auto i = 0u; i < index; i++) {
|
||||
storage_offset += VerbToOffset(components[i]);
|
||||
}
|
||||
auto& points = data_->points;
|
||||
|
||||
quadratic =
|
||||
QuadraticPathComponent(points[storage_offset], points[storage_offset + 1],
|
||||
points[storage_offset + 2]);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Path::GetCubicComponentAtIndex(size_t index,
|
||||
CubicPathComponent& cubic) const {
|
||||
auto& components = data_->components;
|
||||
if (index >= components.size() ||
|
||||
components[index] != ComponentType::kCubic) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t storage_offset = 0u;
|
||||
for (auto i = 0u; i < index; i++) {
|
||||
storage_offset += VerbToOffset(components[i]);
|
||||
}
|
||||
auto& points = data_->points;
|
||||
|
||||
cubic = CubicPathComponent(points[storage_offset], points[storage_offset + 1],
|
||||
points[storage_offset + 2],
|
||||
points[storage_offset + 3]);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Path::GetContourComponentAtIndex(size_t index,
|
||||
ContourComponent& move) const {
|
||||
auto& components = data_->components;
|
||||
if (index >= components.size() ||
|
||||
components[index] != ComponentType::kContour) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t storage_offset = 0u;
|
||||
for (auto i = 0u; i < index; i++) {
|
||||
storage_offset += VerbToOffset(components[i]);
|
||||
}
|
||||
auto& points = data_->points;
|
||||
|
||||
move = ContourComponent(points[storage_offset], points[storage_offset + 1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
Path::Polyline::Polyline(Path::Polyline::PointBufferPtr point_buffer,
|
||||
@ -318,16 +315,6 @@ void Path::EndContour(
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ComponentType::kConic: {
|
||||
auto* conic = reinterpret_cast<const ConicPathComponent*>(
|
||||
&path_points[storage_offset]);
|
||||
auto maybe_end = conic->GetEndDirection();
|
||||
if (maybe_end.has_value()) {
|
||||
contour.end_direction = maybe_end.value();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ComponentType::kCubic: {
|
||||
auto* cubic = reinterpret_cast<const CubicPathComponent*>(
|
||||
&path_points[storage_offset]);
|
||||
@ -390,19 +377,6 @@ Path::Polyline Path::CreatePolyline(
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ComponentType::kConic: {
|
||||
poly_components.push_back({
|
||||
.component_start_index = polyline.points->size() - 1,
|
||||
.is_curve = true,
|
||||
});
|
||||
auto* conic = reinterpret_cast<const ConicPathComponent*>(
|
||||
&path_points[storage_offset]);
|
||||
conic->AppendPolylinePoints(scale, *polyline.points);
|
||||
if (!start_direction.has_value()) {
|
||||
start_direction = conic->GetStartDirection();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ComponentType::kCubic: {
|
||||
poly_components.push_back({
|
||||
.component_start_index = polyline.points->size() - 1,
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
@ -56,50 +55,16 @@ class Path {
|
||||
enum class ComponentType {
|
||||
kLinear,
|
||||
kQuadratic,
|
||||
kConic,
|
||||
kCubic,
|
||||
kContour,
|
||||
};
|
||||
|
||||
class ComponentIterator {
|
||||
public:
|
||||
ComponentType type() const;
|
||||
|
||||
// Return pointer to path component or null if the type is wrong or
|
||||
// the iterator is past the end of the path.
|
||||
const LinearPathComponent* linear() const;
|
||||
const QuadraticPathComponent* quadratic() const;
|
||||
const ConicPathComponent* conic() const;
|
||||
const CubicPathComponent* cubic() const;
|
||||
const ContourComponent* contour() const;
|
||||
|
||||
ComponentIterator& operator++();
|
||||
bool operator==(const ComponentIterator& other) const {
|
||||
return component_index_ == other.component_index_;
|
||||
}
|
||||
bool operator!=(const ComponentIterator& other) const {
|
||||
return component_index_ != other.component_index_;
|
||||
}
|
||||
|
||||
private:
|
||||
ComponentIterator(const Path& path, size_t index, size_t offset)
|
||||
: path_(path), component_index_(index), storage_offset_(offset) {}
|
||||
|
||||
const Path& path_;
|
||||
size_t component_index_ = 0u;
|
||||
size_t storage_offset_ = 0u;
|
||||
|
||||
friend class Path;
|
||||
};
|
||||
|
||||
static constexpr size_t VerbToOffset(Path::ComponentType verb) {
|
||||
switch (verb) {
|
||||
case Path::ComponentType::kLinear:
|
||||
return 2u;
|
||||
case Path::ComponentType::kQuadratic:
|
||||
return 3u;
|
||||
case Path::ComponentType::kConic:
|
||||
return 4u;
|
||||
case Path::ComponentType::kCubic:
|
||||
return 4u;
|
||||
case Path::ComponentType::kContour:
|
||||
@ -192,8 +157,18 @@ class Path {
|
||||
/// @brief Whether the line contains a single contour.
|
||||
bool IsSingleContour() const;
|
||||
|
||||
ComponentIterator begin() const;
|
||||
ComponentIterator end() const;
|
||||
ComponentType GetComponentTypeAtIndex(size_t index) const;
|
||||
|
||||
bool GetLinearComponentAtIndex(size_t index,
|
||||
LinearPathComponent& linear) const;
|
||||
|
||||
bool GetQuadraticComponentAtIndex(size_t index,
|
||||
QuadraticPathComponent& quadratic) const;
|
||||
|
||||
bool GetCubicComponentAtIndex(size_t index, CubicPathComponent& cubic) const;
|
||||
|
||||
bool GetContourComponentAtIndex(size_t index,
|
||||
ContourComponent& contour) const;
|
||||
|
||||
/// Callers must provide the scale factor for how this path will be
|
||||
/// transformed.
|
||||
|
@ -252,17 +252,6 @@ PathBuilder& PathBuilder::QuadraticCurveTo(Point controlPoint,
|
||||
return *this;
|
||||
}
|
||||
|
||||
PathBuilder& PathBuilder::ConicCurveTo(Point controlPoint,
|
||||
Point point,
|
||||
Scalar weight,
|
||||
bool relative) {
|
||||
point = relative ? current_ + point : point;
|
||||
controlPoint = relative ? current_ + controlPoint : controlPoint;
|
||||
AddConicComponent(current_, controlPoint, point, weight);
|
||||
current_ = point;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PathBuilder& PathBuilder::SetConvexity(Convexity value) {
|
||||
prototype_.convexity = value;
|
||||
return *this;
|
||||
@ -280,33 +269,22 @@ PathBuilder& PathBuilder::CubicCurveTo(Point controlPoint1,
|
||||
return *this;
|
||||
}
|
||||
|
||||
PathBuilder& PathBuilder::AddQuadraticCurve(const Point& p1,
|
||||
const Point& cp,
|
||||
const Point& p2) {
|
||||
PathBuilder& PathBuilder::AddQuadraticCurve(Point p1, Point cp, Point p2) {
|
||||
MoveTo(p1);
|
||||
AddQuadraticComponent(p1, cp, p2);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PathBuilder& PathBuilder::AddConicCurve(const Point& p1,
|
||||
const Point& cp,
|
||||
const Point& p2,
|
||||
Scalar weight) {
|
||||
MoveTo(p1);
|
||||
AddConicComponent(p1, cp, p2, weight);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PathBuilder& PathBuilder::AddCubicCurve(const Point& p1,
|
||||
const Point& cp1,
|
||||
const Point& cp2,
|
||||
const Point& p2) {
|
||||
PathBuilder& PathBuilder::AddCubicCurve(Point p1,
|
||||
Point cp1,
|
||||
Point cp2,
|
||||
Point p2) {
|
||||
MoveTo(p1);
|
||||
AddCubicComponent(p1, cp1, cp2, p2);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PathBuilder& PathBuilder::AddRect(const Rect& rect) {
|
||||
PathBuilder& PathBuilder::AddRect(Rect rect) {
|
||||
auto origin = rect.GetOrigin();
|
||||
auto size = rect.GetSize();
|
||||
|
||||
@ -537,28 +515,6 @@ void PathBuilder::AddQuadraticComponent(const Point& p1,
|
||||
prototype_.bounds.reset();
|
||||
}
|
||||
|
||||
void PathBuilder::AddConicComponent(const Point& p1,
|
||||
const Point& cp,
|
||||
const Point& p2,
|
||||
Scalar weight) {
|
||||
if (!std::isfinite(weight)) {
|
||||
AddLinearComponent(p1, cp);
|
||||
AddLinearComponent(cp, p2);
|
||||
} else if (weight <= 0) {
|
||||
AddLinearComponent(p1, p2);
|
||||
} else if (weight == 1) {
|
||||
AddQuadraticComponent(p1, cp, p2);
|
||||
} else {
|
||||
auto& points = prototype_.points;
|
||||
points.push_back(p1);
|
||||
points.push_back(cp);
|
||||
points.push_back(p2);
|
||||
points.emplace_back(weight, weight);
|
||||
prototype_.components.push_back(Path::ComponentType::kConic);
|
||||
prototype_.bounds.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void PathBuilder::AddCubicComponent(const Point& p1,
|
||||
const Point& cp1,
|
||||
const Point& cp2,
|
||||
@ -728,13 +684,6 @@ PathBuilder& PathBuilder::Shift(Point offset) {
|
||||
quad->p2 += offset;
|
||||
quad->cp += offset;
|
||||
} break;
|
||||
case Path::ComponentType::kConic: {
|
||||
auto* conic =
|
||||
reinterpret_cast<ConicPathComponent*>(&points[storage_offset]);
|
||||
conic->p1 += offset;
|
||||
conic->p2 += offset;
|
||||
conic->cp += offset;
|
||||
} break;
|
||||
case Path::ComponentType::kCubic: {
|
||||
auto* cubic =
|
||||
reinterpret_cast<CubicPathComponent*>(&points[storage_offset]);
|
||||
@ -818,13 +767,6 @@ std::optional<std::pair<Point, Point>> PathBuilder::GetMinMaxCoveragePoints()
|
||||
clamp(extrema);
|
||||
}
|
||||
break;
|
||||
case Path::ComponentType::kConic:
|
||||
for (const auto& extrema : reinterpret_cast<const ConicPathComponent*>(
|
||||
&points[storage_offset])
|
||||
->Extrema()) {
|
||||
clamp(extrema);
|
||||
}
|
||||
break;
|
||||
case Path::ComponentType::kCubic:
|
||||
for (const auto& extrema : reinterpret_cast<const CubicPathComponent*>(
|
||||
&points[storage_offset])
|
||||
|
@ -60,16 +60,6 @@ class PathBuilder {
|
||||
Point point,
|
||||
bool relative = false);
|
||||
|
||||
/// @brief Insert a conic curve from the current position to `point` using
|
||||
/// the control point `controlPoint` and the weight `weight`.
|
||||
///
|
||||
/// If `relative` is true the `point` and `controlPoint` are relative to
|
||||
/// current location.
|
||||
PathBuilder& ConicCurveTo(Point controlPoint,
|
||||
Point point,
|
||||
Scalar weight,
|
||||
bool relative = false);
|
||||
|
||||
/// @brief Insert a cubic curve from the curren position to `point` using the
|
||||
/// control points `controlPoint1` and `controlPoint2`.
|
||||
///
|
||||
@ -80,7 +70,7 @@ class PathBuilder {
|
||||
Point point,
|
||||
bool relative = false);
|
||||
|
||||
PathBuilder& AddRect(const Rect& rect);
|
||||
PathBuilder& AddRect(Rect rect);
|
||||
|
||||
PathBuilder& AddCircle(const Point& center, Scalar radius);
|
||||
|
||||
@ -96,23 +86,11 @@ class PathBuilder {
|
||||
|
||||
/// @brief Move to point `p1`, then insert a quadradic curve from `p1` to `p2`
|
||||
/// with the control point `cp`.
|
||||
PathBuilder& AddQuadraticCurve(const Point& p1,
|
||||
const Point& cp,
|
||||
const Point& p2);
|
||||
|
||||
/// @brief Move to point `p1`, then insert a conic curve from `p1` to `p2`
|
||||
/// with the control point `cp` and weight `weight`.
|
||||
PathBuilder& AddConicCurve(const Point& p1,
|
||||
const Point& cp,
|
||||
const Point& p2,
|
||||
Scalar weight);
|
||||
PathBuilder& AddQuadraticCurve(Point p1, Point cp, Point p2);
|
||||
|
||||
/// @brief Move to point `p1`, then insert a cubic curve from `p1` to `p2`
|
||||
/// with control points `cp1` and `cp2`.
|
||||
PathBuilder& AddCubicCurve(const Point& p1,
|
||||
const Point& cp1,
|
||||
const Point& cp2,
|
||||
const Point& p2);
|
||||
PathBuilder& AddCubicCurve(Point p1, Point cp1, Point cp2, Point p2);
|
||||
|
||||
/// @brief Transform the existing path segments and contours by the given
|
||||
/// `offset`.
|
||||
@ -157,11 +135,6 @@ class PathBuilder {
|
||||
|
||||
void AddQuadraticComponent(const Point& p1, const Point& cp, const Point& p2);
|
||||
|
||||
void AddConicComponent(const Point& p1,
|
||||
const Point& cp,
|
||||
const Point& p2,
|
||||
Scalar weight);
|
||||
|
||||
void AddCubicComponent(const Point& p1,
|
||||
const Point& cp1,
|
||||
const Point& cp2,
|
||||
|
@ -7,9 +7,8 @@
|
||||
#include <cmath>
|
||||
#include <utility>
|
||||
|
||||
#include "flutter/fml/logging.h"
|
||||
#include "flutter/impeller/geometry/scalar.h"
|
||||
#include "flutter/impeller/geometry/wangs_formula.h"
|
||||
#include "impeller/geometry/scalar.h"
|
||||
#include "impeller/geometry/wangs_formula.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
@ -184,20 +183,6 @@ static inline Scalar QuadraticSolveDerivative(Scalar t,
|
||||
2 * t * (p2 - p1);
|
||||
}
|
||||
|
||||
static inline Scalar ConicSolve(Scalar t,
|
||||
Scalar p0,
|
||||
Scalar p1,
|
||||
Scalar p2,
|
||||
Scalar w) {
|
||||
auto u = (1 - t);
|
||||
auto coefficient_p0 = t * t;
|
||||
auto coefficient_p1 = 2 * t * u * w;
|
||||
auto coefficient_p2 = u * u;
|
||||
|
||||
return ((p0 * coefficient_p0 + p1 * coefficient_p1 + p2 * coefficient_p2) /
|
||||
(coefficient_p0 + coefficient_p1 + coefficient_p2));
|
||||
}
|
||||
|
||||
static inline Scalar CubicSolve(Scalar t,
|
||||
Scalar p0,
|
||||
Scalar p1,
|
||||
@ -324,115 +309,6 @@ std::optional<Vector2> QuadraticPathComponent::GetEndDirection() const {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
Point ConicPathComponent::Solve(Scalar time) const {
|
||||
return {
|
||||
ConicSolve(time, p1.x, cp.x, p2.x, weight.x), // x
|
||||
ConicSolve(time, p1.y, cp.y, p2.y, weight.y), // y
|
||||
};
|
||||
}
|
||||
|
||||
void ConicPathComponent::AppendPolylinePoints(
|
||||
Scalar scale_factor,
|
||||
std::vector<Point>& points) const {
|
||||
for (auto quad : ToQuadraticPathComponents()) {
|
||||
quad.AppendPolylinePoints(scale_factor, points);
|
||||
}
|
||||
}
|
||||
|
||||
void ConicPathComponent::ToLinearPathComponents(Scalar scale,
|
||||
VertexWriter& writer) const {
|
||||
for (auto quad : ToQuadraticPathComponents()) {
|
||||
quad.ToLinearPathComponents(scale, writer);
|
||||
}
|
||||
}
|
||||
|
||||
size_t ConicPathComponent::CountLinearPathComponents(Scalar scale) const {
|
||||
size_t count = 0;
|
||||
for (auto quad : ToQuadraticPathComponents()) {
|
||||
count += quad.CountLinearPathComponents(scale);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
std::vector<Point> ConicPathComponent::Extrema() const {
|
||||
std::vector<Point> points;
|
||||
for (auto quad : ToQuadraticPathComponents()) {
|
||||
auto quad_extrema = quad.Extrema();
|
||||
points.insert(points.end(), quad_extrema.begin(), quad_extrema.end());
|
||||
}
|
||||
return points;
|
||||
}
|
||||
|
||||
std::optional<Vector2> ConicPathComponent::GetStartDirection() const {
|
||||
if (p1 != cp) {
|
||||
return (p1 - cp).Normalize();
|
||||
}
|
||||
if (p1 != p2) {
|
||||
return (p1 - p2).Normalize();
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<Vector2> ConicPathComponent::GetEndDirection() const {
|
||||
if (p2 != cp) {
|
||||
return (p2 - cp).Normalize();
|
||||
}
|
||||
if (p2 != p1) {
|
||||
return (p2 - p1).Normalize();
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void ConicPathComponent::SubdivideToQuadraticPoints(
|
||||
std::array<Point, 5>& points) const {
|
||||
FML_DCHECK(weight.IsFinite() && weight.x > 0 && weight.y > 0);
|
||||
|
||||
// Observe that scale will always be smaller than 1 because weight > 0.
|
||||
const Scalar scale = 1.0f / (1.0f + weight.x);
|
||||
|
||||
// The subdivided control points below are the sums of the following three
|
||||
// terms. Because the terms are multiplied by something <1, and the resulting
|
||||
// control points lie within the control points of the original then the
|
||||
// terms and the sums below will not overflow. Note that weight * scale
|
||||
// approaches 1 as weight becomes very large.
|
||||
Point tp1 = p1 * scale;
|
||||
Point tcp = cp * (weight.x * scale);
|
||||
Point tp2 = p2 * scale;
|
||||
|
||||
// Calculate the subdivided control points
|
||||
Point sub_cp1 = tp1 + tcp;
|
||||
Point sub_cp2 = tcp + tp2;
|
||||
|
||||
// The middle point shared by the 2 sub-divisions, the interpolation of
|
||||
// the original curve at its halfway point.
|
||||
Point sub_mid = (tp1 + tcp + tcp + tp2) * 0.5f;
|
||||
|
||||
FML_DCHECK(sub_cp1.IsFinite() && sub_mid.IsFinite() && sub_cp2.IsFinite());
|
||||
|
||||
points[0] = p1;
|
||||
points[1] = sub_cp1;
|
||||
points[2] = sub_mid;
|
||||
points[3] = sub_cp2;
|
||||
points[4] = p2;
|
||||
|
||||
// Update w.
|
||||
// Currently this method only subdivides a single time directly to 2
|
||||
// quadratics, but if we eventually want to keep the weights for further
|
||||
// subdivision, this was the code that did it in Skia:
|
||||
// sub_w1 = sub_w2 = SkScalarSqrt(SK_ScalarHalf + w * SK_ScalarHalf)
|
||||
}
|
||||
|
||||
std::array<QuadraticPathComponent, 2>
|
||||
ConicPathComponent::ToQuadraticPathComponents() const {
|
||||
std::array<Point, 5> points;
|
||||
SubdivideToQuadraticPoints(points);
|
||||
|
||||
return {
|
||||
QuadraticPathComponent(points[0], points[1], points[2]),
|
||||
QuadraticPathComponent(points[2], points[3], points[4]),
|
||||
};
|
||||
}
|
||||
|
||||
Point CubicPathComponent::Solve(Scalar time) const {
|
||||
return {
|
||||
CubicSolve(time, p1.x, cp1.x, cp2.x, p2.x), // x
|
||||
|
@ -5,7 +5,6 @@
|
||||
#ifndef FLUTTER_IMPELLER_GEOMETRY_PATH_COMPONENT_H_
|
||||
#define FLUTTER_IMPELLER_GEOMETRY_PATH_COMPONENT_H_
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
@ -171,107 +170,6 @@ struct QuadraticPathComponent {
|
||||
std::optional<Vector2> GetEndDirection() const;
|
||||
};
|
||||
|
||||
// A component that represets a Conic section curve.
|
||||
//
|
||||
// A conic section is basically nearly a quadratic bezier curve, but it
|
||||
// has an additional weight that is applied to the middle term (the control
|
||||
// point term).
|
||||
//
|
||||
// Starting with the equation for a quadratic curve which is:
|
||||
// (A) P1 * (1 - t) * (1 - t)
|
||||
// + CP * 2 * t * (1 - t)
|
||||
// + P2 * t * t
|
||||
// One thing to note is that the quadratic coefficients always sum to 1:
|
||||
// (B) (1-t)(1-t) + 2t(1-t) + tt
|
||||
// == 1 - 2t + tt + 2t - 2tt + tt
|
||||
// == 1
|
||||
// which means that the resulting point is always a weighted average of
|
||||
// the 3 points without having to "divide by the sum of the coefficients"
|
||||
// that is normally done when computing weighted averages.
|
||||
//
|
||||
// The conic equation, though, would then be:
|
||||
// (C) P1 * (1 - t) * (1 - t)
|
||||
// + CP * 2 * t * (1 - t) * w
|
||||
// + P2 * t * t
|
||||
// That would be the final equation, but if we look at the coefficients:
|
||||
// (D) (1-t)(1-t) + 2wt(1-t) + tt
|
||||
// == 1 - 2t + tt + 2wt - 2wtt + tt
|
||||
// == 1 + (2w - 2)t + (2 - 2w)tt
|
||||
// These only sum to 1 if the weight (w) is 1. In order for this math to
|
||||
// produce a point that is the weighted average of the 3 points, we have
|
||||
// to compute both and divide the equation (C) by the equation (D).
|
||||
//
|
||||
// Note that there are important potential optimizations we could apply.
|
||||
// If w is 0,
|
||||
// then this equation devolves into a straight line from P1 to P2.
|
||||
// Note that the "progress" from P1 to P2, as a function of t, is
|
||||
// quadratic if you compute it as the indicated numerator and denominator,
|
||||
// but the actual points generated are all on the line from P1 to P2
|
||||
// If w is (sqrt(2) / 2),
|
||||
// then this math is exactly an elliptical section, provided the 3 points
|
||||
// P1, CP, P2 form a right angle, and a circular section if they are also
|
||||
// of equal length (|P1,CP| == |CP,P2|)
|
||||
// If w is 1,
|
||||
// then we really don't need the division since the denominator will always
|
||||
// be 1 and we could just treat that curve as a quadratic.
|
||||
// If w is (infinity/large enough),
|
||||
// then the equation devolves into 2 straight lines P1->CP->P2, but
|
||||
// the straightforward math may encounter infinity/NaN values in the
|
||||
// intermediate stages. The limit as w approaches infinity are those
|
||||
// two lines.
|
||||
//
|
||||
// Some examples: https://fiddle.skia.org/c/986b521feb3b832f04cbdfeefc9fbc58
|
||||
// Note that the quadratic drawn in red in the center is identical to the
|
||||
// conic with a weight of 1, drawn in green in the lower left.
|
||||
struct ConicPathComponent {
|
||||
// Start point.
|
||||
Point p1;
|
||||
// Control point.
|
||||
Point cp;
|
||||
// End point.
|
||||
Point p2;
|
||||
|
||||
// Weight
|
||||
//
|
||||
// We only need one value, but the underlying storage allocation is always
|
||||
// a multiple of Point objects. To avoid confusion over which field the
|
||||
// weight is stored in, and what the value of the other field may be, we
|
||||
// store it in both x,y components of the |weight| field.
|
||||
//
|
||||
// This may also be an advantage eventually for code that can vectorize
|
||||
// the conic calculations on both X & Y simultaneously.
|
||||
Point weight;
|
||||
|
||||
ConicPathComponent() {}
|
||||
|
||||
ConicPathComponent(Point ap1, Point acp, Point ap2, Scalar weight)
|
||||
: p1(ap1), cp(acp), p2(ap2), weight(weight, weight) {}
|
||||
|
||||
Point Solve(Scalar time) const;
|
||||
|
||||
void AppendPolylinePoints(Scalar scale_factor,
|
||||
std::vector<Point>& points) const;
|
||||
|
||||
void ToLinearPathComponents(Scalar scale, VertexWriter& writer) const;
|
||||
|
||||
size_t CountLinearPathComponents(Scalar scale) const;
|
||||
|
||||
std::vector<Point> Extrema() const;
|
||||
|
||||
bool operator==(const ConicPathComponent& other) const {
|
||||
return p1 == other.p1 && cp == other.cp && p2 == other.p2 &&
|
||||
weight == other.weight;
|
||||
}
|
||||
|
||||
std::optional<Vector2> GetStartDirection() const;
|
||||
|
||||
std::optional<Vector2> GetEndDirection() const;
|
||||
|
||||
std::array<QuadraticPathComponent, 2> ToQuadraticPathComponents() const;
|
||||
|
||||
void SubdivideToQuadraticPoints(std::array<Point, 5>& points) const;
|
||||
};
|
||||
|
||||
// A component that represets a Cubic Bézier curve.
|
||||
struct CubicPathComponent {
|
||||
// Start point.
|
||||
|
@ -113,14 +113,6 @@ TEST(PathTest, PathSingleContour) {
|
||||
|
||||
EXPECT_TRUE(path.IsSingleContour());
|
||||
}
|
||||
|
||||
{
|
||||
Path path = PathBuilder{}
|
||||
.AddConicCurve({100, 100}, {100, 50}, {200, 100}, 0.75f)
|
||||
.TakePath();
|
||||
|
||||
EXPECT_TRUE(path.IsSingleContour());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PathTest, PathSingleContourDoubleShapes) {
|
||||
@ -223,50 +215,34 @@ TEST(PathTest, PathSingleContourDoubleShapes) {
|
||||
|
||||
EXPECT_FALSE(path.IsSingleContour());
|
||||
}
|
||||
|
||||
{
|
||||
Path path = PathBuilder{}
|
||||
.AddConicCurve({100, 100}, {100, 50}, {200, 100}, 0.75f)
|
||||
.Close()
|
||||
.AddConicCurve({100, 100}, {100, 50}, {200, 100}, 0.75f)
|
||||
.TakePath();
|
||||
|
||||
EXPECT_FALSE(path.IsSingleContour());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PathTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) {
|
||||
// Closed shapes.
|
||||
{
|
||||
Path path = PathBuilder{}.AddCircle({100, 100}, 50).TakePath();
|
||||
EXPECT_NE(path.begin(), path.end());
|
||||
EXPECT_EQ(path.begin().type(), Path::ComponentType::kContour);
|
||||
auto contour = path.begin().contour();
|
||||
ASSERT_NE(contour, nullptr);
|
||||
EXPECT_POINT_NEAR(contour->destination, Point(100, 50));
|
||||
EXPECT_TRUE(contour->IsClosed());
|
||||
ContourComponent contour;
|
||||
path.GetContourComponentAtIndex(0, contour);
|
||||
EXPECT_POINT_NEAR(contour.destination, Point(100, 50));
|
||||
EXPECT_TRUE(contour.IsClosed());
|
||||
}
|
||||
|
||||
{
|
||||
Path path =
|
||||
PathBuilder{}.AddOval(Rect::MakeXYWH(100, 100, 100, 100)).TakePath();
|
||||
EXPECT_NE(path.begin(), path.end());
|
||||
EXPECT_EQ(path.begin().type(), Path::ComponentType::kContour);
|
||||
auto contour = path.begin().contour();
|
||||
ASSERT_NE(contour, nullptr);
|
||||
EXPECT_POINT_NEAR(contour->destination, Point(150, 100));
|
||||
EXPECT_TRUE(contour->IsClosed());
|
||||
ContourComponent contour;
|
||||
path.GetContourComponentAtIndex(0, contour);
|
||||
EXPECT_POINT_NEAR(contour.destination, Point(150, 100));
|
||||
EXPECT_TRUE(contour.IsClosed());
|
||||
}
|
||||
|
||||
{
|
||||
Path path =
|
||||
PathBuilder{}.AddRect(Rect::MakeXYWH(100, 100, 100, 100)).TakePath();
|
||||
EXPECT_NE(path.begin(), path.end());
|
||||
EXPECT_EQ(path.begin().type(), Path::ComponentType::kContour);
|
||||
auto contour = path.begin().contour();
|
||||
ASSERT_NE(contour, nullptr);
|
||||
EXPECT_POINT_NEAR(contour->destination, Point(100, 100));
|
||||
EXPECT_TRUE(contour->IsClosed());
|
||||
ContourComponent contour;
|
||||
path.GetContourComponentAtIndex(0, contour);
|
||||
EXPECT_POINT_NEAR(contour.destination, Point(100, 100));
|
||||
EXPECT_TRUE(contour.IsClosed());
|
||||
}
|
||||
|
||||
{
|
||||
@ -274,12 +250,10 @@ TEST(PathTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) {
|
||||
.AddRoundRect(RoundRect::MakeRectRadius(
|
||||
Rect::MakeXYWH(100, 100, 100, 100), 10))
|
||||
.TakePath();
|
||||
EXPECT_NE(path.begin(), path.end());
|
||||
EXPECT_EQ(path.begin().type(), Path::ComponentType::kContour);
|
||||
auto contour = path.begin().contour();
|
||||
ASSERT_NE(contour, nullptr);
|
||||
EXPECT_POINT_NEAR(contour->destination, Point(110, 100));
|
||||
EXPECT_TRUE(contour->IsClosed());
|
||||
ContourComponent contour;
|
||||
path.GetContourComponentAtIndex(0, contour);
|
||||
EXPECT_POINT_NEAR(contour.destination, Point(110, 100));
|
||||
EXPECT_TRUE(contour.IsClosed());
|
||||
}
|
||||
|
||||
{
|
||||
@ -287,12 +261,10 @@ TEST(PathTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) {
|
||||
.AddRoundRect(RoundRect::MakeRectXY(
|
||||
Rect::MakeXYWH(100, 100, 100, 100), Size(10, 20)))
|
||||
.TakePath();
|
||||
EXPECT_NE(path.begin(), path.end());
|
||||
EXPECT_EQ(path.begin().type(), Path::ComponentType::kContour);
|
||||
auto contour = path.begin().contour();
|
||||
ASSERT_NE(contour, nullptr);
|
||||
EXPECT_POINT_NEAR(contour->destination, Point(110, 100));
|
||||
EXPECT_TRUE(contour->IsClosed());
|
||||
ContourComponent contour;
|
||||
path.GetContourComponentAtIndex(0, contour);
|
||||
EXPECT_POINT_NEAR(contour.destination, Point(110, 100));
|
||||
EXPECT_TRUE(contour.IsClosed());
|
||||
}
|
||||
|
||||
{
|
||||
@ -300,12 +272,10 @@ TEST(PathTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) {
|
||||
.AddRoundSuperellipse(RoundSuperellipse::MakeRectRadius(
|
||||
Rect::MakeXYWH(100, 100, 100, 100), 10))
|
||||
.TakePath();
|
||||
EXPECT_NE(path.begin(), path.end());
|
||||
EXPECT_EQ(path.begin().type(), Path::ComponentType::kContour);
|
||||
auto contour = path.begin().contour();
|
||||
ASSERT_NE(contour, nullptr);
|
||||
EXPECT_POINT_NEAR(contour->destination, Point(150, 100));
|
||||
EXPECT_TRUE(contour->IsClosed());
|
||||
ContourComponent contour;
|
||||
path.GetContourComponentAtIndex(0, contour);
|
||||
EXPECT_POINT_NEAR(contour.destination, Point(150, 100));
|
||||
EXPECT_TRUE(contour.IsClosed());
|
||||
}
|
||||
|
||||
{
|
||||
@ -313,24 +283,20 @@ TEST(PathTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) {
|
||||
.AddRoundSuperellipse(RoundSuperellipse::MakeRectXY(
|
||||
Rect::MakeXYWH(100, 100, 100, 100), Size(10, 20)))
|
||||
.TakePath();
|
||||
EXPECT_NE(path.begin(), path.end());
|
||||
EXPECT_EQ(path.begin().type(), Path::ComponentType::kContour);
|
||||
auto contour = path.begin().contour();
|
||||
ASSERT_NE(contour, nullptr);
|
||||
EXPECT_POINT_NEAR(contour->destination, Point(150, 100));
|
||||
EXPECT_TRUE(contour->IsClosed());
|
||||
ContourComponent contour;
|
||||
path.GetContourComponentAtIndex(0, contour);
|
||||
EXPECT_POINT_NEAR(contour.destination, Point(150, 100));
|
||||
EXPECT_TRUE(contour.IsClosed());
|
||||
}
|
||||
|
||||
// Open shapes.
|
||||
{
|
||||
Point p(100, 100);
|
||||
Path path = PathBuilder{}.AddLine(p, {200, 100}).TakePath();
|
||||
EXPECT_NE(path.begin(), path.end());
|
||||
EXPECT_EQ(path.begin().type(), Path::ComponentType::kContour);
|
||||
auto contour = path.begin().contour();
|
||||
ASSERT_NE(contour, nullptr);
|
||||
EXPECT_POINT_NEAR(contour->destination, p);
|
||||
EXPECT_FALSE(contour->IsClosed());
|
||||
ContourComponent contour;
|
||||
path.GetContourComponentAtIndex(0, contour);
|
||||
ASSERT_POINT_NEAR(contour.destination, p);
|
||||
ASSERT_FALSE(contour.IsClosed());
|
||||
}
|
||||
|
||||
{
|
||||
@ -338,36 +304,20 @@ TEST(PathTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) {
|
||||
PathBuilder{}
|
||||
.AddCubicCurve({100, 100}, {100, 50}, {100, 150}, {200, 100})
|
||||
.TakePath();
|
||||
EXPECT_NE(path.begin(), path.end());
|
||||
EXPECT_EQ(path.begin().type(), Path::ComponentType::kContour);
|
||||
auto contour = path.begin().contour();
|
||||
ASSERT_NE(contour, nullptr);
|
||||
EXPECT_POINT_NEAR(contour->destination, Point(100, 100));
|
||||
EXPECT_FALSE(contour->IsClosed());
|
||||
ContourComponent contour;
|
||||
path.GetContourComponentAtIndex(0, contour);
|
||||
ASSERT_POINT_NEAR(contour.destination, Point(100, 100));
|
||||
ASSERT_FALSE(contour.IsClosed());
|
||||
}
|
||||
|
||||
{
|
||||
Path path = PathBuilder{}
|
||||
.AddQuadraticCurve({100, 100}, {100, 50}, {200, 100})
|
||||
.TakePath();
|
||||
EXPECT_NE(path.begin(), path.end());
|
||||
EXPECT_EQ(path.begin().type(), Path::ComponentType::kContour);
|
||||
auto contour = path.begin().contour();
|
||||
ASSERT_NE(contour, nullptr);
|
||||
EXPECT_POINT_NEAR(contour->destination, Point(100, 100));
|
||||
EXPECT_FALSE(contour->IsClosed());
|
||||
}
|
||||
|
||||
{
|
||||
Path path = PathBuilder{}
|
||||
.AddConicCurve({100, 100}, {100, 50}, {200, 100}, 0.75f)
|
||||
.TakePath();
|
||||
EXPECT_NE(path.begin(), path.end());
|
||||
EXPECT_EQ(path.begin().type(), Path::ComponentType::kContour);
|
||||
auto contour = path.begin().contour();
|
||||
ASSERT_NE(contour, nullptr);
|
||||
EXPECT_POINT_NEAR(contour->destination, Point(100, 100));
|
||||
EXPECT_FALSE(contour->IsClosed());
|
||||
ContourComponent contour;
|
||||
path.GetContourComponentAtIndex(0, contour);
|
||||
ASSERT_POINT_NEAR(contour.destination, Point(100, 100));
|
||||
ASSERT_FALSE(contour.IsClosed());
|
||||
}
|
||||
}
|
||||
|
||||
@ -483,68 +433,35 @@ TEST(PathTest, PathShifting) {
|
||||
auto path =
|
||||
builder.AddLine(Point(0, 0), Point(10, 10))
|
||||
.AddQuadraticCurve(Point(10, 10), Point(15, 15), Point(20, 20))
|
||||
.AddConicCurve(Point(10, 10), Point(15, 10), Point(15, 15), 0.75f)
|
||||
.AddCubicCurve(Point(20, 20), Point(25, 25), Point(-5, -5),
|
||||
Point(30, 30))
|
||||
.Close()
|
||||
.Shift(Point(1, 1))
|
||||
.TakePath();
|
||||
|
||||
auto it = path.begin();
|
||||
ContourComponent contour;
|
||||
LinearPathComponent linear;
|
||||
QuadraticPathComponent quad;
|
||||
CubicPathComponent cubic;
|
||||
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kContour);
|
||||
const ContourComponent* contour = it.contour();
|
||||
ASSERT_NE(contour, nullptr);
|
||||
++it;
|
||||
ASSERT_TRUE(path.GetContourComponentAtIndex(0, contour));
|
||||
ASSERT_TRUE(path.GetLinearComponentAtIndex(1, linear));
|
||||
ASSERT_TRUE(path.GetQuadraticComponentAtIndex(3, quad));
|
||||
ASSERT_TRUE(path.GetCubicComponentAtIndex(5, cubic));
|
||||
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kLinear);
|
||||
const LinearPathComponent* linear = it.linear();
|
||||
ASSERT_NE(linear, nullptr);
|
||||
++it;
|
||||
EXPECT_EQ(contour.destination, Point(1, 1));
|
||||
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kContour);
|
||||
++it;
|
||||
EXPECT_EQ(linear.p1, Point(1, 1));
|
||||
EXPECT_EQ(linear.p2, Point(11, 11));
|
||||
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kQuadratic);
|
||||
const QuadraticPathComponent* quad = it.quadratic();
|
||||
ASSERT_NE(quad, nullptr);
|
||||
++it;
|
||||
EXPECT_EQ(quad.cp, Point(16, 16));
|
||||
EXPECT_EQ(quad.p1, Point(11, 11));
|
||||
EXPECT_EQ(quad.p2, Point(21, 21));
|
||||
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kContour);
|
||||
++it;
|
||||
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kConic);
|
||||
const ConicPathComponent* conic = it.conic();
|
||||
ASSERT_NE(conic, nullptr);
|
||||
++it;
|
||||
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kContour);
|
||||
++it;
|
||||
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kCubic);
|
||||
const CubicPathComponent* cubic = it.cubic();
|
||||
ASSERT_NE(cubic, nullptr);
|
||||
++it;
|
||||
|
||||
// Close always opens a new contour, even if it isn't needed
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kContour);
|
||||
++it;
|
||||
|
||||
EXPECT_EQ(it, path.end());
|
||||
|
||||
EXPECT_EQ(contour->destination, Point(1, 1));
|
||||
|
||||
EXPECT_EQ(linear->p1, Point(1, 1));
|
||||
EXPECT_EQ(linear->p2, Point(11, 11));
|
||||
|
||||
EXPECT_EQ(quad->cp, Point(16, 16));
|
||||
EXPECT_EQ(quad->p1, Point(11, 11));
|
||||
EXPECT_EQ(quad->p2, Point(21, 21));
|
||||
|
||||
EXPECT_EQ(cubic->cp1, Point(26, 26));
|
||||
EXPECT_EQ(cubic->cp2, Point(-4, -4));
|
||||
EXPECT_EQ(cubic->p1, Point(21, 21));
|
||||
EXPECT_EQ(cubic->p2, Point(31, 31));
|
||||
EXPECT_EQ(cubic.cp1, Point(26, 26));
|
||||
EXPECT_EQ(cubic.cp2, Point(-4, -4));
|
||||
EXPECT_EQ(cubic.p1, Point(21, 21));
|
||||
EXPECT_EQ(cubic.p2, Point(31, 31));
|
||||
}
|
||||
|
||||
TEST(PathTest, PathBuilderWillComputeBounds) {
|
||||
@ -573,68 +490,34 @@ TEST(PathTest, PathHorizontalLine) {
|
||||
PathBuilder builder;
|
||||
auto path = builder.HorizontalLineTo(10).TakePath();
|
||||
|
||||
auto it = path.begin();
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kContour);
|
||||
++it;
|
||||
LinearPathComponent linear;
|
||||
path.GetLinearComponentAtIndex(1, linear);
|
||||
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kLinear);
|
||||
const LinearPathComponent* linear = it.linear();
|
||||
ASSERT_NE(linear, nullptr);
|
||||
|
||||
EXPECT_EQ(linear->p1, Point(0, 0));
|
||||
EXPECT_EQ(linear->p2, Point(10, 0));
|
||||
EXPECT_EQ(linear.p1, Point(0, 0));
|
||||
EXPECT_EQ(linear.p2, Point(10, 0));
|
||||
}
|
||||
|
||||
TEST(PathTest, PathVerticalLine) {
|
||||
PathBuilder builder;
|
||||
auto path = builder.VerticalLineTo(10).TakePath();
|
||||
|
||||
auto it = path.begin();
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kContour);
|
||||
++it;
|
||||
LinearPathComponent linear;
|
||||
path.GetLinearComponentAtIndex(1, linear);
|
||||
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kLinear);
|
||||
const LinearPathComponent* linear = it.linear();
|
||||
ASSERT_NE(linear, nullptr);
|
||||
|
||||
EXPECT_EQ(linear->p1, Point(0, 0));
|
||||
EXPECT_EQ(linear->p2, Point(0, 10));
|
||||
EXPECT_EQ(linear.p1, Point(0, 0));
|
||||
EXPECT_EQ(linear.p2, Point(0, 10));
|
||||
}
|
||||
|
||||
TEST(PathTest, QuadradicPath) {
|
||||
PathBuilder builder;
|
||||
auto path = builder.QuadraticCurveTo(Point(10, 10), Point(20, 20)).TakePath();
|
||||
|
||||
auto it = path.begin();
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kContour);
|
||||
++it;
|
||||
QuadraticPathComponent quad;
|
||||
path.GetQuadraticComponentAtIndex(1, quad);
|
||||
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kQuadratic);
|
||||
const QuadraticPathComponent* quad = it.quadratic();
|
||||
ASSERT_NE(quad, nullptr);
|
||||
|
||||
EXPECT_EQ(quad->p1, Point(0, 0));
|
||||
EXPECT_EQ(quad->cp, Point(10, 10));
|
||||
EXPECT_EQ(quad->p2, Point(20, 20));
|
||||
}
|
||||
|
||||
TEST(PathTest, ConicPath) {
|
||||
PathBuilder builder;
|
||||
auto path =
|
||||
builder.ConicCurveTo(Point(10, 10), Point(20, 20), 0.75f).TakePath();
|
||||
|
||||
auto it = path.begin();
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kContour);
|
||||
++it;
|
||||
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kConic);
|
||||
const ConicPathComponent* conic = it.conic();
|
||||
ASSERT_NE(conic, nullptr);
|
||||
|
||||
EXPECT_EQ(conic->p1, Point(0, 0));
|
||||
EXPECT_EQ(conic->cp, Point(10, 10));
|
||||
EXPECT_EQ(conic->p2, Point(20, 20));
|
||||
EXPECT_EQ(conic->weight, Point(0.75f, 0.75f));
|
||||
EXPECT_EQ(quad.p1, Point(0, 0));
|
||||
EXPECT_EQ(quad.cp, Point(10, 10));
|
||||
EXPECT_EQ(quad.p2, Point(20, 20));
|
||||
}
|
||||
|
||||
TEST(PathTest, CubicPath) {
|
||||
@ -643,18 +526,13 @@ TEST(PathTest, CubicPath) {
|
||||
builder.CubicCurveTo(Point(10, 10), Point(-10, -10), Point(20, 20))
|
||||
.TakePath();
|
||||
|
||||
auto it = path.begin();
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kContour);
|
||||
++it;
|
||||
CubicPathComponent cubic;
|
||||
path.GetCubicComponentAtIndex(1, cubic);
|
||||
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kCubic);
|
||||
const CubicPathComponent* cubic = it.cubic();
|
||||
ASSERT_NE(cubic, nullptr);
|
||||
|
||||
EXPECT_EQ(cubic->p1, Point(0, 0));
|
||||
EXPECT_EQ(cubic->cp1, Point(10, 10));
|
||||
EXPECT_EQ(cubic->cp2, Point(-10, -10));
|
||||
EXPECT_EQ(cubic->p2, Point(20, 20));
|
||||
EXPECT_EQ(cubic.p1, Point(0, 0));
|
||||
EXPECT_EQ(cubic.cp1, Point(10, 10));
|
||||
EXPECT_EQ(cubic.cp2, Point(-10, -10));
|
||||
EXPECT_EQ(cubic.p2, Point(20, 20));
|
||||
}
|
||||
|
||||
TEST(PathTest, BoundingBoxCubic) {
|
||||
@ -707,8 +585,9 @@ TEST(PathTest, EmptyPath) {
|
||||
auto path = PathBuilder{}.TakePath();
|
||||
ASSERT_EQ(path.GetComponentCount(), 1u);
|
||||
|
||||
const ContourComponent* c = path.begin().contour();
|
||||
ASSERT_POINT_NEAR(c->destination, Point());
|
||||
ContourComponent c;
|
||||
path.GetContourComponentAtIndex(0, c);
|
||||
ASSERT_POINT_NEAR(c.destination, Point());
|
||||
|
||||
Path::Polyline polyline = path.CreatePolyline(1.0f);
|
||||
ASSERT_TRUE(polyline.points->empty());
|
||||
@ -720,122 +599,77 @@ TEST(PathTest, SimplePath) {
|
||||
|
||||
auto path = builder.AddLine({0, 0}, {100, 100})
|
||||
.AddQuadraticCurve({100, 100}, {200, 200}, {300, 300})
|
||||
.AddConicCurve({100, 100}, {200, 200}, {300, 300}, 0.75f)
|
||||
.AddCubicCurve({300, 300}, {400, 400}, {500, 500}, {600, 600})
|
||||
.TakePath();
|
||||
|
||||
EXPECT_EQ(path.GetComponentCount(), 8u);
|
||||
EXPECT_EQ(path.GetComponentCount(), 6u);
|
||||
EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kLinear), 1u);
|
||||
EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kQuadratic), 1u);
|
||||
EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kConic), 1u);
|
||||
EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kCubic), 1u);
|
||||
EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kContour), 4u);
|
||||
|
||||
auto it = path.begin();
|
||||
EXPECT_EQ(path.GetComponentCount(Path::ComponentType::kContour), 3u);
|
||||
|
||||
{
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kContour);
|
||||
const ContourComponent* contour = it.contour();
|
||||
ASSERT_NE(contour, nullptr);
|
||||
++it;
|
||||
|
||||
Point p1(0, 0);
|
||||
EXPECT_EQ(contour->destination, p1);
|
||||
EXPECT_FALSE(contour->IsClosed());
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kLinear);
|
||||
const LinearPathComponent* linear = it.linear();
|
||||
ASSERT_NE(linear, nullptr);
|
||||
++it;
|
||||
LinearPathComponent linear;
|
||||
EXPECT_TRUE(path.GetLinearComponentAtIndex(1, linear));
|
||||
|
||||
Point p1(0, 0);
|
||||
Point p2(100, 100);
|
||||
EXPECT_EQ(linear->p1, p1);
|
||||
EXPECT_EQ(linear->p2, p2);
|
||||
EXPECT_EQ(linear.p1, p1);
|
||||
EXPECT_EQ(linear.p2, p2);
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kContour);
|
||||
const ContourComponent* contour = it.contour();
|
||||
ASSERT_NE(contour, nullptr);
|
||||
++it;
|
||||
|
||||
Point p1(100, 100);
|
||||
EXPECT_EQ(contour->destination, p1);
|
||||
EXPECT_FALSE(contour->IsClosed());
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kQuadratic);
|
||||
const QuadraticPathComponent* quad = it.quadratic();
|
||||
ASSERT_NE(quad, nullptr);
|
||||
++it;
|
||||
QuadraticPathComponent quad;
|
||||
EXPECT_TRUE(path.GetQuadraticComponentAtIndex(3, quad));
|
||||
|
||||
Point p1(100, 100);
|
||||
Point cp(200, 200);
|
||||
Point p2(300, 300);
|
||||
EXPECT_EQ(quad->p1, p1);
|
||||
EXPECT_EQ(quad->cp, cp);
|
||||
EXPECT_EQ(quad->p2, p2);
|
||||
EXPECT_EQ(quad.p1, p1);
|
||||
EXPECT_EQ(quad.cp, cp);
|
||||
EXPECT_EQ(quad.p2, p2);
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kContour);
|
||||
const ContourComponent* contour = it.contour();
|
||||
ASSERT_NE(contour, nullptr);
|
||||
++it;
|
||||
|
||||
Point p1(100, 100);
|
||||
EXPECT_EQ(contour->destination, p1);
|
||||
EXPECT_FALSE(contour->IsClosed());
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kConic);
|
||||
const ConicPathComponent* conic = it.conic();
|
||||
ASSERT_NE(conic, nullptr);
|
||||
++it;
|
||||
|
||||
Point p1(100, 100);
|
||||
Point cp(200, 200);
|
||||
Point p2(300, 300);
|
||||
Point weight(0.75f, 0.75f);
|
||||
EXPECT_EQ(conic->p1, p1);
|
||||
EXPECT_EQ(conic->cp, cp);
|
||||
EXPECT_EQ(conic->p2, p2);
|
||||
EXPECT_EQ(conic->weight, weight);
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kContour);
|
||||
const ContourComponent* contour = it.contour();
|
||||
ASSERT_NE(contour, nullptr);
|
||||
++it;
|
||||
|
||||
Point p1(300, 300);
|
||||
EXPECT_EQ(contour->destination, p1);
|
||||
EXPECT_FALSE(contour->IsClosed());
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kCubic);
|
||||
const CubicPathComponent* cubic = it.cubic();
|
||||
ASSERT_NE(cubic, nullptr);
|
||||
++it;
|
||||
CubicPathComponent cubic;
|
||||
EXPECT_TRUE(path.GetCubicComponentAtIndex(5, cubic));
|
||||
|
||||
Point p1(300, 300);
|
||||
Point cp1(400, 400);
|
||||
Point cp2(500, 500);
|
||||
Point p2(600, 600);
|
||||
EXPECT_EQ(cubic->p1, p1);
|
||||
EXPECT_EQ(cubic->cp1, cp1);
|
||||
EXPECT_EQ(cubic->cp2, cp2);
|
||||
EXPECT_EQ(cubic->p2, p2);
|
||||
EXPECT_EQ(cubic.p1, p1);
|
||||
EXPECT_EQ(cubic.cp1, cp1);
|
||||
EXPECT_EQ(cubic.cp2, cp2);
|
||||
EXPECT_EQ(cubic.p2, p2);
|
||||
}
|
||||
|
||||
EXPECT_EQ(it, path.end());
|
||||
{
|
||||
ContourComponent contour;
|
||||
EXPECT_TRUE(path.GetContourComponentAtIndex(0, contour));
|
||||
|
||||
Point p1(0, 0);
|
||||
EXPECT_EQ(contour.destination, p1);
|
||||
EXPECT_FALSE(contour.IsClosed());
|
||||
}
|
||||
|
||||
{
|
||||
ContourComponent contour;
|
||||
EXPECT_TRUE(path.GetContourComponentAtIndex(2, contour));
|
||||
|
||||
Point p1(100, 100);
|
||||
EXPECT_EQ(contour.destination, p1);
|
||||
EXPECT_FALSE(contour.IsClosed());
|
||||
}
|
||||
|
||||
{
|
||||
ContourComponent contour;
|
||||
EXPECT_TRUE(path.GetContourComponentAtIndex(4, contour));
|
||||
|
||||
Point p1(300, 300);
|
||||
EXPECT_EQ(contour.destination, p1);
|
||||
EXPECT_FALSE(contour.IsClosed());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PathTest, RepeatCloseDoesNotAddNewLines) {
|
||||
@ -1081,36 +915,23 @@ TEST(PathTest, PathBuilderDoesNotMutateCopiedPaths) {
|
||||
} else {
|
||||
EXPECT_EQ(path.GetComponentCount(), 3u) << label;
|
||||
}
|
||||
auto it = path.begin();
|
||||
{
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kContour) << label;
|
||||
const ContourComponent* contour = it.contour();
|
||||
ASSERT_NE(contour, nullptr) << label;
|
||||
++it;
|
||||
|
||||
EXPECT_EQ(contour->destination, offset + Point(10, 10)) << label;
|
||||
EXPECT_EQ(contour->IsClosed(), is_closed) << label;
|
||||
ContourComponent contour;
|
||||
EXPECT_TRUE(path.GetContourComponentAtIndex(0, contour)) << label;
|
||||
EXPECT_EQ(contour.destination, offset + Point(10, 10)) << label;
|
||||
EXPECT_EQ(contour.IsClosed(), is_closed) << label;
|
||||
}
|
||||
{
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kLinear) << label;
|
||||
const LinearPathComponent* line = it.linear();
|
||||
ASSERT_NE(line, nullptr) << label;
|
||||
++it;
|
||||
|
||||
EXPECT_EQ(line->p1, offset + Point(10, 10)) << label;
|
||||
EXPECT_EQ(line->p2, offset + Point(20, 20)) << label;
|
||||
LinearPathComponent line;
|
||||
EXPECT_TRUE(path.GetLinearComponentAtIndex(1, line)) << label;
|
||||
EXPECT_EQ(line.p1, offset + Point(10, 10)) << label;
|
||||
EXPECT_EQ(line.p2, offset + Point(20, 20)) << label;
|
||||
}
|
||||
{
|
||||
ASSERT_EQ(it.type(), Path::ComponentType::kLinear) << label;
|
||||
const LinearPathComponent* line = it.linear();
|
||||
ASSERT_NE(line, nullptr) << label;
|
||||
++it;
|
||||
|
||||
EXPECT_EQ(line->p1, offset + Point(20, 20)) << label;
|
||||
EXPECT_EQ(line->p2, offset + Point(20, 10)) << label;
|
||||
}
|
||||
if (!is_mutated) {
|
||||
EXPECT_EQ(it, path.end()) << label;
|
||||
LinearPathComponent line;
|
||||
EXPECT_TRUE(path.GetLinearComponentAtIndex(2, line)) << label;
|
||||
EXPECT_EQ(line.p1, offset + Point(20, 20)) << label;
|
||||
EXPECT_EQ(line.p2, offset + Point(20, 10)) << label;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1213,18 +1034,6 @@ TEST(PathTest, PathBuilderDoesNotMutateCopiedPaths) {
|
||||
},
|
||||
false, {}, "Relative QuadraticCurveTo");
|
||||
|
||||
test_isolation(
|
||||
[](PathBuilder& builder) {
|
||||
builder.ConicCurveTo({20, 30}, {30, 20}, 0.75f, false);
|
||||
},
|
||||
false, {}, "Absolute ConicCurveTo");
|
||||
|
||||
test_isolation(
|
||||
[](PathBuilder& builder) {
|
||||
builder.ConicCurveTo({20, 30}, {30, 20}, 0.75f, true);
|
||||
},
|
||||
false, {}, "Relative ConicCurveTo");
|
||||
|
||||
test_isolation(
|
||||
[](PathBuilder& builder) {
|
||||
builder.CubicCurveTo({20, 30}, {30, 20}, {30, 30}, false);
|
||||
@ -1274,12 +1083,6 @@ TEST(PathTest, PathBuilderDoesNotMutateCopiedPaths) {
|
||||
},
|
||||
false, {}, "AddQuadraticCurve");
|
||||
|
||||
test_isolation(
|
||||
[](PathBuilder& builder) {
|
||||
builder.AddConicCurve({100, 100}, {150, 100}, {150, 150}, 0.75f);
|
||||
},
|
||||
false, {}, "AddConicCurve");
|
||||
|
||||
test_isolation(
|
||||
[](PathBuilder& builder) {
|
||||
builder.AddCubicCurve({100, 100}, {150, 100}, {100, 150}, {150, 150});
|
||||
|
Loading…
x
Reference in New Issue
Block a user