#include
#include "html.h"
#include "background.h"
#include "render_item.h"
#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif
bool litehtml::background::get_layer(int idx, position pos, const element* el, const std::shared_ptr& ri, background_layer& layer) const
{
if(idx < 0 || idx >= get_layers_number())
{
return false;
}
position content_box = pos;
position padding_box = pos;
padding_box += ri->get_paddings();
position border_box = padding_box;
border_box += ri->get_borders();
layer.border_radius = el->css().get_borders().radius.calc_percents(border_box.width, border_box.height);
layer.border_box = border_box;
layer.is_root = el->is_root();
int clip;
css_size size;
css_length position_x;
css_length position_y;
if(idx == (int) m_image.size())
{
if(m_image.empty())
{
clip = !m_clip.empty() ? m_clip.front() : background_box_border;
} else
{
clip = m_clip.empty() ? background_box_border :
m_clip[(idx - 1) % m_clip.size()];
}
} else
{
layer.attachment = m_attachment.empty() ? background_attachment_scroll :
(background_attachment) m_attachment[idx % m_attachment.size()];
layer.repeat = m_repeat.empty() ? background_repeat_repeat :
(background_repeat) m_repeat[idx % m_repeat.size()];
clip = m_clip.empty() ? background_box_border :
m_clip[idx % m_clip.size()];
int origin = m_origin.empty() ? background_box_padding :
m_origin[idx % m_origin.size()];
const css_size auto_auto(css_length::predef_value(background_size_auto),
css_length::predef_value(background_size_auto));
size = m_size.empty() ? auto_auto :
m_size[idx % m_size.size()];
position_x = m_position_x.empty() ? css_length(0, css_units_percentage) :
m_position_x[idx % m_position_x.size()];
position_y = m_position_y.empty() ? css_length(0, css_units_percentage) :
m_position_y[idx % m_position_y.size()];
switch(origin)
{
case background_box_border:
layer.origin_box = border_box;
break;
case background_box_content:
layer.origin_box = content_box;
break;
default:
layer.origin_box = padding_box;
break;
}
}
switch(clip)
{
case background_box_padding:
layer.clip_box = padding_box;
break;
case background_box_content:
layer.clip_box = content_box;
break;
default:
layer.clip_box = border_box;
break;
}
litehtml::size bg_size(layer.origin_box.width, layer.origin_box.height);
if(get_layer_type(idx) == type_image)
{
auto image_layer = get_image_layer(idx);
if(image_layer)
{
litehtml::size img_size;
el->get_document()->container()->get_image_size(image_layer->url.c_str(), image_layer->base_url.c_str(),
img_size);
if (img_size.width && img_size.height)
{
litehtml::size img_new_sz = img_size;
double img_ar_width = (double) img_size.width / (double) img_size.height;
double img_ar_height = (double) img_size.height / (double) img_size.width;
if (size.width.is_predefined())
{
switch (size.width.predef())
{
case background_size_contain:
if ((int) ((double) layer.origin_box.width * img_ar_height) <= layer.origin_box.height)
{
img_new_sz.width = layer.origin_box.width;
img_new_sz.height = (int) ((double) layer.origin_box.width * img_ar_height);
} else
{
img_new_sz.height = layer.origin_box.height;
img_new_sz.width = (int) ((double) layer.origin_box.height * img_ar_width);
}
break;
case background_size_cover:
if ((int) ((double) layer.origin_box.width * img_ar_height) >= layer.origin_box.height)
{
img_new_sz.width = layer.origin_box.width;
img_new_sz.height = (int) ((double) layer.origin_box.width * img_ar_height);
} else
{
img_new_sz.height = layer.origin_box.height;
img_new_sz.width = (int) ((double) layer.origin_box.height * img_ar_width);
}
break;
case background_size_auto:
if (!size.height.is_predefined())
{
img_new_sz.height = size.height.calc_percent(layer.origin_box.height);
img_new_sz.width = (int) ((double) img_new_sz.height * img_ar_width);
}
break;
}
} else
{
img_new_sz.width = size.width.calc_percent(layer.origin_box.width);
if (size.height.is_predefined())
{
img_new_sz.height = (int) ((double) img_new_sz.width * img_ar_height);
} else
{
img_new_sz.height = size.height.calc_percent(layer.origin_box.height);
}
}
bg_size = img_new_sz;
}
}
} else
{
if(!size.width.is_predefined())
{
bg_size.width = size.width.calc_percent(layer.origin_box.width);
}
if(!size.height.is_predefined())
{
bg_size.height = size.height.calc_percent(layer.origin_box.height);
}
}
position new_origin_box;
new_origin_box.width = bg_size.width;
new_origin_box.height = bg_size.height;
new_origin_box.x = layer.origin_box.x + (int) position_x.calc_percent(layer.origin_box.width - bg_size.width);
new_origin_box.y = layer.origin_box.y + (int) position_y.calc_percent(layer.origin_box.height - bg_size.height);
layer.origin_box = new_origin_box;
return true;
}
std::unique_ptr litehtml::background::get_image_layer(int idx) const
{
if(idx >= 0 && idx < (int) m_image.size())
{
if(m_image[idx].type == background_image::bg_image_type_url)
{
auto ret = std::unique_ptr(new background_layer::image());
ret->url = m_image[idx].url;
ret->base_url = m_baseurl;
return ret;
}
}
return {};
}
std::unique_ptr litehtml::background::get_color_layer(int idx) const
{
if(idx == (int) m_image.size())
{
auto ret = std::unique_ptr(new background_layer::color());
ret->color = m_color;
return ret;
}
return {};
}
// Compute the endpoints so that a gradient of the given angle covers a box of
// the given size.
// https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/css/css_gradient_value.cc;drc=7061f1585ab97cc3358d1e0fc9e950e5a107a7f9;l=1070
static void EndPointsFromAngle(float angle_deg,
const litehtml::size& size,
litehtml::pointF& first_point,
litehtml::pointF& second_point)
{
angle_deg = fmodf(angle_deg, 360);
if (angle_deg < 0)
angle_deg += 360;
if (angle_deg == 0)
{
first_point.set(0, (float) size.height);
second_point.set(0, 0);
return;
}
if (angle_deg == 90)
{
first_point.set(0, 0);
second_point.set((float) size.width, 0);
return;
}
if (angle_deg == 180)
{
first_point.set(0, 0);
second_point.set(0, (float) size.height);
return;
}
if (angle_deg == 270)
{
first_point.set((float) size.width, 0);
second_point.set(0, 0);
return;
}
// angleDeg is a "bearing angle" (0deg = N, 90deg = E),
// but tan expects 0deg = E, 90deg = N.
auto slope = (float) tan((90.0 - angle_deg) * M_PI / 180.0);
// We find the endpoint by computing the intersection of the line formed by
// the slope, and a line perpendicular to it that intersects the corner.
float perpendicular_slope = -1 / slope;
// Compute start corner relative to center, in Cartesian space (+y = up).
float half_height = (float) size.height / 2.0f;
float half_width = (float) size.width / 2.0f;
litehtml::pointF end_corner;
if (angle_deg < 90)
end_corner.set(half_width, half_height);
else if (angle_deg < 180)
end_corner.set(half_width, -half_height);
else if (angle_deg < 270)
end_corner.set(-half_width, -half_height);
else
end_corner.set(-half_width, half_height);
// Compute c (of y = mx + c) using the corner point.
float c = end_corner.y - perpendicular_slope * end_corner.x;
float end_x = c / (slope - perpendicular_slope);
float end_y = perpendicular_slope * end_x + c;
// We computed the end point, so set the second point, taking into account the
// moved origin and the fact that we're in drawing space (+y = down).
second_point.set(half_width + end_x, half_height - end_y);
// Reflect around the center for the start point.
first_point.set(half_width - end_x, half_height + end_y);
}
static float distance(const litehtml::pointF& p1, const litehtml::pointF& p2)
{
double dx = p2.x - p1.x;
double dy = p2.y - p1.y;
return (float) sqrt(dx * dx + dy * dy);
}
std::unique_ptr litehtml::background::get_linear_gradient_layer(int idx, const background_layer& layer) const
{
if(idx < 0 || idx >= (int) m_image.size()) return {};
if(m_image[idx].type != background_image::bg_image_type_gradient) return {};
if(m_image[idx].gradient.m_type != background_gradient::linear_gradient &&
m_image[idx].gradient.m_type != background_gradient::repeating_linear_gradient) return {};
auto ret = std::unique_ptr(new background_layer::linear_gradient());
float angle;
if(m_image[idx].gradient.m_side == 0)
{
angle = m_image[idx].gradient.angle;
} else
{
auto rise = (float) layer.origin_box.width;
auto run = (float) layer.origin_box.height;
if(m_image[idx].gradient.m_side & background_gradient::gradient_side_left)
{
run *= -1;
}
if(m_image[idx].gradient.m_side & background_gradient::gradient_side_bottom)
{
rise *= -1;
}
angle = (float) (90 - atan2(rise, run) * 180 / M_PI);
}
EndPointsFromAngle(angle, {layer.origin_box.width, layer.origin_box.height}, ret->start,
ret->end);
ret->start.x += (float) layer.origin_box.x;
ret->start.y += (float) layer.origin_box.y;
ret->end.x += (float) layer.origin_box.x;
ret->end.y += (float) layer.origin_box.y;
auto line_len = distance(ret->start, ret->end);
if(!ret->prepare_color_points(line_len, m_image[idx].gradient.m_type, m_image[idx].gradient.m_colors))
{
return {};
}
return ret;
}
static inline litehtml::pointF calc_ellipse_radius(const litehtml::pointF& offset, float aspect_ratio)
{
// If the aspectRatio is 0 or infinite, the ellipse is completely flat.
// (If it is NaN, the ellipse is 0x0, and should be handled as zero width.)
if (!std::isfinite(aspect_ratio) || aspect_ratio == 0)
{
return {0, 0};
}
// x^2/a^2 + y^2/b^2 = 1
// a/b = aspectRatio, b = a/aspectRatio
// a = sqrt(x^2 + y^2/(1/aspect_ratio^2))
float a = sqrtf(offset.x * offset.x +
offset.y * offset.y *
aspect_ratio * aspect_ratio);
return {a, a / aspect_ratio};
}
static inline litehtml::pointF find_corner(const litehtml::pointF& center, const litehtml::position& box, bool farthest)
{
struct descr
{
float distance;
float x;
float y;
descr(float _distance, float _x, float _y) : distance(_distance), x(_x), y(_y) {}
};
litehtml::pointF ret;
// Default is left-top corner
ret.x = (float) box.left();
ret.y = (float) box.top();
auto dist = distance(center, {(float) box.left(), (float) box.top()});
// Check right-top corner
auto next_dist = distance(center, {(float) box.right(), (float) box.top()});
if((farthest && next_dist > dist) || (!farthest && next_dist < dist))
{
ret.x = (float) box.right();
ret.y = (float) box.top();
dist = next_dist;
}
// Check right-bottom corner
next_dist = distance(center, {(float) box.right(), (float) box.bottom()});
if((farthest && next_dist > dist) || (!farthest && next_dist < dist))
{
ret.x = (float) box.right();
ret.y = (float) box.bottom();
dist = next_dist;
}
// Check left-bottom corner
next_dist = distance(center, {(float) box.left(), (float) box.bottom()});
if((farthest && next_dist > dist) || (!farthest && next_dist < dist))
{
ret.x = (float) box.left();
ret.y = (float) box.bottom();
dist = next_dist;
}
ret.x -= center.x;
ret.y -= center.y;
return ret;
}
std::unique_ptr litehtml::background::get_radial_gradient_layer(int idx, const background_layer& layer) const
{
if(idx < 0 || idx >= (int) m_image.size()) return {};
if(m_image[idx].type != background_image::bg_image_type_gradient) return {};
if(m_image[idx].gradient.m_type != background_gradient::radial_gradient &&
m_image[idx].gradient.m_type != background_gradient::repeating_radial_gradient) return {};
auto ret = std::unique_ptr(new background_layer::radial_gradient());
ret->position.x = (float) layer.origin_box.x + (float) layer.origin_box.width / 2.0f;
ret->position.y = (float) layer.origin_box.y + (float) layer.origin_box.height / 2.0f;
if(m_image[idx].gradient.m_side & background_gradient::gradient_side_left)
{
ret->position.x = (float) layer.origin_box.left();
} else if(m_image[idx].gradient.m_side & background_gradient::gradient_side_right)
{
ret->position.x = (float) layer.origin_box.right();
} else if(m_image[idx].gradient.m_side & background_gradient::gradient_side_x_center)
{
ret->position.x = (float) layer.origin_box.left() + (float) layer.origin_box.width / 2.0f;
} else if(m_image[idx].gradient.m_side & background_gradient::gradient_side_x_length)
{
ret->position.x = (float) layer.origin_box.left() + (float) m_image[idx].gradient.radial_position_x.calc_percent(layer.origin_box.width);
}
if(m_image[idx].gradient.m_side & background_gradient::gradient_side_top)
{
ret->position.y = (float) layer.origin_box.top();
} else if(m_image[idx].gradient.m_side & background_gradient::gradient_side_bottom)
{
ret->position.y = (float) layer.origin_box.bottom();
} else if(m_image[idx].gradient.m_side & background_gradient::gradient_side_y_center)
{
ret->position.y = (float) layer.origin_box.top() + (float) layer.origin_box.height / 2.0f;
} else if(m_image[idx].gradient.m_side & background_gradient::gradient_side_y_length)
{
ret->position.y = (float) layer.origin_box.top() + (float) m_image[idx].gradient.radial_position_y.calc_percent(layer.origin_box.height);
}
if(m_image[idx].gradient.radial_extent)
{
switch (m_image[idx].gradient.radial_extent)
{
case background_gradient::radial_extent_closest_corner:
{
if (m_image[idx].gradient.radial_shape == background_gradient::radial_shape_circle)
{
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.x = ret->radius.y = std::min({corner1, corner2, corner3, corner4});
} else
{
// Aspect ratio is the same as for radial_extent_closest_side
float aspect_ration = std::min(
std::abs(ret->position.x - (float) layer.origin_box.left()),
std::abs(ret->position.x - (float) layer.origin_box.right())
) / std::min(
std::abs(ret->position.y - (float) layer.origin_box.top()),
std::abs(ret->position.y - (float) layer.origin_box.bottom())
);
auto corner = find_corner(ret->position, layer.origin_box, false);
auto radius = calc_ellipse_radius(corner, aspect_ration);
ret->radius.x = radius.x;
ret->radius.y = radius.y;
}
}
break;
case background_gradient::radial_extent_closest_side:
if (m_image[idx].gradient.radial_shape == background_gradient::radial_shape_circle)
{
ret->radius.x = ret->radius.y = std::min(
{
std::abs(ret->position.x - (float) layer.origin_box.left()),
std::abs(ret->position.x - (float) layer.origin_box.right()),
std::abs(ret->position.y - (float) layer.origin_box.top()),
std::abs(ret->position.y - (float) layer.origin_box.bottom()),
});
} else
{
ret->radius.x = std::min(
std::abs(ret->position.x - (float) layer.origin_box.left()),
std::abs(ret->position.x - (float) layer.origin_box.right())
);
ret->radius.y = std::min(
std::abs(ret->position.y - (float) layer.origin_box.top()),
std::abs(ret->position.y - (float) layer.origin_box.bottom())
);
}
break;
case background_gradient::radial_extent_farthest_corner:
{
if (m_image[idx].gradient.radial_shape == background_gradient::radial_shape_circle)
{
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.x = ret->radius.y = std::max({corner1, corner2, corner3, corner4});
} else
{
// Aspect ratio is the same as for radial_extent_farthest_side
float aspect_ration = std::max(
std::abs(ret->position.x - (float) layer.origin_box.left()),
std::abs(ret->position.x - (float) layer.origin_box.right())
) / std::max(
std::abs(ret->position.y - (float) layer.origin_box.top()),
std::abs(ret->position.y - (float) layer.origin_box.bottom())
);
auto corner = find_corner(ret->position, layer.origin_box, true);
auto radius = calc_ellipse_radius(corner, aspect_ration);
ret->radius.x = radius.x;
ret->radius.y = radius.y;
}
}
break;
case background_gradient::radial_extent_farthest_side:
if (m_image[idx].gradient.radial_shape == background_gradient::radial_shape_circle)
{
ret->radius.x = ret->radius.y = std::max(
{
std::abs(ret->position.x - (float) layer.origin_box.left()),
std::abs(ret->position.x - (float) layer.origin_box.right()),
std::abs(ret->position.y - (float) layer.origin_box.top()),
std::abs(ret->position.y - (float) layer.origin_box.bottom()),
});
} else
{
ret->radius.x = std::max(
std::abs(ret->position.x - (float) layer.origin_box.left()),
std::abs(ret->position.x - (float) layer.origin_box.right())
);
ret->radius.y = std::max(
std::abs(ret->position.y - (float) layer.origin_box.top()),
std::abs(ret->position.y - (float) layer.origin_box.bottom())
);
}
break;
default:
break;
}
}
if(!m_image[idx].gradient.radial_length_x.is_predefined())
{
ret->radius.x = (float) m_image[idx].gradient.radial_length_x.calc_percent(layer.origin_box.width);
}
if(!m_image[idx].gradient.radial_length_y.is_predefined())
{
ret->radius.y = (float) m_image[idx].gradient.radial_length_y.calc_percent(layer.origin_box.height);
}
if(ret->prepare_color_points(ret->radius.x, m_image[idx].gradient.m_type, m_image[idx].gradient.m_colors))
{
return ret;
}
return {};
}
std::unique_ptr litehtml::background::get_conic_gradient_layer(int idx, const background_layer& layer) const
{
if(idx < 0 || idx >= (int) m_image.size()) return {};
if(m_image[idx].type != background_image::bg_image_type_gradient) return {};
if(m_image[idx].gradient.m_type != background_gradient::conic_gradient) return {};
auto ret = std::unique_ptr(new background_layer::conic_gradient());
ret->position.x = (float) layer.origin_box.x + (float) layer.origin_box.width / 2.0f;
ret->position.y = (float) layer.origin_box.y + (float) layer.origin_box.height / 2.0f;
if(m_image[idx].gradient.m_side & background_gradient::gradient_side_left)
{
ret->position.x = (float) layer.origin_box.left();
} else if(m_image[idx].gradient.m_side & background_gradient::gradient_side_right)
{
ret->position.x = (float) layer.origin_box.right();
} else if(m_image[idx].gradient.m_side & background_gradient::gradient_side_x_center)
{
ret->position.x = (float) layer.origin_box.left() + (float) layer.origin_box.width / 2.0f;
} else if(m_image[idx].gradient.m_side & background_gradient::gradient_side_x_length)
{
ret->position.x = (float) layer.origin_box.left() + (float) m_image[idx].gradient.radial_position_x.calc_percent(layer.origin_box.width);
}
if(m_image[idx].gradient.m_side & background_gradient::gradient_side_top)
{
ret->position.y = (float) layer.origin_box.top();
} else if(m_image[idx].gradient.m_side & background_gradient::gradient_side_bottom)
{
ret->position.y = (float) layer.origin_box.bottom();
} else if(m_image[idx].gradient.m_side & background_gradient::gradient_side_y_center)
{
ret->position.y = (float) layer.origin_box.top() + (float) layer.origin_box.height / 2.0f;
} else if(m_image[idx].gradient.m_side & background_gradient::gradient_side_y_length)
{
ret->position.y = (float) layer.origin_box.top() + (float) m_image[idx].gradient.radial_position_y.calc_percent(layer.origin_box.height);
}
ret->angle = m_image[idx].gradient.conic_from_angle;
ret->color_space = m_image[idx].gradient.conic_color_space;
ret->interpolation = m_image[idx].gradient.conic_interpolation;
if(ret->prepare_angle_color_points(m_image[idx].gradient.m_type, m_image[idx].gradient.m_colors))
{
return ret;
}
return {};
}
litehtml::background::layer_type litehtml::background::get_layer_type(int idx) const
{
if(idx >= 0 && idx < (int) m_image.size())
{
switch (m_image[idx].type)
{
case background_image::bg_image_type_url:
return type_image;
case background_image::bg_image_type_gradient:
switch (m_image[idx].gradient.m_type)
{
case background_gradient::linear_gradient:
case background_gradient::repeating_linear_gradient:
return type_linear_gradient;
case background_gradient::radial_gradient:
case background_gradient::repeating_radial_gradient:
return type_radial_gradient;
case background_gradient::conic_gradient:
case background_gradient::repeating_conic_gradient:
return type_conic_gradient;
default:
break;
}
break;
default:
break;
}
} else if(idx == (int) m_image.size())
{
return type_color;
}
return type_none;
}
void litehtml::background::draw_layer(uint_ptr hdc, int idx, const background_layer& layer, document_container* container) const
{
switch (get_layer_type(idx))
{
case background::type_color:
{
auto color_layer = get_color_layer(idx);
if(color_layer)
{
container->draw_solid_fill(hdc, layer, color_layer->color);
}
}
break;
case background::type_image:
{
auto image_layer = get_image_layer(idx);
if(image_layer)
{
container->draw_image(hdc, layer, image_layer->url, image_layer->base_url);
}
}
break;
case background::type_linear_gradient:
{
auto gradient_layer = get_linear_gradient_layer(idx, layer);
if(gradient_layer)
{
container->draw_linear_gradient(hdc, layer, *gradient_layer);
}
}
break;
case background::type_radial_gradient:
{
auto gradient_layer = get_radial_gradient_layer(idx, layer);
if(gradient_layer)
{
container->draw_radial_gradient(hdc, layer, *gradient_layer);
}
}
break;
case background::type_conic_gradient:
{
auto gradient_layer = get_conic_gradient_layer(idx, layer);
if(gradient_layer)
{
container->draw_conic_gradient(hdc, layer, *gradient_layer);
}
}
break;
default:
break;
}
}
static void repeat_color_points(std::vector& color_points, float max_val)
{
auto old_points = color_points;
if(color_points.back().offset < max_val)
{
float gd_size = color_points.back().offset - old_points.front().offset;
auto iter = old_points.begin();
while (color_points.back().offset < max_val)
{
color_points.emplace_back(iter->offset + gd_size, iter->color);
std::advance(iter, 1);
if (iter == old_points.end())
{
iter = old_points.begin();
gd_size = color_points.back().offset - old_points.front().offset;
}
}
}
if(color_points.front().offset > 0)
{
float gd_size = color_points.front().offset;
auto iter = old_points.rbegin();
while (color_points.front().offset > 0)
{
color_points.emplace(color_points.begin(), gd_size - (old_points.back().offset - iter->offset), iter->color);
std::advance(iter, 1);
if (iter == old_points.rend())
{
iter = old_points.rbegin();
gd_size = color_points.front().offset;
}
}
}
}
void litehtml::background_layer::gradient_base::color_points_transparent_fix()
{
for(int i = 0; i < (int) color_points.size(); i++)
{
if(color_points[i].color.alpha == 0)
{
if(i == 0)
{
if(i + 1 < (int) color_points.size())
{
color_points[i].color = color_points[i + 1].color;
color_points[i].color.alpha = 0;
}
} else if(i + 1 == (int) color_points.size())
{
if(i - 1 >= 0)
{
color_points[i].color = color_points[i - 1].color;
color_points[i].color.alpha = 0;
}
} else
{
color_points[i].color = color_points[i + 1].color;
color_points[i].color.alpha = 0;
background_layer::color_point cpt;
cpt.color = color_points[i - 1].color;
cpt.color.alpha = 0;
cpt.offset = color_points[i].offset;
color_points.emplace(std::next(color_points.begin(), i), cpt);
i++;
}
}
}
}
bool litehtml::background_layer::gradient_base::prepare_color_points(float line_len, background_gradient::gradient_type g_type, const std::vector &colors)
{
bool repeating;
if(g_type == background_gradient::linear_gradient || g_type == background_gradient::radial_gradient)
{
repeating = false;
} else if(g_type == background_gradient::repeating_linear_gradient || g_type == background_gradient::repeating_radial_gradient)
{
repeating = true;
} else
{
return false;
}
int none_units = 0;
bool has_transparent = false;
for(const auto& item : colors)
{
if(item.color.alpha == 0)
{
has_transparent = true;
}
if(item.length.units() == css_units_percentage)
{
color_points.emplace_back(item.length.val() / 100.0f, item.color);
} else if(item.length.units() != css_units_none)
{
if(line_len != 0)
{
color_points.emplace_back(item.length.val() / line_len, item.color);
}
} else
{
if(!color_points.empty())
{
none_units++;
}
color_points.emplace_back(0.0f, item.color);
}
}
if(color_points.empty())
{
return false;
}
if(!repeating)
{
// Add color point with offset 0 if not exists
if(color_points[0].offset != 0)
{
color_points.emplace(color_points.begin(), 0.0f, color_points[0].color);
}
// Add color point with offset 1.0 if not exists
if (color_points.back().offset < 1)
{
if (color_points.back().offset == 0)
{
color_points.back().offset = 1;
none_units--;
} else
{
color_points.emplace_back(1.0f, color_points.back().color);
}
}
} else
{
// Add color point with offset 1.0 if not exists
if (color_points.back().offset == 0)
{
color_points.back().offset = 1;
none_units--;
}
}
if(none_units > 0)
{
size_t i = 1;
while(i < color_points.size())
{
if(color_points[i].offset != 0)
{
i++;
continue;
}
// Find next defined offset
size_t j = i + 1;
while (color_points[j].offset == 0) j++;
size_t num = j - i;
float sum = color_points[i - 1].offset + color_points[j].offset;
float offset = sum / (float) (num + 1);
while(i < j)
{
color_points[i].offset = color_points[i - 1].offset + offset;
i++;
}
}
}
// process transparent
if(has_transparent)
{
color_points_transparent_fix();
}
if(repeating)
{
repeat_color_points(color_points, 1.0f);
}
return true;
}
bool litehtml::background_layer::gradient_base::prepare_angle_color_points(background_gradient::gradient_type g_type, const std::vector &colors)
{
bool repeating;
if(g_type != background_gradient::conic_gradient)
{
repeating = false;
} else if(g_type != background_gradient::repeating_conic_gradient)
{
repeating = true;
} else
{
return false;
}
int none_units = 0;
bool has_transparent = false;
for(const auto& item : colors)
{
if(item.color.alpha == 0)
{
has_transparent = true;
}
if(item.angle.is_default())
{
if(!color_points.empty())
{
none_units++;
}
color_points.emplace_back(0.0f, item.color);
} else
{
color_points.emplace_back(item.angle, item.color);
}
}
if(color_points.empty())
{
return false;
}
if(!repeating)
{
// Add color point with offset 0 if not exists
if (color_points[0].offset != 0)
{
color_points.emplace(color_points.begin(), 0.0f, color_points[0].color);
}
// Add color point with offset 1.0 if not exists
if (color_points.back().offset < 360.0f)
{
if (color_points.back().offset == 0)
{
color_points.back().offset = 360.0f;
none_units--;
} else
{
color_points.emplace_back(360.0f, color_points.back().color);
}
}
} else
{
if (color_points.back().offset == 0)
{
color_points.back().offset = 360.0f;
none_units--;
}
}
if(none_units > 0)
{
size_t i = 1;
while(i < color_points.size())
{
if(color_points[i].offset != 0)
{
i++;
continue;
}
// Find next defined offset
size_t j = i + 1;
while (color_points[j].offset == 0) j++;
size_t num = j - i;
float sum = color_points[i - 1].offset + color_points[j].offset;
float offset = sum / (float) (num + 1);
while(i < j)
{
color_points[i].offset = color_points[i - 1].offset + offset;
i++;
}
}
}
// process transparent
if(has_transparent)
{
color_points_transparent_fix();
}
if(repeating)
{
repeat_color_points(color_points, 1.0f);
}
return true;
}