563 lines
16 KiB
C++
563 lines
16 KiB
C++
#ifndef LUNASVG_GRAPHICS_H
|
|
#define LUNASVG_GRAPHICS_H
|
|
|
|
#include <plutovg.h>
|
|
|
|
#include <cstdint>
|
|
#include <algorithm>
|
|
#include <utility>
|
|
#include <memory>
|
|
#include <vector>
|
|
#include <array>
|
|
#include <string>
|
|
|
|
namespace lunasvg {
|
|
|
|
enum class LineCap : uint8_t {
|
|
Butt = PLUTOVG_LINE_CAP_BUTT,
|
|
Round = PLUTOVG_LINE_CAP_ROUND,
|
|
Square = PLUTOVG_LINE_CAP_SQUARE
|
|
};
|
|
|
|
enum class LineJoin : uint8_t {
|
|
Miter = PLUTOVG_LINE_JOIN_MITER,
|
|
Round = PLUTOVG_LINE_JOIN_ROUND,
|
|
Bevel = PLUTOVG_LINE_JOIN_BEVEL
|
|
};
|
|
|
|
enum class FillRule : uint8_t {
|
|
NonZero = PLUTOVG_FILL_RULE_NON_ZERO,
|
|
EvenOdd = PLUTOVG_FILL_RULE_EVEN_ODD
|
|
};
|
|
|
|
enum class SpreadMethod : uint8_t {
|
|
Pad = PLUTOVG_SPREAD_METHOD_PAD,
|
|
Reflect = PLUTOVG_SPREAD_METHOD_REFLECT,
|
|
Repeat = PLUTOVG_SPREAD_METHOD_REPEAT
|
|
};
|
|
|
|
class Color {
|
|
public:
|
|
constexpr Color() = default;
|
|
constexpr explicit Color(uint32_t value) : m_value(value) {}
|
|
constexpr Color(int r, int g, int b, int a = 255) : m_value(a << 24 | r << 16 | g << 8 | b) {}
|
|
|
|
constexpr uint8_t alpha() const { return (m_value >> 24) & 0xff; }
|
|
constexpr uint8_t red() const { return (m_value >> 16) & 0xff; }
|
|
constexpr uint8_t green() const { return (m_value >> 8) & 0xff; }
|
|
constexpr uint8_t blue() const { return (m_value >> 0) & 0xff; }
|
|
|
|
constexpr float alphaF() const { return alpha() / 255.f; }
|
|
constexpr float redF() const { return red() / 255.f; }
|
|
constexpr float greenF() const { return green() / 255.f; }
|
|
constexpr float blueF() const { return blue() / 255.f; }
|
|
|
|
constexpr uint32_t value() const { return m_value; }
|
|
|
|
constexpr bool isOpaque() const { return alpha() == 255; }
|
|
constexpr bool isVisible() const { return alpha() > 0; }
|
|
|
|
constexpr Color opaqueColor() const { return Color(m_value | 0xFF000000); }
|
|
constexpr Color colorWithAlpha(float opacity) const;
|
|
|
|
static const Color Transparent;
|
|
static const Color Black;
|
|
static const Color White;
|
|
|
|
private:
|
|
uint32_t m_value = 0;
|
|
};
|
|
|
|
constexpr Color Color::colorWithAlpha(float opacity) const
|
|
{
|
|
auto rgb = m_value & 0x00FFFFFF;
|
|
auto a = static_cast<int>(alpha() * std::clamp(opacity, 0.f, 1.f));
|
|
return Color(rgb | a << 24);
|
|
}
|
|
|
|
class Point {
|
|
public:
|
|
constexpr Point() = default;
|
|
constexpr Point(const plutovg_point_t& point) : Point(point.x, point.y) {}
|
|
constexpr Point(float x, float y) : x(x), y(y) {}
|
|
|
|
constexpr void move(float dx, float dy) { x += dx; y += dy; }
|
|
constexpr void move(float d) { move(d, d); }
|
|
constexpr void move(const Point& p) { move(p.x, p.y); }
|
|
|
|
constexpr void scale(float sx, float sy) { x *= sx; y *= sy; }
|
|
constexpr void scale(float s) { scale(s, s); }
|
|
|
|
constexpr float dot(const Point& p) const { return x * p.x + y * p.y; }
|
|
|
|
public:
|
|
float x{0};
|
|
float y{0};
|
|
};
|
|
|
|
constexpr Point operator+(const Point& a, const Point& b)
|
|
{
|
|
return Point(a.x + b.x, a.y + b.y);
|
|
}
|
|
|
|
constexpr Point operator-(const Point& a, const Point& b)
|
|
{
|
|
return Point(a.x - b.x, a.y - b.y);
|
|
}
|
|
|
|
constexpr Point operator-(const Point& a)
|
|
{
|
|
return Point(-a.x, -a.y);
|
|
}
|
|
|
|
constexpr Point& operator+=(Point& a, const Point& b)
|
|
{
|
|
a.move(b);
|
|
return a;
|
|
}
|
|
|
|
constexpr Point& operator-=(Point& a, const Point& b)
|
|
{
|
|
a.move(-b);
|
|
return a;
|
|
}
|
|
|
|
constexpr float operator*(const Point& a, const Point& b)
|
|
{
|
|
return a.dot(b);
|
|
}
|
|
|
|
class Size {
|
|
public:
|
|
constexpr Size() = default;
|
|
constexpr Size(float w, float h) : w(w), h(h) {}
|
|
|
|
constexpr void expand(float dw, float dh) { w += dw; h += dh; }
|
|
constexpr void expand(float d) { expand(d, d); }
|
|
constexpr void expand(const Size& s) { expand(s.w, s.h); }
|
|
|
|
constexpr void scale(float sw, float sh) { w *= sw; h *= sh; }
|
|
constexpr void scale(float s) { scale(s, s); }
|
|
|
|
constexpr bool isEmpty() const { return w <= 0.f || h <= 0.f; }
|
|
constexpr bool isZero() const { return w <= 0.f && h <= 0.f; }
|
|
constexpr bool isValid() const { return w >= 0.f && h >= 0.f; }
|
|
|
|
public:
|
|
float w{0};
|
|
float h{0};
|
|
};
|
|
|
|
constexpr Size operator+(const Size& a, const Size& b)
|
|
{
|
|
return Size(a.w + b.w, a.h + b.h);
|
|
}
|
|
|
|
constexpr Size operator-(const Size& a, const Size& b)
|
|
{
|
|
return Size(a.w - b.w, a.h - b.h);
|
|
}
|
|
|
|
constexpr Size operator-(const Size& a)
|
|
{
|
|
return Size(-a.w, -a.h);
|
|
}
|
|
|
|
constexpr Size& operator+=(Size& a, const Size& b)
|
|
{
|
|
a.expand(b);
|
|
return a;
|
|
}
|
|
|
|
constexpr Size& operator-=(Size& a, const Size& b)
|
|
{
|
|
a.expand(-b);
|
|
return a;
|
|
}
|
|
|
|
class Box;
|
|
|
|
class Rect {
|
|
public:
|
|
constexpr Rect() = default;
|
|
constexpr explicit Rect(const Size& size) : Rect(size.w, size.h) {}
|
|
constexpr Rect(float width, float height) : Rect(0, 0, width, height) {}
|
|
constexpr Rect(const Point& origin, const Size& size) : Rect(origin.x, origin.y, size.w, size.h) {}
|
|
constexpr Rect(const plutovg_rect_t& rect) : Rect(rect.x, rect.y, rect.w, rect.h) {}
|
|
constexpr Rect(float x, float y, float w, float h) : x(x), y(y), w(w), h(h) {}
|
|
|
|
Rect(const Box& box);
|
|
|
|
constexpr void move(float dx, float dy) { x += dx; y += dy; }
|
|
constexpr void move(float d) { move(d, d); }
|
|
constexpr void move(const Point& p) { move(p.x, p.y); }
|
|
|
|
constexpr void scale(float sx, float sy) { x *= sx; y *= sy; w *= sx; h *= sy; }
|
|
constexpr void scale(float s) { scale(s, s); }
|
|
|
|
constexpr void inflate(float dx, float dy) { x -= dx; y -= dy; w += dx * 2.f; h += dy * 2.f; }
|
|
constexpr void inflate(float d) { inflate(d, d); }
|
|
|
|
constexpr bool contains(float px, float py) const { return px >= x && px <= x + w && py >= y && py <= y + h; }
|
|
constexpr bool contains(const Point& p) const { return contains(p.x, p.y); }
|
|
|
|
constexpr Rect intersected(const Rect& rect) const;
|
|
constexpr Rect united(const Rect& rect) const;
|
|
|
|
constexpr Rect& intersect(const Rect& o);
|
|
constexpr Rect& unite(const Rect& o);
|
|
|
|
constexpr Point origin() const { return Point(x, y); }
|
|
constexpr Size size() const { return Size(w, h); }
|
|
|
|
constexpr float right() const { return x + w; }
|
|
constexpr float bottom() const { return y + h; }
|
|
|
|
constexpr bool isEmpty() const { return w <= 0.f || h <= 0.f; }
|
|
constexpr bool isZero() const { return w <= 0.f && h <= 0.f; }
|
|
constexpr bool isValid() const { return w >= 0.f && h >= 0.f; }
|
|
|
|
static const Rect Empty;
|
|
static const Rect Invalid;
|
|
static const Rect Infinite;
|
|
|
|
public:
|
|
float x{0};
|
|
float y{0};
|
|
float w{0};
|
|
float h{0};
|
|
};
|
|
|
|
constexpr Rect Rect::intersected(const Rect& rect) const
|
|
{
|
|
if(!rect.isValid())
|
|
return *this;
|
|
if(!isValid())
|
|
return rect;
|
|
auto l = std::max(x, rect.x);
|
|
auto t = std::max(y, rect.y);
|
|
auto r = std::min(x + w, rect.x + rect.w);
|
|
auto b = std::min(y + h, rect.y + rect.h);
|
|
if(l >= r || t >= b)
|
|
return Rect::Empty;
|
|
return Rect(l, t, r - l, b - t);
|
|
}
|
|
|
|
constexpr Rect Rect::united(const Rect& rect) const
|
|
{
|
|
if(!rect.isValid())
|
|
return *this;
|
|
if(!isValid())
|
|
return rect;
|
|
auto l = std::min(x, rect.x);
|
|
auto t = std::min(y, rect.y);
|
|
auto r = std::max(x + w, rect.x + rect.w);
|
|
auto b = std::max(y + h, rect.y + rect.h);
|
|
return Rect(l, t, r - l, b - t);
|
|
}
|
|
|
|
constexpr Rect& Rect::intersect(const Rect& o)
|
|
{
|
|
*this = intersected(o);
|
|
return *this;
|
|
}
|
|
|
|
constexpr Rect& Rect::unite(const Rect& o)
|
|
{
|
|
*this = united(o);
|
|
return *this;
|
|
}
|
|
|
|
class Matrix;
|
|
|
|
class Transform {
|
|
public:
|
|
Transform();
|
|
Transform(const Matrix& matrix);
|
|
Transform(float a, float b, float c, float d, float e, float f);
|
|
Transform(const plutovg_matrix_t& matrix) : m_matrix(matrix) {}
|
|
|
|
Transform operator*(const Transform& transform) const;
|
|
Transform& operator*=(const Transform& transform);
|
|
|
|
Transform& multiply(const Transform& transform);
|
|
Transform& translate(float tx, float ty);
|
|
Transform& scale(float sx, float sy);
|
|
Transform& rotate(float angle, float cx = 0.f, float cy = 0.f);
|
|
Transform& shear(float shx, float shy);
|
|
|
|
Transform& postMultiply(const Transform& transform);
|
|
Transform& postTranslate(float tx, float ty);
|
|
Transform& postScale(float sx, float sy);
|
|
Transform& postRotate(float angle, float cx = 0.f, float cy = 0.f);
|
|
Transform& postShear(float shx, float shy);
|
|
|
|
Transform inverse() const;
|
|
Transform& invert();
|
|
|
|
void reset();
|
|
|
|
Point mapPoint(float x, float y) const;
|
|
Point mapPoint(const Point& point) const;
|
|
Rect mapRect(const Rect& rect) const;
|
|
|
|
float xScale() const;
|
|
float yScale() const;
|
|
|
|
const plutovg_matrix_t& matrix() const { return m_matrix; }
|
|
plutovg_matrix_t& matrix() { return m_matrix; }
|
|
|
|
bool parse(const char* data, size_t length);
|
|
|
|
static Transform translated(float tx, float ty);
|
|
static Transform scaled(float sx, float sy);
|
|
static Transform rotated(float angle, float cx, float cy);
|
|
static Transform sheared(float shx, float shy);
|
|
|
|
static const Transform Identity;
|
|
|
|
private:
|
|
plutovg_matrix_t m_matrix;
|
|
};
|
|
|
|
enum class PathCommand {
|
|
MoveTo = PLUTOVG_PATH_COMMAND_MOVE_TO,
|
|
LineTo = PLUTOVG_PATH_COMMAND_LINE_TO,
|
|
CubicTo = PLUTOVG_PATH_COMMAND_CUBIC_TO,
|
|
Close = PLUTOVG_PATH_COMMAND_CLOSE
|
|
};
|
|
|
|
class Path {
|
|
public:
|
|
Path() = default;
|
|
Path(const Path& path);
|
|
Path(Path&& path);
|
|
~Path();
|
|
|
|
Path& operator=(const Path& path);
|
|
Path& operator=(Path&& path);
|
|
|
|
void swap(Path& path);
|
|
|
|
void moveTo(float x, float y);
|
|
void lineTo(float x, float y);
|
|
void quadTo(float x1, float y1, float x2, float y2);
|
|
void cubicTo(float x1, float y1, float x2, float y2, float x3, float y3);
|
|
void arcTo(float rx, float ry, float xAxisRotation, bool largeArcFlag, bool sweepFlag, float x, float y);
|
|
void close();
|
|
|
|
void addEllipse(float cx, float cy, float rx, float ry);
|
|
void addRoundRect(float x, float y, float w, float h, float rx, float ry);
|
|
void addRect(float x, float y, float w, float h);
|
|
|
|
void addEllipse(const Point& center, const Size& radii);
|
|
void addRoundRect(const Rect& rect, const Size& radii);
|
|
void addRect(const Rect& rect);
|
|
|
|
void reset();
|
|
|
|
Rect boundingRect() const;
|
|
bool isEmpty() const;
|
|
bool isUnique() const;
|
|
bool isNull() const { return m_data == nullptr; }
|
|
plutovg_path_t* data() const { return m_data; }
|
|
|
|
bool parse(const char* data, size_t length);
|
|
|
|
private:
|
|
plutovg_path_t* release();
|
|
plutovg_path_t* ensure();
|
|
plutovg_path_t* m_data = nullptr;
|
|
};
|
|
|
|
inline void Path::swap(Path& path)
|
|
{
|
|
std::swap(m_data, path.m_data);
|
|
}
|
|
|
|
inline plutovg_path_t* Path::release()
|
|
{
|
|
return std::exchange(m_data, nullptr);
|
|
}
|
|
|
|
class PathIterator {
|
|
public:
|
|
PathIterator(const Path& path);
|
|
|
|
PathCommand currentSegment(std::array<Point, 3>& points) const;
|
|
bool isDone() const { return m_index >= m_size; }
|
|
void next();
|
|
|
|
private:
|
|
const plutovg_path_element_t* m_elements;
|
|
const int m_size;
|
|
int m_index;
|
|
};
|
|
|
|
class FontFace {
|
|
public:
|
|
FontFace() = default;
|
|
explicit FontFace(plutovg_font_face_t* face);
|
|
FontFace(const void* data, size_t length, plutovg_destroy_func_t destroy_func, void* closure);
|
|
FontFace(const char* filename);
|
|
FontFace(const FontFace& face);
|
|
FontFace(FontFace&& face);
|
|
~FontFace();
|
|
|
|
FontFace& operator=(const FontFace& face);
|
|
FontFace& operator=(FontFace&& face);
|
|
|
|
void swap(FontFace& face);
|
|
|
|
bool isNull() const { return m_face == nullptr; }
|
|
plutovg_font_face_t* get() const { return m_face; }
|
|
|
|
private:
|
|
plutovg_font_face_t* release();
|
|
plutovg_font_face_t* m_face = nullptr;
|
|
};
|
|
|
|
class FontFaceCache {
|
|
public:
|
|
bool addFontFace(const std::string& family, bool bold, bool italic, const FontFace& face);
|
|
FontFace getFontFace(const std::string& family, bool bold, bool italic) const;
|
|
|
|
private:
|
|
FontFaceCache();
|
|
plutovg_font_face_cache_t* m_cache;
|
|
friend FontFaceCache* fontFaceCache();
|
|
};
|
|
|
|
FontFaceCache* fontFaceCache();
|
|
|
|
class Font {
|
|
public:
|
|
Font() = default;
|
|
Font(const FontFace& face, float size);
|
|
|
|
float ascent() const { return m_ascent; }
|
|
float descent() const { return m_descent; }
|
|
float height() const { return m_ascent - m_descent; }
|
|
float lineGap() const { return m_lineGap; }
|
|
float xHeight() const;
|
|
|
|
float measureText(const std::u32string_view& text) const;
|
|
|
|
const FontFace& face() const { return m_face; }
|
|
float size() const { return m_size; }
|
|
|
|
bool isNull() const { return m_size <= 0.f || m_face.isNull(); }
|
|
|
|
private:
|
|
FontFace m_face;
|
|
float m_size = 0.f;
|
|
float m_ascent = 0.f;
|
|
float m_descent = 0.f;
|
|
float m_lineGap = 0.f;
|
|
};
|
|
|
|
enum class TextureType {
|
|
Plain = PLUTOVG_TEXTURE_TYPE_PLAIN,
|
|
Tiled = PLUTOVG_TEXTURE_TYPE_TILED
|
|
};
|
|
|
|
enum class BlendMode {
|
|
Src = PLUTOVG_OPERATOR_SRC,
|
|
Src_Over = PLUTOVG_OPERATOR_SRC_OVER,
|
|
Dst_In = PLUTOVG_OPERATOR_DST_IN,
|
|
Dst_Out = PLUTOVG_OPERATOR_DST_OUT
|
|
};
|
|
|
|
using DashArray = std::vector<float>;
|
|
|
|
class StrokeData {
|
|
public:
|
|
explicit StrokeData(float lineWidth = 1.f) : m_lineWidth(lineWidth) {}
|
|
|
|
void setLineWidth(float lineWidth) { m_lineWidth = lineWidth; }
|
|
float lineWidth() const { return m_lineWidth; }
|
|
|
|
void setMiterLimit(float miterLimit) { m_miterLimit = miterLimit; }
|
|
float miterLimit() const { return m_miterLimit; }
|
|
|
|
void setDashOffset(float dashOffset) { m_dashOffset = dashOffset; }
|
|
float dashOffset() const { return m_dashOffset; }
|
|
|
|
void setDashArray(DashArray dashArray) { m_dashArray = std::move(dashArray); }
|
|
const DashArray& dashArray() const { return m_dashArray; }
|
|
|
|
void setLineCap(LineCap lineCap) { m_lineCap = lineCap; }
|
|
LineCap lineCap() const { return m_lineCap; }
|
|
|
|
void setLineJoin(LineJoin lineJoin) { m_lineJoin = lineJoin; }
|
|
LineJoin lineJoin() const { return m_lineJoin; }
|
|
|
|
private:
|
|
float m_lineWidth;
|
|
float m_miterLimit{4.f};
|
|
float m_dashOffset{0.f};
|
|
LineCap m_lineCap{LineCap::Butt};
|
|
LineJoin m_lineJoin{LineJoin::Miter};
|
|
DashArray m_dashArray;
|
|
};
|
|
|
|
using GradientStop = plutovg_gradient_stop_t;
|
|
using GradientStops = std::vector<GradientStop>;
|
|
|
|
class Bitmap;
|
|
|
|
class Canvas {
|
|
public:
|
|
static std::shared_ptr<Canvas> create(const Bitmap& bitmap);
|
|
static std::shared_ptr<Canvas> create(float x, float y, float width, float height);
|
|
static std::shared_ptr<Canvas> create(const Rect& extents);
|
|
|
|
void setColor(const Color& color);
|
|
void setColor(float r, float g, float b, float a);
|
|
void setLinearGradient(float x1, float y1, float x2, float y2, SpreadMethod spread, const GradientStops& stops, const Transform& transform);
|
|
void setRadialGradient(float cx, float cy, float r, float fx, float fy, SpreadMethod spread, const GradientStops& stops, const Transform& transform);
|
|
void setTexture(const Canvas& source, TextureType type, float opacity, const Transform& transform);
|
|
|
|
void fillPath(const Path& path, FillRule fillRule, const Transform& transform);
|
|
void strokePath(const Path& path, const StrokeData& strokeData, const Transform& transform);
|
|
|
|
void fillText(const std::u32string_view& text, const Font& font, const Point& origin, const Transform& transform);
|
|
void strokeText(const std::u32string_view& text, float strokeWidth, const Font& font, const Point& origin, const Transform& transform);
|
|
|
|
void clipPath(const Path& path, FillRule clipRule, const Transform& transform);
|
|
void clipRect(const Rect& rect, FillRule clipRule, const Transform& transform);
|
|
|
|
void drawImage(const Bitmap& image, const Rect& dstRect, const Rect& srcRect, const Transform& transform);
|
|
void blendCanvas(const Canvas& canvas, BlendMode blendMode, float opacity);
|
|
|
|
void save();
|
|
void restore();
|
|
|
|
void convertToLuminanceMask();
|
|
|
|
int x() const { return m_x; }
|
|
int y() const { return m_y; }
|
|
int width() const;
|
|
int height() const;
|
|
|
|
Rect extents() const { return Rect(m_x, m_y, width(), height()); }
|
|
|
|
plutovg_surface_t* surface() const { return m_surface; }
|
|
plutovg_canvas_t* canvas() const { return m_canvas; }
|
|
|
|
~Canvas();
|
|
|
|
private:
|
|
Canvas(const Bitmap& bitmap);
|
|
Canvas(int x, int y, int width, int height);
|
|
plutovg_surface_t* m_surface;
|
|
plutovg_canvas_t* m_canvas;
|
|
plutovg_matrix_t m_translation;
|
|
const int m_x;
|
|
const int m_y;
|
|
};
|
|
|
|
} // namespace lunasvg
|
|
|
|
#endif // LUNASVG_GRAPHICS_H
|