From bb6dcf756f02237f52d0c51fc2351fe38049723d Mon Sep 17 00:00:00 2001 From: Yuri Kobets Date: Tue, 27 May 2025 02:06:15 +0300 Subject: [PATCH] Conic gradient support for the container_cairo --- CMakeLists.txt | 2 +- containers/cairo/conic_gradient.cpp | 99 ++++++++++++++++++++++++++++ containers/cairo/conic_gradient.h | 11 ++++ containers/cairo/container_cairo.cpp | 23 ++++++- doc/document_container.md | 8 ++- include/litehtml/background.h | 5 +- include/litehtml/css_calc.h | 59 +++++++++++++++++ src/background.cpp | 6 ++ src/css_calc.cpp | 30 +++++++++ 9 files changed, 236 insertions(+), 7 deletions(-) create mode 100644 containers/cairo/conic_gradient.cpp create mode 100644 containers/cairo/conic_gradient.h create mode 100644 include/litehtml/css_calc.h create mode 100644 src/css_calc.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 12d119835..27f8980ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -200,7 +200,7 @@ else () ExternalProject_Add( litehtml-tests GIT_REPOSITORY https://github.com/litehtml/litehtml-tests.git - GIT_TAG 81e4d257f2701556385e91db5d18567ff29ad75d + GIT_TAG 7511158e62cdac071de9071e060828e9e3c0bcc6 SOURCE_DIR "${CMAKE_BINARY_DIR}/litehtml-tests-src" BINARY_DIR "${CMAKE_BINARY_DIR}/litehtml-tests-build" CMAKE_ARGS -DLITEHTML_PATH=${CMAKE_CURRENT_SOURCE_DIR} diff --git a/containers/cairo/conic_gradient.cpp b/containers/cairo/conic_gradient.cpp new file mode 100644 index 000000000..36b3430fe --- /dev/null +++ b/containers/cairo/conic_gradient.cpp @@ -0,0 +1,99 @@ +#include "conic_gradient.h" + +#define INTERPOLATE_COLOR(C1, C2, t) (((C2) - (C1)) * (t) + (C1)) + +static void sector_patch (cairo_pattern_t *pattern, double radius, + double angle_A, + double A_r, double A_g, double A_b, double A_a, + double angle_B, + double B_r, double B_g, double B_b, double B_a) +{ + double r_sin_A, r_cos_A; + double r_sin_B, r_cos_B; + double h; + + r_sin_A = radius * sin (angle_A); + r_cos_A = radius * cos (angle_A); + r_sin_B = radius * sin (angle_B); + r_cos_B = radius * cos (angle_B); + + h = 4.0/3.0 * tan ((angle_B - angle_A) / 4.0); + + double x0 = r_cos_A; + double y0 = r_sin_A; + double x1 = r_cos_A - h * r_sin_A; + double y1 = r_sin_A + h * r_cos_A; + double x2 = r_cos_B + h * r_sin_B; + double y2 = r_sin_B - h * r_cos_B; + double x3 = r_cos_B; + double y3 = r_sin_B; + + cairo_mesh_pattern_begin_patch (pattern); + + cairo_mesh_pattern_move_to (pattern, 0, 0); + cairo_mesh_pattern_line_to (pattern, x0, y0); + + cairo_mesh_pattern_curve_to (pattern, + x1, y1, + x2, y2, + x3, y3); + cairo_mesh_pattern_line_to (pattern, 0, 0); + + cairo_mesh_pattern_set_corner_color_rgba (pattern, 0, A_r, A_g, A_b, A_a); + cairo_mesh_pattern_set_corner_color_rgba (pattern, 1, A_r, A_g, A_b, A_a); + cairo_mesh_pattern_set_corner_color_rgba (pattern, 2, B_r, B_g, B_b, B_a); + cairo_mesh_pattern_set_corner_color_rgba (pattern, 3, B_r, B_g, B_b, B_a); + + cairo_mesh_pattern_end_patch (pattern); +} + +cairo_pattern_t* create_conic_gradient_pattern(double angle, double radius, const std::vector& color_points) +{ + if (color_points.empty()) + { + return nullptr; + } + + if(color_points.size() == 2) + { + std::vector points; + points.push_back(color_points[0]); + bg_color_point cp; + cp.offset = 0.5f; + cp.color.red = INTERPOLATE_COLOR(color_points[0].color.red, color_points[1].color.red, 0.5f); + cp.color.green = INTERPOLATE_COLOR(color_points[0].color.green, color_points[1].color.green, 0.5f); + cp.color.blue = INTERPOLATE_COLOR(color_points[0].color.blue, color_points[1].color.blue, 0.5f); + cp.color.alpha = INTERPOLATE_COLOR(color_points[0].color.alpha, color_points[1].color.alpha, 0.5f); + points.push_back(cp); + points.push_back(color_points[1]); + return create_conic_gradient_pattern(angle, radius, points); + } + + const double two_pi = 2.0 * M_PI; + + cairo_pattern_t* pattern = cairo_pattern_create_mesh(); + + for(size_t i = 0; i < color_points.size() - 1; ++i) + { + const bg_color_point& cp_A = color_points[i]; + const bg_color_point& cp_B = color_points[i + 1]; + double angle_A = cp_A.offset * two_pi + angle; + double angle_B = color_points[i + 1].offset * two_pi + angle; + + double A_r = cp_A.color.red / 255.0; + double A_g = cp_A.color.green / 255.0; + double A_b = cp_A.color.blue / 255.0; + double A_a = cp_A.color.alpha / 255.0; + + double B_r = cp_B.color.red / 255.0; + double B_g = cp_B.color.green / 255.0; + double B_b = cp_B.color.blue / 255.0; + double B_a = cp_B.color.alpha / 255.0; + + sector_patch(pattern, radius, + angle_A, A_r, A_g, A_b, A_a, + angle_B, B_r, B_g, B_b, B_a); + } + + return pattern; +} diff --git a/containers/cairo/conic_gradient.h b/containers/cairo/conic_gradient.h new file mode 100644 index 000000000..e83779c34 --- /dev/null +++ b/containers/cairo/conic_gradient.h @@ -0,0 +1,11 @@ +#ifndef LITEHTML_CONIC_GRADIENT_H +#define LITEHTML_CONIC_GRADIENT_H + +#include +#include + +using bg_color_point = litehtml::background_layer::color_point; + +cairo_pattern_t* create_conic_gradient_pattern(double angle, double radius, const std::vector& color_points); + +#endif //LITEHTML_CONIC_GRADIENT_H diff --git a/containers/cairo/container_cairo.cpp b/containers/cairo/container_cairo.cpp index 0c565afd5..60dc3969b 100644 --- a/containers/cairo/container_cairo.cpp +++ b/containers/cairo/container_cairo.cpp @@ -1,5 +1,6 @@ #include "container_cairo.h" #include "cairo_borders.h" +#include "conic_gradient.h" #include #ifndef M_PI @@ -738,7 +739,7 @@ void container_cairo::draw_radial_gradient(litehtml::uint_ptr hdc, const litehtm } void container_cairo::draw_conic_gradient(litehtml::uint_ptr hdc, const litehtml::background_layer &layer, - const litehtml::background_layer::conic_gradient &/*gradient*/) + const litehtml::background_layer::conic_gradient &gradient) { auto* cr = (cairo_t*) hdc; cairo_save(cr); @@ -746,6 +747,26 @@ void container_cairo::draw_conic_gradient(litehtml::uint_ptr hdc, const litehtml clip_background_layer(cr, layer); + cairo_pattern_t* pattern = create_conic_gradient_pattern(gradient.angle * M_PI / 180.0 - M_PI / 2.0, gradient.radius, gradient.color_points); + if(!pattern) return; + // Translate a pattern to the (layer.origin_box.x, layer.origin_box.y) point + litehtml::pointF position = gradient.position; + position.x -= (float) layer.origin_box.x; + position.y -= (float) layer.origin_box.y; + + draw_pattern(cr, pattern, layer, [&position](cairo_t* cr, cairo_pattern_t* pattern, int x, int y, int w, int h) + { + cairo_matrix_t flib_m; + cairo_matrix_init_translate(&flib_m, -(position.x + (float ) x), -(position.y + (float ) y)); + cairo_pattern_set_matrix(pattern, &flib_m); + + cairo_set_source(cr, pattern); + cairo_rectangle(cr, x, y, w, h); + cairo_fill(cr); + } + ); + + cairo_pattern_destroy(pattern); cairo_restore(cr); } diff --git a/doc/document_container.md b/doc/document_container.md index d24c8c5df..d9d617cc8 100644 --- a/doc/document_container.md +++ b/doc/document_container.md @@ -357,11 +357,13 @@ class conic_gradient : public gradient_base { public: pointF position; - float angle; + float angle; + float radius; }; ``` -* ```position``` - the center of the gradient. -* ```angle``` - the angle of the gradient. +* ```position``` - the center of the conic gradient +* ```angle``` - the angle of the gradient in degrees, starting from 0 at the top and going clockwise +* ```radius``` - the distance from the center to the farthest corner of the background box ### draw_borders ```cpp diff --git a/include/litehtml/background.h b/include/litehtml/background.h index 07b07aa3c..51db39d00 100644 --- a/include/litehtml/background.h +++ b/include/litehtml/background.h @@ -82,8 +82,9 @@ namespace litehtml class conic_gradient : public gradient_base { public: - pointF position; - float angle = 0; + pointF position; // position is the center of the conic gradient + float angle = 0; // angle is the angle of the gradient in degrees, starting from 0 at the top and going clockwise + float radius = 0; // radius is the distance from the center to the farthest corner of the background box }; }; diff --git a/include/litehtml/css_calc.h b/include/litehtml/css_calc.h new file mode 100644 index 000000000..4d06be303 --- /dev/null +++ b/include/litehtml/css_calc.h @@ -0,0 +1,59 @@ +#ifndef LITEHTML_CSS_CALC_H +#define LITEHTML_CSS_CALC_H + +#include "litehtml/css_length.h" +#include "litehtml/css_tokenizer.h" +#include "litehtml/types.h" +#include +#include + +namespace litehtml +{ + namespace expr + { + // Node base class + class node + { + public: + node() {} + virtual ~node() = default; + }; + + // Operation base class + class node_op : public node + { + std::unique_ptr m_left; + std::unique_ptr m_right; + }; + + // Value node + class node_value : public node + { + float value = 0; + css_units units = css_units_none; + }; + + // Function base class + class node_func : public node + { + std::unique_ptr m_root; + }; + + // Expression class + class expression + { + std::unique_ptr m_root; + + public: + expression(); + + bool from_token(const css_token& token, int options); + + private: + + }; + } // namespace expr + +} // namespace litehtml + +#endif // LITEHTML_CSS_CALC_H diff --git a/src/background.cpp b/src/background.cpp index eaf115da7..12198e5af 100644 --- a/src/background.cpp +++ b/src/background.cpp @@ -602,6 +602,12 @@ std::unique_ptr litehtml::background ret->color_space = m_image[idx].m_gradient.color_space; ret->hue_interpolation = m_image[idx].m_gradient.hue_interpolation; + float corner1 = distance(ret->position, {(float) layer.origin_box.left(), (float) layer.origin_box.top()}); + float corner2 = distance(ret->position, {(float) layer.origin_box.right(), (float) layer.origin_box.top()}); + float corner3 = distance(ret->position, {(float) layer.origin_box.left(), (float) layer.origin_box.bottom()}); + float corner4 = distance(ret->position, {(float) layer.origin_box.right(), (float) layer.origin_box.bottom()}); + ret->radius = std::max({corner1, corner2, corner3, corner4}); + if(ret->prepare_color_points(0, m_image[idx].m_gradient.m_type, m_image[idx].m_gradient.m_colors)) { return ret; diff --git a/src/css_calc.cpp b/src/css_calc.cpp new file mode 100644 index 000000000..87614dee8 --- /dev/null +++ b/src/css_calc.cpp @@ -0,0 +1,30 @@ +#include "css_calc.h" +#include "css_length.h" +#include "css_tokenizer.h" +#include + +bool litehtml::css_calc::from_token(const css_token& token, int options) +{ + if(token.type == CV_FUNCTION && token.name == "calc") + { + m_root = std::make_unique(css_calc_node_type::root); + + for(const auto& tok : token.value) + { + switch(tok.type) + { + case PERCENTAGE: + case NUMBER: + case DIMENSION: + { + css_length len; + if(!len.from_token(tok, options)) + return false; + m_root + } + break; + } + } + } + return false; +}