ES-DE/external/lunasvg/source/canvas.cpp

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