#include #include "cairo_borders.h" #include #ifndef M_PI # define M_PI 3.14159265358979323846 #endif void cairo::add_path_arc(cairo_t* cr, double x, double y, double rx, double ry, double a1, double a2, bool neg) { if(rx > 0 && ry > 0) { cairo_save(cr); cairo_translate(cr, x, y); cairo_scale(cr, 1, ry / rx); cairo_translate(cr, -x, -y); if(neg) { cairo_arc_negative(cr, x, y, rx, a1, a2); } else { cairo_arc(cr, x, y, rx, a1, a2); } cairo_restore(cr); } else { cairo_move_to(cr, x, y); } } /** * Draw border at the left side. Use the only function to draw all border by using rotation transfer * @param cr cairo context * @param left left position of the border * @param top top position of the border * @param bottom bottom position of the border * @param data border data */ void cairo::border::draw_border() { cairo_save(cr); if(radius_top_x && radius_top_y) { double start_angle = M_PI; double end_angle = start_angle + M_PI / 2.0 / ((double) top_border_width / (double) border_width + 1); add_path_arc(cr, left + radius_top_x, top + radius_top_y, radius_top_x - border_width, radius_top_y - border_width + (border_width - top_border_width), start_angle, end_angle, false); add_path_arc(cr, left + radius_top_x, top + radius_top_y, radius_top_x, radius_top_y, end_angle, start_angle, true); } else { cairo_move_to(cr, left + border_width, top + top_border_width); cairo_line_to(cr, left, top); } if(radius_bottom_x && radius_bottom_y) { cairo_line_to(cr, left, bottom - radius_bottom_y); double end_angle = M_PI; double start_angle = end_angle - M_PI / 2.0 / ((double) bottom_border_width / (double) border_width + 1); add_path_arc(cr, left + radius_bottom_x, bottom - radius_bottom_y, radius_bottom_x, radius_bottom_y, end_angle, start_angle, true); add_path_arc(cr, left + radius_bottom_x, bottom - radius_bottom_y, radius_bottom_x - border_width, radius_bottom_y - border_width + (border_width - bottom_border_width), start_angle, end_angle, false); } else { cairo_line_to(cr, left, bottom); cairo_line_to(cr, left + border_width, bottom - bottom_border_width); } cairo_clip(cr); switch (style) { case litehtml::border_style_dotted: draw_dotted(); break; case litehtml::border_style_dashed: draw_dashed(); break; case litehtml::border_style_double: draw_double(); break; case litehtml::border_style_inset: draw_inset_outset(true); break; case litehtml::border_style_outset: draw_inset_outset(false); break; case litehtml::border_style_groove: draw_groove_ridge(true); break; case litehtml::border_style_ridge: draw_groove_ridge(false); break; default: draw_solid(); break; } cairo_restore(cr); } void cairo::border::draw_line(double line_offset, double top_line_offset, double bottom_line_offset) { if(radius_top_x && radius_top_y) { double end_angle = M_PI; double start_angle = end_angle + M_PI / 2.0 / ((double) top_border_width / (double) border_width + 1); add_path_arc(cr, left + radius_top_x, top + radius_top_y, radius_top_x - line_offset, radius_top_y - line_offset + (line_offset - top_line_offset), start_angle, end_angle, true); } else { cairo_move_to(cr, left + line_offset, top); } if(radius_bottom_x && radius_bottom_y) { cairo_line_to(cr, left + line_offset, bottom - radius_bottom_y); double start_angle = M_PI; double end_angle = start_angle - M_PI / 2.0 / ((double) bottom_border_width / (double) border_width + 1); add_path_arc(cr, left + radius_bottom_x, bottom - radius_bottom_y, radius_bottom_x - line_offset, radius_bottom_y - line_offset + (line_offset - bottom_line_offset), start_angle, end_angle, true); } else { cairo_line_to(cr, left + line_offset, bottom); } } void cairo::border::draw_inset_outset(bool is_inset) { litehtml::web_color line_color; litehtml::web_color light_color = color; litehtml::web_color dark_color = color.darken(0.33); if(color.red == 0 && color.green == 0 && color.blue == 0) { dark_color.red = dark_color.green = dark_color.blue = 0x4C; light_color.red = light_color.green = light_color.blue = 0xB2; } if (real_side == left_side || real_side == top_side) { line_color = is_inset ? dark_color : light_color; } else { line_color = is_inset ? light_color : dark_color; } draw_line(border_width / 2.0, top_border_width / 2.0, bottom_border_width / 2.0); cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT); cairo_set_dash(cr, nullptr, 0, 0); set_color(cr, line_color); cairo_set_line_width(cr, border_width); cairo_stroke(cr); } void cairo::border::draw_double() { if (border_width < 3) { draw_solid(); } else { cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT); cairo_set_dash(cr, nullptr, 0, 0); set_color(cr, color); double line_width = border_width / 3.0; cairo_set_line_width(cr, line_width); // draw external line draw_line(line_width / 2.0, top_border_width / 6.0, bottom_border_width / 6.0); cairo_stroke(cr); // draw internal line draw_line(border_width - line_width / 2.0, top_border_width - top_border_width / 6.0, bottom_border_width - bottom_border_width / 6.0); cairo_stroke(cr); } } void cairo::border::draw_dashed() { draw_line(border_width / 2.0, top_border_width / 2.0, bottom_border_width / 2.0); int line_length = std::abs(bottom - top); int segment_length = border_width * 3; int seg_nums = line_length / segment_length; if(seg_nums < 2) { seg_nums = 2; } if(seg_nums % 2 != 0) { seg_nums = seg_nums + 1; } seg_nums++; double seg_len = (double) line_length / (double) seg_nums; double dashes[2]; dashes[0] = seg_len; dashes[1] = seg_len; cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT); cairo_set_dash(cr, dashes, 2, 0); set_color(cr, color); cairo_set_line_width(cr, border_width); cairo_stroke(cr); } void cairo::border::draw_solid() { draw_line(border_width / 2.0, top_border_width / 2.0, bottom_border_width / 2.0); cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT); cairo_set_dash(cr, nullptr, 0, 0); set_color(cr, color); cairo_set_line_width(cr, border_width); cairo_stroke(cr); } void cairo::border::draw_dotted() { draw_line(border_width / 2.0, top_border_width / 2.0, bottom_border_width / 2.0); double line_length = std::abs(bottom - top); double dot_size = border_width; int num_dots = (int) std::nearbyint(line_length / (dot_size * 2.0)); if(num_dots < 2) { num_dots = 2; } if(num_dots % 2 != 0) { num_dots = num_dots + 1; } num_dots++; double space_len = ((double) line_length - (double) border_width) / (num_dots - 1.0); double dashes[2]; dashes[0] = 0; dashes[1] = space_len; cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); cairo_set_dash(cr, dashes, 2, -dot_size / 2.0); set_color(cr, color); cairo_set_line_width(cr, border_width); cairo_stroke(cr); } void cairo::border::draw_groove_ridge(bool is_groove) { if(border_width == 1) { draw_solid(); } else { litehtml::web_color inner_line_color; litehtml::web_color outer_line_color; litehtml::web_color light_color = color; litehtml::web_color dark_color = color.darken(0.33); if (color.red == 0 && color.green == 0 && color.blue == 0) { dark_color.red = dark_color.green = dark_color.blue = 0x4C; light_color.red = light_color.green = light_color.blue = 0xB2; } if (real_side == left_side || real_side == top_side) { outer_line_color = is_groove ? dark_color : light_color; inner_line_color = is_groove ? light_color : dark_color; } else { outer_line_color = is_groove ? light_color : dark_color; inner_line_color = is_groove ? dark_color : light_color; } cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT); cairo_set_dash(cr, nullptr, 0, 0); double line_width = border_width / 2.0; cairo_set_line_width(cr, line_width); // draw external line draw_line(line_width / 2.0, top_border_width / 4.0, bottom_border_width / 4.0); set_color(cr, outer_line_color); cairo_stroke(cr); // draw internal line set_color(cr, inner_line_color); draw_line(border_width - line_width / 2.0, top_border_width - top_border_width / 4.0, bottom_border_width - bottom_border_width / 4.0); cairo_stroke(cr); } }