mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-24 23:25:38 +00:00
262 lines
9.2 KiB
C++
262 lines
9.2 KiB
C++
#include "canvas.h"
|
|
|
|
#include <cmath>
|
|
|
|
namespace lunasvg {
|
|
|
|
static plutovg_matrix_t to_plutovg_matrix(const Transform& transform);
|
|
static plutovg_fill_rule_t to_plutovg_fill_rule(WindRule winding);
|
|
static plutovg_operator_t to_plutovg_operator(BlendMode mode);
|
|
static plutovg_line_cap_t to_plutovg_line_cap(LineCap cap);
|
|
static plutovg_line_join_t to_plutovg_line_join(LineJoin join);
|
|
static plutovg_spread_method_t to_plutovg_spread_method(SpreadMethod spread);
|
|
static plutovg_texture_type_t to_plutovg_texture_type(TextureType type);
|
|
static void to_plutovg_stops(plutovg_gradient_t* gradient, const GradientStops& stops);
|
|
static void to_plutovg_path(plutovg_t* pluto, const Path& path);
|
|
|
|
std::shared_ptr<Canvas> Canvas::create(unsigned char* data, unsigned int width, unsigned int height, unsigned int stride)
|
|
{
|
|
return std::shared_ptr<Canvas>(new Canvas(data, static_cast<int>(width), static_cast<int>(height), static_cast<int>(stride)));
|
|
}
|
|
|
|
std::shared_ptr<Canvas> Canvas::create(double x, double y, double width, double height)
|
|
{
|
|
if(width <= 0.0 || height <= 0.0)
|
|
return std::shared_ptr<Canvas>(new Canvas(0, 0, 1, 1));
|
|
|
|
auto l = static_cast<int>(floor(x));
|
|
auto t = static_cast<int>(floor(y));
|
|
auto r = static_cast<int>(ceil(x + width));
|
|
auto b = static_cast<int>(ceil(y + height));
|
|
return std::shared_ptr<Canvas>(new Canvas(l, t, r - l, b - t));
|
|
}
|
|
|
|
std::shared_ptr<Canvas> Canvas::create(const Rect& box)
|
|
{
|
|
return create(box.x, box.y, box.w, box.h);
|
|
}
|
|
|
|
Canvas::Canvas(unsigned char* data, int width, int height, int stride)
|
|
{
|
|
surface = plutovg_surface_create_for_data(data, width, height, stride);
|
|
pluto = plutovg_create(surface);
|
|
plutovg_matrix_init_identity(&translation);
|
|
plutovg_rect_init(&rect, 0, 0, width, height);
|
|
}
|
|
|
|
Canvas::Canvas(int x, int y, int width, int height)
|
|
{
|
|
surface = plutovg_surface_create(width, height);
|
|
pluto = plutovg_create(surface);
|
|
plutovg_matrix_init_translate(&translation, -x, -y);
|
|
plutovg_rect_init(&rect, x, y, width, height);
|
|
}
|
|
|
|
Canvas::~Canvas()
|
|
{
|
|
plutovg_surface_destroy(surface);
|
|
plutovg_destroy(pluto);
|
|
}
|
|
|
|
void Canvas::setColor(const Color& color)
|
|
{
|
|
plutovg_set_rgba(pluto, color.red() / 255.0, color.green() / 255.0, color.blue() / 255.0, color.alpha() / 255.0);
|
|
}
|
|
|
|
void Canvas::setLinearGradient(double x1, double y1, double x2, double y2, const GradientStops& stops, SpreadMethod spread, const Transform& transform)
|
|
{
|
|
auto gradient = plutovg_set_linear_gradient(pluto, x1, y1, x2, y2);
|
|
auto matrix = to_plutovg_matrix(transform);
|
|
to_plutovg_stops(gradient, stops);
|
|
plutovg_gradient_set_spread(gradient, to_plutovg_spread_method(spread));
|
|
plutovg_gradient_set_matrix(gradient, &matrix);
|
|
}
|
|
|
|
void Canvas::setRadialGradient(double cx, double cy, double r, double fx, double fy, const GradientStops& stops, SpreadMethod spread, const Transform& transform)
|
|
{
|
|
auto gradient = plutovg_set_radial_gradient(pluto, cx, cy, r, fx, fy, 0);
|
|
auto matrix = to_plutovg_matrix(transform);
|
|
to_plutovg_stops(gradient, stops);
|
|
plutovg_gradient_set_spread(gradient, to_plutovg_spread_method(spread));
|
|
plutovg_gradient_set_matrix(gradient, &matrix);
|
|
}
|
|
|
|
void Canvas::setTexture(const Canvas* source, TextureType type, const Transform& transform)
|
|
{
|
|
auto texture = plutovg_set_texture(pluto, source->surface, to_plutovg_texture_type(type));
|
|
auto matrix = to_plutovg_matrix(transform);
|
|
plutovg_texture_set_matrix(texture, &matrix);
|
|
}
|
|
|
|
void Canvas::fill(const Path& path, const Transform& transform, WindRule winding, BlendMode mode, double opacity)
|
|
{
|
|
auto matrix = to_plutovg_matrix(transform);
|
|
plutovg_matrix_multiply(&matrix, &matrix, &translation);
|
|
to_plutovg_path(pluto, path);
|
|
plutovg_set_matrix(pluto, &matrix);
|
|
plutovg_set_fill_rule(pluto, to_plutovg_fill_rule(winding));
|
|
plutovg_set_opacity(pluto, opacity);
|
|
plutovg_set_operator(pluto, to_plutovg_operator(mode));
|
|
plutovg_fill(pluto);
|
|
}
|
|
|
|
void Canvas::stroke(const Path& path, const Transform& transform, double width, LineCap cap, LineJoin join, double miterlimit, const DashData& dash, BlendMode mode, double opacity)
|
|
{
|
|
auto matrix = to_plutovg_matrix(transform);
|
|
plutovg_matrix_multiply(&matrix, &matrix, &translation);
|
|
to_plutovg_path(pluto, path);
|
|
plutovg_set_matrix(pluto, &matrix);
|
|
plutovg_set_line_width(pluto, width);
|
|
plutovg_set_line_cap(pluto, to_plutovg_line_cap(cap));
|
|
plutovg_set_line_join(pluto, to_plutovg_line_join(join));
|
|
plutovg_set_miter_limit(pluto, miterlimit);
|
|
plutovg_set_dash(pluto, dash.offset, dash.array.data(), static_cast<int>(dash.array.size()));
|
|
plutovg_set_operator(pluto, to_plutovg_operator(mode));
|
|
plutovg_set_opacity(pluto, opacity);
|
|
plutovg_stroke(pluto);
|
|
}
|
|
|
|
void Canvas::blend(const Canvas* source, BlendMode mode, double opacity)
|
|
{
|
|
plutovg_set_texture_surface(pluto, source->surface, source->rect.x, source->rect.y);
|
|
plutovg_set_operator(pluto, to_plutovg_operator(mode));
|
|
plutovg_set_opacity(pluto, opacity);
|
|
plutovg_set_matrix(pluto, &translation);
|
|
plutovg_paint(pluto);
|
|
}
|
|
|
|
void Canvas::mask(const Rect& clip, const Transform& transform)
|
|
{
|
|
auto matrix = to_plutovg_matrix(transform);
|
|
auto path = plutovg_path_create();
|
|
plutovg_path_add_rect(path, clip.x, clip.y, clip.w, clip.h);
|
|
plutovg_path_transform(path, &matrix);
|
|
plutovg_rect(pluto, rect.x, rect.y, rect.w, rect.h);
|
|
plutovg_add_path(pluto, path);
|
|
plutovg_path_destroy(path);
|
|
|
|
plutovg_set_rgba(pluto, 0, 0, 0, 0);
|
|
plutovg_set_fill_rule(pluto, plutovg_fill_rule_even_odd);
|
|
plutovg_set_operator(pluto, plutovg_operator_src);
|
|
plutovg_set_opacity(pluto, 0.0);
|
|
plutovg_set_matrix(pluto, &translation);
|
|
plutovg_fill(pluto);
|
|
}
|
|
|
|
void Canvas::luminance()
|
|
{
|
|
auto width = plutovg_surface_get_width(surface);
|
|
auto height = plutovg_surface_get_height(surface);
|
|
auto stride = plutovg_surface_get_stride(surface);
|
|
auto data = plutovg_surface_get_data(surface);
|
|
for(int y = 0; y < height; y++) {
|
|
auto pixels = reinterpret_cast<uint32_t*>(data + stride * y);
|
|
for(int x = 0; x < width; x++) {
|
|
auto pixel = pixels[x];
|
|
auto r = (pixel >> 16) & 0xFF;
|
|
auto g = (pixel >> 8) & 0xFF;
|
|
auto b = (pixel >> 0) & 0xFF;
|
|
auto l = (2*r + 3*g + b) / 6;
|
|
|
|
pixels[x] = l << 24;
|
|
}
|
|
}
|
|
}
|
|
|
|
unsigned int Canvas::width() const
|
|
{
|
|
return plutovg_surface_get_width(surface);
|
|
}
|
|
|
|
unsigned int Canvas::height() const
|
|
{
|
|
return plutovg_surface_get_height(surface);
|
|
}
|
|
|
|
unsigned int Canvas::stride() const
|
|
{
|
|
return plutovg_surface_get_stride(surface);
|
|
}
|
|
|
|
unsigned char* Canvas::data() const
|
|
{
|
|
return plutovg_surface_get_data(surface);
|
|
}
|
|
|
|
Rect Canvas::box() const
|
|
{
|
|
return Rect(rect.x, rect.y, rect.w, rect.h);
|
|
}
|
|
|
|
plutovg_matrix_t to_plutovg_matrix(const Transform& transform)
|
|
{
|
|
plutovg_matrix_t matrix;
|
|
plutovg_matrix_init(&matrix, transform.m00, transform.m10, transform.m01, transform.m11, transform.m02, transform.m12);
|
|
return matrix;
|
|
}
|
|
|
|
plutovg_fill_rule_t to_plutovg_fill_rule(WindRule winding)
|
|
{
|
|
return winding == WindRule::EvenOdd ? plutovg_fill_rule_even_odd : plutovg_fill_rule_non_zero;
|
|
}
|
|
|
|
plutovg_operator_t to_plutovg_operator(BlendMode mode)
|
|
{
|
|
return mode == BlendMode::Src ? plutovg_operator_src : mode == BlendMode::Src_Over ? plutovg_operator_src_over : mode == BlendMode::Dst_In ? plutovg_operator_dst_in : plutovg_operator_dst_out;
|
|
}
|
|
|
|
plutovg_line_cap_t to_plutovg_line_cap(LineCap cap)
|
|
{
|
|
return cap == LineCap::Butt ? plutovg_line_cap_butt : cap == LineCap::Round ? plutovg_line_cap_round : plutovg_line_cap_square;
|
|
}
|
|
|
|
plutovg_line_join_t to_plutovg_line_join(LineJoin join)
|
|
{
|
|
return join == LineJoin::Miter ? plutovg_line_join_miter : join == LineJoin::Round ? plutovg_line_join_round : plutovg_line_join_bevel;
|
|
}
|
|
|
|
static plutovg_spread_method_t to_plutovg_spread_method(SpreadMethod spread)
|
|
{
|
|
return spread == SpreadMethod::Pad ? plutovg_spread_method_pad : spread == SpreadMethod::Reflect ? plutovg_spread_method_reflect : plutovg_spread_method_repeat;
|
|
}
|
|
|
|
static plutovg_texture_type_t to_plutovg_texture_type(TextureType type)
|
|
{
|
|
return type == TextureType::Plain ? plutovg_texture_type_plain : plutovg_texture_type_tiled;
|
|
}
|
|
|
|
static void to_plutovg_stops(plutovg_gradient_t* gradient, const GradientStops& stops)
|
|
{
|
|
for(const auto& stop : stops) {
|
|
auto offset = std::get<0>(stop);
|
|
auto& color = std::get<1>(stop);
|
|
plutovg_gradient_add_stop_rgba(gradient, offset, color.red() / 255.0, color.green() / 255.0, color.blue() / 255.0, color.alpha() / 255.0);
|
|
}
|
|
}
|
|
|
|
void to_plutovg_path(plutovg_t* pluto, const Path& path)
|
|
{
|
|
PathIterator it(path);
|
|
std::array<Point, 3> p;
|
|
while(!it.isDone()) {
|
|
switch(it.currentSegment(p)) {
|
|
case PathCommand::MoveTo:
|
|
plutovg_move_to(pluto, p[0].x, p[0].y);
|
|
break;
|
|
case PathCommand::LineTo:
|
|
plutovg_line_to(pluto, p[0].x, p[0].y);
|
|
break;
|
|
case PathCommand::CubicTo:
|
|
plutovg_cubic_to(pluto, p[0].x, p[0].y, p[1].x, p[1].y, p[2].x, p[2].y);
|
|
break;
|
|
case PathCommand::Close:
|
|
plutovg_close_path(pluto);
|
|
break;
|
|
}
|
|
|
|
it.next();
|
|
}
|
|
}
|
|
|
|
} // namespace lunasvg
|