#include "graphics.h" #include "lunasvg.h" #include #include namespace lunasvg { const Color Color::Black(0xFF000000); const Color Color::White(0xFFFFFFFF); const Color Color::Transparent(0x00000000); const Rect Rect::Empty(0, 0, 0, 0); const Rect Rect::Invalid(0, 0, -1, -1); const Rect Rect::Infinite(-FLT_MAX / 2.f, -FLT_MAX / 2.f, FLT_MAX, FLT_MAX); Rect::Rect(const Box& box) : x(box.x), y(box.y), w(box.w), h(box.h) { } const Transform Transform::Identity(1, 0, 0, 1, 0, 0); Transform::Transform() { plutovg_matrix_init_identity(&m_matrix); } Transform::Transform(float a, float b, float c, float d, float e, float f) { plutovg_matrix_init(&m_matrix, a, b, c, d, e, f); } Transform::Transform(const Matrix& matrix) : Transform(matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f) { } Transform Transform::operator*(const Transform& transform) const { plutovg_matrix_t result; plutovg_matrix_multiply(&result, &transform.m_matrix, &m_matrix); return result; } Transform& Transform::operator*=(const Transform& transform) { return (*this = *this * transform); } Transform& Transform::multiply(const Transform& transform) { return (*this *= transform); } Transform& Transform::translate(float tx, float ty) { return multiply(translated(tx, ty)); } Transform& Transform::scale(float sx, float sy) { return multiply(scaled(sx, sy)); } Transform& Transform::rotate(float angle, float cx, float cy) { return multiply(rotated(angle, cx, cy)); } Transform& Transform::shear(float shx, float shy) { return multiply(sheared(shx, shy)); } Transform& Transform::postMultiply(const Transform& transform) { return (*this = transform * *this); } Transform& Transform::postTranslate(float tx, float ty) { return postMultiply(translated(tx, ty)); } Transform& Transform::postScale(float sx, float sy) { return postMultiply(scaled(sx, sy)); } Transform& Transform::postRotate(float angle, float cx, float cy) { return postMultiply(rotated(angle, cx, cy)); } Transform& Transform::postShear(float shx, float shy) { return postMultiply(sheared(shx, shy)); } Transform Transform::inverse() const { plutovg_matrix_t inverse; plutovg_matrix_invert(&m_matrix, &inverse); return inverse; } Transform& Transform::invert() { plutovg_matrix_invert(&m_matrix, &m_matrix); return *this; } void Transform::reset() { plutovg_matrix_init_identity(&m_matrix); } Point Transform::mapPoint(float x, float y) const { plutovg_matrix_map(&m_matrix, x, y, &x, &y); return Point(x, y); } Point Transform::mapPoint(const Point& point) const { return mapPoint(point.x, point.y); } Rect Transform::mapRect(const Rect& rect) const { if(!rect.isValid()) { return Rect::Invalid; } plutovg_rect_t result = {rect.x, rect.y, rect.w, rect.h}; plutovg_matrix_map_rect(&m_matrix, &result, &result); return result; } float Transform::xScale() const { return std::sqrt(m_matrix.a * m_matrix.a + m_matrix.b * m_matrix.b); } float Transform::yScale() const { return std::sqrt(m_matrix.c * m_matrix.c + m_matrix.d * m_matrix.d); } bool Transform::parse(const char* data, size_t length) { return plutovg_matrix_parse(&m_matrix, data, length); } Transform Transform::rotated(float angle, float cx, float cy) { plutovg_matrix_t matrix; if(cx == 0.f && cy == 0.f) { plutovg_matrix_init_rotate(&matrix, PLUTOVG_DEG2RAD(angle)); } else { plutovg_matrix_init_translate(&matrix, cx, cy); plutovg_matrix_rotate(&matrix, PLUTOVG_DEG2RAD(angle)); plutovg_matrix_translate(&matrix, -cx, -cy); } return matrix; } Transform Transform::scaled(float sx, float sy) { plutovg_matrix_t matrix; plutovg_matrix_init_scale(&matrix, sx, sy); return matrix; } Transform Transform::sheared(float shx, float shy) { plutovg_matrix_t matrix; plutovg_matrix_init_shear(&matrix, PLUTOVG_DEG2RAD(shx), PLUTOVG_DEG2RAD(shy)); return matrix; } Transform Transform::translated(float tx, float ty) { plutovg_matrix_t matrix; plutovg_matrix_init_translate(&matrix, tx, ty); return matrix; } Path::Path(const Path& path) : m_data(plutovg_path_reference(path.data())) { } Path::Path(Path&& path) : m_data(path.release()) { } Path::~Path() { plutovg_path_destroy(m_data); } Path& Path::operator=(const Path& path) { Path(path).swap(*this); return *this; } Path& Path::operator=(Path&& path) { Path(std::move(path)).swap(*this); return *this; } void Path::moveTo(float x, float y) { plutovg_path_move_to(ensure(), x, y); } void Path::lineTo(float x, float y) { plutovg_path_line_to(ensure(), x, y); } void Path::quadTo(float x1, float y1, float x2, float y2) { plutovg_path_quad_to(ensure(), x1, y1, x2, y2); } void Path::cubicTo(float x1, float y1, float x2, float y2, float x3, float y3) { plutovg_path_cubic_to(ensure(), x1, y1, x2, y2, x3, y3); } void Path::arcTo(float rx, float ry, float xAxisRotation, bool largeArcFlag, bool sweepFlag, float x, float y) { plutovg_path_arc_to(ensure(), rx, ry, PLUTOVG_DEG2RAD(xAxisRotation), largeArcFlag, sweepFlag, x, y); } void Path::close() { plutovg_path_close(ensure()); } void Path::addEllipse(float cx, float cy, float rx, float ry) { plutovg_path_add_ellipse(ensure(), cx, cy, rx, ry); } void Path::addRoundRect(float x, float y, float w, float h, float rx, float ry) { plutovg_path_add_round_rect(ensure(), x, y, w, h, rx, ry); } void Path::addRect(float x, float y, float w, float h) { plutovg_path_add_rect(ensure(), x, y, w, h); } void Path::addEllipse(const Point& center, const Size& radii) { addEllipse(center.x, center.y, radii.w, radii.h); } void Path::addRoundRect(const Rect& rect, const Size& radii) { addRoundRect(rect.x, rect.y, rect.w, rect.h, radii.w, radii.h); } void Path::addRect(const Rect& rect) { addRect(rect.x, rect.y, rect.w, rect.h); } void Path::reset() { if(m_data == nullptr) return; if(isUnique()) { plutovg_path_reset(m_data); } else { plutovg_path_destroy(m_data); m_data = nullptr; } } Rect Path::boundingRect() const { if(m_data == nullptr) return Rect::Empty; plutovg_rect_t extents; plutovg_path_extents(m_data, &extents, false); return extents; } bool Path::isEmpty() const { if(m_data) return plutovg_path_get_elements(m_data, nullptr) == 0; return true; } bool Path::isUnique() const { return plutovg_path_get_reference_count(m_data) == 1; } bool Path::parse(const char* data, size_t length) { plutovg_path_reset(ensure()); return plutovg_path_parse(m_data, data, length); } plutovg_path_t* Path::ensure() { if(isNull()) { m_data = plutovg_path_create(); } else if(!isUnique()) { plutovg_path_destroy(m_data); m_data = plutovg_path_clone(m_data); } return m_data; } PathIterator::PathIterator(const Path& path) : m_size(plutovg_path_get_elements(path.data(), &m_elements)) , m_index(0) { } PathCommand PathIterator::currentSegment(std::array& points) const { auto command = m_elements[m_index].header.command; switch(command) { case PLUTOVG_PATH_COMMAND_MOVE_TO: points[0] = m_elements[m_index + 1].point; break; case PLUTOVG_PATH_COMMAND_LINE_TO: points[0] = m_elements[m_index + 1].point; break; case PLUTOVG_PATH_COMMAND_CUBIC_TO: points[0] = m_elements[m_index + 1].point; points[1] = m_elements[m_index + 2].point; points[2] = m_elements[m_index + 3].point; break; case PLUTOVG_PATH_COMMAND_CLOSE: points[0] = m_elements[m_index + 1].point; break; } return PathCommand(command); } void PathIterator::next() { m_index += m_elements[m_index].header.length; } FontFace::FontFace(plutovg_font_face_t* face) : m_face(plutovg_font_face_reference(face)) { } FontFace::FontFace(const void* data, size_t length, plutovg_destroy_func_t destroy_func, void* closure) : m_face(plutovg_font_face_load_from_data(data, length, 0, destroy_func, closure)) { } FontFace::FontFace(const char* filename) : m_face(plutovg_font_face_load_from_file(filename, 0)) { } FontFace::FontFace(const FontFace& face) : m_face(plutovg_font_face_reference(face.get())) { } FontFace::FontFace(FontFace&& face) : m_face(face.release()) { } FontFace::~FontFace() { plutovg_font_face_destroy(m_face); } FontFace& FontFace::operator=(const FontFace& face) { FontFace(face).swap(*this); return *this; } FontFace& FontFace::operator=(FontFace&& face) { FontFace(std::move(face)).swap(*this); return *this; } void FontFace::swap(FontFace& face) { std::swap(m_face, face.m_face); } plutovg_font_face_t* FontFace::release() { return std::exchange(m_face, nullptr); } bool FontFaceCache::addFontFace(const std::string& family, bool bold, bool italic, const FontFace& face) { if(!face.isNull()) plutovg_font_face_cache_add(m_cache, family.data(), bold, italic, face.get()); return !face.isNull(); } FontFace FontFaceCache::getFontFace(const std::string& family, bool bold, bool italic) const { if(auto face = plutovg_font_face_cache_get(m_cache, family.data(), bold, italic)) { return FontFace(face); } static const struct { const char* generic; const char* fallback; } generic_fallbacks[] = { #if defined(__linux__) {"sans-serif", "DejaVu Sans"}, {"serif", "DejaVu Serif"}, {"monospace", "DejaVu Sans Mono"}, #else {"sans-serif", "Arial"}, {"serif", "Times New Roman"}, {"monospace", "Courier New"}, #endif {"cursive", "Comic Sans MS"}, {"fantasy", "Impact"} }; for(auto value : generic_fallbacks) { if(value.generic == family || family.empty()) { return FontFace(plutovg_font_face_cache_get(m_cache, value.fallback, bold, italic)); } } return FontFace(); } FontFaceCache::FontFaceCache() : m_cache(plutovg_font_face_cache_create()) { #ifndef LUNASVG_DISABLE_LOAD_SYSTEM_FONTS plutovg_font_face_cache_load_sys(m_cache); #endif } FontFaceCache* fontFaceCache() { static FontFaceCache cache; return &cache; } Font::Font(const FontFace& face, float size) : m_face(face), m_size(size) { if(m_size > 0.f && !m_face.isNull()) { plutovg_font_face_get_metrics(m_face.get(), m_size, &m_ascent, &m_descent, &m_lineGap, nullptr); } } float Font::xHeight() const { plutovg_rect_t extents = {0}; if(m_size > 0.f && !m_face.isNull()) plutovg_font_face_get_glyph_metrics(m_face.get(), m_size, 'x', nullptr, nullptr, &extents); return extents.h; } float Font::measureText(const std::u32string_view& text) const { if(m_size > 0.f && !m_face.isNull()) return plutovg_font_face_text_extents(m_face.get(), m_size, text.data(), text.length(), PLUTOVG_TEXT_ENCODING_UTF32, nullptr); return 0; } std::shared_ptr Canvas::create(const Bitmap& bitmap) { return std::shared_ptr(new Canvas(bitmap)); } std::shared_ptr Canvas::create(float x, float y, float width, float height) { constexpr int kMaxSize = 1 << 15; if(width <= 0 || height <= 0 || width >= kMaxSize || height >= kMaxSize) return std::shared_ptr(new Canvas(0, 0, 1, 1)); auto l = static_cast(std::floor(x)); auto t = static_cast(std::floor(y)); auto r = static_cast(std::ceil(x + width)); auto b = static_cast(std::ceil(y + height)); return std::shared_ptr(new Canvas(l, t, r - l, b - t)); } std::shared_ptr Canvas::create(const Rect& extents) { return create(extents.x, extents.y, extents.w, extents.h); } void Canvas::setColor(const Color& color) { setColor(color.redF(), color.greenF(), color.blueF(), color.alphaF()); } void Canvas::setColor(float r, float g, float b, float a) { plutovg_canvas_set_rgba(m_canvas, r, g, b, a); } void Canvas::setLinearGradient(float x1, float y1, float x2, float y2, SpreadMethod spread, const GradientStops& stops, const Transform& transform) { plutovg_canvas_set_linear_gradient(m_canvas, x1, y1, x2, y2, static_cast(spread), stops.data(), stops.size(), &transform.matrix()); } void Canvas::setRadialGradient(float cx, float cy, float r, float fx, float fy, SpreadMethod spread, const GradientStops& stops, const Transform& transform) { plutovg_canvas_set_radial_gradient(m_canvas, cx, cy, r, fx, fy, 0.f, static_cast(spread), stops.data(), stops.size(), &transform.matrix()); } void Canvas::setTexture(const Canvas& source, TextureType type, float opacity, const Transform& transform) { plutovg_canvas_set_texture(m_canvas, source.surface(), static_cast(type), opacity, &transform.matrix()); } void Canvas::fillPath(const Path& path, FillRule fillRule, const Transform& transform) { plutovg_canvas_set_matrix(m_canvas, &m_translation); plutovg_canvas_transform(m_canvas, &transform.matrix()); plutovg_canvas_set_fill_rule(m_canvas, static_cast(fillRule)); plutovg_canvas_set_operator(m_canvas, PLUTOVG_OPERATOR_SRC_OVER); plutovg_canvas_fill_path(m_canvas, path.data()); } void Canvas::strokePath(const Path& path, const StrokeData& strokeData, const Transform& transform) { plutovg_canvas_set_matrix(m_canvas, &m_translation); plutovg_canvas_transform(m_canvas, &transform.matrix()); plutovg_canvas_set_line_width(m_canvas, strokeData.lineWidth()); plutovg_canvas_set_miter_limit(m_canvas, strokeData.miterLimit()); plutovg_canvas_set_line_cap(m_canvas, static_cast(strokeData.lineCap())); plutovg_canvas_set_line_join(m_canvas, static_cast(strokeData.lineJoin())); plutovg_canvas_set_dash_offset(m_canvas, strokeData.dashOffset()); plutovg_canvas_set_dash_array(m_canvas, strokeData.dashArray().data(), strokeData.dashArray().size()); plutovg_canvas_set_operator(m_canvas, PLUTOVG_OPERATOR_SRC_OVER); plutovg_canvas_stroke_path(m_canvas, path.data()); } void Canvas::fillText(const std::u32string_view& text, const Font& font, const Point& origin, const Transform& transform) { plutovg_canvas_set_matrix(m_canvas, &m_translation); plutovg_canvas_transform(m_canvas, &transform.matrix()); plutovg_canvas_set_fill_rule(m_canvas, PLUTOVG_FILL_RULE_NON_ZERO); plutovg_canvas_set_operator(m_canvas, PLUTOVG_OPERATOR_SRC_OVER); plutovg_canvas_set_font(m_canvas, font.face().get(), font.size()); plutovg_canvas_fill_text(m_canvas, text.data(), text.length(), PLUTOVG_TEXT_ENCODING_UTF32, origin.x, origin.y); } void Canvas::strokeText(const std::u32string_view& text, float strokeWidth, const Font& font, const Point& origin, const Transform& transform) { plutovg_canvas_set_matrix(m_canvas, &m_translation); plutovg_canvas_transform(m_canvas, &transform.matrix()); plutovg_canvas_set_line_width(m_canvas, strokeWidth); plutovg_canvas_set_miter_limit(m_canvas, 4.f); plutovg_canvas_set_line_cap(m_canvas, PLUTOVG_LINE_CAP_BUTT); plutovg_canvas_set_line_join(m_canvas, PLUTOVG_LINE_JOIN_MITER); plutovg_canvas_set_dash_offset(m_canvas, 0.f); plutovg_canvas_set_dash_array(m_canvas, nullptr, 0); plutovg_canvas_set_operator(m_canvas, PLUTOVG_OPERATOR_SRC_OVER); plutovg_canvas_set_font(m_canvas, font.face().get(), font.size()); plutovg_canvas_stroke_text(m_canvas, text.data(), text.length(), PLUTOVG_TEXT_ENCODING_UTF32, origin.x, origin.y); } void Canvas::clipPath(const Path& path, FillRule clipRule, const Transform& transform) { plutovg_canvas_set_matrix(m_canvas, &m_translation); plutovg_canvas_transform(m_canvas, &transform.matrix()); plutovg_canvas_set_fill_rule(m_canvas, static_cast(clipRule)); plutovg_canvas_clip_path(m_canvas, path.data()); } void Canvas::clipRect(const Rect& rect, FillRule clipRule, const Transform& transform) { plutovg_canvas_set_matrix(m_canvas, &m_translation); plutovg_canvas_transform(m_canvas, &transform.matrix()); plutovg_canvas_set_fill_rule(m_canvas, static_cast(clipRule)); plutovg_canvas_clip_rect(m_canvas, rect.x, rect.y, rect.w, rect.h); } void Canvas::drawImage(const Bitmap& image, const Rect& dstRect, const Rect& srcRect, const Transform& transform) { auto xScale = dstRect.w / srcRect.w; auto yScale = dstRect.h / srcRect.h; plutovg_matrix_t matrix = { xScale, 0, 0, yScale, -srcRect.x * xScale, -srcRect.y * yScale }; plutovg_canvas_set_matrix(m_canvas, &m_translation); plutovg_canvas_transform(m_canvas, &transform.matrix()); plutovg_canvas_translate(m_canvas, dstRect.x, dstRect.y); plutovg_canvas_set_fill_rule(m_canvas, PLUTOVG_FILL_RULE_NON_ZERO); plutovg_canvas_set_operator(m_canvas, PLUTOVG_OPERATOR_SRC_OVER); plutovg_canvas_set_texture(m_canvas, image.surface(), PLUTOVG_TEXTURE_TYPE_PLAIN, 1.f, &matrix); plutovg_canvas_fill_rect(m_canvas, 0, 0, dstRect.w, dstRect.h); } void Canvas::blendCanvas(const Canvas& canvas, BlendMode blendMode, float opacity) { plutovg_matrix_t matrix = { 1, 0, 0, 1, static_cast(canvas.x()), static_cast(canvas.y()) }; plutovg_canvas_set_matrix(m_canvas, &m_translation); plutovg_canvas_set_operator(m_canvas, static_cast(blendMode)); plutovg_canvas_set_texture(m_canvas, canvas.surface(), PLUTOVG_TEXTURE_TYPE_PLAIN, opacity, &matrix); plutovg_canvas_paint(m_canvas); } void Canvas::save() { plutovg_canvas_save(m_canvas); } void Canvas::restore() { plutovg_canvas_restore(m_canvas); } int Canvas::width() const { return plutovg_surface_get_width(m_surface); } int Canvas::height() const { return plutovg_surface_get_height(m_surface); } void Canvas::convertToLuminanceMask() { auto width = plutovg_surface_get_width(m_surface); auto height = plutovg_surface_get_height(m_surface); auto stride = plutovg_surface_get_stride(m_surface); auto data = plutovg_surface_get_data(m_surface); for(int y = 0; y < height; y++) { auto pixels = reinterpret_cast(data + stride * y); for(int x = 0; x < width; x++) { auto pixel = pixels[x]; auto a = (pixel >> 24) & 0xFF; auto r = (pixel >> 16) & 0xFF; auto g = (pixel >> 8) & 0xFF; auto b = (pixel >> 0) & 0xFF; if(a) { r = (r * 255) / a; g = (g * 255) / a; b = (b * 255) / a; } auto l = (r * 0.2125 + g * 0.7154 + b * 0.0721); pixels[x] = static_cast(l * (a / 255.0)) << 24; } } } Canvas::~Canvas() { plutovg_canvas_destroy(m_canvas); plutovg_surface_destroy(m_surface); } Canvas::Canvas(const Bitmap& bitmap) : m_surface(plutovg_surface_reference(bitmap.surface())) , m_canvas(plutovg_canvas_create(m_surface)) , m_translation({1, 0, 0, 1, 0, 0}) , m_x(0), m_y(0) { } Canvas::Canvas(int x, int y, int width, int height) : m_surface(plutovg_surface_create(width, height)) , m_canvas(plutovg_canvas_create(m_surface)) , m_translation({1, 0, 0, 1, -static_cast(x), -static_cast(y)}) , m_x(x), m_y(y) { } } // namespace lunasvg