blob: 780d6f258887290ce5053669aab71b9aa902c001 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_GRAPHICS_LIB_COMPUTE_TESTS_COMMON_PATH_SINK_H_
#define SRC_GRAPHICS_LIB_COMPUTE_TESTS_COMMON_PATH_SINK_H_
#include <float.h>
#include <stdbool.h>
#include <stddef.h>
#include "tests/common/affine_transform.h"
// An abstract base class for objects that can be used to build vector path
// objects by adding individual moveto/lineto/quadto/etc items to it,
//
// Derived classes should override the begin(), addItem() and end() methods
// only, while callers may use the convenience methods like addMoveTo(),
// addLineTo(), addXXXTo(), addXXXPath() instead.
//
class PathSink {
public:
virtual ~PathSink() = default;
enum ItemType
{
MOVE_TO = 0,
LINE_TO,
QUAD_TO,
CUBIC_TO,
RAT_QUAD_TO,
RAT_CUBIC_TO,
ItemTypeCount, // Must be last, do not remove.
};
static constexpr char kArgsPerItemType[ItemTypeCount] = {
2, // MOVE_TO x y
2, // LINE_TO x y
4, // QUAD_TO cx cy x y
6, // CUBIC_TO c1x c1y c2x c2y x y
5, // RAT_QUAD_TO cx cy x y w
8, // RAT_CUBIC_TO c1x c1y c2x c2y x y w1 w2
};
static constexpr char kCoordPairsPerItemType[ItemTypeCount] = {
1, // MOVE_TO x y
1, // LINE_TO x y
2, // QUAD_TO cx cy x y
3, // CUBIC_TO c1x c1y c2x c2y x y
2, // RAT_QUAD_TO cx cy x y [w ignored]
3, // RAT_CUBIC_TO c1x c1y c2x c2y x y [w1 w2 ignored]
};
static constexpr size_t kMaxCoords = 8;
// Begin a new path.
virtual void
begin() = 0;
// Generic function to add a new path item. The only one to be overloaded
// by implementations. Callers can use the helper functions below
// instead.
virtual void
addItem(ItemType item_type, const double * coords) = 0;
void
addMoveTo(double x, double y)
{
double coords[2] = { x, y };
addItem(MOVE_TO, coords);
}
void
addLineTo(double x, double y)
{
double coords[2] = { x, y };
addItem(LINE_TO, coords);
}
void
addQuadTo(double cx, double cy, double x, double y)
{
double coords[4] = { cx, cy, x, y };
addItem(QUAD_TO, coords);
}
void
addCubicTo(double c1x, double c1y, double c2x, double c2y, double x, double y)
{
double coords[6] = { c1x, c1y, c2x, c2y, x, y };
addItem(CUBIC_TO, coords);
}
void
addRatQuadTo(double cx, double cy, double x, double y, double w)
{
double coords[5] = { cx, cy, x, y, w };
addItem(RAT_QUAD_TO, coords);
}
void
addRatCubicTo(
double c1x, double c1y, double c2x, double c2y, double x, double y, double w1, double w2)
{
double coords[8] = { c1x, c1y, c2x, c2y, x, y, w1, w2 };
addItem(RAT_CUBIC_TO, coords);
}
// Add rational quadratics that match an elliptical arc segment to the
// current path.
//
// |cx, cy| is the ellipse's center.
// |rx, ry| are the ellipse's radii.
// |x_axis_rotation| is the ellipse's rotation of the x-axis in radians.
// |angle_start| and |angle_delta| define the start angle and the sweep
// to perform.
//
void
addArcTo(double cx,
double cy,
double rx,
double ry,
double x_axis_rotation,
double angle_start,
double angle_delta);
// Alternative way to add elliptical arcs to the current path, using
// SVG-specific parameters. |x0, y0| must be the current/last point
// added to the path. |x, y| is the final arc point, |rx, ry| are the
// ellipse's radii, |x_axis_rotation| is the ellipse's rotation in radians,
// and |large_arc_flag| and |sweep_flag| control the arc's selection.
void
addSvgArcTo(double x1,
double y1,
double x2,
double y2,
double rx,
double ry,
double x_axis_rotation,
bool large_arc_flag,
bool sweep_flag);
// Finish the path. Returns true on success, or false on failure,
// which can be used to notify the caller that a fatal error happened.
bool virtual end() = 0;
// Add an ellipse path to a given builder. The ellipse is always axis-aligned.
// Note that this adds a full path (i.e. with begin() ... end() calls).
bool
addEllipsePath(double center_x, double center_y, double radius_x, double radius_y);
// Adds a rectangle path to a given builder.
// |x,y,w,h| are the rectangle's origin and dimensions.
bool
addRectPath(double x, double y, double w, double h);
// Add a rounded rectangle path to a given builder.
// |x,y,w,h| are the rectangle's origin and dimensions.
// |rx,ry| are the rounded corner radiuses in horizontal and vertical
// dimensions (e.g. the top-left corner is (x,y+ry) -> (x,y) -> (x+rx,y)).
bool
addRoundedRectPath(double x, double y, double w, double h, double rx, double ry);
};
//
// Base path builder implementations.
//
// An AffinePathSink applies an affine_transform_t to all coordinates
// it receives then send them to a target PathSink instance.
//
// Usage is:
// 1) Create instance, passing the transform by value, and a reference
// to the target.
//
// 2) Build a path with it as usual.
//
class AffinePathSink : public PathSink {
public:
AffinePathSink(const affine_transform_t * transform, PathSink * target) : target_(*target)
{
resetTransform(transform);
}
void
resetTransform(const affine_transform_t * transform)
{
transform_ = transform ? *transform : affine_transform_identity;
}
void
begin() override
{
target_.begin();
}
void
addItem(ItemType item_type, const double * coords) override
{
double new_coords[kMaxCoords];
for (int nn = 0; nn < kArgsPerItemType[item_type]; ++nn)
new_coords[nn] = coords[nn];
// Transform only the needed coordinates pairs.
int num_pairs = kCoordPairsPerItemType[item_type];
for (int nn = 0; nn < num_pairs; ++nn)
affine_transform_apply_xy(&transform_, &new_coords[nn * 2]);
target_.addItem(item_type, new_coords);
}
bool
end() override
{
return target_.end();
}
protected:
affine_transform_t transform_;
PathSink & target_;
};
// A PathSink derived class that computes the bounding box of all path
// points. Usage is:
//
// 1) Create instance.
// 2) Send path items to it as usual.
// 3) Retrieve bounds with xmin(), xmax(), ymin(), and ymax() methods.
//
// NOTE: If not path points were recorded, then
// |xmin() > xmax() && ymin() > ymax()| will be true.
//
class BoundingPathSink : public PathSink {
public:
BoundingPathSink() = default;
struct Bounds
{
double xmin = DBL_MAX;
double ymin = DBL_MAX;
double xmax = -DBL_MAX;
double ymax = -DBL_MAX;
bool
valid() const
{
return xmin <= ymin && xmax <= ymax;
}
};
const Bounds &
bounds() const
{
return b_;
}
// Method overrides.
void
begin() override
{
}
bool
end() override
{
return true;
}
void
addItem(PathSink::ItemType item_type, const double * coords) override
{
for (int nn = 0; nn < kCoordPairsPerItemType[item_type]; ++nn)
{
double x = coords[nn * 2];
double y = coords[nn * 2 + 1];
if (x < b_.xmin)
b_.xmin = x;
if (x > b_.xmax)
b_.xmax = x;
if (y < b_.ymin)
b_.ymin = y;
if (y > b_.ymax)
b_.ymax = y;
}
}
private:
Bounds b_;
};
#endif // SRC_GRAPHICS_LIB_COMPUTE_TESTS_COMMON_PATH_SINK_H_