Skip to content

Conic gradient support for the container_cairo #407

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
99 changes: 99 additions & 0 deletions containers/cairo/conic_gradient.cpp
Original file line number Diff line number Diff line change
@@ -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<bg_color_point>& color_points)
{
if (color_points.empty())
{
return nullptr;
}

if(color_points.size() == 2)
{
std::vector<bg_color_point> 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;
}
11 changes: 11 additions & 0 deletions containers/cairo/conic_gradient.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#ifndef LITEHTML_CONIC_GRADIENT_H
#define LITEHTML_CONIC_GRADIENT_H

#include <litehtml.h>
#include <cairo.h>

using bg_color_point = litehtml::background_layer::color_point;

cairo_pattern_t* create_conic_gradient_pattern(double angle, double radius, const std::vector<bg_color_point>& color_points);

#endif //LITEHTML_CONIC_GRADIENT_H
23 changes: 22 additions & 1 deletion containers/cairo/container_cairo.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "container_cairo.h"
#include "cairo_borders.h"
#include "conic_gradient.h"
#include <cmath>

#ifndef M_PI
Expand Down Expand Up @@ -738,14 +739,34 @@ 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);
apply_clip(cr);

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);
}
8 changes: 5 additions & 3 deletions doc/document_container.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions include/litehtml/background.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
};

Expand Down
59 changes: 59 additions & 0 deletions include/litehtml/css_calc.h
Original file line number Diff line number Diff line change
@@ -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 <memory>
#include <utility>

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<node> m_left;
std::unique_ptr<node> 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<node> m_root;
};

// Expression class
class expression
{
std::unique_ptr<node> m_root;

public:
expression();

bool from_token(const css_token& token, int options);

private:

};
} // namespace expr

} // namespace litehtml

#endif // LITEHTML_CSS_CALC_H
6 changes: 6 additions & 0 deletions src/background.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,12 @@ std::unique_ptr<litehtml::background_layer::conic_gradient> 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;
Expand Down
30 changes: 30 additions & 0 deletions src/css_calc.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include "css_calc.h"
#include "css_length.h"
#include "css_tokenizer.h"
#include <memory>

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>(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;
}