diff options
Diffstat (limited to 'libs/litehtml/src/background.cpp')
-rw-r--r-- | libs/litehtml/src/background.cpp | 979 |
1 files changed, 979 insertions, 0 deletions
diff --git a/libs/litehtml/src/background.cpp b/libs/litehtml/src/background.cpp new file mode 100644 index 0000000000..2a3ac7b88b --- /dev/null +++ b/libs/litehtml/src/background.cpp @@ -0,0 +1,979 @@ +#include <cmath> +#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<render_item>& 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_layer::image> 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<background_layer::image>(new background_layer::image()); + ret->url = m_image[idx].url; + ret->base_url = m_baseurl; + return ret; + } + } + return {}; +} + +std::unique_ptr<litehtml::background_layer::color> litehtml::background::get_color_layer(int idx) const +{ + if(idx == (int) m_image.size()) + { + auto ret = std::unique_ptr<background_layer::color>(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_layer::linear_gradient> 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<background_layer::linear_gradient>(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_layer::radial_gradient> 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<background_layer::radial_gradient>(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_layer::conic_gradient> 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<background_layer::conic_gradient>(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<litehtml::background_layer::color_point>& 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<background_gradient::gradient_color> &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.0, 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, 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, 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.0, 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<background_gradient::gradient_color> &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, 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, 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; +} |