diff --git a/.gitignore b/.gitignore index e9d3d1fae..d61ae9f72 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ build/ *.exe *.clang_tidy *.pc +*.a inc/fastuidraw/gl_backend/ngl_gl.hpp src/fastuidraw/gl_backend/ngl_gl.cpp inc/fastuidraw/gl_backend/ngl_gles3.hpp @@ -17,5 +18,9 @@ src/fastuidraw/ngl_egl.cpp *-GL-debug *-GLES-release *-GLES-debug +*-GL-release-static +*-GL-debug-static +*-GLES-release-static +*-GLES-debug-static fastuidraw-config.nodir fastuidraw-config diff --git a/Makefile b/Makefile index cfef2b417..569059432 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,11 @@ ENVIRONMENTALDESCRIPTIONS := #install location INSTALL_LOCATION ?= /usr/local -ENVIRONMENTALDESCRIPTIONS += "INSTALL_LOCATION: provides install location (default /usr/local)" +ENVIRONMENTALDESCRIPTIONS += "INSTALL_LOCATION: provides install location (default /usr/local)" + +INSTALL_STATIC ?= 0 +ENVIRONMENTALDESCRIPTIONS += "INSTALL_STATIC: if 1, install static libraries (default 0). NOTE: if linking static libs, make sure one links in the entire archive (for example via the linker option --whole-archive (from g++ do -Wl,--whole-archive)" + # Mark all intermediate files as secondary and precious .PRECIOUS: .SECONDARY: diff --git a/README.md b/README.md index 4e7f9f869..a501a8fd2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Fast UI Draw ============ -Fast UI Draw in a library that provides a higher performance Canvas interface. +Fast UI Draw is a library that provides a higher performance Canvas interface. It is designed so that it always draws using a GPU. In contrast to many common implementations of Canvas drawing, Fast UI Draw diff --git a/demo_data/paths/default_path.txt b/demo_data/paths/default_path.txt new file mode 100644 index 000000000..3afcd64e8 --- /dev/null +++ b/demo_data/paths/default_path.txt @@ -0,0 +1,11 @@ +[ (50.0, 35.0) [[(60.0, 50.0)]] (70.0, 35.0) + arc 180 (70.0, -100.0) + [[(60.0, -150.0) (30.0, -50.0)]] + (0.0, -100.0) arc 90] + + +[ (200, 200) (400, 200) (400, 400) (200, 400)] + +[ (-50, 100) (0, 200) (100, 300) (150, 325) (150, 100)] + +[(300 300)] diff --git a/demos/common/command_line_list.hpp b/demos/common/command_line_list.hpp new file mode 100644 index 000000000..c3b46c334 --- /dev/null +++ b/demos/common/command_line_list.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include +#include "generic_command_line.hpp" + +template +class command_line_list: + public command_line_argument, + public std::set +{ +public: + command_line_list(const std::string &nm, + const std::string &desc, + command_line_register &p): + command_line_argument(p), + m_name(nm) + { + std::ostringstream ostr; + ostr << "\n\t" << m_name << " value" + << format_description_string(m_name, desc); + m_description = tabs_to_spaces(ostr.str()); + } + + virtual + int + check_arg(const std::vector &argv, int location) + { + int argc(argv.size()); + if (location + 1 < argc && argv[location] == m_name) + { + T v; + readvalue_from_string(v, argv[location + 1]); + this->insert(v); + std::cout << "\n\t" << m_name << " added: "; + writevalue_to_stream(v, std::cout); + return 2; + } + return 0; + } + + virtual + void + print_command_line_description(std::ostream &ostr) const + { + ostr << "[" << m_name << " value] "; + } + + virtual + void + print_detailed_description(std::ostream &ostr) const + { + ostr << m_description; + } + +private: + std::string m_name, m_description; +}; diff --git a/demos/common/sdl_painter_demo.cpp b/demos/common/sdl_painter_demo.cpp index 99987157a..416660041 100644 --- a/demos/common/sdl_painter_demo.cpp +++ b/demos/common/sdl_painter_demo.cpp @@ -630,15 +630,9 @@ init_gl(int w, int h) std::cout << "\tSolid StrokeShaders:\n"; print_stroke_shader_ids(sh.stroke_shader()); - std::cout << "\tPixel Width Stroke Shaders:\n"; - print_stroke_shader_ids(sh.pixel_width_stroke_shader()); - std::cout << "\tDashed Stroke Shader:\n"; print_dashed_stroke_shader_ids(sh.dashed_stroke_shader()); - std::cout << "\tPixel Width Dashed Stroke Shader:\n"; - print_dashed_stroke_shader_ids(sh.pixel_width_dashed_stroke_shader()); - std::cout << "\tFill Shader:" << sh.fill_shader().item_shader()->tag() << "\n"; } diff --git a/demos/painter_glyph_test/main.cpp b/demos/painter_glyph_test/main.cpp index ef6130fdf..37869762a 100644 --- a/demos/painter_glyph_test/main.cpp +++ b/demos/painter_glyph_test/main.cpp @@ -702,6 +702,10 @@ draw_frame(void) PainterStrokeParams st; st.miter_limit(5.0f); st.width(m_stroke_width); + if (m_pixel_width_stroking) + { + st.stroking_units(PainterStrokeParams::pixel_stroking_units); + } src = m_current_drawer; @@ -731,22 +735,12 @@ draw_frame(void) //path, thus we also need to negate in the y-direction. m_painter->shear(sc, -sc); - if (m_pixel_width_stroking) - { - m_painter->stroke_path_pixel_width(PainterData(pst, pbr), - glyphs[i].path(), - true, PainterEnums::flat_caps, - static_cast(m_join_style), - m_anti_alias_path_stroking); - } - else - { - m_painter->stroke_path(PainterData(pst, pbr), - glyphs[i].path(), - true, PainterEnums::flat_caps, - static_cast(m_join_style), - m_anti_alias_path_stroking); - } + m_painter->stroke_path(PainterData(pst, pbr), + glyphs[i].path(), + true, PainterEnums::flat_caps, + static_cast(m_join_style), + m_anti_alias_path_stroking); + m_painter->restore(); } } diff --git a/demos/painter_path_test/main.cpp b/demos/painter_path_test/main.cpp index 402d2d4e1..c194c6e58 100644 --- a/demos/painter_path_test/main.cpp +++ b/demos/painter_path_test/main.cpp @@ -17,6 +17,7 @@ #include "cycle_value.hpp" #include "ostream_utility.hpp" #include "text_helper.hpp" +#include "command_line_list.hpp" using namespace fastuidraw; @@ -122,12 +123,39 @@ enable_wire_frame(bool b) #endif } +class PerPath +{ +public: + PerPath(const Path &path, const std::string &label, + int w, int h, bool from_gylph); + + Path m_path; + std::string m_label; + bool m_from_glyph; + unsigned int m_fill_rule; + unsigned int m_end_fill_rule; + vec2 m_shear, m_shear2; + float m_angle; + PanZoomTrackerSDLEvent m_path_zoomer; + + bool m_translate_brush, m_matrix_brush; + + vec2 m_gradient_p0, m_gradient_p1; + float m_gradient_r0, m_gradient_r1; + bool m_repeat_gradient; + + bool m_repeat_window; + vec2 m_repeat_xy, m_repeat_wh; + + bool m_clipping_window; + vec2 m_clipping_xy, m_clipping_wh; +}; + class painter_stroke_test:public sdl_painter_demo { public: painter_stroke_test(void); - protected: void @@ -164,10 +192,10 @@ class painter_stroke_test:public sdl_painter_demo reference_counted_ptr > named_color_stop; void - construct_path(void); + construct_paths(int w, int h); void - create_stroked_path_attributes(void); + per_path_processing(void); void construct_color_stops(void); @@ -184,11 +212,131 @@ class painter_stroke_test:public sdl_painter_demo void update_cts_params(void); + PanZoomTrackerSDLEvent& + zoomer(void) + { + return m_paths[m_selected_path].m_path_zoomer; + } + + Path& + path(void) + { + return m_paths[m_selected_path].m_path; + } + + unsigned int& + current_fill_rule(void) + { + return m_paths[m_selected_path].m_fill_rule; + } + + unsigned int + current_end_fill_rule(void) + { + return m_paths[m_selected_path].m_end_fill_rule; + } + + vec2& + shear(void) + { + return m_paths[m_selected_path].m_shear; + } + + vec2& + shear2(void) + { + return m_paths[m_selected_path].m_shear2; + } + + float& + angle(void) + { + return m_paths[m_selected_path].m_angle; + } + + vec2& + gradient_p0(void) + { + return m_paths[m_selected_path].m_gradient_p0; + } + + vec2& + gradient_p1(void) + { + return m_paths[m_selected_path].m_gradient_p1; + } + + float& + gradient_r0(void) + { + return m_paths[m_selected_path].m_gradient_r0; + } + + float& + gradient_r1(void) + { + return m_paths[m_selected_path].m_gradient_r1; + } + + bool& + repeat_gradient(void) + { + return m_paths[m_selected_path].m_repeat_gradient; + } + + bool& + translate_brush(void) + { + return m_paths[m_selected_path].m_translate_brush; + } + + bool& + matrix_brush(void) + { + return m_paths[m_selected_path].m_matrix_brush; + } + + bool& + repeat_window(void) + { + return m_paths[m_selected_path].m_repeat_window; + } + + vec2& + repeat_xy(void) + { + return m_paths[m_selected_path].m_repeat_xy; + } + + vec2& + repeat_wh(void) + { + return m_paths[m_selected_path].m_repeat_wh; + } + + bool& + clipping_window(void) + { + return m_paths[m_selected_path].m_clipping_window; + } + + vec2& + clipping_xy(void) + { + return m_paths[m_selected_path].m_clipping_xy; + } + + vec2& + clipping_wh(void) + { + return m_paths[m_selected_path].m_clipping_wh; + } + command_line_argument_value m_change_miter_limit_rate; command_line_argument_value m_change_stroke_width_rate; command_line_argument_value m_window_change_rate; command_line_argument_value m_radial_gradient_change_rate; - command_line_argument_value m_path_file; + command_line_list m_path_file_list; DashPatternList m_dash_pattern_files; command_line_argument_value m_print_path; color_stop_arguments m_color_stop_args; @@ -197,8 +345,7 @@ class painter_stroke_test:public sdl_painter_demo command_line_argument_value m_sub_image_x, m_sub_image_y; command_line_argument_value m_sub_image_w, m_sub_image_h; command_line_argument_value m_font_file; - command_line_argument_value m_path_from_glyph; - command_line_argument_value m_character_code; + command_line_list m_character_code_list; command_line_argument_value m_stroke_red; command_line_argument_value m_stroke_green; command_line_argument_value m_stroke_blue; @@ -208,14 +355,13 @@ class painter_stroke_test:public sdl_painter_demo command_line_argument_value m_fill_blue; command_line_argument_value m_fill_alpha; - Path m_path; + std::vector m_paths; reference_counted_ptr m_image; uvec2 m_image_offset, m_image_size; std::vector m_color_stops; std::vector > m_dash_patterns; reference_counted_ptr m_font; - PanZoomTrackerSDLEvent m_zoomer; vecN m_gradient_mode_labels; vecN m_cap_labels; vecN m_join_labels; @@ -226,10 +372,11 @@ class painter_stroke_test:public sdl_painter_demo PainterPackedValue m_white_pen; PainterPackedValue m_stroke_pen; + unsigned int m_selected_path; unsigned int m_join_style; unsigned int m_cap_style; bool m_close_contour; - unsigned int m_fill_rule; + /* m_dash pattern: 0 -> undashed stroking [1, m_dash_patterns.size()] -> dashed stroking @@ -248,40 +395,24 @@ class painter_stroke_test:public sdl_painter_demo return m_dash - 1; } - unsigned int m_end_fill_rule; bool m_have_miter_limit; float m_miter_limit, m_stroke_width; bool m_draw_fill, m_aa_fill_by_stroking; unsigned int m_active_color_stop; unsigned int m_gradient_draw_mode; - bool m_repeat_gradient; unsigned int m_image_filter; bool m_draw_stats; float m_curve_flatness; bool m_print_submit_stroke_time, m_print_submit_fill_time; - vec2 m_gradient_p0, m_gradient_p1; - float m_gradient_r0, m_gradient_r1; - bool m_translate_brush, m_matrix_brush; - - bool m_repeat_window; - vec2 m_repeat_xy, m_repeat_wh; - - bool m_clipping_window; - vec2 m_clipping_xy, m_clipping_wh; - bool m_with_aa; bool m_wire_frame; bool m_stroke_width_in_pixels; - bool m_stroke_width_pixels_scaled_by_zoom; bool m_force_square_viewport; bool m_fill_by_clipping; - vec2 m_shear, m_shear2; bool m_draw_grid; - float m_angle; - simple_time m_draw_timer, m_fps_timer; Path m_grid_path; bool m_grid_path_dirty; @@ -290,6 +421,57 @@ class painter_stroke_test:public sdl_painter_demo bool m_clip_window_path_dirty; }; +/////////////////////////////////// +// PerPath methods +PerPath:: +PerPath(const Path &path, const std::string &label, int w, int h, bool from_gylph): + m_path(path), + m_label(label), + m_from_glyph(from_gylph), + m_fill_rule(PainterEnums::odd_even_fill_rule), + m_end_fill_rule(PainterEnums::fill_rule_data_count), + m_shear(1.0f, 1.0f), + m_shear2(1.0f, 1.0f), + m_angle(0.0f), + m_translate_brush(false), + m_matrix_brush(false), + m_repeat_gradient(true), + m_repeat_window(false), + m_clipping_window(false) +{ + m_end_fill_rule = + m_path.tessellation()->filled()->subset(0).winding_numbers().size() + PainterEnums::fill_rule_data_count; + + /* set transformation to center and contain path. */ + vec2 p0, p1, delta, dsp(w, h), ratio, mid; + float mm; + p0 = m_path.tessellation()->bounding_box_min(); + p1 = m_path.tessellation()->bounding_box_max(); + + delta = p1 - p0; + ratio = delta / dsp; + mm = t_max(0.00001f, t_max(ratio.x(), ratio.y()) ); + mid = 0.5 * (p1 + p0); + + ScaleTranslate sc, tr1, tr2; + tr1.translation(-mid); + sc.scale( 1.0f / mm); + tr2.translation(dsp * 0.5f); + m_path_zoomer.transformation(tr2 * sc * tr1); + + m_gradient_p0 = p0; + m_gradient_p1 = p1; + + m_gradient_r0 = 0.0f; + m_gradient_r1 = 200.0f / m_path_zoomer.transformation().scale(); + + m_repeat_xy = vec2(0.0f, 0.0f); + m_repeat_wh = m_path.tessellation()->bounding_box_max() - m_path.tessellation()->bounding_box_min(); + + m_clipping_xy = m_path.tessellation()->bounding_box_min(); + m_clipping_wh = m_repeat_wh; +} + ////////////////////////////////////// // painter_stroke_test methods painter_stroke_test:: @@ -309,10 +491,10 @@ painter_stroke_test(void): m_radial_gradient_change_rate(0.1f, "change_rate_brush_radial_gradient", "rate of change in pixels/sec when changing the radial gradient radius", *this), - m_path_file("", "path_file", - "if non-empty read the geometry of the path from the specified file, " - "otherwise use a default path", - *this), + m_path_file_list("add_path_file", + "add a path read from file to path list; if path list is empty then " + "a default path will be used to render ", + *this), m_dash_pattern_files(*this), m_print_path(false, "print_path", "If true, print the geometry data of the path drawn to stdout", @@ -333,10 +515,8 @@ painter_stroke_test(void): "sub-image height of sub-image rectange (negative value means no-subimage)", *this), m_font_file(default_font(), "font", "File from which to take font", *this), - m_path_from_glyph(false, "path_from_glyph", - "If true, draw a path from a glyph of the font", *this), - m_character_code('W', "path_character_code", - "If path_from_glyph is true, selects which glyph via character code to use", *this), + m_character_code_list("add_path_character_code", + "add a path of a glyph selected via character code", *this), m_stroke_red(1.0f, "stroke_red", "red component of stroking pen color", *this), m_stroke_green(1.0f, "stroke_green", "green component of stroking pen color", *this), m_stroke_blue(1.0f, "stroke_blue", "blue component of stroking pen olor", *this), @@ -345,39 +525,30 @@ painter_stroke_test(void): m_fill_green(1.0f, "fill_green", "green component of fill pen color", *this), m_fill_blue(1.0f, "fill_blue", "blue component of fill pen color", *this), m_fill_alpha(1.0f, "fill_alpha", "alpha component of fill pen color", *this), + m_selected_path(0), m_join_style(PainterEnums::miter_clip_joins), m_cap_style(PainterEnums::square_caps), m_close_contour(true), - m_fill_rule(PainterEnums::odd_even_fill_rule), m_dash(0), - m_end_fill_rule(PainterEnums::fill_rule_data_count), m_have_miter_limit(true), m_miter_limit(5.0f), m_stroke_width(10.0f), m_draw_fill(false), m_aa_fill_by_stroking(false), m_active_color_stop(0), m_gradient_draw_mode(draw_no_gradient), - m_repeat_gradient(true), m_image_filter(image_nearest_filter), m_draw_stats(false), - m_translate_brush(false), - m_matrix_brush(false), - m_repeat_window(false), - m_clipping_window(false), m_with_aa(true), m_wire_frame(false), m_stroke_width_in_pixels(false), - m_stroke_width_pixels_scaled_by_zoom(false), m_force_square_viewport(false), m_fill_by_clipping(false), - m_shear(1.0f, 1.0f), - m_shear2(1.0f, 1.0f), m_draw_grid(false), - m_angle(0.0f), m_grid_path_dirty(true), m_clip_window_path_dirty(true) { std::cout << "Controls:\n" + << "\tk: select next path\n" << "\ta: toggle anti-aliased stroking\n" << "\tj: cycle through join styles for stroking\n" << "\tc: cycle through cap style for stroking\n" @@ -472,11 +643,11 @@ update_cts_params(void) speed_shear = -speed_shear; } - vec2 *pshear(&m_shear); + vec2 *pshear(&shear()); c_string shear_txt = ""; if (keyboard_state[SDL_SCANCODE_RETURN]) { - pshear = &m_shear2; + pshear = &shear2(); shear_txt = "2"; } @@ -493,19 +664,19 @@ update_cts_params(void) if (keyboard_state[SDL_SCANCODE_9]) { - m_angle += speed * 0.1f; - std::cout << "Angle set to: " << m_angle << "\n"; + angle() += speed * 0.1f; + std::cout << "Angle set to: " << angle() << "\n"; } if (keyboard_state[SDL_SCANCODE_0]) { - m_angle -= speed * 0.1f; - std::cout << "Angle set to: " << m_angle << "\n"; + angle() -= speed * 0.1f; + std::cout << "Angle set to: " << angle() << "\n"; } speed_stroke = speed * m_change_stroke_width_rate.m_value; if (!m_stroke_width_in_pixels) { - speed_stroke /= m_zoomer.transformation().scale(); + speed_stroke /= zoomer().transformation().scale(); } if (keyboard_state[SDL_SCANCODE_RIGHTBRACKET]) @@ -526,20 +697,20 @@ update_cts_params(void) std::cout << "Stroke width set to: " << m_stroke_width << "\n"; } - if (m_repeat_window) + if (repeat_window()) { vec2 *changer; float delta, delta_y; - delta = m_window_change_rate.m_value * speed / m_zoomer.transformation().scale(); + delta = m_window_change_rate.m_value * speed / zoomer().transformation().scale(); if (keyboard_state[SDL_SCANCODE_LCTRL] || keyboard_state[SDL_SCANCODE_RCTRL]) { - changer = &m_repeat_wh; + changer = &repeat_wh(); delta_y = delta; } else { - changer = &m_repeat_xy; + changer = &repeat_xy(); delta_y = -delta; } @@ -569,7 +740,7 @@ update_cts_params(void) if (keyboard_state[SDL_SCANCODE_UP] || keyboard_state[SDL_SCANCODE_DOWN] || keyboard_state[SDL_SCANCODE_RIGHT] || keyboard_state[SDL_SCANCODE_LEFT]) { - std::cout << "Brush repeat window set to: xy = " << m_repeat_xy << " wh = " << m_repeat_wh << "\n"; + std::cout << "Brush repeat window set to: xy = " << repeat_xy() << " wh = " << repeat_wh() << "\n"; } } @@ -578,33 +749,33 @@ update_cts_params(void) { float delta; - delta = m_radial_gradient_change_rate.m_value * speed / m_zoomer.transformation().scale(); + delta = m_radial_gradient_change_rate.m_value * speed / zoomer().transformation().scale(); if (keyboard_state[SDL_SCANCODE_1]) { - m_gradient_r0 -= delta; - m_gradient_r0 = fastuidraw::t_max(0.0f, m_gradient_r0); + gradient_r0() -= delta; + gradient_r0() = fastuidraw::t_max(0.0f, gradient_r0()); } if (keyboard_state[SDL_SCANCODE_2]) { - m_gradient_r0 += delta; + gradient_r0() += delta; } if (keyboard_state[SDL_SCANCODE_3]) { - m_gradient_r1 -= delta; - m_gradient_r1 = fastuidraw::t_max(0.0f, m_gradient_r1); + gradient_r1() -= delta; + gradient_r1() = fastuidraw::t_max(0.0f, gradient_r1()); } if (keyboard_state[SDL_SCANCODE_4]) { - m_gradient_r1 += delta; + gradient_r1() += delta; } if (keyboard_state[SDL_SCANCODE_1] || keyboard_state[SDL_SCANCODE_2] || keyboard_state[SDL_SCANCODE_3] || keyboard_state[SDL_SCANCODE_4]) { std::cout << "Radial gradient values set to: r0 = " - << m_gradient_r0 << " r1 = " << m_gradient_r1 << "\n"; + << gradient_r0() << " r1 = " << gradient_r1() << "\n"; } } @@ -628,20 +799,20 @@ update_cts_params(void) } } - if (m_clipping_window) + if (clipping_window()) { vec2 *changer; float delta, delta_y; - delta = m_window_change_rate.m_value * speed / m_zoomer.transformation().scale(); + delta = m_window_change_rate.m_value * speed / zoomer().transformation().scale(); if (keyboard_state[SDL_SCANCODE_LCTRL] || keyboard_state[SDL_SCANCODE_RCTRL]) { - changer = &m_clipping_wh; + changer = &clipping_wh(); delta_y = delta; } else { - changer = &m_clipping_xy; + changer = &clipping_xy(); delta_y = -delta; } @@ -669,13 +840,11 @@ update_cts_params(void) || keyboard_state[SDL_SCANCODE_KP_6] || keyboard_state[SDL_SCANCODE_KP_8]) { m_clip_window_path_dirty = true; - std::cout << "Clipping window set to: xy = " << m_clipping_xy << " wh = " << m_clipping_wh << "\n"; + std::cout << "Clipping window set to: xy = " << clipping_xy() << " wh = " << clipping_wh() << "\n"; } } } - - vec2 painter_stroke_test:: brush_item_coordinate(ivec2 scr) @@ -683,14 +852,14 @@ brush_item_coordinate(ivec2 scr) vec2 p; p = item_coordinates(scr); - if (m_matrix_brush) + if (matrix_brush()) { - p *= m_zoomer.transformation().scale(); + p *= zoomer().transformation().scale(); } - if (m_translate_brush) + if (translate_brush()) { - p += m_zoomer.transformation().translation(); + p += zoomer().transformation().translation(); } return p; } @@ -701,19 +870,19 @@ item_coordinates(ivec2 scr) { vec2 p(scr); - /* unapply m_zoomer + /* unapply zoomer() */ - p = m_zoomer.transformation().apply_inverse_to_point(p); + p = zoomer().transformation().apply_inverse_to_point(p); - /* unapply m_shear + /* unapply shear() */ - p /= m_shear; + p /= shear(); - /* unapply rotation by m_angle + /* unapply rotation by angle() */ float s, c, a; float2x2 tr; - a = -m_angle * M_PI / 180.0f; + a = -angle() * M_PI / 180.0f; s = t_sin(a); c = t_cos(a); @@ -724,9 +893,20 @@ item_coordinates(ivec2 scr) p = tr * p; - /* unapply m_shear2 + /* unapply shear2() + */ + p /= shear2(); + + /* unapply glyph-flip */ - p /= m_shear2; + if (m_paths[m_selected_path].m_from_glyph) + { + float y; + y = path().tessellation()->bounding_box_min().y() + + path().tessellation()->bounding_box_max().y(); + p.y() -= y; + p.y() *= -1.0f; + } return p; } @@ -735,7 +915,7 @@ void painter_stroke_test:: handle_event(const SDL_Event &ev) { - m_zoomer.handle_event(ev); + zoomer().handle_event(ev); switch(ev.type) { case SDL_QUIT: @@ -757,11 +937,11 @@ handle_event(const SDL_Event &ev) if (ev.motion.state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) { - m_gradient_p0 = brush_item_coordinate(c); + gradient_p0() = brush_item_coordinate(c); } else if (ev.motion.state & SDL_BUTTON(SDL_BUTTON_RIGHT)) { - m_gradient_p1 = brush_item_coordinate(c); + gradient_p1() = brush_item_coordinate(c); } } break; @@ -774,20 +954,9 @@ handle_event(const SDL_Event &ev) break; case SDLK_k: - if (m_stroke_width_in_pixels) - { - m_stroke_width_pixels_scaled_by_zoom = !m_stroke_width_pixels_scaled_by_zoom; - std::cout << "Stroke width pixels scale by zoom factor: "; - if (m_stroke_width_pixels_scaled_by_zoom) - { - std::cout << "ON"; - } - else - { - std::cout << "OFF"; - } - std::cout << "\n"; - } + cycle_value(m_selected_path, ev.key.keysym.mod & (KMOD_SHIFT|KMOD_CTRL|KMOD_ALT), m_paths.size()); + std::cout << "Path " << m_paths[m_selected_path].m_label << " selected\n"; + m_clip_window_path_dirty = true; break; case SDLK_a: @@ -812,7 +981,7 @@ handle_event(const SDL_Event &ev) break; case SDLK_q: - m_shear = m_shear2 = vec2(1.0f, 1.0f); + shear() = shear2() = vec2(1.0f, 1.0f); break; case SDLK_p: @@ -840,30 +1009,30 @@ handle_event(const SDL_Event &ev) break; case SDLK_o: - m_clipping_window = !m_clipping_window; - std::cout << "Clipping window: " << on_off(m_clipping_window) << "\n"; + clipping_window() = !clipping_window(); + std::cout << "Clipping window: " << on_off(clipping_window()) << "\n"; break; case SDLK_w: - m_repeat_window = !m_repeat_window; - std::cout << "Brush Repeat window: " << on_off(m_repeat_window) << "\n"; + repeat_window() = !repeat_window(); + std::cout << "Brush Repeat window: " << on_off(repeat_window()) << "\n"; break; case SDLK_y: - m_matrix_brush = !m_matrix_brush; - std::cout << "Matrix brush: " << on_off(m_matrix_brush) << "\n"; + matrix_brush() = !matrix_brush(); + std::cout << "Matrix brush: " << on_off(matrix_brush()) << "\n"; break; case SDLK_t: - m_translate_brush = !m_translate_brush; - std::cout << "Translate brush: " << on_off(m_translate_brush) << "\n"; + translate_brush() = !translate_brush(); + std::cout << "Translate brush: " << on_off(translate_brush()) << "\n"; break; case SDLK_h: if (m_gradient_draw_mode != draw_no_gradient) { - m_repeat_gradient = !m_repeat_gradient; - if (!m_repeat_gradient) + repeat_gradient() = !repeat_gradient(); + if (!repeat_gradient()) { std::cout << "non-"; } @@ -969,12 +1138,14 @@ handle_event(const SDL_Event &ev) case SDLK_r: if (m_draw_fill) { - cycle_value(m_fill_rule, ev.key.keysym.mod & (KMOD_SHIFT|KMOD_CTRL|KMOD_ALT), m_end_fill_rule + 1); - if (m_fill_rule < PainterEnums::fill_rule_data_count) + cycle_value(current_fill_rule(), + ev.key.keysym.mod & (KMOD_SHIFT|KMOD_CTRL|KMOD_ALT), + current_end_fill_rule() + 1); + if (current_fill_rule() < PainterEnums::fill_rule_data_count) { - std::cout << "Fill rule set to: " << m_fill_labels[m_fill_rule] << "\n"; + std::cout << "Fill rule set to: " << m_fill_labels[current_fill_rule()] << "\n"; } - else if (m_fill_rule == m_end_fill_rule) + else if (current_fill_rule() == current_end_fill_rule()) { std::cout << "Fill rule set to custom fill rule: all winding numbers filled\n"; } @@ -982,8 +1153,8 @@ handle_event(const SDL_Event &ev) { c_array wnd; int value; - wnd = m_path.tessellation()->filled()->subset(0).winding_numbers(); - value = wnd[m_fill_rule - PainterEnums::fill_rule_data_count]; + wnd = path().tessellation()->filled()->subset(0).winding_numbers(); + value = wnd[current_fill_rule() - PainterEnums::fill_rule_data_count]; std::cout << "Fill rule set to custom fill rule: winding_number == " << value << "\n"; } @@ -1058,122 +1229,134 @@ handle_event(const SDL_Event &ev) void painter_stroke_test:: -construct_path(void) +construct_paths(int w, int h) { - if (m_path_from_glyph.m_value) + for(const std::string &file : m_path_file_list) + { + std::ifstream path_file(file.c_str()); + if (path_file) + { + std::stringstream buffer; + Path P; + + buffer << path_file.rdbuf(); + read_path(P, buffer.str()); + if (P.number_contours() > 0) + { + m_paths.push_back(PerPath(P, file, w, h, false)); + } + } + } + + for (uint32_t character_code : m_character_code_list) { uint32_t glyph_code; GlyphRender renderer(distance_field_glyph); Glyph g; - glyph_code = m_font->glyph_code(m_character_code.m_value); + glyph_code = m_font->glyph_code(character_code); g = m_glyph_cache->fetch_glyph(renderer, m_font, glyph_code); - if (g.valid()) + if (g.valid() && g.path().number_contours() > 0) { - m_path = g.path(); - return; + std::ostringstream str; + str << "character code:" << character_code; + m_paths.push_back(PerPath(g.path(), str.str(), w, h, true)); } } - if (!m_path_file.m_value.empty()) + if (m_paths.empty()) { - std::ifstream path_file(m_path_file.m_value.c_str()); - if (path_file) - { - std::stringstream buffer; - buffer << path_file.rdbuf(); - read_path(m_path, buffer.str()); - return; - } + Path path; + + path << vec2(50.0f, 35.0f) + << Path::control_point(60.0f, 50.0f) + << vec2(70.0f, 35.0f) + << Path::arc_degrees(180.0, vec2(70.0f, -100.0f)) + << Path::control_point(60.0f, -150.0f) + << Path::control_point(30.0f, -50.0f) + << vec2(0.0f, -100.0f) + << Path::contour_end_arc_degrees(90.0f) + << vec2(200.0f, 200.0f) + << vec2(400.0f, 200.0f) + << vec2(400.0f, 400.0f) + << vec2(200.0f, 400.0f) + << Path::contour_end() + << vec2(-50.0f, 100.0f) + << vec2(0.0f, 200.0f) + << vec2(100.0f, 300.0f) + << vec2(150.0f, 325.0f) + << vec2(150.0f, 100.0f) + << Path::contour_end() + << vec2(300.0f, 300.0f) + << Path::contour_end(); + m_paths.push_back(PerPath(path, "Default Path", w, h, false)); } - - m_path << vec2(50.0f, 35.0f) - << Path::control_point(60.0f, 50.0f) - << vec2(70.0f, 35.0f) - << Path::arc_degrees(180.0, vec2(70.0f, -100.0f)) - << Path::control_point(60.0f, -150.0f) - << Path::control_point(30.0f, -50.0f) - << vec2(0.0f, -100.0f) - << Path::contour_end_arc_degrees(90.0f) - << vec2(200.0f, 200.0f) - << vec2(400.0f, 200.0f) - << vec2(400.0f, 400.0f) - << vec2(200.0f, 400.0f) - << Path::contour_end() - << vec2(-50.0f, 100.0f) - << vec2(0.0f, 200.0f) - << vec2(100.0f, 300.0f) - << vec2(150.0f, 325.0f) - << vec2(150.0f, 100.0f) - << Path::contour_end() - << vec2(300.0f, 300.0f) - << Path::contour_end(); } void painter_stroke_test:: -create_stroked_path_attributes(void) +per_path_processing(void) { - reference_counted_ptr tessellated; - reference_counted_ptr stroked; - const PainterAttributeData *data; - c_array miter_points; - - tessellated = m_path.tessellation(-1.0f); - stroked = tessellated->stroked(); - data = &stroked->miter_clip_joins(); - m_miter_limit = 0.0f; - for(unsigned int J = 0, endJ = stroked->number_joins(true); J < endJ; ++J) + for(const PerPath &P : m_paths) { - unsigned int chunk; + reference_counted_ptr tess; + const StrokedCapsJoins *stroked; + const PainterAttributeData *data; + c_array miter_points; - chunk = stroked->join_chunk(J); - miter_points = data->attribute_data_chunk(chunk); - for(unsigned p = 0, endp = miter_points.size(); p < endp; ++p) + tess = P.m_path.tessellation(-1.0f); + stroked = &tess->stroked()->caps_joins(); + data = &stroked->miter_clip_joins(); + + for(unsigned int J = 0, endJ = stroked->number_joins(true); J < endJ; ++J) { - float v; - StrokedPath::point pt; + unsigned int chunk; - StrokedPath::point::unpack_point(&pt, miter_points[p]); - v = pt.miter_distance(); - if (std::isfinite(v)) + chunk = stroked->join_chunk(J); + miter_points = data->attribute_data_chunk(chunk); + for(unsigned p = 0, endp = miter_points.size(); p < endp; ++p) { - m_miter_limit = fastuidraw::t_max(m_miter_limit, fastuidraw::t_abs(v)); + float v; + StrokedPoint pt; + + StrokedPoint::unpack_point(&pt, miter_points[p]); + v = pt.miter_distance(); + if (std::isfinite(v)) + { + m_miter_limit = fastuidraw::t_max(m_miter_limit, fastuidraw::t_abs(v)); + } } } - } - m_miter_limit = fastuidraw::t_min(100.0f, m_miter_limit); //100 is an insane miter limit. - if (m_print_path.m_value) - { - reference_counted_ptr tess; - - tess = m_path.tessellation(); - std::cout << "Path tessellated:\n"; - for(unsigned int c = 0; c < tess->number_contours(); ++c) + if (m_print_path.m_value) { - std::cout << "\tContour #" << c << "\n"; - for(unsigned int e = 0; e < tess->number_edges(c); ++e) + std::cout << "Path \"" << P.m_label << "\" tessellated:\n"; + for(unsigned int c = 0; c < tess->number_contours(); ++c) { - fastuidraw::c_array pts; - - std::cout << "\t\tEdge #" << e << " has " - << tess->edge_point_data(c, e).size() << " pts\n"; - pts = tess->edge_point_data(c, e); - for(unsigned int i = 0; i < pts.size(); ++i) + std::cout << "\tContour #" << c << "\n"; + for(unsigned int e = 0; e < tess->number_edges(c); ++e) { - std::cout << "\t\t\tPoint #" << i << ":\n" - << "\t\t\t\tp = " << pts[i].m_p << "\n" - << "\t\t\t\tedge_d = " << pts[i].m_distance_from_edge_start << "\n" - << "\t\t\t\tcontour_d = " << pts[i].m_distance_from_contour_start << "\n" - << "\t\t\t\tedge_l = " << pts[i].m_edge_length << "\n" - << "\t\t\t\tcontour_l = " << pts[i].m_open_contour_length << "\n" - << "\t\t\t\tcontour_cl = " << pts[i].m_closed_contour_length << "\n"; + fastuidraw::c_array pts; + + std::cout << "\t\tEdge #" << e << " has " + << tess->edge_point_data(c, e).size() << " pts\n"; + pts = tess->edge_point_data(c, e); + for(unsigned int i = 0; i < pts.size(); ++i) + { + std::cout << "\t\t\tPoint #" << i << ":\n" + << "\t\t\t\tp = " << pts[i].m_p << "\n" + << "\t\t\t\tedge_d = " << pts[i].m_distance_from_edge_start << "\n" + << "\t\t\t\tcontour_d = " << pts[i].m_distance_from_contour_start << "\n" + << "\t\t\t\tedge_l = " << pts[i].m_edge_length << "\n" + << "\t\t\t\tcontour_l = " << pts[i].m_open_contour_length << "\n" + << "\t\t\t\tcontour_cl = " << pts[i].m_closed_contour_length << "\n"; + } } } } } + m_miter_limit = fastuidraw::t_min(100.0f, m_miter_limit); //100 is an insane miter limit. } void @@ -1319,31 +1502,43 @@ draw_frame(void) false); } - /* apply m_zoomer + /* apply zoomer() */ - m_painter->concat(m_zoomer.transformation().matrix3()); + m_painter->concat(zoomer().transformation().matrix3()); /* apply shear */ - m_painter->shear(m_shear.x(), m_shear.y()); + m_painter->shear(shear().x(), shear().y()); /* apply rotation */ - m_painter->rotate(m_angle * M_PI / 180.0f); + m_painter->rotate(angle() * M_PI / 180.0f); /* apply shear2 */ - m_painter->shear(m_shear2.x(), m_shear2.y()); + m_painter->shear(shear2().x(), shear2().y()); + + if (m_paths[m_selected_path].m_from_glyph) + { + /* Glyphs have y-increasing upwards, rather than + * downwards; so we reverse the y + */ + float y; + y = path().tessellation()->bounding_box_min().y() + + path().tessellation()->bounding_box_max().y(); + m_painter->translate(vec2(0.0f, y)); + m_painter->shear(1.0f, -1.0f); + } - if (m_clipping_window) + if (clipping_window()) { if (m_clip_window_path_dirty) { Path clip_window_path; - clip_window_path << m_clipping_xy - << vec2(m_clipping_xy.x(), m_clipping_xy.y() + m_clipping_wh.y()) - << m_clipping_xy + m_clipping_wh - << vec2(m_clipping_xy.x() + m_clipping_wh.x(), m_clipping_xy.y()) + clip_window_path << clipping_xy() + << vec2(clipping_xy().x(), clipping_xy().y() + clipping_wh().y()) + << clipping_xy() + clipping_wh() + << vec2(clipping_xy().x() + clipping_wh().x(), clipping_xy().y()) << Path::contour_end(); m_clip_window_path.swap(clip_window_path); m_clip_window_path_dirty = false; @@ -1361,7 +1556,7 @@ draw_frame(void) PainterEnums::miter_clip_joins, false); m_painter->restore(); - m_painter->clipInRect(m_clipping_xy, m_clipping_wh); + m_painter->clipInRect(clipping_xy(), clipping_wh()); } @@ -1372,19 +1567,19 @@ draw_frame(void) fill_brush.pen(m_fill_red.m_value, m_fill_green.m_value, m_fill_blue.m_value, m_fill_alpha.m_value); - if (m_translate_brush) + if (translate_brush()) { - fill_brush.transformation_translate(m_zoomer.transformation().translation()); + fill_brush.transformation_translate(zoomer().transformation().translation()); } else { fill_brush.no_transformation_translation(); } - if (m_matrix_brush) + if (matrix_brush()) { float2x2 m; - m(0, 0) = m(1, 1) = m_zoomer.transformation().scale(); + m(0, 0) = m(1, 1) = zoomer().transformation().scale(); fill_brush.transformation_matrix(m); } else @@ -1392,9 +1587,9 @@ draw_frame(void) fill_brush.no_transformation_matrix(); } - if (m_repeat_window) + if (repeat_window()) { - fill_brush.repeat_window(m_repeat_xy, m_repeat_wh); + fill_brush.repeat_window(repeat_xy(), repeat_wh()); } else { @@ -1404,14 +1599,14 @@ draw_frame(void) if (m_gradient_draw_mode == draw_linear_gradient) { fill_brush.linear_gradient(m_color_stops[m_active_color_stop].second, - m_gradient_p0, m_gradient_p1, m_repeat_gradient); + gradient_p0(), gradient_p1(), repeat_gradient()); } else if (m_gradient_draw_mode == draw_radial_gradient) { fill_brush.radial_gradient(m_color_stops[m_active_color_stop].second, - m_gradient_p0, m_gradient_r0, - m_gradient_p1, m_gradient_r1, - m_repeat_gradient); + gradient_p0(), gradient_r0(), + gradient_p1(), gradient_r1(), + repeat_gradient()); } else { @@ -1448,17 +1643,17 @@ draw_frame(void) WindingValueFillRule value_fill_rule; CustomFillRuleBase *fill_rule(&fill_rule_function); - if (m_fill_rule < PainterEnums::fill_rule_data_count) + if (current_fill_rule() < PainterEnums::fill_rule_data_count) { - fill_rule_function = CustomFillRuleFunction(static_cast(m_fill_rule)); + fill_rule_function = CustomFillRuleFunction(static_cast(current_fill_rule())); } - else if (m_fill_rule != m_end_fill_rule) + else if (current_fill_rule() != current_end_fill_rule()) { int value; c_array wnd; - wnd = m_path.tessellation()->filled()->subset(0).winding_numbers(); - value = wnd[m_fill_rule - PainterEnums::fill_rule_data_count]; + wnd = path().tessellation()->filled()->subset(0).winding_numbers(); + value = wnd[current_fill_rule() - PainterEnums::fill_rule_data_count]; value_fill_rule = WindingValueFillRule(value); fill_rule = &value_fill_rule; } @@ -1466,33 +1661,38 @@ draw_frame(void) if (m_fill_by_clipping) { m_painter->save(); - m_painter->clipInPath(m_path, *fill_rule); + m_painter->clipInPath(path(), *fill_rule); m_painter->transformation(float3x3()); m_painter->draw_rect(PainterData(&fill_brush), vec2(-1.0f, -1.0f), vec2(2.0f, 2.0f), false); m_painter->restore(); } else { - m_painter->fill_path(PainterData(&fill_brush), m_path, *fill_rule, m_with_aa && !m_aa_fill_by_stroking); + m_painter->fill_path(PainterData(&fill_brush), path(), + *fill_rule, m_with_aa && !m_aa_fill_by_stroking); } if (m_aa_fill_by_stroking && m_with_aa) { PainterStrokeParams st; - st.miter_limit(-1.0f); - st.width(2.0f); - m_painter->stroke_path_pixel_width(PainterData(&fill_brush, &st), m_path, true, - PainterEnums::flat_caps, - PainterEnums::bevel_joins, - true); + st + .miter_limit(-1.0f) + .width(2.0f) + .stroking_units(PainterStrokeParams::pixel_stroking_units); + + m_painter->stroke_path(PainterData(&fill_brush, &st), + path(), true, + PainterEnums::flat_caps, + PainterEnums::bevel_joins, + true); } submit_fill_time = measure.elapsed_us(); } if (!m_stroke_pen) { - PainterBrush br; - br.pen(m_stroke_red.m_value, m_stroke_green.m_value, m_stroke_blue.m_value, m_stroke_alpha.m_value); + PainterBrush br; + br.pen(m_stroke_red.m_value, m_stroke_green.m_value, m_stroke_blue.m_value, m_stroke_alpha.m_value); m_stroke_pen = m_painter->packed_value_pool().create_packed_value(br); } @@ -1513,25 +1713,19 @@ draw_frame(void) st.width(m_stroke_width); unsigned int D(dash_pattern()); - c_array dash_ptr(&m_dash_patterns[D][0], m_dash_patterns[D].size()); + c_array dash_ptr(&m_dash_patterns[D][0], + m_dash_patterns[D].size()); st.dash_pattern(dash_ptr); - if (m_stroke_width_in_pixels) { - m_painter->stroke_dashed_path_pixel_width(PainterData(m_stroke_pen, &st), - m_path, m_close_contour, - static_cast(m_cap_style), - static_cast(m_join_style), - m_with_aa); - } - else - { - m_painter->stroke_dashed_path(PainterData(m_stroke_pen, &st), - m_path, m_close_contour, - static_cast(m_cap_style), - static_cast(m_join_style), - m_with_aa); + st.stroking_units(PainterStrokeParams::pixel_stroking_units); } + + m_painter->stroke_dashed_path(PainterData(m_stroke_pen, &st), + path(), m_close_contour, + static_cast(m_cap_style), + static_cast(m_join_style), + m_with_aa); } else { @@ -1545,27 +1739,16 @@ draw_frame(void) st.miter_limit(-1.0f); } st.width(m_stroke_width); - if (m_stroke_width_in_pixels) { - if (m_stroke_width_pixels_scaled_by_zoom) - { - st.width(m_stroke_width * m_zoomer.transformation().scale()); - } - m_painter->stroke_path_pixel_width(PainterData(m_stroke_pen, &st), - m_path, m_close_contour, - static_cast(m_cap_style), - static_cast(m_join_style), - m_with_aa); - } - else - { - m_painter->stroke_path(PainterData(m_stroke_pen, &st), - m_path, m_close_contour, - static_cast(m_cap_style), - static_cast(m_join_style), - m_with_aa); + st.stroking_units(PainterStrokeParams::pixel_stroking_units); } + + m_painter->stroke_path(PainterData(m_stroke_pen, &st), + path(), m_close_contour, + static_cast(m_cap_style), + static_cast(m_join_style), + m_with_aa); } submit_stroke_time = measure.elapsed_us(); } @@ -1576,20 +1759,20 @@ draw_frame(void) vec2 p0, p1; float inv_scale; - inv_scale = 1.0f / m_zoomer.transformation().scale(); + inv_scale = 1.0f / zoomer().transformation().scale(); r0 = 15.0f * inv_scale; r1 = 30.0f * inv_scale; - p0 = m_gradient_p0; - p1 = m_gradient_p1; + p0 = gradient_p0(); + p1 = gradient_p1(); - if (m_translate_brush) + if (translate_brush()) { - p0 -= m_zoomer.transformation().translation(); - p1 -= m_zoomer.transformation().translation(); + p0 -= zoomer().transformation().translation(); + p1 -= zoomer().transformation().translation(); } - if (m_matrix_brush) + if (matrix_brush()) { p0 *= inv_scale; p1 *= inv_scale; @@ -1628,6 +1811,7 @@ draw_frame(void) } ostr << "\nms = " << us / 1000.0f + << "\nDrawing Path: " << m_paths[m_selected_path].m_label << "\nAttribs: " << m_painter->query_stat(PainterPacker::num_attributes) << "\nIndices: " @@ -1688,12 +1872,10 @@ derived_init(int w, int h) << "-----------------------------------------------------\n"; } - construct_path(); - create_stroked_path_attributes(); + construct_paths(w, h); + per_path_processing(); construct_color_stops(); construct_dash_patterns(); - m_end_fill_rule = - m_path.tessellation()->filled()->subset(0).winding_numbers().size() + PainterEnums::fill_rule_data_count; /* set shader anti-alias to false if doing msaa rendering */ if (m_surface->properties().msaa() > 1) @@ -1701,24 +1883,6 @@ derived_init(int w, int h) m_with_aa = false; } - /* set transformation to center and contain path. - */ - vec2 p0, p1, delta, dsp(w, h), ratio, mid; - float mm; - p0 = m_path.tessellation()->bounding_box_min(); - p1 = m_path.tessellation()->bounding_box_max(); - - delta = p1 - p0 + vec2(m_stroke_width, m_stroke_width); - ratio = delta / dsp; - mm = t_max(0.00001f, t_max(ratio.x(), ratio.y()) ); - mid = 0.5 * (p1 + p0); - - ScaleTranslate sc, tr1, tr2; - tr1.translation(-mid); - sc.scale( 1.0f / mm); - tr2.translation(dsp * 0.5f); - m_zoomer.transformation(tr2 * sc * tr1); - if (!m_image_file.m_value.empty()) { std::vector image_data; @@ -1747,18 +1911,6 @@ derived_init(int w, int h) } } - m_gradient_p0 = item_coordinates(ivec2(0, 0)); - m_gradient_p1 = item_coordinates(ivec2(w, h)); - - m_gradient_r0 = 0.0f; - m_gradient_r1 = 200.0f / m_zoomer.transformation().scale(); - - m_repeat_xy = vec2(0.0f, 0.0f); - m_repeat_wh = m_path.tessellation()->bounding_box_max() - m_path.tessellation()->bounding_box_min(); - - m_clipping_xy = m_path.tessellation()->bounding_box_min(); - m_clipping_wh = m_repeat_wh; - m_curve_flatness = m_painter->curveFlatness(); m_print_submit_stroke_time = true; m_print_submit_fill_time = true; diff --git a/demos/painter_test/main.cpp b/demos/painter_test/main.cpp index ead38769f..a7e81676d 100644 --- a/demos/painter_test/main.cpp +++ b/demos/painter_test/main.cpp @@ -135,6 +135,7 @@ class painter_test:public sdl_painter_demo int main(int argc, char **argv) { + FASTUIDRAWassert(StrokedPoint::number_offset_types < FASTUIDRAW_MAX_VALUE_FROM_NUM_BITS(StrokedPoint::offset_type_num_bits)); std::cout << std::setw(40) << "header_size = " << PainterHeader::header_size << "\n" << std::setw(40) << "clip_equations_data_size = " << PainterClipEquations::clip_data_size << "\n" << std::setw(40) << "item_matrix_data_size = " << PainterItemMatrix::matrix_data_size << "\n" @@ -160,19 +161,19 @@ main(int argc, char **argv) << std::setw(40) << "transformation_translation_mask = " << bitset(PainterBrush::transformation_translation_mask) << "\n" << std::setw(40) << "transformation_matrix_mask = " << bitset(PainterBrush::transformation_matrix_mask) << "\n" - << std::setw(40) << "stroked_number_offset_types = " << StrokedPath::point::number_offset_types << "\n" - << std::setw(40) << "stroked_offset_type_bit0 = " << StrokedPath::point::offset_type_bit0 << "\n" - << std::setw(40) << "stroked_offset_type_num_bits = " << StrokedPath::point::offset_type_num_bits << "\n" - << std::setw(40) << "stroked_boundary_bit = " << StrokedPath::point::boundary_bit << "\n" - << std::setw(40) << "stroked_depth_bit0 = " << StrokedPath::point::depth_bit0 << "\n" - << std::setw(40) << "stroked_depth_num_bits = " << StrokedPath::point::depth_num_bits << "\n" - << std::setw(40) << "stroked_join_bit = " << StrokedPath::point::join_bit << "\n" - << std::setw(40) << "stroked_number_common_bits = " << StrokedPath::point::number_common_bits << "\n" - << std::setw(40) << "stroked_normal0_y_sign_bit = " << StrokedPath::point::normal0_y_sign_bit << "\n" - << std::setw(40) << "stroked_normal1_y_sign_bit = " << StrokedPath::point::normal1_y_sign_bit << "\n" - << std::setw(40) << "stroked_sin_sign_bit = " << StrokedPath::point::sin_sign_bit << "\n" - << std::setw(40) << "stroked_adjustable_cap_ending_bit = " << StrokedPath::point::adjustable_cap_ending_bit << "\n" - << std::setw(40) << "stroked_bevel_edge_bit = " << StrokedPath::point::bevel_edge_bit << "\n"; + << std::setw(40) << "stroked_number_offset_types = " << StrokedPoint::number_offset_types << "\n" + << std::setw(40) << "stroked_offset_type_bit0 = " << StrokedPoint::offset_type_bit0 << "\n" + << std::setw(40) << "stroked_offset_type_num_bits = " << StrokedPoint::offset_type_num_bits << "\n" + << std::setw(40) << "stroked_boundary_bit = " << StrokedPoint::boundary_bit << "\n" + << std::setw(40) << "stroked_depth_bit0 = " << StrokedPoint::depth_bit0 << "\n" + << std::setw(40) << "stroked_depth_num_bits = " << StrokedPoint::depth_num_bits << "\n" + << std::setw(40) << "stroked_join_bit = " << StrokedPoint::join_bit << "\n" + << std::setw(40) << "stroked_number_common_bits = " << StrokedPoint::number_common_bits << "\n" + << std::setw(40) << "stroked_normal0_y_sign_bit = " << StrokedPoint::normal0_y_sign_bit << "\n" + << std::setw(40) << "stroked_normal1_y_sign_bit = " << StrokedPoint::normal1_y_sign_bit << "\n" + << std::setw(40) << "stroked_sin_sign_bit = " << StrokedPoint::sin_sign_bit << "\n" + << std::setw(40) << "stroked_adjustable_cap_ending_bit = " << StrokedPoint::adjustable_cap_ending_bit << "\n" + << std::setw(40) << "stroked_bevel_edge_bit = " << StrokedPoint::bevel_edge_bit << "\n"; painter_test P; return P.main(argc, argv); diff --git a/fastuidraw-config.in b/fastuidraw-config.in index c6856fbcb..041432058 100644 --- a/fastuidraw-config.in +++ b/fastuidraw-config.in @@ -6,6 +6,7 @@ print_help() { echo " --debug: indicate to compile/link for debug" echo " --cflags: print compile flags" echo " --libs: print link flags" + echo " --static: modifier to indicate statically linking against FastUIDraw" echo " --nodirs: exclude directory directives of FastUIDraw install location" echo " --libdir=/some/path: specify library directory" echo " --incdir=/some/path: specify include directory" @@ -33,6 +34,8 @@ fi mode=unknown +static_linking=0 + print_cflags=0 print_libs=0 print_dirs=1 @@ -80,6 +83,7 @@ while test $# -gt 0; do --ngl) add_ngl=1 ;; --ngles) add_ngles=1 ;; --negl) add_negl=1 ;; + --static) static_linking=1 ;; --help) printhelp=1 ;; *) print_help @@ -92,11 +96,11 @@ cflags= base_cflags= libs= -base_libs= +dir_libs= if [ "$print_dirs" = "1" ]; then base_cflags=-I$include_location - base_libs=-L$lib_location + dir_libs=-L$lib_location fi if [ "$printhelp" = "1" ]; then @@ -121,7 +125,7 @@ if [ "$print_cflags" = "1" ] || [ "$print_libs" = "1" ]; then fi fi -libs="@FASTUIDRAW_LIBS@ $base_libs -lFastUIDraw_$mode" +libs="$dir_libs -lFastUIDraw_$mode" cflags="$base_cflags" if [ "$mode" = "release" ]; then @@ -157,6 +161,16 @@ if [ "$print_cflags" = "1" ]; then echo $cflags fi +if [ "$static_linking" = "1" ]; then + pre_link="-Wl,--whole-archive,-Bstatic" + post_link="-Wl,--no-whole-archive -static-libstdc++ -static-libgcc" + base_libs="@FASTUIDRAW_DEPS_STATIC_LIBS@" +else + pre_link= + post_link= + base_libs="@FASTUIDRAW_DEPS_LIBS@" +fi + if [ "$print_libs" = "1" ]; then - echo $libs + echo $base_libs $pre_link $libs $post_link fi diff --git a/fastuidraw.pc.in b/fastuidraw.pc.in index 902913c3c..fa9ebf70f 100644 --- a/fastuidraw.pc.in +++ b/fastuidraw.pc.in @@ -9,5 +9,5 @@ Version: Requires: freetype2 Conflicts: fastuidraw-@OTHER_TYPE@ Cflags: -I${includedir} @FASTUIDRAW_CFLAGS@ -Libs: -L${libdir} -lFastUIDraw_@TYPE@ -lm +Libs: -L${libdir} -lFastUIDraw_@TYPE@ Libs.private: diff --git a/inc/fastuidraw/arc_tessellated_path.hpp b/inc/fastuidraw/arc_tessellated_path.hpp new file mode 100644 index 000000000..fcdd84a7c --- /dev/null +++ b/inc/fastuidraw/arc_tessellated_path.hpp @@ -0,0 +1,378 @@ +/*! + * \file arc_tessellated_path.hpp + * \brief file arc_tessellated_path.hpp + * + * Copyright 2018 by Intel. + * + * Contact: kevin.rogovin@intel.com + * + * This Source Code Form is subject to the + * terms of the Mozilla Public License, v. 2.0. + * If a copy of the MPL was not distributed with + * this file, You can obtain one at + * http://mozilla.org/MPL/2.0/. + * + * \author Kevin Rogovin + * + */ + + +#pragma once + + +#include +#include +#include +#include +#include + +namespace fastuidraw { + +///@cond +class Path; +class ArcStrokedPath; +///@endcond + +/*!\addtogroup Paths + * @{ + */ + +/*! + * \brief + * An ArcTessellatedPath represents the tessellation of a Path + * into line segments and arcs. + * + * A single contour of a ArcTessellatedPath is constructed from + * a single \ref PathContour of the source \ref Path. Each + * edge of a contour of a ArcTessellatedPath is contructed from + * a single \ref PathContour::interpolator_base of the source \ref + * PathContour. The ordering of the contours of a + * ArcTessellatedPath is the same ordering as the source + * \ref PathContour objects of the source \ref Path. Also, + * the ordering of edges within a contour is the same ordering + * as the \ref PathContour::interpolator_base objects of + * the source \ref PathContour. In particular, for each contour + * of a ArcTessellatedPath, the closing edge is the last edge. + */ +class ArcTessellatedPath: + public reference_counted::non_concurrent +{ +public: + /*! + */ + enum segment_type_t + { + arc_segment, + line_segment, + }; + + /*! + * \brief + * A TessellationParams stores how finely to tessellate + * the curves of a path. + */ + class TessellationParams + { + public: + /*! + * Ctor, initializes values. + */ + TessellationParams(void): + m_threshhold(1.0f), + m_max_recursion(5) + {} + + /*! + * Provided as a conveniance. Equivalent to + * \code + * m_threshhold_type = tp; + * \endcode + * \param p value to which to assign to \ref m_threshhold + */ + TessellationParams& + threshhold(float p) + { + m_threshhold = p; + return *this; + } + + /*! + * Set the value of \ref m_max_segments. + * \param v value to which to assign to \ref m_max_segments + */ + TessellationParams& + max_recursion(unsigned int v) + { + m_max_recursion = v; + return *this; + } + + /*! + * Default value is 1.0. + */ + float m_threshhold; + + /*! + * Maximum number of times to cut a single edge in + * half. Default value is 5. + */ + unsigned int m_max_recursion; + }; + + /*! + * \brief + * Represents segment of an arc-tessellated path. + */ + class segment + { + public: + /*! + * Specifies the segment type which in turn determines the + * meaning of \ref m_p, \ref m_data and \ref m_radius + */ + enum segment_type_t m_type; + + /*! + * If \ref m_type is \ref line_segment, then gives the + * start position of the segment. If \ref m_type is \ref + * arc_segment, gives the center of the arc. + */ + vec2 m_p; + + /*! + * If \ref m_type is \ref line_segment, then gives the + * the position where the segment ends. If \ref m_type + * is \ref arc_segment, gives the start and end angles + * of the arc. + */ + vec2 m_data; + + /*! + * Only valid if \ref m_type is \ref arc_segment; gives + * the radius of the arc. + */ + float m_radius; + + /*! + * Gives the length of the segment. + */ + float m_length; + + /*! + * Gives the distance of the start of the segment from + * the start of the edge (i.e PathContour::interpolator_base). + */ + float m_distance_from_edge_start; + + /*! + * Gives the distance of the start of segment to the + * start of the -contour-. + */ + float m_distance_from_contour_start; + + /*! + * Gives the length of the edge (i.e. + * PathContour::interpolator_base) on which the + * segment lies. This value is the same for all + * segments along a fixed edge. + */ + float m_edge_length; + + /*! + * Gives the length of the contour open on which + * the segment lies. This value is the same for all + * segments along a fixed contour. + */ + float m_open_contour_length; + + /*! + * Gives the length of the contour closed on which + * the segment lies. This value is the same for all + * segments along a fixed contour. + */ + float m_closed_contour_length; + }; + + /*! + * \brief + * A wrapper over a dynamic array of \ref segment objects; + * segment values added to SegmentStorage must be added + * in order of time along the domain of a \ref + * PathContour::interpolate_base + */ + class SegmentStorage:fastuidraw::noncopyable + { + public: + /*! + * Add a \ref segment to the SegmentStorage. + */ + void + add_segment(const segment&); + + private: + SegmentStorage(void) {} + ~SegmentStorage() {} + + friend class ArcTessellatedPath; + void *m_d; + }; + + /*! + * Ctor. Construct a TessellatedPath from a Path + * \param input source path to tessellate + * \param P parameters on how to tessellate the source Path + */ + ArcTessellatedPath(const Path &input, TessellationParams P); + + ~ArcTessellatedPath(); + + /*! + * Returns the tessellation parameters used to construct + * this TessellatedPath. + */ + const TessellationParams& + tessellation_parameters(void) const; + + /*! + * Returns the tessellation threshold achieved + */ + float + effective_threshhold(void) const; + + /*! + * Returns the maximum number of segments any edge needed + */ + unsigned int + max_segments(void) const; + + /*! + * Returns the maximum recursion employed by any edge + */ + unsigned int + max_recursion(void) const; + + /*! + * Returns all the segment data + */ + c_array + segment_data(void) const; + + /*! + * Returns the number of contours + */ + unsigned int + number_contours(void) const; + + /*! + * Returns the range into segment_data() for the named + * contour. + * \param contour which path contour to query, must have + * that 0 <= contour < number_contours() + */ + range_type + contour_range(unsigned int contour) const; + + /*! + * Returns the range into segment_data() for the named + * contour lacking the closing edge. + * replicated (because the derivatives are different). + * \param contour which path contour to query, must have + * that 0 <= contour < number_contours() + */ + range_type + unclosed_contour_range(unsigned int contour) const; + + /*! + * Returns the segment data of the named contour including + * the closing edge. Provided as a conveniance equivalent to + * \code + * segment_data().sub_array(contour_range(contour)) + * \endcode + * \param contour which path contour to query, must have + * that 0 <= contour < number_contours() + */ + c_array + contour_segment_data(unsigned int contour) const; + + /*! + * Returns the segment data of the named contour + * lacking the segment data of the closing edge. + * Provided as a conveniance, equivalent to + * \code + * segment_data().sub_array(unclosed_contour_range(contour)) + * \endcode + * \param contour which path contour to query, must have + * that 0 <= contour < number_contours() + */ + c_array + unclosed_contour_segment_data(unsigned int contour) const; + + /*! + * Returns the number of edges for the named contour + * \param contour which path contour to query, must have + * that 0 <= contour < number_contours() + */ + unsigned int + number_edges(unsigned int contour) const; + + /*! + * Returns the range into segment_data(void) + * for the named edge of the named contour. + * \param contour which path contour to query, must have + * that 0 <= contour < number_contours() + * \param edge which edge of the contour to query, must + * have that 0 <= edge < number_edges(contour) + */ + range_type + edge_range(unsigned int contour, unsigned int edge) const; + + /*! + * Returns the segment data of the named edge of the + * named contour, provided as a conveniance, equivalent + * to + * \code + * segment_data().sub_array(edge_range(contour, edge)) + * \endcode + * \param contour which path contour to query, must have + * that 0 <= contour < number_contours() + * \param edge which edge of the contour to query, must + * have that 0 <= edge < number_edges(contour) + */ + c_array + edge_segment_data(unsigned int contour, unsigned int edge) const; + + /*! + * Returns the minimum point of the bounding box of + * the tessellation. + */ + vec2 + bounding_box_min(void) const; + + /*! + * Returns the maximum point of the bounding box of + * the tessellation. + */ + vec2 + bounding_box_max(void) const; + + /*! + * Returns the dimensions of the bounding box + * of the tessellated path. + */ + vec2 + bounding_box_size(void) const; + + /*! + * Returns this ArcTessellatedPath stroked. The ArcStrokedPath object + * is constructed lazily. + */ + const reference_counted_ptr& + stroked(void) const; + +private: + void *m_d; +}; + +/*! @} */ + +} diff --git a/inc/fastuidraw/painter/arc_stroked_path.hpp b/inc/fastuidraw/painter/arc_stroked_path.hpp new file mode 100644 index 000000000..ae62e724e --- /dev/null +++ b/inc/fastuidraw/painter/arc_stroked_path.hpp @@ -0,0 +1,199 @@ +/*! + * \file stroked_path.hpp + * \brief file stroked_path.hpp + * + * Copyright 2016 by Intel. + * + * Contact: kevin.rogovin@intel.com + * + * This Source Code Form is subject to the + * terms of the Mozilla Public License, v. 2.0. + * If a copy of the MPL was not distributed with + * this file, You can obtain one at + * http://mozilla.org/MPL/2.0/. + * + * \author Kevin Rogovin + * + */ + + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fastuidraw { + +///@cond +class ArcTessellatedPath; +class Path; +class PainterAttribute; +class PainterAttributeData; +///@endcond + +/*!\addtogroup Paths + * @{ + */ + +/*! + * \brief + * A ArcStrokedPath represents the data needed to draw a path stroked. + * In contrast to StrokedPath, an ArcStrokedPath will use arc-edges + * to approximate the edges of a Path, resulting in far few vertices. + * It contains -all- the data needed to stroke a path regardless of + * stroking style. In particular, for a given TessellatedPath, + * one only needs to construct a ArcStrokedPath once regardless + * of how one strokes the original path for drawing. + * + * The data is stored as \ref PainterAttributeData, a seperate + * object for edges, each type of join and each type of caps. + * What chunks to use from these objects is computed by + * the member function compute_chunks(); the PainterAttributeData + * chunking for joins and caps is the same regardless of + * the cap and join type. + */ +class ArcStrokedPath: + public reference_counted::non_concurrent +{ +public: + /*! + * \brief + * Opaque object to hold work room needed for functions + * of ArcStrokedPath that require scratch space. + */ + class ScratchSpace:fastuidraw::noncopyable + { + public: + ScratchSpace(void); + ~ScratchSpace(); + private: + friend class ArcStrokedPath; + void *m_d; + }; + + /*! + * \brief + * Object to hold the output of a chunk query via compute_chunks(). + */ + class ChunkSet:fastuidraw::noncopyable + { + public: + ChunkSet(void); + + ~ChunkSet(); + + /*! + * The list of chunks to take from ArcStrokedPath::edges() + * that have visible content. + */ + c_array + edge_chunks(void) const; + + private: + friend class ArcStrokedPath; + void *m_d; + }; + + /*! + * Enumeration to select the chunk of all edges of the closing + * edges or all edges of the non-closing edges of a ArcStrokedPath. + */ + enum chunk_selection + { + /*! + * Select the chunk that holds all the edges of + * ONLY the non-closing edges of the ArcStrokedPath + */ + all_non_closing = StrokedCapsJoins::all_non_closing, + + /*! + * Select the chunk that holds all the edges of + * ONLY the closing edges of the ArcStrokedPath + */ + all_closing = StrokedCapsJoins::all_closing, + }; + + /*! + * Ctor. Construct a ArcStrokedPath from the data + * of a TessellatedPath. + * \param P source ArcTessellatedPath + */ + explicit + ArcStrokedPath(const ArcTessellatedPath &P); + + ~ArcStrokedPath(); + + /*! + * Given a set of clip equations in clip coordinates + * and a tranformation from local coordiante to clip + * coordinates, compute what chunks are not completely + * culled by the clip equations. + * \param scratch_space scratch space for computations + * \param clip_equations array of clip equations + * \param clip_matrix_local 3x3 transformation from local (x, y, 1) + * coordinates to clip coordinates. + * \param recip_dimensions holds the reciprocal of the dimensions of the viewport + * \param pixels_additional_room amount in -pixels- to push clip equations by + * to grab additional edges + * \param item_space_additional_room amount in local coordinates to push clip + * equations by to grab additional edges + * draw the closing edges of each contour + * \param include_closing_edges if true include the chunks needed to + * \param max_attribute_cnt only allow those chunks for which have no more + * than max_attribute_cnt attributes + * \param max_index_cnt only allow those chunks for which have no more + * than max_index_cnt indices + * \param take_joins_outside_of_region if true, take even those joins outside of the region + * (this is for handling miter-joins where the weather + * or not a miter-join is included is also a function of + * the miter-limit when stroking). + * \param[out] dst location to which to write output + */ + void + compute_chunks(ScratchSpace &scratch_space, + c_array clip_equations, + const float3x3 &clip_matrix_local, + const vec2 &recip_dimensions, + float pixels_additional_room, + float item_space_additional_room, + bool include_closing_edges, + unsigned int max_attribute_cnt, + unsigned int max_index_cnt, + ChunkSet &dst) const; + + /*! + * Returns the data to draw the edges of a stroked path. + */ + const PainterAttributeData& + edges(void) const; + + /*! + * Return the chunk to feed to edges() that holds all + * the edges of the closing edges or all the edges + * of the non-closing edges. + * \param c select edges of closing or non-closing edges + */ + unsigned int + chunk_of_edges(enum chunk_selection c) const; + + /*! + * Returns the StrokedCapsJoins of the ArcStrokedPath that + * provides the attribute data for the stroking of the + * joins and caps. + */ + const StrokedCapsJoins& + caps_joins(void) const; + +private: + void *m_d; +}; + +/*! @} */ + +} diff --git a/inc/fastuidraw/painter/painter.hpp b/inc/fastuidraw/painter/painter.hpp index 95803211c..32a98e22a 100644 --- a/inc/fastuidraw/painter/painter.hpp +++ b/inc/fastuidraw/painter/painter.hpp @@ -493,28 +493,6 @@ namespace fastuidraw bool with_shader_based_anti_aliasing, const reference_counted_ptr &call_back = reference_counted_ptr()); - /*! - * Stroke a path using PainterShaderSet::pixel_width_stroke_shader() - * of default_shaders(). - * \param draw data for how to draw - * \param path Path to stroke - * \param close_contours if true, draw the closing edges (and joins) of each contour - * of the path - * \param cp cap style - * \param js join style - * \param with_shader_based_anti_aliasing draw the path in two passes using shader - * based anti-aliasing; one should NEVER - * have this as true if the surface passed - * in begin() is a multi-sampled surface - * \param call_back if non-nullptr handle, call back called when attribute data - * is added. - */ - void - stroke_path_pixel_width(const PainterData &draw, const Path &path, - bool close_contours, enum PainterEnums::cap_style cp, enum PainterEnums::join_style js, - bool with_shader_based_anti_aliasing, - const reference_counted_ptr &call_back = reference_counted_ptr()); - /*! * Stroke a path dashed. * \param shader shader with which to draw @@ -584,27 +562,6 @@ namespace fastuidraw bool with_shader_based_anti_aliasing, const reference_counted_ptr &call_back = reference_counted_ptr()); - /*! - * Stroke a path using PainterShaderSet::pixel_width_dashed_stroke_shader() of default_shaders(). - * \param draw data for how to draw - * \param path Path to stroke - * \param close_contours if true, draw the closing edges (and joins) of each contour - * of the path - * \param cp cap style - * \param js join style - * \param with_shader_based_anti_aliasing draw the path in two passes using shader - * based anti-aliasing; one should NEVER - * have this as true if the surface passed - * in begin() is a multi-sampled surface - * \param call_back if non-nullptr handle, call back called when attribute data - * is added. - */ - void - stroke_dashed_path_pixel_width(const PainterData &draw, const Path &path, - bool close_contours, enum PainterEnums::cap_style cp, enum PainterEnums::join_style js, - bool with_shader_based_anti_aliasing, - const reference_counted_ptr &call_back = reference_counted_ptr()); - /*! * Fill a path. * \param shader shader with which to fill the attribute data diff --git a/inc/fastuidraw/painter/painter_dashed_stroke_params.hpp b/inc/fastuidraw/painter/painter_dashed_stroke_params.hpp index 692b596a6..f43fd71c4 100644 --- a/inc/fastuidraw/painter/painter_dashed_stroke_params.hpp +++ b/inc/fastuidraw/painter/painter_dashed_stroke_params.hpp @@ -19,6 +19,7 @@ #pragma once #include +#include #include namespace fastuidraw @@ -117,10 +118,12 @@ namespace fastuidraw width(void) const; /*! - * Set the value of width(void) const + * Set the value of width(void) const, + * values are clamped to be non-negative. */ PainterDashedStrokeParams& width(float f); + /*! * The stroking radius, equivalent to * \code @@ -140,6 +143,20 @@ namespace fastuidraw PainterDashedStrokeParams& radius(float f); + /*! + * Returns the units of the stroking, default value is + * \ref PainterStrokeParams::path_stroking_units + */ + enum PainterStrokeParams::stroking_units_t + stroking_units(void) const; + + /*! + * Set the value of stroking_units(void) const, + * values are clamped to be non-negative. + */ + PainterDashedStrokeParams& + stroking_units(enum PainterStrokeParams::stroking_units_t); + /*! * The dashed offset, i.e. the starting point of the * dash pattern to start dashed stroking. @@ -169,16 +186,10 @@ namespace fastuidraw /*! * Constructs and returns a DashEvaluator compatible * with the data of PainterDashedStrokeParams. - * \param pixel_width_stroking if true return an object to - * be used when stroking width - * is in pixels; if false return - * an object to be used when - * stroking width is in coordinates - * of the path. */ static reference_counted_ptr - dash_evaluator(bool pixel_width_stroking); + dash_evaluator(void); /*! * Returns a StrokingDataSelectorBase suitable for @@ -186,7 +197,7 @@ namespace fastuidraw */ static reference_counted_ptr - stroking_data_selector(bool pixel_width_stroking); + stroking_data_selector(void); }; /*! @} */ diff --git a/inc/fastuidraw/painter/painter_shader_set.hpp b/inc/fastuidraw/painter/painter_shader_set.hpp index 2bce859db..c6998e4db 100644 --- a/inc/fastuidraw/painter/painter_shader_set.hpp +++ b/inc/fastuidraw/painter/painter_shader_set.hpp @@ -112,22 +112,6 @@ namespace fastuidraw PainterShaderSet& stroke_shader(const PainterStrokeShader &sh); - /*! - * Shader set for stroking of paths where the stroking - * width is given in pixels. The stroking parameters are - * given by PainterStrokeParams. - */ - const PainterStrokeShader& - pixel_width_stroke_shader(void) const; - - /*! - * Set the value returned by - * pixel_width_stroke_shader(void) const. - * \param sh value to use - */ - PainterShaderSet& - pixel_width_stroke_shader(const PainterStrokeShader &sh); - /*! * Shader set for stroking of paths where the stroking * width is given in same units as the original path. @@ -143,22 +127,6 @@ namespace fastuidraw PainterShaderSet& dashed_stroke_shader(const PainterDashedStrokeShaderSet &sh); - /*! - * Shader set for stroking of paths where the stroking - * width is given in pixels. The stroking parameters are - * given by PainterStrokeParams. - */ - const PainterDashedStrokeShaderSet& - pixel_width_dashed_stroke_shader(void) const; - - /*! - * Set the value returned by - * pixel_width_stroke_shader(void) const. - * \param sh value to use - */ - PainterShaderSet& - pixel_width_dashed_stroke_shader(const PainterDashedStrokeShaderSet &sh); - /*! * Shader for filling of paths. */ diff --git a/inc/fastuidraw/painter/painter_stroke_params.hpp b/inc/fastuidraw/painter/painter_stroke_params.hpp index 04af11ddb..49bed2928 100644 --- a/inc/fastuidraw/painter/painter_stroke_params.hpp +++ b/inc/fastuidraw/painter/painter_stroke_params.hpp @@ -35,6 +35,24 @@ namespace fastuidraw class PainterStrokeParams:public PainterItemShaderData { public: + /*! + * \brief + * Enumeration to specify the units of the stroking radius + */ + enum stroking_units_t + { + /*! + * Indicates that the stroking units is in local + * coordinates of the Path being stroking + */ + path_stroking_units, + + /*! + * Indicates that the stroking units are in pixels + */ + pixel_stroking_units, + }; + /*! * \brief * Enumeration that provides offsets for the stroking @@ -42,7 +60,14 @@ namespace fastuidraw */ enum stroke_data_offset_t { - stroke_radius_offset, /*!< offset to stroke radius (packed as float) */ + /*! + * Offset to stroke radius (packed as float). + * The absolute value gives the stroking radius, + * if teh value is negative then the stroking radius + * is in units of pixels, if positive it is in local + * path coordinates. + */ + stroke_radius_offset, stroke_miter_limit_offset, /*!< offset to stroke miter limit (packed as float) */ stroke_data_size /*!< size of data for stroking*/ @@ -66,13 +91,14 @@ namespace fastuidraw miter_limit(float f); /*! - * The stroking width + * The stroking width, always non-negative */ float width(void) const; /*! - * Set the value of width(void) const + * Set the value of width(void) const, + * values are clamped to be non-negative. */ PainterStrokeParams& width(float f); @@ -96,13 +122,27 @@ namespace fastuidraw PainterStrokeParams& radius(float f); + /*! + * Returns the units of the stroking, default value is + * \ref path_stroking_units + */ + enum stroking_units_t + stroking_units(void) const; + + /*! + * Set the value of stroking_units(void) const, + * values are clamped to be non-negative. + */ + PainterStrokeParams& + stroking_units(enum stroking_units_t); + /*! * Returns a StrokingDataSelectorBase suitable for * PainterStrokeParams */ static reference_counted_ptr - stroking_data_selector(bool pixel_width_stroking); + stroking_data_selector(void); }; /*! @} */ diff --git a/inc/fastuidraw/painter/stroked_caps_joins.hpp b/inc/fastuidraw/painter/stroked_caps_joins.hpp new file mode 100644 index 000000000..4bf7fa66a --- /dev/null +++ b/inc/fastuidraw/painter/stroked_caps_joins.hpp @@ -0,0 +1,332 @@ +/*! + * \file stroked_caps_joins.hpp + * \brief file stroked_caps_joins.hpp + * + * Copyright 2018 by Intel. + * + * Contact: kevin.rogovin@intel.com + * + * This Source Code Form is subject to the + * terms of the Mozilla Public License, v. 2.0. + * If a copy of the MPL was not distributed with + * this file, You can obtain one at + * http://mozilla.org/MPL/2.0/. + * + * \author Kevin Rogovin + * + */ + + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace fastuidraw { + +///@cond +class PainterAttributeData; +class DashEvaluatorBase; +///@endcond + +/*!\addtogroup Paths + * @{ + */ + +/*! + * \brief + * A StrokedCapsJoins represents the data needed to draw a the joins + * and caps of a stroked path. It contains -all- the data needed to + * do regardless of stroking parameters. + * + * The data is stored as \ref PainterAttributeData, a seperate + * object for each type of join and each type of cap. What chunks + * to use from these objects is computed by the member function + * compute_chunks(); the PainterAttributeData chunking for joins + * and caps is the same regardless of the cap and join type. + */ +class StrokedCapsJoins:noncopyable +{ +public: + /*! + * \brief + * Opaque object to hold work room needed for functions + * of StrokedPath that require scratch space. + */ + class ScratchSpace:fastuidraw::noncopyable + { + public: + ScratchSpace(void); + ~ScratchSpace(); + private: + friend class StrokedCapsJoins; + void *m_d; + }; + + /*! + * \brief + * Object to hold the output of a chunk query via compute_chunks(). + */ + class ChunkSet:fastuidraw::noncopyable + { + public: + ChunkSet(void); + + ~ChunkSet(); + + /*! + * The list of chunks to take from any of StrokedCapsJoins::bevel_joins(), + * StrokedCapsJoins::miter_clip_joins(), StrokedCapsJoins::miter_bevel_joins(), + * StrokedCapsJoins::miter_joins() or StrokedCapsJoins::rounded_joins() that + * have visible content. + */ + c_array + join_chunks(void) const; + + /*! + * The list of chunks to take from any of StrokedCapsJoins::square_caps(), + * StrokedCapsJoins::adjustable_caps() or StrokedCapsJoins::rounded_caps() + * that have visible content. + */ + c_array + cap_chunks(void) const; + + private: + friend class StrokedCapsJoins; + void *m_d; + }; + + /*! + * Enumeration to select the chunk of all joins + * of the closing edges or non-closing edges + */ + enum chunk_selection + { + /*! + * Select the chunk that holds all the joins + * of ONLY the non-closing edges + */ + all_non_closing, + + /*! + * Select the chunk that holds all the joins + * of ONLY the closing edges + */ + all_closing, + }; + + /*! + * \brief + * A Builder is used to specify the nature of the contours + * from which to geneate joins and caps. + */ + class Builder:fastuidraw::noncopyable + { + public: + Builder(void); + ~Builder(); + + /*! + * Begin a contour + * \param start_pt starting point of contour + * \param start_direction unit vector from start point to next point + */ + void + begin_contour(const vec2 &start_pt, + const vec2 &start_direction); + + + /*! + * Add a join + * \param join_pt location of join + * \param distance_from_previous_join distance from previous join + * \param direction_into_join unit vector of path into the join + * \param direction_leaving_join unit vector of path leaving the join + */ + void + add_join(const vec2 &join_pt, + float distance_from_previous_join, + const vec2 &direction_into_join, + const vec2 &direction_leaving_join); + + /*! + * End the contour. + * \param distance_from_previous_join distance from previous join + * \param direction_into_join unit vector of path into the join that + * links the closing edge to the 1st point + */ + void + end_contour(float distance_from_previous_join, + const vec2 &direction_into_join); + + private: + friend class StrokedCapsJoins; + void *m_d; + }; + + /*! + * Ctor. + * \param b Builder holding the data to generate joins and caps + */ + explicit + StrokedCapsJoins(const Builder &b); + + ~StrokedCapsJoins(); + + /*! + * Given a set of clip equations in clip coordinates + * and a tranformation from local coordiante to clip + * coordinates, compute what chunks are not completely + * culled by the clip equations. + * \param scratch_space scratch space for computations + * \param dash_evaluator if doing dashed stroking, the dash evalulator will cull + * joins not to be drawn, if nullptr only those joins not in the + * visible area defined by clip_equations are culled. + * \param dash_data data to pass to dast evaluator + * \param clip_equations array of clip equations + * \param clip_matrix_local 3x3 transformation from local (x, y, 1) + * coordinates to clip coordinates. + * \param recip_dimensions holds the reciprocal of the dimensions of the viewport + * \param pixels_additional_room amount in -pixels- to push clip equations by + * to grab additional edges + * \param item_space_additional_room amount in local coordinates to push clip + * equations by to grab additional edges + * draw the closing edges of each contour + * \param include_closing_edges if true include the chunks needed to + * \param max_attribute_cnt only allow those chunks for which have no more + * than max_attribute_cnt attributes + * \param max_index_cnt only allow those chunks for which have no more + * than max_index_cnt indices + * \param take_joins_outside_of_region if true, take even those joins outside of the region + * (this is for handling miter-joins where the weather + * or not a miter-join is included is also a function of + * the miter-limit when stroking). + * \param[out] dst location to which to write output + */ + void + compute_chunks(ScratchSpace &scratch_space, + const DashEvaluatorBase *dash_evaluator, + const PainterShaderData::DataBase *dash_data, + c_array clip_equations, + const float3x3 &clip_matrix_local, + const vec2 &recip_dimensions, + float pixels_additional_room, + float item_space_additional_room, + bool include_closing_edges, + unsigned int max_attribute_cnt, + unsigned int max_index_cnt, + bool take_joins_outside_of_region, + ChunkSet &dst) const; + + /*! + * Returns the number of joins of the StrokedPath + * \param include_joins_of_closing_edge if false disclude from the count, + * this joins of the closing edges + */ + unsigned int + number_joins(bool include_joins_of_closing_edge) const; + + /*! + * Returns a chunk value for Painter::attribute_data_chunk() + * and related calls to feed the return value to any of + * bevel_joins(), miter_clip_joins(), miter_bevel_joins(), + * miter_joins() or rounded_joins() to fetch the chunk + * of the named join. + * \param J join ID with 0 <= J < number_joins(true) + */ + unsigned int + join_chunk(unsigned int J) const; + + /*! + * Return the chunk to feed to any of bevel_joins(), + * miter_clip_joins(), miter_bevel_joins(), + * miter_joins() or rounded_joins() that holds all + * the joins of the closing edges or all the joins + * of the non-closing edges. + * \param c select joins of closing or non-closing edges + */ + unsigned int + chunk_of_joins(enum chunk_selection c) const; + + /*! + * Return the chunk to feed any of square_caps(), + * adjustable_caps() or rounded_caps() that holds + * data for all caps of this StrokedPath. + */ + unsigned int + chunk_of_caps(void) const; + + /*! + * Returns the data to draw the square caps of a stroked path. + */ + const PainterAttributeData& + square_caps(void) const; + + /*! + * Returns the data to draw the caps of a stroked path used + * when stroking with a dash pattern. + */ + const PainterAttributeData& + adjustable_caps(void) const; + + /*! + * Returns the data to draw the bevel joins of a stroked path. + */ + const PainterAttributeData& + bevel_joins(void) const; + + /*! + * Returns the data to draw the miter joins of a stroked path, + * if the miter-limit is exceeded on stroking, the miter-join + * is clipped to the miter-limit. + */ + const PainterAttributeData& + miter_clip_joins(void) const; + + /*! + * Returns the data to draw the miter joins of a stroked path, + * if the miter-limit is exceeded on stroking, the miter-join + * is to be drawn as a bevel join. + */ + const PainterAttributeData& + miter_bevel_joins(void) const; + + /*! + * Returns the data to draw the miter joins of a stroked path, + * if the miter-limit is exceeded on stroking, the miter-join + * end point is clamped to the miter-distance. + */ + const PainterAttributeData& + miter_joins(void) const; + + /*! + * Returns the data to draw rounded joins of a stroked path. + * \param thresh will return rounded joins so that the distance + * between the approximation of the round and the + * actual round is no more than thresh. + */ + const PainterAttributeData& + rounded_joins(float thresh) const; + + /*! + * Returns the data to draw rounded caps of a stroked path. + * \param thresh will return rounded caps so that the distance + * between the approximation of the round and the + * actual round is no more than thresh. + */ + const PainterAttributeData& + rounded_caps(float thresh) const; + +private: + void *m_d; +}; + +/*! @} */ + + +} diff --git a/inc/fastuidraw/painter/stroked_path.hpp b/inc/fastuidraw/painter/stroked_path.hpp index 0b1bd80ac..98796322d 100644 --- a/inc/fastuidraw/painter/stroked_path.hpp +++ b/inc/fastuidraw/painter/stroked_path.hpp @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include namespace fastuidraw { @@ -59,537 +61,6 @@ class StrokedPath: public reference_counted::non_concurrent { public: - /*! - * \brief - * A point holds the data for a point of stroking. - * The data is so that changing the stroking width - * or miter limit does not change the stroking data. - */ - class point - { - public: - - /*! - * \brief - * Enumeration for specifing how to compute - * \ref offset_vector()). - */ - enum offset_type_t - { - /*! - * The point is for an edge of the path, - * point signifies the start of a sub-edge - * (quad) of drawing an edge. - */ - offset_start_sub_edge, - - /*! - * The point is for an edge of the path, - * point signifies the end of a sub-edge - * (quad) of drawing an edge. - */ - offset_end_sub_edge, - - /*! - * The point is at a position that has the - * same value as point on an edge but the - * point itself is part of a cap or join. - */ - offset_shared_with_edge, - - /*! - * The point is for a boundary point of a rounded join of the path - */ - offset_rounded_join, - - /*! - * Point type for miter-clip join point whose position - * depends on the miter-limit. - */ - offset_miter_clip_join, - - /*! - * Point type for miter-clip join point whose position - * depends on the miter-limit where the lambda of the - * computation is negatted. - */ - offset_miter_clip_join_lambda_negated, - - /*! - * Point type for miter-bevel join point whose position - * depends on the miter-limit. - */ - offset_miter_bevel_join, - - /*! - * Point type for miter join whose position depends on - * the miter-limit. - */ - offset_miter_join, - - /*! - * The point is for a boundary point of a rounded cap of the path - */ - offset_rounded_cap, - - /*! - * The point is for a boundary point of a square cap of the path - */ - offset_square_cap, - - /*! - * The point is from \ref adjustable_caps() point set. - * It is for a point for a cap at the start of a contour. - * These points are for dashed stroking with caps; they - * contain data to allow one from a vertex shader to extend - * or shrink the cap area correctly to implement dashed - * stroking. - */ - offset_adjustable_cap_contour_start, - - /*! - * The point is from \ref adjustable_caps() point set. - * It is for a point for a cap at the end of a contour. - * These points are for dashed stroking with caps; they - * contain data to allow one from a vertex shader to extend - * or shrink the cap area correctly to implement dashed - * stroking. - */ - offset_adjustable_cap_contour_end, - - /*! - * Number different point types with respect to rendering - */ - number_offset_types - }; - - /*! - * \brief - * Enumeration encoding of bits of point::m_packed_data - * common to all offset types. - */ - enum packed_data_bit_layout_common_t - { - /*! - * Bit0 for holding the offset_type() value - * of the point. - */ - offset_type_bit0 = 0, - - /*! - * number of bits needed to hold the - * offset_type() value of the point. - */ - offset_type_num_bits = 4, - - /*! - * Bit for holding boundary() value - * of the point - */ - boundary_bit = offset_type_bit0 + offset_type_num_bits, - - /*! - * Bit0 for holding the depth() value - * of the point - */ - depth_bit0, - - /*! - * number of bits needed to hold the - * depth() value of the point. - */ - depth_num_bits = 20, - - /*! - * Bit to indicate point is from a join. For these - * points, during dashed stroking, Painter does the - * check if a join should be drawn, as such when the - * bit is up encountered in a shader, the computation - * to check that it is drawn from dashing can be - * skipped and assume that fragments from such points - * are covered by the dash pattern. - */ - join_bit = depth_bit0 + depth_num_bits, - - /*! - * Number of bits used on common packed data - */ - number_common_bits, - }; - - /*! - * \brief - * Enumeration encoding of bits of point::m_packed_data - * for those with offset type \ref offset_rounded_join - */ - enum packed_data_bit_layout_rounded_join_t - { - /*! - * Bit for holding the the sign of - * the y-coordinate of the normal 0 - * for the offset_type() \ref - * offset_rounded_join. - */ - normal0_y_sign_bit = number_common_bits, - - /*! - * Bit for holding the the sign of - * the y-coordinate of the normal 1 - * for the offset_type() \ref - * offset_rounded_join. - */ - normal1_y_sign_bit, - - /*! - * Bit for holding the the sign of - * sin() value for the offset_type() - * \ref offset_rounded_join. - */ - sin_sign_bit, - }; - - /*! - * \brief - * Enumeration encoding of bits of point::m_packed_data for - * those with offset type \ref offset_adjustable_cap_contour_end - * or \ref offset_adjustable_cap_contour_start. - */ - enum packed_data_bit_adjustable_cap_t - { - /*! - * The bit is up if the point is for end of point - * of a cap (i.e. the side to be extended to make - * sure the entire cap near the end of edge is drawn). - */ - adjustable_cap_ending_bit = number_common_bits, - }; - - /*! - * \brief - * Enumeration encoding of bits of point::m_packed_data - * for those with offset type \ref offset_start_sub_edge - * or \ref offset_end_sub_edge. - */ - enum packed_data_bit_sub_edge_t - { - /*! - * The bit is up if the point is for the - * geometry of a bevel between two sub-edges. - */ - bevel_edge_bit = number_common_bits, - }; - - /*! - * \brief - * Enumeration holding bit masks generated from - * values in \ref packed_data_bit_layout_common_t, - * \ref packed_data_bit_layout_rounded_join_t, - * \ref packed_data_bit_adjustable_cap_t and \ref - * packed_data_bit_sub_edge_t. - */ - enum packed_data_bit_masks_t - { - /*! - * Mask generated for \ref offset_type_bit0 and - * \ref offset_type_num_bits - */ - offset_type_mask = FASTUIDRAW_MASK(offset_type_bit0, offset_type_num_bits), - - /*! - * Mask generated for \ref normal0_y_sign_bit - */ - normal0_y_sign_mask = FASTUIDRAW_MASK(normal0_y_sign_bit, 1), - - /*! - * Mask generated for \ref normal1_y_sign_bit - */ - normal1_y_sign_mask = FASTUIDRAW_MASK(normal1_y_sign_bit, 1), - - /*! - * Mask generated for \ref sin_sign_bit sin_sign_bit - */ - sin_sign_mask = FASTUIDRAW_MASK(sin_sign_bit, 1), - - /*! - * Mask generated for \ref boundary_bit - */ - boundary_mask = FASTUIDRAW_MASK(boundary_bit, 1), - - /*! - * Mask generated for \ref join_bit - */ - join_mask = FASTUIDRAW_MASK(join_bit, 1), - - /*! - * Mask generated for \ref adjustable_cap_ending_bit - */ - adjustable_cap_ending_mask = FASTUIDRAW_MASK(adjustable_cap_ending_bit, 1), - - /*! - * Mask generated for \ref bevel_edge_bit - */ - bevel_edge_mask = FASTUIDRAW_MASK(bevel_edge_bit, 1), - - /*! - * Mask generated for \ref depth_bit0 and \ref depth_num_bits - */ - depth_mask = FASTUIDRAW_MASK(depth_bit0, depth_num_bits), - }; - - /*! - * The base position of a point, taken directly from - * the TessellatedPath from which the - * StrokedPath is constructed. - */ - vec2 m_position; - - /*! - * Gives the offset vector used to help compute - * the point offset vector. - */ - vec2 m_pre_offset; - - /*! - * Provides an auxiliary offset data - */ - vec2 m_auxiliary_offset; - - /*! - * Gives the distance of the point from the start - * of the -edge- on which the point resides. - */ - float m_distance_from_edge_start; - - /*! - * Gives the distance of the point from the start - * of the -contour- on which the point resides. - */ - float m_distance_from_contour_start; - - /*! - * Gives the length of the edge on which the - * point lies. This value is the same for all - * points along a fixed edge. - */ - float m_edge_length; - - /*! - * Gives the length of the contour open on which - * the point lies. This value is the same for all - * points along a fixed contour. - */ - float m_open_contour_length; - - /*! - * Gives the length of the contour closed on which - * the point lies. This value is the same for all - * points along a fixed contour. - */ - float m_closed_contour_length; - - /*! - * Bit field with data packed as according to - * \ref packed_data_bit_layout_common_t, \ref - * packed_data_bit_layout_rounded_join_t, \ref - * packed_data_bit_adjustable_cap_t and \ref - * packed_data_bit_sub_edge_t. See also, - * \ref packed_data_bit_masks_t for bit masks - * generated. - */ - uint32_t m_packed_data; - - /*! - * Provides the point type from a value of \ref m_packed_data. - * The return value is one of the enumerations of - * \ref offset_type_t. - * \param packed_data_value value suitable for \ref m_packed_data - */ - static - enum offset_type_t - offset_type(uint32_t packed_data_value) - { - uint32_t v; - v = unpack_bits(offset_type_bit0, offset_type_num_bits, packed_data_value); - return static_cast(v); - } - - /*! - * Provides the point type for the point. The return value - * is one of the enumerations of \ref offset_type_t. - */ - enum offset_type_t - offset_type(void) const - { - return offset_type(m_packed_data); - } - - /*! - * When stroking the data, the depth test to only - * pass when the depth value is -strictly- larger - * so that a fixed pixel is not stroked twice by - * a single path. The value returned by depth() is - * a relative z-value for a vertex. The points - * drawn first have the largest z-values. - */ - uint32_t - depth(void) const - { - return unpack_bits(depth_bit0, depth_num_bits, m_packed_data); - } - - /*! - * Has value 0 or 1. If the value is 0, then - * the point is on the path. If the value has - * absolute value 1, then indicates a point that - * is on the boundary of the stroked path. The triangles - * produced from stroking are so that when - * on_boundary() is interpolated across the triangle - * the center of stroking the value is 0 and the - * value has value 1 on the boundary. - */ - int - on_boundary(void) const - { - return unpack_bits(boundary_bit, 1u, m_packed_data); - } - - /*! - * Computes the offset vector for a point. The final position - * of a point when stroking with a width of W is given by - * \code - * m_position + 0.5f * W * offset_vector(). - * \endcode - * The computation for offset_vector() is as follows. - * - For those with offset_type() being \ref offset_start_sub_edge, - * \ref offset_end_sub_edge and \ref offset_shared_with_edge, - * the offset is given by - * \code - * m_pre_offset - * \endcode - * In addition, for these offset types, \ref m_auxiliary_offset - * holds the the delta vector to the point on edge with - * which the point makes a quad. - * - For those with offset_type() being \ref offset_rounded_join, - * the value is given by the following code - * \code - * vec2 cs; - * - * cs.x() = m_auxiliary_offset.y(); - * cs.y() = sqrt(1.0 - cs.x() * cs.x()); - * - * if (m_packed_data & sin_sign_mask) - * cs.y() = -cs.y(); - * - * offset = cs - * \endcode - * In addition, the source data for join is encoded as follows: - * \code - * float t; - * vec2 n0, n1; - * - * t = m_auxiliary_offset.x(); - * n0.x() = m_pre_offset.x(); - * n1.x() = m_pre_offset.y(); - * - * n0.y() = sqrt(1.0 - n0.x() * n0.x()); - * n1.y() = sqrt(1.0 - n1.x() * n1.x()); - * - * if (m_packed_data & normal0_y_sign_mask) - * n0.y() = -n0.y(); - * - * if (m_packed_data & normal1_y_sign_mask) - * n1.y() = -n1.y(); - * \endcode - * The vector n0 represents the normal of the path going into the join, - * the vector n1 represents the normal of the path going out of the join - * and t represents how much to interpolate from n0 to n1. - * - For those with offset_type() being \ref offset_miter_clip_join - * or \ref offset_miter_clip_join_lambda_negated - * the value is given by the following code - * \code - * vec2 n0(m_pre_offset), Jn0(n0.y(), -n0.x()); - * vec2 n1(m_auxiliary_offset), Jn1(n1.y(), -n1.x()); - * float r, det, lambda; - * det = dot(Jn1, n0); - * lambda = -t_sign(det); - * r = (det != 0.0) ? (dot(n0, n1) - 1.0) / det : 0.0; - * if (offset_type() == offset_miter_clip_join_lambda_negated) - * { - * lambda = -lambda; - * } - * //result: - * offset = lambda * (n + r * v); - * \endcode - * - For those with offset_type() being \ref offset_miter_join, - * or \ref offset_miter_bevel_join the value is given by the - * following code: - * \code - * vec2 n0(m_pre_offset), Jn0(n0.y(), -n0.x()); - * vec2 n1(m_auxiliary_offset), Jn1(n1.y(), -n1.x()); - * float lambda, r, d; - * lambda = t_sign(dot(Jn0, n1)); - * r = lambda / (1.0 + dot(n0, n1)); - * offset = r * (n0 + n1); - * \endcode - * - For those with offset_type() being \ref offset_rounded_cap, - * the value is given by the following code - * \code - * vec2 n(m_pre_offset), v(n.y(), -n.x()); - * offset = m_auxiliary_offset.x() * v + m_auxiliary_offset.y() * n; - * \endcode - * - For those with offset_type() being \ref offset_square_cap, - * \ref offset_adjustable_cap_contour_start or - * \ref offset_adjustable_cap_contour_end the value the value - * is given by - * \code - * m_pre_offset + m_auxiliary_offset - * \endcode - * In addition, \ref m_auxiliary_offset the tangent vector along - * the path in the direction of the path and \ref m_pre_offset - * holds a vector perpindicular to the path or a zero vector - * (indicating it is not on boundary of cap). - */ - vec2 - offset_vector(void); - - /*! - * When offset_type() is one of \ref offset_miter_clip_join, - * \ref offset_miter_clip_join_lambda_negated, - * \ref offset_miter_bevel_join or \ref offset_miter_join, - * returns the distance to the miter point. For other point - * types, returns 0.0. - */ - float - miter_distance(void) const; - - /*! - * Pack the data of this \ref point into a \ref - * PainterAttribute. The packing is as follows: - * - PainterAttribute::m_attrib0 .xy -> point::m_position (float) - * - PainterAttribute::m_attrib0 .zw -> point::m_pre_offset (float) - * - PainterAttribute::m_attrib1 .x -> point::m_distance_from_edge_start (float) - * - PainterAttribute::m_attrib1 .y -> point::m_distance_from_contour_start (float) - * - PainterAttribute::m_attrib1 .zw -> point::m_auxiliary_offset (float) - * - PainterAttribute::m_attrib2 .x -> point::m_packed_data (uint) - * - PainterAttribute::m_attrib2 .y -> point::m_edge_length (float) - * - PainterAttribute::m_attrib2 .z -> point::m_open_contour_length (float) - * - PainterAttribute::m_attrib2 .w -> point::m_closed_contour_length (float) - * - * \param dst PainterAttribute to which to pack - */ - void - pack_point(PainterAttribute *dst) const; - - /*! - * Unpack a \ref point from a \ref PainterAttribute. - * \param dst point to which to unpack data - * \param src PainterAttribute from which to unpack data - */ - static - void - unpack_point(point *dst, const PainterAttribute &src); - }; - /*! * \brief * Opaque object to hold work room needed for functions @@ -623,46 +94,28 @@ class StrokedPath: c_array edge_chunks(void) const; - /*! - * The list of chunks to take from any of StrokedPath::bevel_joins(), - * StrokedPath::miter_clip_joins(), StrokedPath::miter_bevel_joins(), - * StrokedPath::miter_joins() or StrokedPath::rounded_joins() that - * have visible content. - */ - c_array - join_chunks(void) const; - - /*! - * The list of chunks to take from any of StrokedPath::square_caps(), - * StrokedPath::adjustable_caps() or StrokedPath::rounded_caps() - * that have visible content. - */ - c_array - cap_chunks(void) const; - private: friend class StrokedPath; void *m_d; }; /*! - * Enumeration to select the chunk of all joins or edges - * of the closing edges or non-closing edges of a - * StrokedPath. + * Enumeration to select the chunk of all edges of the closing + * edges or all edges of the non-closing edges of a StrokedPath. */ enum chunk_selection { /*! - * Select the chunk that holds all the joins/edges - * of ONLY the non-closing edges of the StrokedPath + * Select the chunk that holds all the edges of + * ONLY the non-closing edges of the StrokedPath */ - all_non_closing, + all_non_closing = StrokedCapsJoins::all_non_closing, /*! - * Select the chunk that holds all the joins/edges - * of ONLY the closing edges of the StrokedPath + * Select the chunk that holds all the edges of + * ONLY the closing edges of the StrokedPath */ - all_closing, + all_closing = StrokedCapsJoins::all_closing, }; /*! @@ -681,10 +134,6 @@ class StrokedPath: * coordinates, compute what chunks are not completely * culled by the clip equations. * \param scratch_space scratch space for computations - * \param dash_evaluator if doing dashed stroking, the dash evalulator will cull - * joins not to be drawn, if nullptr only those joins not in the - * visible area defined by clip_equations are culled. - * \param dash_data data to pass to dast evaluator * \param clip_equations array of clip equations * \param clip_matrix_local 3x3 transformation from local (x, y, 1) * coordinates to clip coordinates. @@ -707,8 +156,6 @@ class StrokedPath: */ void compute_chunks(ScratchSpace &scratch_space, - const DashEvaluatorBase *dash_evaluator, - const PainterShaderData::DataBase *dash_data, c_array clip_equations, const float3x3 &clip_matrix_local, const vec2 &recip_dimensions, @@ -717,39 +164,8 @@ class StrokedPath: bool include_closing_edges, unsigned int max_attribute_cnt, unsigned int max_index_cnt, - bool take_joins_outside_of_region, ChunkSet &dst) const; - /*! - * Returns the number of joins of the StrokedPath - * \param include_joins_of_closing_edge if false disclude from the count, - * this joins of the closing edges - */ - unsigned int - number_joins(bool include_joins_of_closing_edge) const; - - /*! - * Returns a chunk value for Painter::attribute_data_chunk() - * and related calls to feed the return value to any of - * bevel_joins(), miter_clip_joins(), miter_bevel_joins(), - * miter_joins() or rounded_joins() to fetch the chunk - * of the named join. - * \param J join ID with 0 <= J < number_joins(true) - */ - unsigned int - join_chunk(unsigned int J) const; - - /*! - * Return the chunk to feed to any of bevel_joins(), - * miter_clip_joins(), miter_bevel_joins(), - * miter_joins() or rounded_joins() that holds all - * the joins of the closing edges or all the joins - * of the non-closing edges. - * \param c select joins of closing or non-closing edges - */ - unsigned int - chunk_of_joins(enum chunk_selection c) const; - /*! * Returns the data to draw the edges of a stroked path. */ @@ -766,73 +182,12 @@ class StrokedPath: chunk_of_edges(enum chunk_selection c) const; /*! - * Return the chunk to feed any of square_caps(), - * adjustable_caps() or rounded_caps() that holds - * data for all caps of this StrokedPath. + * Returns the StrokedCapsJoins of the StrokedPath that + * provides the attribute data for teh stroking of the + * joins and caps. */ - unsigned int - chunk_of_caps(void) const; - - /*! - * Returns the data to draw the square caps of a stroked path. - */ - const PainterAttributeData& - square_caps(void) const; - - /*! - * Returns the data to draw the caps of a stroked path used - * when stroking with a dash pattern. - */ - const PainterAttributeData& - adjustable_caps(void) const; - - /*! - * Returns the data to draw the bevel joins of a stroked path. - */ - const PainterAttributeData& - bevel_joins(void) const; - - /*! - * Returns the data to draw the miter joins of a stroked path, - * if the miter-limit is exceeded on stroking, the miter-join - * is clipped to the miter-limit. - */ - const PainterAttributeData& - miter_clip_joins(void) const; - - /*! - * Returns the data to draw the miter joins of a stroked path, - * if the miter-limit is exceeded on stroking, the miter-join - * is to be drawn as a bevel join. - */ - const PainterAttributeData& - miter_bevel_joins(void) const; - - /*! - * Returns the data to draw the miter joins of a stroked path, - * if the miter-limit is exceeded on stroking, the miter-join - * end point is clamped to the miter-distance. - */ - const PainterAttributeData& - miter_joins(void) const; - - /*! - * Returns the data to draw rounded joins of a stroked path. - * \param thresh will return rounded joins so that the distance - * between the approximation of the round and the - * actual round is no more than thresh. - */ - const PainterAttributeData& - rounded_joins(float thresh) const; - - /*! - * Returns the data to draw rounded caps of a stroked path. - * \param thresh will return rounded caps so that the distance - * between the approximation of the round and the - * actual round is no more than thresh. - */ - const PainterAttributeData& - rounded_caps(float thresh) const; + const StrokedCapsJoins& + caps_joins(void) const; private: void *m_d; diff --git a/inc/fastuidraw/painter/stroked_point.hpp b/inc/fastuidraw/painter/stroked_point.hpp new file mode 100644 index 000000000..29e5768f1 --- /dev/null +++ b/inc/fastuidraw/painter/stroked_point.hpp @@ -0,0 +1,592 @@ +/*! + * \file stroked_point.hpp + * \brief file stroked_point.hpp + * + * Copyright 2018 by Intel. + * + * Contact: kevin.rogovin@intel.com + * + * This Source Code Form is subject to the + * terms of the Mozilla Public License, v. 2.0. + * If a copy of the MPL was not distributed with + * this file, You can obtain one at + * http://mozilla.org/MPL/2.0/. + * + * \author Kevin Rogovin + * + */ + + +#pragma once + +#include +#include +#include + +namespace fastuidraw { + +/*!\addtogroup Paths + * @{ + */ + +/*! + * \brief + * A Stroked holds the data for a point of stroking. + * The data is so that changing the stroking width + * or miter limit does not change the stroking data. + */ +class StrokedPoint +{ +public: + /*! + * \brief + * Enumeration for specifing the point type which in turn + * determines the meaning of the fields \ref m_pre_offset + * and \ref m_auxiliary_offset + */ + enum offset_type_t + { + /*! + * The point is for an edge of the path, point signifies the start + * of a sub-edge (quad) of drawing an edge. The meanings of \ref + * m_pre_offset and \ref m_auxiliary_offset are: + * - \ref m_pre_offset the normal vector to the edge in which + * move the point by when stroking + * - \ref m_auxiliary_offset when added to \ref m_position, gives + * the position of the point on the other + * side of the edge. + */ + offset_start_sub_edge, + + /*! + * The point is for an edge of the path, point signifies the end + * of a sub-edge (quad) of drawing an edge. The meanings of the + * members \ref m_pre_offset and \ref m_auxiliary_offset are + * identical to \ref offset_start_sub_edge. + */ + offset_end_sub_edge, + + /*! + * The point is at a position that has the same value as point on + * an edge but the point itself is part of a cap or join. The + * meanings of the members \ref m_pre_offset and \ref + * m_auxiliary_offset are: + * - \ref m_pre_offset the normal vector to the edge in which move + * the point by when stroking; this vector can + * be (0, 0). + * - \ref m_auxiliary_offset unused (set to (0, 0)) + */ + offset_shared_with_edge, + + /*! + * The point is a point to bound an arc-edge which is to be used for + * stroking an arc where rather than approximating the arc-edge with + * a sequence of line segments, a pixel shader is used to determine + * if inside or outside of the arc. + * - \ref m_position holds the center of the circle of the arc + * - \ref m_pre_offset radius of the circle of the arc + * - \ref m_auxilary holds the angle range of the arc, the start + * at .x() and the end at .y(). + */ + offset_edge_arc, + + /*! + * The point is for a boundary point of a rounded join of the path. + * The meanings of the members \ref m_pre_offset and and \ref + * m_auxiliary_offset are: + * - \ref m_pre_offset the .x() component holds the unit normal vector + * between the join point and the edge going into + * the join. The .y() component holds the unit + * normal vector between the join point and the edge + * leaving the join. The packing is that the + * x-coordinate value is given and the y-coordinate + * magnitude is \f$ sqrt(1 - x^2) \f$. If the bit + * \ref normal0_y_sign_bit is up, then the y-coordinate + * for the normal vector of going into the join, + * then the y-value is negative. If the bit \ref + * normal1_y_sign_bit is up, then the y-coordinate + * for the normal vector of leaving the join, then + * the y-value is negative. + * - \ref m_auxiliary_offset the .x() component gives an interpolation + * in the range [0, 1] to interpolate between + * the normal vectors packed in \ref m_pre_offset. + * The .y() value gives the normal vector directly + * but packed (as in \ref m_pre_offset) where the + * y-coordinate sign is negative if the bit \ref + * sin_sign_mask is up. + */ + offset_rounded_join, + + /*! + * Point type for miter-clip join point whose position depends on the + * stroking radius and the miter-limit. The meanings of \ref m_pre_offset + * and \ref m_auxiliary_offset are: + * - \ref m_pre_offset gives the unit normal vector of teh edge going + * into the join + * - \ref m_auxiliary_offset gives the unit normal vector of teh edge + * leaving the join + */ + offset_miter_clip_join, + + /*! + * Point type for miter-bevel join point whose position depends on the + * stroking radius and the miter-limit. The meanings of \ref m_pre_offset + * and \ref m_auxiliary_offset are: + * - \ref m_pre_offset gives the unit normal vector of teh edge going + * into the join + * - \ref m_auxiliary_offset gives the unit normal vector of teh edge + * leaving the join + */ + offset_miter_bevel_join, + + /*! + * Point type for miter join whose position position depends on the + * stroking radius and the miter-limit. The meanings of \ref m_pre_offset + * and \ref m_auxiliary_offset are: + * - \ref m_pre_offset gives the unit normal vector of teh edge going + * into the join + * - \ref m_auxiliary_offset gives the unit normal vector of teh edge + * leaving the join + */ + offset_miter_join, + + /*! + * The point is for a boundary point of a rounded cap of the path. + * The meanings of \ref m_pre_offset and \ref m_auxiliary_offset + * are: + * - \ref m_pre_offset is the normal vector to the path to start + * drawing the rounded cap + * - \ref m_auxiliary_offset gives the the unit vector (cos, sin) + * of the angle to make with the vector + * given by \ref m_pre_offset + */ + offset_rounded_cap, + + /*! + * The point is for a boundary point of a square cap of the path, + * the meanings of \ref m_pre_offset and \ref m_auxiliary_offset are: + * - \ref m_pre_offset is the normal vector to the path by which + * to move the point + * - \ref m_auxiliary_offset is the tangent vector to the path + * by which to move the point + */ + offset_square_cap, + + /*! + * The point is a point of an adjustable cap. It is for a point for a + * cap at the start of a contour. These points are for dashed stroking + * with caps; they contain data to allow one from a vertex shader to + * extend or shrink the cap area correctly to implement dashed stroking. + * The meanings of \ref m_pre_offset and \ref m_auxiliary_offset are: + * - \ref m_pre_offset is the normal vector to the path by which + * to move the point; this value can be (0, 0) + * to indicate to not move perpindicular to the + * path + * - \ref m_auxiliary_offset is the tangent vector to the path + * by which to move the point; this value + * can be (0, 0) to indicate to not move + * parallel to the path + */ + offset_adjustable_cap_contour_start, + + /*! + * The point is a point of an adjustable cap. It is for a point for a + * cap at the end of a contour. These points are for dashed stroking + * with caps; they contain data to allow one from a vertex shader to + * extend or shrink the cap area correctly to implement dashed stroking. + * The meanings of \ref m_pre_offset and \ref m_auxiliary_offset are + * identical to the meanings for \ref offset_adjustable_cap_contour_start. + */ + offset_adjustable_cap_contour_end, + + /*! + * Number different point types with respect to rendering + */ + number_offset_types + }; + + /*! + * \brief + * Enumeration encoding of bits of point::m_packed_data + * common to all offset types. + */ + enum packed_data_bit_layout_common_t + { + /*! + * Bit0 for holding the offset_type() value + * of the point. + */ + offset_type_bit0 = 0, + + /*! + * number of bits needed to hold the + * offset_type() value of the point. + */ + offset_type_num_bits = 4, + + /*! + * Bit for holding boundary() value + * of the point + */ + boundary_bit = offset_type_bit0 + offset_type_num_bits, + + /*! + * Bit0 for holding the depth() value + * of the point + */ + depth_bit0, + + /*! + * number of bits needed to hold the + * depth() value of the point. + */ + depth_num_bits = 20, + + /*! + * Bit to indicate point is from a join. For these + * points, during dashed stroking, Painter does the + * check if a join should be drawn, as such when the + * bit is up encountered in a shader, the computation + * to check that it is drawn from dashing can be + * skipped and assume that fragments from such points + * are covered by the dash pattern. + */ + join_bit = depth_bit0 + depth_num_bits, + + /*! + * Number of bits used on common packed data + */ + number_common_bits, + }; + + /*! + * \brief + * Enumeration encoding of bits of point::m_packed_data + * for those with offset type \ref offset_edge_arc + */ + enum packed_data_bit_layout_arc_edge_t + { + /*! + * If bit is up, indicates that the arc edge point + * is to be extended away from the center when stroking + * (i.e. it is the outside of teh stroking edge). + */ + outside_arc_bit = number_common_bits, + + /*! + * If bit is up, indicates that the arc edge point + * is at the end of the arc. + */ + end_of_arc_bit, + }; + + /*! + * \brief + * Enumeration encoding of bits of point::m_packed_data + * for those with offset type \ref offset_rounded_join + */ + enum packed_data_bit_layout_rounded_join_t + { + /*! + * Bit for holding the the sign of + * the y-coordinate of the normal 0 + * for the offset_type() \ref + * offset_rounded_join. + */ + normal0_y_sign_bit = number_common_bits, + + /*! + * Bit for holding the the sign of + * the y-coordinate of the normal 1 + * for the offset_type() \ref + * offset_rounded_join. + */ + normal1_y_sign_bit, + + /*! + * Bit for holding the the sign of + * sin() value for the offset_type() + * \ref offset_rounded_join. + */ + sin_sign_bit, + }; + + /*! + * \brief + * Enumeration encoding of bits of point::m_packed_data + * for those with offset type \ref offset_miter_clip_join. + */ + enum packed_data_bit_layout_miter_join_t + { + /*! + * Indicates that the lambda of the miter-join + * computation should be negated. + */ + lambda_negated_bit = number_common_bits, + }; + + /*! + * \brief + * Enumeration encoding of bits of point::m_packed_data for + * those with offset type \ref offset_adjustable_cap_contour_end + * or \ref offset_adjustable_cap_contour_start. + */ + enum packed_data_bit_adjustable_cap_t + { + /*! + * The bit is up if the point is for end of point + * of a cap (i.e. the side to be extended to make + * sure the entire cap near the end of edge is drawn). + */ + adjustable_cap_ending_bit = number_common_bits, + }; + + /*! + * \brief + * Enumeration encoding of bits of point::m_packed_data + * for those with offset type \ref offset_start_sub_edge + * or \ref offset_end_sub_edge. + */ + enum packed_data_bit_sub_edge_t + { + /*! + * The bit is up if the point is for the + * geometry of a bevel between two sub-edges. + */ + bevel_edge_bit = number_common_bits, + }; + + /*! + * \brief + * Enumeration holding bit masks generated from + * values in \ref packed_data_bit_layout_common_t, + * \ref packed_data_bit_layout_rounded_join_t, + * \ref packed_data_bit_adjustable_cap_t and \ref + * packed_data_bit_sub_edge_t. + */ + enum packed_data_bit_masks_t + { + /*! + * Mask generated for \ref offset_type_bit0 and + * \ref offset_type_num_bits + */ + offset_type_mask = FASTUIDRAW_MASK(offset_type_bit0, offset_type_num_bits), + + /*! + * Mask generated for \ref outside_arc_bit + */ + outside_arc_mask = FASTUIDRAW_MASK(outside_arc_bit, 1), + + /*! + * Mask generated for \ref end_of_arc_bit + */ + end_of_arc_mask = FASTUIDRAW_MASK(end_of_arc_bit, 1), + + /*! + * Mask generated for \ref normal0_y_sign_bit + */ + normal0_y_sign_mask = FASTUIDRAW_MASK(normal0_y_sign_bit, 1), + + /*! + * Mask generated for \ref normal1_y_sign_bit + */ + normal1_y_sign_mask = FASTUIDRAW_MASK(normal1_y_sign_bit, 1), + + /*! + * Mask generated for \ref sin_sign_bit sin_sign_bit + */ + sin_sign_mask = FASTUIDRAW_MASK(sin_sign_bit, 1), + + /*! + * Mask generated for \ref lambda_negated_mask + */ + lambda_negated_mask = FASTUIDRAW_MASK(lambda_negated_bit, 1), + + /*! + * Mask generated for \ref boundary_bit + */ + boundary_mask = FASTUIDRAW_MASK(boundary_bit, 1), + + /*! + * Mask generated for \ref join_bit + */ + join_mask = FASTUIDRAW_MASK(join_bit, 1), + + /*! + * Mask generated for \ref adjustable_cap_ending_bit + */ + adjustable_cap_ending_mask = FASTUIDRAW_MASK(adjustable_cap_ending_bit, 1), + + /*! + * Mask generated for \ref bevel_edge_bit + */ + bevel_edge_mask = FASTUIDRAW_MASK(bevel_edge_bit, 1), + + /*! + * Mask generated for \ref depth_bit0 and \ref depth_num_bits + */ + depth_mask = FASTUIDRAW_MASK(depth_bit0, depth_num_bits), + }; + + /*! + * The base position of a point before applying the stroking width + * to the position. + */ + vec2 m_position; + + /*! + * Gives values to help compute the location of the point after + * applying the stroking width. See the descriptions to the + * elements of \ref offset_type_t for its meaning for different + * offset types. + */ + vec2 m_pre_offset; + + /*! + * Gives values to help compute the location of the point after + * applying the stroking width. See the descriptions to the + * elements of \ref offset_type_t for its meaning for different + * offset types. + */ + vec2 m_auxiliary_offset; + + /*! + * Gives the distance of the point from the start + * of the -edge- on which the point resides. + */ + float m_distance_from_edge_start; + + /*! + * Gives the distance of the point from the start + * of the -contour- on which the point resides. + */ + float m_distance_from_contour_start; + + /*! + * Gives the length of the edge on which the + * point lies. This value is the same for all + * points along a fixed edge. + */ + float m_edge_length; + + /*! + * Gives the length of the contour open on which + * the point lies. This value is the same for all + * points along a fixed contour. + */ + float m_open_contour_length; + + /*! + * Gives the length of the contour closed on which + * the point lies. This value is the same for all + * points along a fixed contour. + */ + float m_closed_contour_length; + + /*! + * Bit field with data packed as according to + * \ref packed_data_bit_layout_common_t, \ref + * packed_data_bit_layout_rounded_join_t, \ref + * packed_data_bit_adjustable_cap_t and \ref + * packed_data_bit_sub_edge_t. See also, + * \ref packed_data_bit_masks_t for bit masks + * generated. + */ + uint32_t m_packed_data; + + /*! + * Provides the point type from a value of \ref m_packed_data. + * The return value is one of the enumerations of + * \ref offset_type_t. + * \param packed_data_value value suitable for \ref m_packed_data + */ + static + enum offset_type_t + offset_type(uint32_t packed_data_value) + { + uint32_t v; + v = unpack_bits(offset_type_bit0, offset_type_num_bits, packed_data_value); + return static_cast(v); + } + + /*! + * Provides the point type for the point. The return value + * is one of the enumerations of \ref offset_type_t. + */ + enum offset_type_t + offset_type(void) const + { + return offset_type(m_packed_data); + } + + /*! + * When stroking the data, the depth test to only + * pass when the depth value is -strictly- larger + * so that a fixed pixel is not stroked twice by + * a single path. The value returned by depth() is + * a relative z-value for a vertex. The points + * drawn first have the largest z-values. + */ + uint32_t + depth(void) const + { + return unpack_bits(depth_bit0, depth_num_bits, m_packed_data); + } + + /*! + * Has value 0 or 1. If the value is 0, then + * the point is on the path. If the value has + * absolute value 1, then indicates a point that + * is on the boundary of the stroked path. The triangles + * produced from stroking are so that when + * on_boundary() is interpolated across the triangle + * the center of stroking the value is 0 and the + * value has value 1 on the boundary. + */ + int + on_boundary(void) const + { + return unpack_bits(boundary_bit, 1u, m_packed_data); + } + + /*! + * When offset_type() is one of \ref offset_miter_clip_join, + * \ref offset_miter_bevel_join or \ref offset_miter_join, + * returns the distance to the miter point. For other point + * types, returns 0.0. + */ + float + miter_distance(void) const; + + /*! + * Pack the data of this \ref StrokedPoint into a \ref + * PainterAttribute. The packing is as follows: + * - PainterAttribute::m_attrib0 .xy -> \ref m_position (float) + * - PainterAttribute::m_attrib0 .zw -> \ref m_pre_offset (float) + * - PainterAttribute::m_attrib1 .x -> \ref m_distance_from_edge_start (float) + * - PainterAttribute::m_attrib1 .y -> \ref m_distance_from_contour_start (float) + * - PainterAttribute::m_attrib1 .zw -> \ref m_auxiliary_offset (float) + * - PainterAttribute::m_attrib2 .x -> \ref m_packed_data (uint) + * - PainterAttribute::m_attrib2 .y -> \ref m_edge_length (float) + * - PainterAttribute::m_attrib2 .z -> \ref m_open_contour_length (float) + * - PainterAttribute::m_attrib2 .w -> \ref m_closed_contour_length (float) + * + * \param dst PainterAttribute to which to pack + */ + void + pack_point(PainterAttribute *dst) const; + + /*! + * Unpack a \ref StrokedPoint from a \ref PainterAttribute. + * \param dst point to which to unpack data + * \param src PainterAttribute from which to unpack data + */ + static + void + unpack_point(StrokedPoint *dst, const PainterAttribute &src); +}; + + +/*! @} */ + +} diff --git a/inc/fastuidraw/path.hpp b/inc/fastuidraw/path.hpp index df9adee33..1fecd3f28 100644 --- a/inc/fastuidraw/path.hpp +++ b/inc/fastuidraw/path.hpp @@ -25,6 +25,7 @@ #include #include #include +#include namespace fastuidraw { @@ -101,23 +102,45 @@ class PathContour: * To be implemented by a derived class to produce the tessellation * from start_pt() to end_pt(). The routine must include BOTH start_pt() * and end_pt() in the result. Only the fields TessellatedPath::point::m_p, - * and TessellatedPath::point::m_distance_from_edge_start are to be filled; - * the other fields of TessellatedPath::point are filled by TessellatedPath. - * In addition to filling the output array, the function shall return the - * number of points needed to perform the required tessellation. + * and are to be filled; the other fields of TessellatedPath::point are + * filled by TessellatedPath. In addition, returns the deepest number of + * times it recurses (for example each level is dividing the path in half). + * If the routine is not recursive, should return 0. * * \param tess_params tessellation parameters * \param out_data location to which to write the edge tessellated - * \param out_threshholds location to which to write the threshholds - * the tessellation achieved; array is indexed - * by \ref TessellatedPath::threshhold_type_t. - * An implementation MUST write to EACH entry. + * \param out_threshhold location to which to write an upperbound for the + * distance between the curve and the tesseallation + * approximation. */ virtual unsigned int produce_tessellation(const TessellatedPath::TessellationParams &tess_params, - c_array out_data, - c_array out_threshholds) const = 0; + TessellatedPath::PointStorage *out_data, + float *out_threshhold) const = 0; + + /*! + * To be implemented by a derived class to produce the arc-tessellation + * from start_pt() to end_pt(). Only the fields ArcTessellatedPath::segment::m_p, + * and ArcTessellatedPath::m_type, ArcTessellatedPath::m_data and + * ArcTessellatedPath::m_radius are to be filled; the other fields of + * ArcTessellatedPath::segment are filled by ArcTessellatedPath. In addition, + * returns the deepest number of times it recurses (for example each level + * is dividing the path in half). If the routine is not recursive, should + * return 0. + * + * + * \param tess_params tessellation parameters + * \param out_data location to which to write the tessellation + * \param out_threshhold location to which to write an upperbound for the + * distance between the curve and the tesseallation + * approximation. + */ + virtual + unsigned int + produce_tessellation(const ArcTessellatedPath::TessellationParams &tess_params, + ArcTessellatedPath::SegmentStorage *out_data, + float *out_threshhold) const = 0; /*! * To be implemented by a derived class to return a fast (and approximate) @@ -169,8 +192,13 @@ class PathContour: virtual unsigned int produce_tessellation(const TessellatedPath::TessellationParams &tess_params, - c_array out_data, - c_array out_threshholds) const; + TessellatedPath::PointStorage *out_data, + float *out_threshholds) const; + virtual + unsigned int + produce_tessellation(const ArcTessellatedPath::TessellationParams &tess_params, + ArcTessellatedPath::SegmentStorage *out_data, + float *out_threshhold) const; virtual void approximate_bounding_box(vec2 *out_min_bb, vec2 *out_max_bb) const; @@ -193,13 +221,9 @@ class PathContour: * A tessellated_region is a base class for a cookie * used and generated by tessellate(). */ - class tessellated_region - { - public: - virtual - ~tessellated_region() - {} - }; + class tessellated_region: + public reference_counted::non_concurrent + {}; /*! * Ctor. @@ -215,8 +239,13 @@ class PathContour: virtual unsigned int produce_tessellation(const TessellatedPath::TessellationParams &tess_params, - c_array out_data, - c_array out_threshholds) const; + TessellatedPath::PointStorage *out_data, + float *out_threshholds) const; + virtual + unsigned int + produce_tessellation(const ArcTessellatedPath::TessellationParams &tess_params, + ArcTessellatedPath::SegmentStorage *out_data, + float *out_threshhold) const; /*! * To be implemented by a derived to assist in recursive tessellation. @@ -224,25 +253,28 @@ class PathContour: * that the region is the entire interpolator. * \param out_regionA location to which to write the first half * \param out_regionB location to which to write the second half - * \param out_t location to which to write the time (parameter) for - * in the middle of in_region * \param out_p location to which to write the position of the point - * on the curve in the middle of in_region - * \param out_threshholds location to which to write the threshholds - * the tessellation achieved; array is indexed - * by \ref TessellatedPath::threshhold_type_t. - * An implementation MUST write to the index - * \ref TessellatedPath::threshhold_curve_distance. - * For all other values, if a value is negative, - * FastUIDraw will use the data from out_data - * to estimate the threshhold achieved. + * on the curve in the middle (with repsect to time) of + * in_region + * \param out_threshhold location to which to write an upperbound for the + * distance between the curve and the tesseallation + * approximation. */ virtual void - tessellate(tessellated_region *in_region, - tessellated_region **out_regionA, tessellated_region **out_regionB, - float *out_t, vec2 *out_p, - c_array out_threshholds) const = 0; + tessellate(reference_counted_ptr in_region, + reference_counted_ptr *out_regionA, + reference_counted_ptr *out_regionB, + vec2 *out_p, float *out_threshholds) const = 0; + + /*! + * To be implemented by a derived class to return a reasonable + * lower bound on the needed number of times the edge should be + * cut in half in order to capture its shape. + */ + virtual + unsigned int + minimum_tessellation_recursion(void) const = 0; }; /*! @@ -290,10 +322,10 @@ class PathContour: virtual void - tessellate(tessellated_region *in_region, - tessellated_region **out_regionA, tessellated_region **out_regionB, - float *out_t, vec2 *out_p, - c_array out_threshholds) const; + tessellate(reference_counted_ptr in_region, + reference_counted_ptr *out_regionA, + reference_counted_ptr *out_regionB, + vec2 *out_p, float *out_threshholds) const; virtual void approximate_bounding_box(vec2 *out_min_bb, vec2 *out_max_bb) const; @@ -302,6 +334,10 @@ class PathContour: interpolator_base* deep_copy(const reference_counted_ptr &prev) const; + virtual + unsigned int + minimum_tessellation_recursion(void) const; + private: bezier(const bezier &q, const reference_counted_ptr &prev); @@ -329,6 +365,16 @@ class PathContour: */ arc(const reference_counted_ptr &start, float angle, const vec2 &end); + /*! + * Ctor. + * \param start start of curve + * \param center center of curve + * \param end end of curve; if the point is collinear with start and center + * then it is undefined if the arc flips one way or the other + * along the circle. + */ + arc(const reference_counted_ptr &start, + const vec2 ¢er, const vec2 &end); ~arc(); @@ -347,8 +393,13 @@ class PathContour: virtual unsigned int produce_tessellation(const TessellatedPath::TessellationParams &tess_params, - c_array out_data, - c_array out_threshholds) const; + TessellatedPath::PointStorage *out_data, + float *out_threshholds) const; + virtual + unsigned int + produce_tessellation(const ArcTessellatedPath::TessellationParams &tess_params, + ArcTessellatedPath::SegmentStorage *out_data, + float *out_threshhold) const; private: arc(const arc &q, const reference_counted_ptr &prev); @@ -891,14 +942,12 @@ class Path * then a new TessellatedPath will be contructed on the * next call to tessellation(). * \param thresh the returned tessellated path will be so that - * TessellatedPath::curve_distance_threshhold(type) + * TessellatedPath::effective_threshhold() * is no more than thresh. A non-positive value * will return the starting point tessellation. - * \param tp the type of threshhold to use for the tessellation requirement. */ const reference_counted_ptr& - tessellation(float thresh, - enum TessellatedPath::threshhold_type_t tp = TessellatedPath::threshhold_curve_distance) const; + tessellation(float thresh) const; /*! * Provided as a conveniance, returns the starting point tessellation. @@ -910,6 +959,30 @@ class Path const reference_counted_ptr& tessellation(void) const; + /*! + * Return the arc-tessellation of this Path at a specific + * level of detail. The ArcTessellatedPath is constructed + * lazily. Additionally, if this Path changes its geometry, + * then a new ArcTessellatedPath will be contructed on the + * next call to arc_tessellation(). + * \param thresh the returned tessellated path will be so that + * ArcTessellatedPath::effective_threshhold() + * is no more than thresh. A non-positive value + * will return the starting point tessellation. + */ + const reference_counted_ptr& + arc_tessellation(float thresh) const; + + /*! + * Provided as a conveniance, returns the starting point tessellation. + * Equivalent to + * \code + * arc_tessellation(-1.0f) + * \endcode + */ + const reference_counted_ptr& + arc_tessellation(void) const; + private: void *m_d; }; diff --git a/inc/fastuidraw/tessellated_path.hpp b/inc/fastuidraw/tessellated_path.hpp index 8578097b0..a94df22b4 100644 --- a/inc/fastuidraw/tessellated_path.hpp +++ b/inc/fastuidraw/tessellated_path.hpp @@ -24,6 +24,7 @@ #include #include #include +#include namespace fastuidraw { @@ -39,7 +40,8 @@ class FilledPath; /*! * \brief - * A TessellatedPath represents the tessellation of a Path. + * A TessellatedPath represents the tessellation of a Path + * into line segments. * * A single contour of a TessellatedPath is constructed from * a single \ref PathContour of the source \ref Path. Each @@ -57,24 +59,6 @@ class TessellatedPath: public reference_counted::non_concurrent { public: - /*! - * \brief - * Enumeration to specify the type of threshhold - * a \ref TessellationParams is. - */ - enum threshhold_type_t - { - /*! - * Threshhold specifies how much distance is between - * line segment between from successive points of a - * tessellated edge and the sub-curve of the starting - * curve between those two points. - */ - threshhold_curve_distance, - - number_threshholds, - }; - /*! * \brief * A TessellationParams stores how finely to tessellate @@ -87,37 +71,10 @@ class TessellatedPath: * Ctor, initializes values. */ TessellationParams(void): - m_threshhold_type(threshhold_curve_distance), m_threshhold(1.0f), - m_max_segments(32) + m_max_recursion(5) {} - /*! - * Non-equal comparison operator. - * \param rhs value to which to compare against - */ - bool - operator!=(const TessellationParams &rhs) const - { - return m_threshhold_type != rhs.m_threshhold_type - || m_threshhold != rhs.m_threshhold - || m_max_segments != rhs.m_max_segments; - } - - /*! - * Provided as a conveniance. Equivalent to - * \code - * m_threshhold_type = tp; - * \endcode - * \param tp value to which to assign to \ref m_threshhold_type - */ - TessellationParams& - threshhold_type(enum threshhold_type_t tp) - { - m_threshhold_type = tp; - return *this; - } - /*! * Provided as a conveniance. Equivalent to * \code @@ -133,50 +90,29 @@ class TessellatedPath: } /*! - * Provided as a conveniance. Equivalent to - * \code - * m_threshhold_type = threshhold_curve_distance; - * m_threshhold = p; - * \endcode - * \param p value to which to assign to \ref m_threshhold - */ - TessellationParams& - curve_distance_tessellate(float p) - { - m_threshhold_type = threshhold_curve_distance; - m_threshhold = p; - return *this; - } - - /*! - * Set the value of \ref m_max_segments. - * \param v value to which to assign to \ref m_max_segments + * Set the value of \ref m_max_recursion. + * \param v value to which to assign to \ref m_max_recursion */ TessellationParams& - max_segments(unsigned int v) + max_recursion(unsigned int v) { - m_max_segments = v; + m_max_recursion = v; return *this; } /*! - * Specifies the meaning of \ref m_threshhold. - * Default value is \ref threshhold_curvature. - */ - enum threshhold_type_t m_threshhold_type; - - /*! - * Meaning depends on \ref m_threshhold_type. - * Default value is M_PI / 30.0. + * The targetted threshhold, measured by an upper-bound + * (per-tessellated edge) of the distance between the line + * segments of the tessellation and the original curve. + * Default value is 1.0. */ float m_threshhold; /*! - * Maximum number of segments to tessellate each - * PathContour::interpolator_base from each - * PathContour of a Path. Default value is 32. + * Maximum number of times to cut a single edge in + * half. Default value is 5. */ - unsigned int m_max_segments; + unsigned int m_max_recursion; }; /*! @@ -227,6 +163,30 @@ class TessellatedPath: float m_closed_contour_length; }; + /*! + * \brief + * A wrapper over a dynamic array of \ref segment objects; + * segment values added to SegmentStorage must be added + * in order of time along the domain of a \ref + * PathContour::interpolate_base + */ + class PointStorage:fastuidraw::noncopyable + { + public: + /*! + * Add a \ref point to the PointStorage. + */ + void + add_point(const point&); + + private: + PointStorage(void) {} + ~PointStorage() {} + + friend class TessellatedPath; + void *m_d; + }; + /*! * Ctor. Construct a TessellatedPath from a Path * \param input source path to tessellate @@ -244,11 +204,10 @@ class TessellatedPath: tessellation_parameters(void) const; /*! - * Returns the tessellation threshold achieved for - * the named threshhold type. + * Returns the tessellation threshold achieved */ float - effective_threshhold(enum threshhold_type_t tp) const; + effective_threshhold(void) const; /*! * Returns the maximum number of segments any edge needed @@ -256,6 +215,12 @@ class TessellatedPath: unsigned int max_segments(void) const; + /*! + * Returns the maximum recursion employed by any edge + */ + unsigned int + max_recursion(void) const; + /*! * Returns all the point data */ diff --git a/make/Makefile.base.lib.mk b/make/Makefile.base.lib.mk index 5c0150578..01764786b 100644 --- a/make/Makefile.base.lib.mk +++ b/make/Makefile.base.lib.mk @@ -1,4 +1,5 @@ -FASTUIDRAW_LIBS += $(shell freetype-config --libs) -lm +FASTUIDRAW_DEPS_LIBS += $(shell freetype-config --libs) +FASTUIDRAW_DEPS_STATIC_LIBS += $(shell freetype-config --static --libs) FASTUIDRAW_BASE_CFLAGS = -std=c++11 -D_USE_MATH_DEFINES FASTUIDRAW_debug_BASE_CFLAGS = $(FASTUIDRAW_BASE_CFLAGS) -DFASTUIDRAW_DEBUG @@ -22,6 +23,9 @@ build/string_resources_cpp/%.resource_string.cpp: %.resource_string $(STRING_RES @mkdir -p $(dir $@) $(STRING_RESOURCE_CC) $< $(notdir $<) $(dir $@) +${STRING_RESOURCE_CC}: shell_scripts/fastuidraw-create-resource-cpp-file.cpp + $(CXX) $< -o $@ + FASTUIDRAW_STRING_RESOURCES_SRCS = $(patsubst %.resource_string, build/string_resources_cpp/%.resource_string.cpp, $(FASTUIDRAW_RESOURCE_STRING) ) CLEAN_FILES += $(FASTUIDRAW_STRING_RESOURCES_SRCS) @@ -35,7 +39,6 @@ FASTUIDRAW_$(1)_RESOURCE_OBJS = $$(patsubst %.resource_string, build/$(1)/%.reso FASTUIDRAW_$(1)_ALL_OBJS = $$(FASTUIDRAW_$(1)_OBJS) $$(FASTUIDRAW_PRIVATE_$(1)_OBJS) $$(FASTUIDRAW_$(1)_RESOURCE_OBJS) COMPILE_$(1)_CFLAGS=$$(FASTUIDRAW_BUILD_$(1)_FLAGS) $(FASTUIDRAW_BUILD_WARN_FLAGS) $(FASTUIDRAW_BUILD_INCLUDES_CFLAGS) $$(FASTUIDRAW_$(1)_CFLAGS) CLEAN_FILES += $$(FASTUIDRAW_$(1)_ALL_OBJS) $$(FASTUIDRAW_$(1)_RESOURCE_OBJS) -FASTUIDRAW_$(1)_LIBS = -lFastUIDraw_$(1) $(FASTUIDRAW_LIBS) SUPER_CLEAN_FILES += $$(FASTUIDRAW_$(1)_DEPS) build/$(1)/%.resource_string.o: build/string_resources_cpp/%.resource_string.cpp @mkdir -p $$(dir $$@) @@ -55,12 +58,13 @@ build/$(1)/private/%.o: %.cpp build/$(1)/private/%.d build/$(1)/private/%.d: ; .PRECIOUS: build/$(1)/%.d +TARGET_LIST += libFastUIDraw_$(1) ifeq ($(MINGW_BUILD),1) libFastUIDraw_$(1): libFastUIDraw_$(1).dll libFastUIDraw_$(1).dll.a: libFastUIDraw_$(1).dll libFastUIDraw_$(1).dll: $$(FASTUIDRAW_$(1)_ALL_OBJS) - $(CXX) -shared -Wl,--out-implib,libFastUIDraw_$(1).dll.a -o libFastUIDraw_$(1).dll $$(FASTUIDRAW_$(1)_ALL_OBJS) $(FASTUIDRAW_LIBS) + $(CXX) -shared -Wl,--out-implib,libFastUIDraw_$(1).dll.a -o libFastUIDraw_$(1).dll $$(FASTUIDRAW_$(1)_ALL_OBJS) $(FASTUIDRAW_DEPS_LIBS) CLEAN_FILES += libFastUIDraw_$(1).dll libFastUIDraw_$(1).dll.a INSTALL_LIBS += libFastUIDraw_$(1).dll.a INSTALL_EXES += libFastUIDraw_$(1).dll @@ -69,20 +73,28 @@ else libFastUIDraw_$(1): libFastUIDraw_$(1).so libFastUIDraw_$(1).so: $(FASTUIDRAW_STRING_RESOURCES_SRCS) $$(FASTUIDRAW_$(1)_ALL_OBJS) - $(CXX) -shared -Wl,-soname,libFastUIDraw_$(1).so -o libFastUIDraw_$(1).so $$(FASTUIDRAW_$(1)_ALL_OBJS) $(FASTUIDRAW_LIBS) + $(CXX) -shared -Wl,-soname,libFastUIDraw_$(1).so -o libFastUIDraw_$(1).so $$(FASTUIDRAW_$(1)_ALL_OBJS) $(FASTUIDRAW_DEPS_LIBS) CLEAN_FILES += libFastUIDraw_$(1).so INSTALL_LIBS += libFastUIDraw_$(1).so .PHONY: libFastUIDraw_$(1) libFastUIDraw endif +libFastUIDraw-static: libFastUIDraw_$(1)-static +.PHONY: libFastUIDraw-static +libFastUIDraw_$(1)-static: libFastUIDraw_$(1).a +.PHONY: libFastUIDraw_$(1)-static +libFastUIDraw_$(1).a: $(FASTUIDRAW_STRING_RESOURCES_SRCS) $$(FASTUIDRAW_$(1)_ALL_OBJS) + ar rcs $$@ $$(FASTUIDRAW_$(1)_ALL_OBJS) +CLEAN_FILES += libFastUIDraw_$(1).a +TARGETLIST += libFastUIDraw_$(1)-static + -include $$(FASTUIDRAW_$(1)_DEPS) libFastUIDraw: libFastUIDraw_$(1) ) endef -TARGETLIST += libFastUIDraw -TARGETLIST += libFastUIDraw_debug libFastUIDraw_release +TARGETLIST += libFastUIDraw libFastUIDraw-static $(call librules,release) $(call librules,debug) all: libFastUIDraw diff --git a/make/Makefile.demo.rules.mk b/make/Makefile.demo.rules.mk index e1508ab16..cc12a0546 100644 --- a/make/Makefile.demo.rules.mk +++ b/make/Makefile.demo.rules.mk @@ -17,9 +17,12 @@ DEMO_debug_CFLAGS = -g $(DEMO_COMMON_CFLAGS) define demobuildrules $(eval DEMO_$(2)_CFLAGS_$(1) = $$(DEMO_$(2)_CFLAGS) $$(shell ./fastuidraw-config.nodir --$(1) --$(2) --cflags --incdir=inc) -DEMO_$(2)_LIBS_$(1) = $(DEMO_COMMON_LIBS) $$(shell ./fastuidraw-config.nodir --$(1) --$(2) --libs --libdir=.) +DEMO_$(2)_LIBS_$(1) = $$(shell ./fastuidraw-config.nodir --$(1) --$(2) --libs --libdir=.) +DEMO_$(2)_LIBS_STATIC_$(1) = $$(shell ./fastuidraw-config.nodir --$(1) --$(2) --static --libs --libdir=.) ifeq ($(MINGW_BUILD),0) -DEMO_$(2)_LIBS_$(1) += -lEGL -lwayland-egl $$(shell ./fastuidraw-config.nodir --negl --$(2) --libs --libdir=.) +DEMO_COMMON_LIBS += -lEGL -lwayland-egl +DEMO_$(2)_LIBS_$(1) += $$(shell ./fastuidraw-config.nodir --negl --$(2) --libs --libdir=.) +DEMO_$(2)_LIBS_STATIC_$(1) = $$(shell ./fastuidraw-config.nodir --$(1) --$(2) --negl --static --libs --libdir=.) NEGL_$(2)_DEP_$(1) = $(NGL_EGL_HPP) NEGL_$(2)_LIB_DEP_$(1) = libNEGL_$(2).so endif @@ -52,7 +55,9 @@ THISDEMO_$(1)_$(2)_$(3)_OBJS_RAW = $$(patsubst %.cpp, %.o, $$(THISDEMO_$(1)_$(2) THISDEMO_$(1)_$(2)_$(3)_DEPS = $$(addprefix build/demo/$(3)/$(2)/, $$(THISDEMO_$(1)_$(2)_$(3)_DEPS_RAW)) THISDEMO_$(1)_$(2)_$(3)_OBJS = $$(addprefix build/demo/$(3)/$(2)/, $$(THISDEMO_$(1)_$(2)_$(3)_OBJS_RAW)) THISDEMO_$(1)_$(2)_$(3)_ALL_OBJS = $$(THISDEMO_$(1)_$(2)_$(3)_OBJS) $$(THISDEMO_$(1)_$(2)_$(3)_RESOURCE_OBJS) -CLEAN_FILES += $$(THISDEMO_$(1)_$(2)_$(3)_ALL_OBJS) $(1)-$(2)-$(3) $(1)-$(2)-$(3).exe $$(THISDEMO_$(1)_RESOURCE_STRING_SRCS) +CLEAN_FILES += $$(THISDEMO_$(1)_$(2)_$(3)_ALL_OBJS) $$(THISDEMO_$(1)_RESOURCE_STRING_SRCS) +CLEAN_FILES += $(1)-$(2)-$(3) $(1)-$(2)-$(3).exe +CLEAN_FILES += $(1)-$(2)-$(3)-static $(1)-$(2)-$(3)-static.exe SUPER_CLEAN_FILES += $$(THISDEMO_$(1)_$(2)_$(3)_DEPS) ifeq ($(4),1) ifneq ($(MAKECMDGOALS),clean) @@ -78,9 +83,19 @@ $(1)-$(3): $(1)-$(2)-$(3) .PHONY: $(1)-$(3) $(1): $(1)-$(2) .PHONY: $(1) -DEMO_TARGETLIST += $(1)-$(2)-$(3) -$(1)-$(2)-$(3): libFastUIDraw$(2)_$(3) $$(NEGL_$(3)_LIB_DEP_$(2)) $$(THISDEMO_$(1)_$(2)_$(3)_ALL_OBJS) $$(THISDEMO_$(1)_$(2)_$(3)_DEPS) - $$(CXX) -o $$@ $$(THISDEMO_$(1)_$(2)_$(3)_ALL_OBJS) $$(DEMO_$(3)_LIBS_$(2)) +$(1)-$(2)-$(3): libFastUIDraw$(2)_$(3) $$(NEGL_$(3)_LIB_DEP_$(2)) $$(THISDEMO_$(1)_$(2)_$(3)_ALL_OBJS) + $$(CXX) -o $$@ $$(THISDEMO_$(1)_$(2)_$(3)_ALL_OBJS) $$(DEMO_$(3)_LIBS_$(2)) $(DEMO_COMMON_LIBS) + +demos-$(2)-$(3)-static: $(1)-$(2)-$(3)-static +.PHONY: demos-$(2)-$(3)-static +$(1)-$(2)-static: $(1)-$(2)-$(3)-static +.PHONY: $(1)-$(2)-static +$(1)-$(3)-static: $(1)-$(2)-$(3)-static +.PHONY: $(1)-$(3)-static +$(1)-static: $(1)-$(2)-static +.PHONY: $(1)-static +$(1)-$(2)-$(3)-static: libFastUIDraw_$(3).a libFastUIDraw$(2)_$(3).a libN$(2)_$(3).a libNEGL_$(3).a $$(THISDEMO_$(1)_$(2)_$(3)_ALL_OBJS) + $$(CXX) -o $$@ $$(THISDEMO_$(1)_$(2)_$(3)_ALL_OBJS) $(DEMO_COMMON_LIBS) $$(DEMO_$(3)_LIBS_STATIC_$(2)) endif ) endef @@ -93,11 +108,20 @@ $(eval $(call demobuildrules,$(1),$(2)) $(foreach demoname,$(DEMOS),$(call demorule,$(demoname),$(1),$(2),$(3))) ifeq ($(3),1) .PHONY: demos-$(1)-$(2) -TARGETLIST += demos-$(1)-$(2) endif ) endef +define adddemotarget +$(eval DEMO_TARGETLIST+=$(1)) +endef + +$(foreach demoname,$(DEMOS),$(call adddemotarget,$(demoname))) + + +TARGETLIST+=demos demos-debug demos-release +TARGETLIST+=demos-static demos-debug-static demos-release-static + # $1 --> gl or gles # $2 --> (0: skip build targets, 1: add build targets) define demosapi @@ -109,6 +133,11 @@ demos-release: demos-$(1)-release demos-debug: demos-$(1)-debug .PHONY: demos-$(1) TARGETLIST += demos-$(1) +demos-$(1)-static: demos-$(1)-release-static demos-$(1)-debug-static +demos-release-static: demos-$(1)-release-static +demos-debug-static: demos-$(1)-debug-static +.PHONY: demos-$(1)-static +TARGETLIST += demos-$(1)-static endif ) endef @@ -117,5 +146,4 @@ endef $(call demosapi,GL,$(BUILD_GL)) $(call demosapi,GLES,$(BUILD_GLES)) demos: demos-debug demos-release -TARGETLIST+=demos demos-debug demos-release all: demos diff --git a/make/Makefile.egl.lib.mk b/make/Makefile.egl.lib.mk index 5d23b0799..ad8b35112 100644 --- a/make/Makefile.egl.lib.mk +++ b/make/Makefile.egl.lib.mk @@ -14,15 +14,27 @@ build/NEGL/$(1)/%.d: ; libNEGL_$(1): libNEGL_$(1).so libNEGL_$(1).so: $$(NEGL_OBJS_$(1)) libFastUIDraw_$(1).so - $(CXX) -shared -Wl,-soname,libNEGL_$(1).so -o libNEGL_$(1).so $$(NEGL_OBJS_$(1)) -lEGL -L. -lFastUIDraw_$(1) $(FASTUIDRAW_LIBS) + $(CXX) -shared -Wl,-soname,libNEGL_$(1).so -o libNEGL_$(1).so $$(NEGL_OBJS_$(1)) -lEGL -L. -lFastUIDraw_$(1) $(FASTUIDRAW_DEPS_LIBS) CLEAN_FILES += libNEGL_$(1).so INSTALL_LIBS += libNEGL_$(1).so libNEGL: libNEGL_$(1) .PHONY: libNEGL_$(1) libNEGL + +libNEGL_$(1):libNEGL_$(1).a +libNEGL_$(1).a: $$(NEGL_OBJS_$(1)) + ar rcs $$@ $$(NEGL_OBJS_$(1)) +CLEAN_FILES += libNEGL_$(1).a +INSTALL_STATIC_LIBS += libNEGL_$(1).a + +libNEGL_$(1)-static: libNEGL_$(1).a +.PHONY: libNEGL_$(1)-static +libNEGL-static: libNEGL_$(1).a +.PHONY: libNEGL ) endef ifeq ($(BUILD_NEGL),1) $(call egllibrules,release) $(call egllibrules,debug) +TARGETLIST += libNEGL libNEGL-static endif diff --git a/make/Makefile.gl_backend.lib.mk b/make/Makefile.gl_backend.lib.mk index abd81c2a8..d4632b354 100644 --- a/make/Makefile.gl_backend.lib.mk +++ b/make/Makefile.gl_backend.lib.mk @@ -1,7 +1,5 @@ FASTUIDRAW_GL_CFLAGS = FASTUIDRAW_GLES_CFLAGS = -DFASTUIDRAW_GL_USE_GLES -FASTUIDRAW_GL_LIBS = -FASTUIDRAW_GLES_LIBS = FASTUIDRAW_GL_STRING_RESOURCES_SRCS = $(patsubst %.resource_string, build/string_resources_cpp/%.resource_string.cpp, $(FASTUIDRAW_GL_RESOURCE_STRING) ) CLEAN_FILES += $(FASTUIDRAW_GL_STRING_RESOURCES_SRCS) @@ -39,7 +37,6 @@ NGL_$(1)_$(2)_OBJ = $$(patsubst %.cpp, build/$(2)/$(1)/%.o, $$(NGL_$(1)_SRCS)) FASTUIDRAW_$(1)_$(2)_DEPS = $$(patsubst %.cpp, build/$(2)/$(1)/%.d, $$(FASTUIDRAW_GL_SOURCES)) FASTUIDRAW_$(1)_$(2)_RESOURCE_OBJS = $$(patsubst %.resource_string, build/$(2)/$(1)/%.resource_string.o, $$(FASTUIDRAW_GL_RESOURCE_STRING)) FASTUIDRAW_$(1)_$(2)_ALL_OBJS = $$(FASTUIDRAW_$(1)_$(2)_OBJS) $$(FASTUIDRAW_$(1)_$(2)_PRIVATE_OBJS) $$(FASTUIDRAW_$(1)_$(2)_RESOURCE_OBJS) -FASTUIDRAW_$(1)_$(2)_LIBS = -lFastUIDraw$(1)_$(2) -lN$(1)_$(2) $$(FASTUIDRAW_$(2)_LIBS) $$(FASTUIDRAW_$(1)_LIBS) CLEAN_FILES += $$(FASTUIDRAW_$(1)_$(2)_ALL_OBJS) $$(FASTUIDRAW_$(1)_$(2)_ALL_OBJS) SUPER_CLEAN_FILES += $$(FASTUIDRAW_$(1)_$(2)_DEPS) $$(FASTUIDRAW_$(1)_$(2)_DEPS) $$(NGL_$(1)_$(2)_OBJ) CLEAN_FILES += libFastUIDraw$(1)_$(2).dll libFastUIDraw$(1)_$(2).dll.a libN$(1)_$(2).dll libN$(1)_$(2).dll.a @@ -52,27 +49,36 @@ ifeq ($(MINGW_BUILD),1) libFastUIDraw$(1)_$(2): libFastUIDraw$(1)_$(2).dll libFastUIDraw$(1)_$(2).dll.a: libFastUIDraw$(1)_$(2).dll libFastUIDraw$(1)_$(2).dll: libFastUIDraw_$(2).dll libN$(1)_$(2).dll $$(FASTUIDRAW_$(1)_$(2)_ALL_OBJS) - $(CXX) -shared -Wl,--out-implib,libFastUIDraw$(1)_$(2).dll.a -o libFastUIDraw$(1)_$(2).dll $$(FASTUIDRAW_$(1)_$(2)_ALL_OBJS) $$(FASTUIDRAW_$(2)_LIBS) -L. -lN$(1)_$(2) + $(CXX) -shared -Wl,--out-implib,libFastUIDraw$(1)_$(2).dll.a -o libFastUIDraw$(1)_$(2).dll $$(FASTUIDRAW_$(1)_$(2)_ALL_OBJS) -L. -lN$(1)_$(2) -lFastUIDraw_$(2) libN$(1)_$(2): libN$(1)_$(2).dll.a libN$(1)_$(2).dll.a: libN$(1)_$(2).dll libN$(1)_$(2).dll: $$(NGL_$(1)_$(2)_OBJ) libFastUIDraw_$(2) - $(CXX) -shared -Wl,--out-implib,libN$(1)_$(2).dll.a -o libN$(1)_$(2).dll $$(NGL_$(1)_$(2)_OBJ) -L. $$(FASTUIDRAW_$(2)_LIBS) -LIBFASTUIDRAW_$(1)_$(2) = libFastUIDraw$(1)_$(2).dll.a -LIBN$(1)_$(2) = libN$(1)_$(2).dll.a + $(CXX) -shared -Wl,--out-implib,libN$(1)_$(2).dll.a -o libN$(1)_$(2).dll $$(NGL_$(1)_$(2)_OBJ) -L. -lFastUIDraw_$(2) INSTALL_LIBS += libFastUIDraw$(1)_$(2).dll.a libN$(1)_$(2).dll.a INSTALL_EXES += libFastUIDraw$(1)_$(2).dll libN$(1)_$(2).dll else libFastUIDraw$(1)_$(2): libFastUIDraw$(1)_$(2).so libFastUIDraw$(1)_$(2).so: libFastUIDraw_$(2).so libN$(1)_$(2).so $$(FASTUIDRAW_$(1)_$(2)_ALL_OBJS) - $(CXX) -shared -Wl,-soname,libFastUIDraw$(1)_$(2).so -o libFastUIDraw$(1)_$(2).so $$(FASTUIDRAW_$(1)_$(2)_ALL_OBJS) $$(FASTUIDRAW_$(2)_LIBS) -L. -lN$(1)_$(2) + $(CXX) -shared -Wl,-soname,libFastUIDraw$(1)_$(2).so -o libFastUIDraw$(1)_$(2).so $$(FASTUIDRAW_$(1)_$(2)_ALL_OBJS) -L. -lN$(1)_$(2) -lFastUIDraw_$(2) libN$(1)_$(2): libN$(1)_$(2).so libN$(1)_$(2).so: $$(NGL_$(1)_$(2)_OBJ) libFastUIDraw_$(2) - $(CXX) -shared -Wl,-soname,libN$(1)_$(2).so -o libN$(1)_$(2).so $$(NGL_$(1)_$(2)_OBJ) -L. $$(FASTUIDRAW_$(2)_LIBS) -LIBFASTUIDRAW_$(1)_$(2) = libFastUIDraw$(1)_$(2).so -LIBN$(1)_$(2) = libN$(1)_$(2).so + $(CXX) -shared -Wl,-soname,libN$(1)_$(2).so -o libN$(1)_$(2).so $$(NGL_$(1)_$(2)_OBJ) -L. -lFastUIDraw_$(2) INSTALL_LIBS += libFastUIDraw$(1)_$(2).so libN$(1)_$(2).so endif +libFastUIDraw$(1)_$(2)-static: libFastUIDraw$(1)_$(2).a +.PHONY: libFastUIDraw$(1)_$(2)-static +libFastUIDraw$(1)_$(2).a: $$(FASTUIDRAW_$(1)_$(2)_ALL_OBJS) libN$(1)_$(2).a + ar rcs $$@ $$(FASTUIDRAW_$(1)_$(2)_ALL_OBJS) + +libN$(1)_$(2)-static: libN$(1)_$(2).a +.PHONY: libN$(1)_$(2)-static +libN$(1)_$(2).a: $$(NGL_$(1)_$(2)_OBJ) + ar rcs $$@ $$(NGL_$(1)_$(2)_OBJ) + +CLEAN_FILES += libFastUIDraw$(1)_$(2).a libN$(1)_$(2).a +INSTALL_STATIC_LIBS += libFastUIDraw$(1)_$(2).a libN$(1)_$(2).a + endif ) endef @@ -83,17 +89,20 @@ define glrules $(eval $(call glrule,$(1),release,$(2)) $(call glrule,$(1),debug,$(2)) ifeq ($(2),1) -TARGETLIST += libFastUIDraw$(1) libFastUIDraw$(1)_debug libFastUIDraw$(1)_release +TARGETLIST += libFastUIDraw$(1) libFastUIDraw$(1)-static libFastUIDraw$(1): libFastUIDraw$(1)_debug libFastUIDraw$(1)_release +libFastUIDraw$(1)-static: libFastUIDraw$(1)_release-static libFastUIDraw$(1)_debug-static .PHONY: libFastUIDraw$(1) .PHONY: libFastUIDraw$(1)_release .PHONY: libFastUIDraw$(1)_debug libN$(1): libN$(1)_debug libN$(1)_release -TARGETLIST += libN$(1) libN$(1)_debug libN$(1)_release -all: libFastUIDraw$(1) libN$(1) +libN$(1)-static: libN$(1)_debug-static libN$(1)_release-static +TARGETLIST += libN$(1) libN$(1)-static +all: libFastUIDraw$(1)-static libFastUIDraw$(1) libN$(1) libN$(1)-static .PHONY: libN$(1) .PHONY: libN$(1)_debug .PHONY: libN$(1)_release +.PHONY: static-libs-$(1) endif ) endef diff --git a/make/Makefile.install.mk b/make/Makefile.install.mk index 1d44fac69..e4b39077b 100644 --- a/make/Makefile.install.mk +++ b/make/Makefile.install.mk @@ -9,7 +9,8 @@ OTHER_TYPE_debug = release fastuidraw-config.nodir: fastuidraw-config.in @echo Generating $@ @cp $< $@ - @sed -i 's!@FASTUIDRAW_LIBS@!$(FASTUIDRAW_LIBS)!g' $@ + @sed -i 's!@FASTUIDRAW_DEPS_LIBS@!$(FASTUIDRAW_DEPS_LIBS)!g' $@ + @sed -i 's!@FASTUIDRAW_DEPS_STATIC_LIBS@!$(FASTUIDRAW_DEPS_STATIC_LIBS)!g' $@ @sed -i 's!@FASTUIDRAW_release_CFLAGS@!$(FASTUIDRAW_release_CFLAGS)!g' $@ @sed -i 's!@FASTUIDRAW_debug_CFLAGS@!$(FASTUIDRAW_debug_CFLAGS)!g' $@ @sed -i 's!@FASTUIDRAW_GLES_CFLAGS@!$(FASTUIDRAW_GLES_CFLAGS)!g' $@ @@ -60,7 +61,6 @@ fastuidraw$(2)-$(1).pc: fastuidraw-backend.pc.in n$(2)-$(1).pc .SECONDARY: fastuidraw$(2)-$(1).pc n$(2)-$(1).pc pkg-config-files: fastuidraw$(2)-$(1).pc n$(2)-$(1).pc INSTALL_PKG_FILES+=fastuidraw$(2)-$(1).pc n$(2)-$(1).pc -TARGETLIST+=fastuidraw$(2)-$(1).pc n$(2)-$(1).pc endif ) CLEAN_FILES+=fastuidraw$(2)-$(1).pc n$(2)-$(1).pc @@ -87,19 +87,20 @@ nEGL-$(1).pc: n.pc.in fastuidraw-$(1).pc @sed -i 's!@INSTALL_LOCATION@!$(INSTALL_LOCATION_VALUE)!g' $$@ @sed -i 's!@N_ADDITIONAL_LIBS@!-lEGL!g' $$@ INSTALL_PKG_FILES+=nEGL-$(1).pc -TARGETLIST+=nEGL-$(1).pc pkg-config-files: nEGL-$(1).pc endif .PHONY:fastuidraw-$(1).pc nEGL-$(1).pc .SECONDARY: fastuidraw-$(1).pc nEGL-$(1).pc CLEAN_FILES+=fastuidraw-$(1).pc nEGL-$(1).pc INSTALL_PKG_FILES+=fastuidraw-$(1).pc -TARGETLIST+=fastuidraw-$(1).pc pkg-config-files: fastuidraw-$(1).pc $(call pkgconfrulesapi,$(1),GL,$(BUILD_GL)) $(call pkgconfrulesapi,$(1),GLES,$(BUILD_GLES))) endef +ifeq ($(INSTALL_STATIC),1) +INSTALL_LIBS += $(INSTALL_STATIC_LIBS) +endif $(call pkgconfrules,release) $(call pkgconfrules,debug) diff --git a/make/Makefile.settings.mk b/make/Makefile.settings.mk index 6715169f6..889a5b0d2 100644 --- a/make/Makefile.settings.mk +++ b/make/Makefile.settings.mk @@ -2,6 +2,7 @@ MINGW_BUILD = 0 UNAME = $(shell uname -s) UNAMER= $(shell uname -r) +EXE_SUFFIX = ifeq ($(findstring MINGW,$(UNAME)),MINGW) MINGW_BUILD = 1 ifeq ($(findstring 1.0, $(UNAMER)), 1.0) @@ -11,6 +12,7 @@ ifeq ($(findstring MINGW,$(UNAME)),MINGW) else ifeq ($(findstring MINGW32,$(UNAME)),MINGW32) MINGW_MODE = MINGW32 endif + EXE_SUFFIX = .exe endif #TODO: detection for DARWIN @@ -20,14 +22,13 @@ DARWIN_BUILD = 0 # Setting for building libFastUIDraw.so base library CXX ?= g++ CC ?= gcc -STRING_RESOURCE_CC = shell_scripts/fastuidraw-create-resource-cpp-file.sh -FASTUIDRAW_LIBS = - +STRING_RESOURCE_CC = shell_scripts/fastuidraw-create-resource-cpp-file${EXE_SUFFIX} ####################################### # Platform specific libraries needed ifeq ($(MINGW_BUILD),1) - FASTUIDRAW_LIBS += -lmingw32 + FASTUIDRAW_DEPS_LIBS += -lmingw32 + FASTUIDRAW_DEPS_STATIC_LIBS += -lmingw32 endif ############################################# diff --git a/n.pc.in b/n.pc.in index c27f292e7..e4c7b8ace 100644 --- a/n.pc.in +++ b/n.pc.in @@ -9,5 +9,5 @@ Version: Requires: fastuidraw-@TYPE@ Conflicts: n@API@-@OTHER_TYPE@ n@OTHER_API@-@TYPE@ n@OTHER_API@-@OTHER_TYPE@ Cflags: -Libs: -L${libdir} -lN@API@_@TYPE@ @N_ADDITIONAL_LIBS@ -Libs.private: +Libs: -L${libdir} -lN@API@_@TYPE@ +Libs.private: @N_ADDITIONAL_LIBS@ diff --git a/shell_scripts/fastuidraw-create-resource-cpp-file.cpp b/shell_scripts/fastuidraw-create-resource-cpp-file.cpp new file mode 100644 index 000000000..4bfbf59be --- /dev/null +++ b/shell_scripts/fastuidraw-create-resource-cpp-file.cpp @@ -0,0 +1,56 @@ +#include +#include +#include + +using namespace std; + +void show_usage(const char* app) +{ + cerr << "Usage: " << app << " input_file output_name output_directory" << endl + << "Creates a .cpp file named output_name.cpp in the directory output_directory" << endl + << "which when added to a project adds a resource for fastuidraw::fetch_static_resource()" << endl + << "named output_name with value the contents of input_file. " << endl; +} + +int main(int argc, char **argv) +{ + if (argc < 4) { + show_usage(argv[0]); + return EXIT_FAILURE; + } + + string in_filename(argv[1]); + string out_filename(argv[2]); + string out_dirname(argv[3]); + + ifstream inf(in_filename); + if (inf.fail()) { + cerr << "Input file " << in_filename << " not found" << endl; + return EXIT_FAILURE; + } + + string out_path = out_dirname + '/' + out_filename + ".cpp"; + ofstream outf(out_path, ios::binary | ios::out); + if (outf.fail()) { + cerr << "Can't open output file at:" << out_path << endl; + return EXIT_FAILURE; + } + + outf << "#include \n" << endl + << "namespace { \n\tconst uint8_t values[]={ " << endl; + + string line; + while (getline(inf, line).good()) { + for (int c : line) { + outf << c << ","; + } + outf << 10 << ","; // End-of-Line + } + + outf << " 0 };" << endl + <<" fastuidraw::static_resource R(\"" << out_filename << "\", fastuidraw::c_array(values, sizeof(values)));" << endl + <<"\n}" << endl; + + + return EXIT_SUCCESS; +} diff --git a/shell_scripts/fastuidraw-create-resource-cpp-file.sh b/shell_scripts/fastuidraw-create-resource-cpp-file.sh index 0bb74ede9..272a32f20 100755 --- a/shell_scripts/fastuidraw-create-resource-cpp-file.sh +++ b/shell_scripts/fastuidraw-create-resource-cpp-file.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Adapted from: create_cpp_from_string_resource.sh of WRATH: # diff --git a/src/fastuidraw/Rules.mk b/src/fastuidraw/Rules.mk index f56f754b7..84e279990 100644 --- a/src/fastuidraw/Rules.mk +++ b/src/fastuidraw/Rules.mk @@ -28,7 +28,10 @@ include $(dir)/Rules.mk dir := $(d)/gl_backend include $(dir)/Rules.mk -FASTUIDRAW_SOURCES += $(call filelist, image.cpp colorstop.cpp colorstop_atlas.cpp path.cpp tessellated_path.cpp) +FASTUIDRAW_SOURCES += $(call filelist, image.cpp colorstop.cpp \ + colorstop_atlas.cpp path.cpp tessellated_path.cpp \ + arc_tessellated_path.cpp) + NEGL_SRCS += $(call filelist, egl_binding.cpp) # Begin standard footer diff --git a/src/fastuidraw/arc_tessellated_path.cpp b/src/fastuidraw/arc_tessellated_path.cpp new file mode 100644 index 000000000..578089370 --- /dev/null +++ b/src/fastuidraw/arc_tessellated_path.cpp @@ -0,0 +1,391 @@ +/*! + * \file arc_tessellated_path.cpp + * \brief file arc_tessellated_path.cpp + * + * Copyright 2018 by Intel. + * + * Contact: kevin.rogovin@intel.com + * + * This Source Code Form is subject to the + * terms of the Mozilla Public License, v. 2.0. + * If a copy of the MPL was not distributed with + * this file, You can obtain one at + * http://mozilla.org/MPL/2.0/. + * + * \author Kevin Rogovin + * + */ + + +#include +#include +#include +#include +#include +#include +#include "private/util_private.hpp" +#include "private/bounding_box.hpp" + +namespace +{ + class ArcTessellatedPathPrivate + { + public: + ArcTessellatedPathPrivate(const fastuidraw::Path &input, + fastuidraw::ArcTessellatedPath::TessellationParams TP); + + std::vector > > m_edge_ranges; + std::vector m_segment_data; + fastuidraw::BoundingBox m_bounding_box; + fastuidraw::ArcTessellatedPath::TessellationParams m_params; + float m_effective_threshhold; + unsigned int m_max_segments, m_max_recursion; + }; + + void + union_segment(const fastuidraw::ArcTessellatedPath::segment &S, + fastuidraw::BoundingBox &BB) + { + using namespace fastuidraw; + if (S.m_type == ArcTessellatedPath::line_segment) + { + BB.union_point(S.m_p); + BB.union_point(S.m_data); + } + else + { + vec2 s, e; + for (int i = 0; i < 2; ++i) + { + vec2 q; + q = S.m_p + S.m_radius * vec2(t_cos(S.m_data[i]), t_sin(S.m_data[i])); + BB.union_point(q); + } + } + } + + float + segment_length(const fastuidraw::ArcTessellatedPath::segment &S) + { + using namespace fastuidraw; + if (S.m_type == ArcTessellatedPath::line_segment) + { + return (S.m_p - S.m_data).magnitude(); + } + else + { + return t_abs(S.m_data[0] - S.m_data[1]) * S.m_radius; + } + } +} + +////////////////////////////////////////////// +// TessellatedPathPrivate methods +ArcTessellatedPathPrivate:: +ArcTessellatedPathPrivate(const fastuidraw::Path &input, + fastuidraw::ArcTessellatedPath::TessellationParams TP): + m_edge_ranges(input.number_contours()), + m_params(TP), + m_effective_threshhold(0.0f), + m_max_segments(0u), + m_max_recursion(0u) +{ +} + +////////////////////////////////////////////////////////// +// fastuidraw::ArcTessellatedPath::SegmentStorage methods +void +fastuidraw::ArcTessellatedPath::SegmentStorage:: +add_segment(const segment &S) +{ + std::vector *d; + d = static_cast*>(m_d); + d->push_back(S); +} + +////////////////////////////////////// +// fastuidraw::ArcTessellatedPath methods +fastuidraw::ArcTessellatedPath:: +ArcTessellatedPath(const Path &input, + fastuidraw::ArcTessellatedPath::TessellationParams TP) +{ + ArcTessellatedPathPrivate *d; + m_d = d = FASTUIDRAWnew ArcTessellatedPathPrivate(input, TP); + + if (input.number_contours() > 0) + { + std::list > temp; + std::vector work_room; + std::list >::const_iterator iter, end_iter; + + for(unsigned int loc = 0, o = 0, endo = input.number_contours(); o < endo; ++o) + { + reference_counted_ptr contour(input.contour(o)); + float contour_length(0.0f), open_contour_length(0.0f), closed_contour_length(0.0f); + std::list >::iterator start_contour; + + d->m_edge_ranges[o].resize(contour->number_points()); + for(unsigned int e = 0, ende = contour->number_points(); e < ende; ++e) + { + unsigned int needed, recurse_depth; + float tmp; + SegmentStorage segment_storage; + + FASTUIDRAWassert(work_room.empty()); + segment_storage.m_d = &work_room; + + recurse_depth = contour->interpolator(e)->produce_tessellation(d->m_params, &segment_storage, &tmp); + d->m_max_recursion = t_max(d->m_max_recursion, recurse_depth); + + needed = work_room.size(); + d->m_edge_ranges[o][e] = range_type(loc, loc + needed); + loc += needed; + + FASTUIDRAWassert(needed > 0u); + d->m_max_segments = t_max(d->m_max_segments, needed); + d->m_effective_threshhold = t_max(d->m_effective_threshhold, tmp); + + work_room.resize(needed); + for(unsigned int n = 0; n < work_room.size(); ++n) + { + union_segment(work_room[n], d->m_bounding_box); + work_room[n].m_length = segment_length(work_room[n]); + + if (n != 0) + { + work_room[n].m_distance_from_edge_start = + work_room[n - 1].m_distance_from_edge_start + + work_room[n - 1].m_length; + } + else + { + work_room[n].m_distance_from_edge_start = 0.0f; + } + + work_room[n].m_distance_from_contour_start = contour_length; + contour_length += work_room[n].m_distance_from_edge_start; + } + + for(unsigned int n = 0; n < needed; ++n) + { + work_room[n].m_edge_length = work_room[needed - 1].m_distance_from_edge_start + + work_room[needed - 1].m_length; + } + + if (e + 2 == ende) + { + open_contour_length = contour_length; + } + else if (e + 1 == ende) + { + closed_contour_length = contour_length; + } + + /* append the data to temp and clear work_room for the next edge */ + std::list >::iterator t; + t = temp.insert(temp.end(), std::vector()); + t->swap(work_room); + + if (e == 0) + { + start_contour = t; + } + } + + for(std::list >::iterator + t = start_contour, endt = temp.end(); t != endt; ++t) + { + for(unsigned int e = 0, ende = t->size(); e < ende; ++e) + { + (*t)[e].m_open_contour_length = open_contour_length; + (*t)[e].m_closed_contour_length = closed_contour_length; + } + } + } + + unsigned int total_needed; + + total_needed = d->m_edge_ranges.back().back().m_end; + d->m_segment_data.reserve(total_needed); + for(iter = temp.begin(), end_iter = temp.end(); iter != end_iter; ++iter) + { + std::copy(iter->begin(), iter->end(), std::back_inserter(d->m_segment_data)); + } + FASTUIDRAWassert(total_needed == d->m_segment_data.size()); + } +} + +fastuidraw::ArcTessellatedPath:: +~ArcTessellatedPath() +{ + ArcTessellatedPathPrivate *d; + d = static_cast(m_d); + FASTUIDRAWdelete(d); + m_d = nullptr; +} + +const fastuidraw::ArcTessellatedPath::TessellationParams& +fastuidraw::ArcTessellatedPath:: +tessellation_parameters(void) const +{ + ArcTessellatedPathPrivate *d; + d = static_cast(m_d); + + return d->m_params; +} + +float +fastuidraw::ArcTessellatedPath:: +effective_threshhold(void) const +{ + ArcTessellatedPathPrivate *d; + d = static_cast(m_d); + return d->m_effective_threshhold; +} + +unsigned int +fastuidraw::ArcTessellatedPath:: +max_segments(void) const +{ + ArcTessellatedPathPrivate *d; + d = static_cast(m_d); + return d->m_max_segments; +} + +unsigned int +fastuidraw::ArcTessellatedPath:: +max_recursion(void) const +{ + ArcTessellatedPathPrivate *d; + d = static_cast(m_d); + return d->m_max_recursion; +} + +fastuidraw::c_array +fastuidraw::ArcTessellatedPath:: +segment_data(void) const +{ + ArcTessellatedPathPrivate *d; + d = static_cast(m_d); + + return make_c_array(d->m_segment_data); +} + +unsigned int +fastuidraw::ArcTessellatedPath:: +number_contours(void) const +{ + ArcTessellatedPathPrivate *d; + d = static_cast(m_d); + + return d->m_edge_ranges.size(); +} + +fastuidraw::range_type +fastuidraw::ArcTessellatedPath:: +contour_range(unsigned int contour) const +{ + ArcTessellatedPathPrivate *d; + d = static_cast(m_d); + + return range_type(d->m_edge_ranges[contour].front().m_begin, + d->m_edge_ranges[contour].back().m_end); +} + +fastuidraw::range_type +fastuidraw::ArcTessellatedPath:: +unclosed_contour_range(unsigned int contour) const +{ + ArcTessellatedPathPrivate *d; + d = static_cast(m_d); + + range_type return_value; + unsigned int num_edges(number_edges(contour)); + + return_value.m_begin = d->m_edge_ranges[contour].front().m_begin; + return_value.m_end = (num_edges > 1) ? + d->m_edge_ranges[contour][num_edges - 2].m_end: + d->m_edge_ranges[contour][num_edges - 1].m_end; + + return return_value; +} + +fastuidraw::c_array +fastuidraw::ArcTessellatedPath:: +contour_segment_data(unsigned int contour) const +{ + ArcTessellatedPathPrivate *d; + d = static_cast(m_d); + + return make_c_array(d->m_segment_data).sub_array(contour_range(contour)); +} + +fastuidraw::c_array +fastuidraw::ArcTessellatedPath:: +unclosed_contour_segment_data(unsigned int contour) const +{ + ArcTessellatedPathPrivate *d; + d = static_cast(m_d); + + return make_c_array(d->m_segment_data).sub_array(unclosed_contour_range(contour)); +} + +unsigned int +fastuidraw::ArcTessellatedPath:: +number_edges(unsigned int contour) const +{ + ArcTessellatedPathPrivate *d; + d = static_cast(m_d); + + return d->m_edge_ranges[contour].size(); +} + +fastuidraw::range_type +fastuidraw::ArcTessellatedPath:: +edge_range(unsigned int contour, unsigned int edge) const +{ + ArcTessellatedPathPrivate *d; + d = static_cast(m_d); + + return d->m_edge_ranges[contour][edge]; +} + +fastuidraw::c_array +fastuidraw::ArcTessellatedPath:: +edge_segment_data(unsigned int contour, unsigned int edge) const +{ + ArcTessellatedPathPrivate *d; + d = static_cast(m_d); + + return make_c_array(d->m_segment_data).sub_array(edge_range(contour, edge)); +} + +fastuidraw::vec2 +fastuidraw::ArcTessellatedPath:: +bounding_box_min(void) const +{ + ArcTessellatedPathPrivate *d; + d = static_cast(m_d); + + return d->m_bounding_box.min_point(); +} + +fastuidraw::vec2 +fastuidraw::ArcTessellatedPath:: +bounding_box_max(void) const +{ + ArcTessellatedPathPrivate *d; + d = static_cast(m_d); + + return d->m_bounding_box.max_point();; +} + +fastuidraw::vec2 +fastuidraw::ArcTessellatedPath:: +bounding_box_size(void) const +{ + ArcTessellatedPathPrivate *d; + d = static_cast(m_d); + + return d->m_bounding_box.size(); +} diff --git a/src/fastuidraw/glsl/painter_backend_glsl.cpp b/src/fastuidraw/glsl/painter_backend_glsl.cpp index ec5fc467d..c4c25d937 100644 --- a/src/fastuidraw/glsl/painter_backend_glsl.cpp +++ b/src/fastuidraw/glsl/painter_backend_glsl.cpp @@ -493,34 +493,34 @@ add_enums(fastuidraw::glsl::ShaderSource &src) /* offset types for stroking. */ - .add_macro("fastuidraw_stroke_offset_start_sub_edge", StrokedPath::point::offset_start_sub_edge) - .add_macro("fastuidraw_stroke_offset_end_sub_edge", StrokedPath::point::offset_end_sub_edge) - .add_macro("fastuidraw_stroke_offset_shared_with_edge", StrokedPath::point::offset_shared_with_edge) - .add_macro("fastuidraw_stroke_offset_rounded_join", StrokedPath::point::offset_rounded_join) - - .add_macro("fastuidraw_stroke_offset_miter_bevel_join", StrokedPath::point::offset_miter_bevel_join) - .add_macro("fastuidraw_stroke_offset_miter_join", StrokedPath::point::offset_miter_join) - .add_macro("fastuidraw_stroke_offset_miter_clip_join", StrokedPath::point::offset_miter_clip_join) - .add_macro("fastuidraw_stroke_offset_miter_clip_join_lambda_negated", StrokedPath::point::offset_miter_clip_join_lambda_negated) - - .add_macro("fastuidraw_stroke_offset_rounded_cap", StrokedPath::point::offset_rounded_cap) - .add_macro("fastuidraw_stroke_offset_square_cap", StrokedPath::point::offset_square_cap) - .add_macro("fastuidraw_stroke_offset_adjustable_cap_contour_start", StrokedPath::point::offset_adjustable_cap_contour_start) - .add_macro("fastuidraw_stroke_offset_adjustable_cap_contour_end", StrokedPath::point::offset_adjustable_cap_contour_end) - .add_macro("fastuidraw_stroke_offset_type_bit0", StrokedPath::point::offset_type_bit0) - .add_macro("fastuidraw_stroke_offset_type_num_bits", StrokedPath::point::offset_type_num_bits) - - /* bit masks for StrokedPath::point::m_packed_data + .add_macro("fastuidraw_stroke_offset_start_sub_edge", StrokedPoint::offset_start_sub_edge) + .add_macro("fastuidraw_stroke_offset_end_sub_edge", StrokedPoint::offset_end_sub_edge) + .add_macro("fastuidraw_stroke_offset_shared_with_edge", StrokedPoint::offset_shared_with_edge) + .add_macro("fastuidraw_stroke_offset_rounded_join", StrokedPoint::offset_rounded_join) + + .add_macro("fastuidraw_stroke_offset_miter_bevel_join", StrokedPoint::offset_miter_bevel_join) + .add_macro("fastuidraw_stroke_offset_miter_join", StrokedPoint::offset_miter_join) + .add_macro("fastuidraw_stroke_offset_miter_clip_join", StrokedPoint::offset_miter_clip_join) + + .add_macro("fastuidraw_stroke_offset_rounded_cap", StrokedPoint::offset_rounded_cap) + .add_macro("fastuidraw_stroke_offset_square_cap", StrokedPoint::offset_square_cap) + .add_macro("fastuidraw_stroke_offset_adjustable_cap_contour_start", StrokedPoint::offset_adjustable_cap_contour_start) + .add_macro("fastuidraw_stroke_offset_adjustable_cap_contour_end", StrokedPoint::offset_adjustable_cap_contour_end) + .add_macro("fastuidraw_stroke_offset_type_bit0", StrokedPoint::offset_type_bit0) + .add_macro("fastuidraw_stroke_offset_type_num_bits", StrokedPoint::offset_type_num_bits) + + /* bit masks for StrokedPoint::m_packed_data */ - .add_macro("fastuidraw_stroke_sin_sign_mask", StrokedPath::point::sin_sign_mask) - .add_macro("fastuidraw_stroke_normal0_y_sign_mask", StrokedPath::point::normal0_y_sign_mask) - .add_macro("fastuidraw_stroke_normal1_y_sign_mask", StrokedPath::point::normal1_y_sign_mask) - .add_macro("fastuidraw_stroke_boundary_bit", StrokedPath::point::boundary_bit) - .add_macro("fastuidraw_stroke_join_mask", StrokedPath::point::join_mask) - .add_macro("fastuidraw_stroke_bevel_edge_mask", StrokedPath::point::bevel_edge_mask) - .add_macro("fastuidraw_stroke_adjustable_cap_ending_mask", StrokedPath::point::adjustable_cap_ending_mask) - .add_macro("fastuidraw_stroke_depth_bit0", StrokedPath::point::depth_bit0) - .add_macro("fastuidraw_stroke_depth_num_bits", StrokedPath::point::depth_num_bits) + .add_macro("fastuidraw_stroke_sin_sign_mask", StrokedPoint::sin_sign_mask) + .add_macro("fastuidraw_stroke_normal0_y_sign_mask", StrokedPoint::normal0_y_sign_mask) + .add_macro("fastuidraw_stroke_normal1_y_sign_mask", StrokedPoint::normal1_y_sign_mask) + .add_macro("fastuidraw_stroke_lambda_negaged_mask", StrokedPoint::lambda_negated_mask) + .add_macro("fastuidraw_stroke_boundary_bit", StrokedPoint::boundary_bit) + .add_macro("fastuidraw_stroke_join_mask", StrokedPoint::join_mask) + .add_macro("fastuidraw_stroke_bevel_edge_mask", StrokedPoint::bevel_edge_mask) + .add_macro("fastuidraw_stroke_adjustable_cap_ending_mask", StrokedPoint::adjustable_cap_ending_mask) + .add_macro("fastuidraw_stroke_depth_bit0", StrokedPoint::depth_bit0) + .add_macro("fastuidraw_stroke_depth_num_bits", StrokedPoint::depth_num_bits) /* dash shader modes. */ diff --git a/src/fastuidraw/glsl/private/backend_shaders.cpp b/src/fastuidraw/glsl/private/backend_shaders.cpp index 916db6f7b..fd94b98b5 100644 --- a/src/fastuidraw/glsl/private/backend_shaders.cpp +++ b/src/fastuidraw/glsl/private/backend_shaders.cpp @@ -189,28 +189,23 @@ ShaderSetCreatorConstants(void) FASTUIDRAWassert(m_stroke_render_pass_num_bits + m_stroke_dash_style_num_bits + 1u <= 32u); m_stroke_render_pass_bit0 = 0; - m_stroke_width_pixels_bit0 = m_stroke_render_pass_bit0 + m_stroke_render_pass_num_bits; - m_stroke_dash_style_bit0 = m_stroke_width_pixels_bit0 + 1u; + m_stroke_dash_style_bit0 = m_stroke_render_pass_bit0 + m_stroke_render_pass_num_bits; } ShaderSource& ShaderSetCreatorConstants:: add_constants(ShaderSource &src, bool render_pass_varies) const { - unsigned int stroke_width_pixels_bit0(m_stroke_width_pixels_bit0); unsigned int stroke_dash_style_bit0(m_stroke_dash_style_bit0); unsigned int stroke_render_pass_num_bits(m_stroke_render_pass_num_bits); if (!render_pass_varies) { - stroke_width_pixels_bit0 -= m_stroke_render_pass_num_bits; stroke_dash_style_bit0 -= m_stroke_render_pass_num_bits; stroke_render_pass_num_bits -= m_stroke_render_pass_num_bits; } src - .add_macro("fastuidraw_stroke_sub_shader_width_pixels_bit0", stroke_width_pixels_bit0) - .add_macro("fastuidraw_stroke_sub_shader_width_pixels_num_bits", 1) .add_macro("fastuidraw_stroke_sub_shader_render_pass_bit0", m_stroke_render_pass_bit0) .add_macro("fastuidraw_stroke_sub_shader_render_pass_num_bits", stroke_render_pass_num_bits) .add_macro("fastuidraw_stroke_sub_shader_dash_style_bit0", stroke_dash_style_bit0) @@ -444,7 +439,6 @@ create_glyph_shader(bool anisotropic) reference_counted_ptr ShaderSetCreator:: create_stroke_item_shader(enum PainterEnums::cap_style stroke_dash_style, - bool pixel_width_stroking, enum uber_stroke_render_pass_t render_pass) { reference_counted_ptr shader; @@ -452,35 +446,29 @@ create_stroke_item_shader(enum PainterEnums::cap_style stroke_dash_style, if (stroke_dash_style == fastuidraw::PainterEnums::number_cap_styles) { - sub_shader = (render_pass << m_stroke_render_pass_bit0) - | (uint32_t(pixel_width_stroking) << m_stroke_width_pixels_bit0); + sub_shader = (render_pass << m_stroke_render_pass_bit0); shader = FASTUIDRAWnew PainterItemShader(sub_shader, m_uber_stroke_shader); } else { if (render_pass == uber_stroke_non_aa && m_dashed_discard_stroke_shader) { - sub_shader = (stroke_dash_style << (m_stroke_dash_style_bit0 - m_stroke_render_pass_num_bits)) - | (uint32_t(pixel_width_stroking) << (m_stroke_width_pixels_bit0 - m_stroke_render_pass_num_bits)); + sub_shader = (stroke_dash_style << (m_stroke_dash_style_bit0 - m_stroke_render_pass_num_bits)); shader = FASTUIDRAWnew PainterItemShader(sub_shader, m_dashed_discard_stroke_shader); } else { sub_shader = (stroke_dash_style << m_stroke_dash_style_bit0) - | (render_pass << m_stroke_render_pass_bit0) - | (uint32_t(pixel_width_stroking) << m_stroke_width_pixels_bit0); + | (render_pass << m_stroke_render_pass_bit0); shader = FASTUIDRAWnew PainterItemShader(sub_shader, m_uber_dashed_stroke_shader); } } return shader; } - - PainterStrokeShader ShaderSetCreator:: create_stroke_shader(enum PainterEnums::cap_style stroke_style, - bool pixel_width_stroking, const reference_counted_ptr &stroke_data_selector) { using namespace fastuidraw::PainterEnums; @@ -490,29 +478,29 @@ create_stroke_shader(enum PainterEnums::cap_style stroke_style, .aa_type(m_stroke_tp) .stroking_data_selector(stroke_data_selector) .aa_action_pass1(m_stroke_action_pass1) - .aa_shader_pass1(create_stroke_item_shader(stroke_style, pixel_width_stroking, uber_stroke_aa_pass1)) + .aa_shader_pass1(create_stroke_item_shader(stroke_style, uber_stroke_aa_pass1)) .aa_action_pass2(m_stroke_action_pass2) - .aa_shader_pass2(create_stroke_item_shader(stroke_style, pixel_width_stroking, uber_stroke_aa_pass2)) - .non_aa_shader(create_stroke_item_shader(stroke_style, pixel_width_stroking, uber_stroke_non_aa)); + .aa_shader_pass2(create_stroke_item_shader(stroke_style, uber_stroke_aa_pass2)) + .non_aa_shader(create_stroke_item_shader(stroke_style, uber_stroke_non_aa)); return return_value; } PainterDashedStrokeShaderSet ShaderSetCreator:: -create_dashed_stroke_shader_set(bool pixel_width_stroking) +create_dashed_stroke_shader_set(void) { using namespace fastuidraw::PainterEnums; PainterDashedStrokeShaderSet return_value; reference_counted_ptr de; reference_counted_ptr se; - se = PainterDashedStrokeParams::stroking_data_selector(pixel_width_stroking); - de = PainterDashedStrokeParams::dash_evaluator(pixel_width_stroking); + se = PainterDashedStrokeParams::stroking_data_selector(); + de = PainterDashedStrokeParams::dash_evaluator(); return_value .dash_evaluator(de) - .shader(flat_caps, create_stroke_shader(flat_caps, pixel_width_stroking, se)) - .shader(rounded_caps, create_stroke_shader(rounded_caps, pixel_width_stroking, se)) - .shader(square_caps, create_stroke_shader(square_caps, pixel_width_stroking, se)); + .shader(flat_caps, create_stroke_shader(flat_caps, se)) + .shader(rounded_caps, create_stroke_shader(rounded_caps, se)) + .shader(square_caps, create_stroke_shader(square_caps, se)); return return_value; } @@ -549,18 +537,15 @@ create_shader_set(void) { using namespace fastuidraw::PainterEnums; PainterShaderSet return_value; - reference_counted_ptr se, se_pixel; + reference_counted_ptr se; - se = PainterStrokeParams::stroking_data_selector(false); - se_pixel = PainterStrokeParams::stroking_data_selector(true); + se = PainterStrokeParams::stroking_data_selector(); return_value .glyph_shader(create_glyph_shader(false)) .glyph_shader_anisotropic(create_glyph_shader(true)) - .stroke_shader(create_stroke_shader(number_cap_styles, false, se)) - .pixel_width_stroke_shader(create_stroke_shader(number_cap_styles, true, se_pixel)) - .dashed_stroke_shader(create_dashed_stroke_shader_set(false)) - .pixel_width_dashed_stroke_shader(create_dashed_stroke_shader_set(true)) + .stroke_shader(create_stroke_shader(number_cap_styles, se)) + .dashed_stroke_shader(create_dashed_stroke_shader_set()) .fill_shader(create_fill_shader()) .blend_shaders(create_blend_shaders()); return return_value; diff --git a/src/fastuidraw/glsl/private/backend_shaders.hpp b/src/fastuidraw/glsl/private/backend_shaders.hpp index 956b2dd69..74c9189ed 100644 --- a/src/fastuidraw/glsl/private/backend_shaders.hpp +++ b/src/fastuidraw/glsl/private/backend_shaders.hpp @@ -75,7 +75,7 @@ class ShaderSetCreatorConstants remove_constants(ShaderSource &src) const; uint32_t m_stroke_render_pass_num_bits, m_stroke_dash_style_num_bits; - uint32_t m_stroke_width_pixels_bit0, m_stroke_render_pass_bit0, m_stroke_dash_style_bit0; + uint32_t m_stroke_render_pass_bit0, m_stroke_dash_style_bit0; }; class ShaderSetCreator: @@ -107,15 +107,13 @@ class ShaderSetCreator: */ PainterStrokeShader create_stroke_shader(enum PainterEnums::cap_style stroke_dash_style, - bool pixel_width_stroking, const reference_counted_ptr &stroke_data_selector); PainterDashedStrokeShaderSet - create_dashed_stroke_shader_set(bool pixel_width_stroking); + create_dashed_stroke_shader_set(void); reference_counted_ptr create_stroke_item_shader(enum PainterEnums::cap_style stroke_dash_style, - bool pixel_width_stroking, enum uber_stroke_render_pass_t render_pass_macro); PainterFillShader diff --git a/src/fastuidraw/glsl/shaders/painter/fastuidraw_painter_stroke.frag.glsl.resource_string b/src/fastuidraw/glsl/shaders/painter/fastuidraw_painter_stroke.frag.glsl.resource_string index 3e4a30b86..ba90248f0 100644 --- a/src/fastuidraw/glsl/shaders/painter/fastuidraw_painter_stroke.frag.glsl.resource_string +++ b/src/fastuidraw/glsl/shaders/painter/fastuidraw_painter_stroke.frag.glsl.resource_string @@ -66,6 +66,7 @@ fastuidraw_gl_frag_main(in uint sub_shader, for non-anti-aliased shading because it does not allow discard */ #ifdef FASTUIDRAW_STROKE_DASHED + if ((fastuidraw_stroking_dash_bits & uint(fastuidraw_stroke_gauranteed_to_be_covered_mask)) == 0u) { float d, q, fw; @@ -143,12 +144,12 @@ fastuidraw_gl_frag_main(in uint sub_shader, { if (render_pass == uint(fastuidraw_stroke_non_aa) && q <= 0.0) { - FASTUIDRAW_DISCARD; + alpha = 0.0; } if (render_pass == uint(fastuidraw_stroke_aa_pass1) && alpha < 1.0 - 1.0 / 255.0) { - FASTUIDRAW_DISCARD; + alpha = 0.0; } } #endif @@ -169,6 +170,20 @@ fastuidraw_gl_frag_main(in uint sub_shader, alpha = 0.0; } } + #elif defined(FASTUIDRAW_STROKE_DASHED) + { + float dd, q; + + // modulate by coverage to boundary + q = 1.0 - fastuidraw_stroking_on_boundary; + dd = max(q, fwidth(q)); + alpha *= q / dd; + + if (render_pass != uint(fastuidraw_stroke_aa_pass2) && alpha < 1.0 - 1.0 / 255.0) + { + FASTUIDRAW_DISCARD; + } + } #else { if (render_pass == uint(fastuidraw_stroke_aa_pass2)) diff --git a/src/fastuidraw/glsl/shaders/painter/fastuidraw_painter_stroke.vert.glsl.resource_string b/src/fastuidraw/glsl/shaders/painter/fastuidraw_painter_stroke.vert.glsl.resource_string index e95a0760d..d2e1582dc 100644 --- a/src/fastuidraw/glsl/shaders/painter/fastuidraw_painter_stroke.vert.glsl.resource_string +++ b/src/fastuidraw/glsl/shaders/painter/fastuidraw_painter_stroke.vert.glsl.resource_string @@ -10,8 +10,7 @@ fastuidraw_stroke_compute_offset(in uint point_packed_data, in float miter_limit, out vec2 offset) { - if (offset_type == fastuidraw_stroke_offset_miter_clip_join - || offset_type == fastuidraw_stroke_offset_miter_clip_join_lambda_negated) + if (offset_type == fastuidraw_stroke_offset_miter_clip_join) { vec2 n0 = pre_offset, Jn0 = vec2(n0.y, -n0.x); vec2 n1 = auxiliary_offset, Jn1 = vec2(n1.y, -n1.x); @@ -21,7 +20,7 @@ fastuidraw_stroke_compute_offset(in uint point_packed_data, lambda = -sign(det); r = (det != 0.0) ? (dot(n0, n1) - 1.0) / det : 0.0; - if (offset_type == fastuidraw_stroke_offset_miter_clip_join_lambda_negated) + if ((uint(fastuidraw_stroke_lambda_negaged_mask) & point_packed_data) != 0u) { lambda = -lambda; } @@ -103,8 +102,7 @@ fastuidraw_stroke_compute_offset_pixels(in uint point_packed_data, out vec2 offset, inout float stroke_radius) { - if (offset_type == fastuidraw_stroke_offset_miter_clip_join - || offset_type == fastuidraw_stroke_offset_miter_clip_join_lambda_negated) + if (offset_type == fastuidraw_stroke_offset_miter_clip_join) { vec2 n0 = pre_offset, v0 = vec2(n0.y, -n0.x); vec2 n1 = auxiliary_offset, v1 = vec2(n1.y, -n1.x); @@ -113,7 +111,8 @@ fastuidraw_stroke_compute_offset_pixels(in uint point_packed_data, float det, r, r0, r1, lambda; lambda = -sign(dot(v1, n0)); - if (offset_type == fastuidraw_stroke_offset_miter_clip_join_lambda_negated) + + if ((uint(fastuidraw_stroke_lambda_negaged_mask) & point_packed_data) != 0u) { lambda = -lambda; } @@ -364,7 +363,6 @@ fastuidraw_gl_vert_main(in uint sub_shader, primary_attrib = uintBitsToFloat(uprimary_attrib); secondary_attrib = uintBitsToFloat(usecondary_attrib); - uint width_pixels; uint render_pass; bool stroke_width_pixels; int stroking_pass; @@ -382,12 +380,6 @@ fastuidraw_gl_vert_main(in uint sub_shader, } #endif - width_pixels = FASTUIDRAW_EXTRACT_BITS(fastuidraw_stroke_sub_shader_width_pixels_bit0, - fastuidraw_stroke_sub_shader_width_pixels_num_bits, - sub_shader); - - - stroke_width_pixels = (width_pixels == uint(1)); stroking_pass = int(render_pass); vec2 p; @@ -422,7 +414,8 @@ fastuidraw_gl_vert_main(in uint sub_shader, 1, point_packed_data)); - stroke_radius = stroke_params.radius; + stroke_width_pixels = (stroke_params.radius < 0.0); + stroke_radius = abs(stroke_params.radius); #ifdef FASTUIDRAW_STROKE_DASHED { @@ -492,10 +485,15 @@ fastuidraw_gl_vert_main(in uint sub_shader, edge. */ - //BUG: the code to collapse seems to kill something in a test it - // should not, for now just always expand - if (interval_id2 == interval_id && interval_id != -1 && false) + if (interval_id2 == interval_id && interval_id != -1) { + /* since the interval ID's are the same, then the edge is completely + * contained within an interval and does not need to be extended + * regardless of caps added, also the dashing computation can + * be entirely skipped. In addition, if the interval is a "skip" + * interval, then the sub-edge is NOT drawn and we collapse it + * to a point be setting on_boundary to 0. + */ fastuidraw_stroking_dash_bits = uint(fastuidraw_stroke_gauranteed_to_be_covered_mask); fastuidraw_stroking_distance = 0.0f; if (s < 0.0) @@ -505,12 +503,19 @@ fastuidraw_gl_vert_main(in uint sub_shader, } else { + /* since the interval ID's are different, the sub-edge spans + * multiple intervals. We extend the sub-interval on the start + * and on the end by the stroking radius, but we inform the + * fragment shader of the interval boundaries closest to the + * sub-edge boundaries via fastuidraw_stroking_distance_sub_edge_start + * and fastuidraw_stroking_distance_sub_edge_end + */ fastuidraw_stroking_dash_bits = 0u; if (offset_type == fastuidraw_stroke_offset_start_sub_edge) { - float new_d, clamp_interval_end; + float clamp_interval_end; - fastuidraw_stroking_distance_sub_edge_start = (s < 0.0 )? + fastuidraw_stroking_distance_sub_edge_start = (s < 0.0) ? interval_end : d; @@ -518,18 +523,10 @@ fastuidraw_gl_vert_main(in uint sub_shader, interval_begin2 : d2; - if (s < 0.0) + if (s < 0.0 && dash_style != fastuidraw_stroke_dashed_flat_caps) { - clamp_interval_end = min(interval_end, d2); - new_d = clamp_interval_end; - - if (dash_style != fastuidraw_stroke_dashed_flat_caps) - { - new_d -= stroke_radius; - } - - position += (new_d - d) * auxiliary_offset / delta_mag; - fastuidraw_stroking_distance = new_d; + fastuidraw_stroking_distance = d - stroke_radius; + position -= auxiliary_offset * (stroke_radius / delta_mag); } else { @@ -538,7 +535,7 @@ fastuidraw_gl_vert_main(in uint sub_shader, } else { - float new_d, clamp_interval_begin; + float clamp_interval_begin; fastuidraw_stroking_distance_sub_edge_start = (s2 < 0.0) ? interval_end2 : @@ -548,24 +545,10 @@ fastuidraw_gl_vert_main(in uint sub_shader, interval_begin : d; - if (s < 0.0) + if (s < 0.0 && dash_style != fastuidraw_stroke_dashed_flat_caps) { - clamp_interval_begin = max(interval_begin, d2); - new_d = interval_begin; - - if (dash_style != fastuidraw_stroke_dashed_flat_caps) - { - new_d += stroke_radius; - } - - /* auxiliary_offset is the direction from this point - (the end of the sub-edge) to the start of the - sub-edge. This is why we have the coefficient - (d - new_d) because moving in auxiliary_offset - is moving towards the start. - */ - position += (d - new_d) * auxiliary_offset / delta_mag; - fastuidraw_stroking_distance = new_d; + fastuidraw_stroking_distance = d + stroke_radius; + position -= auxiliary_offset * (stroke_radius / delta_mag); } else { diff --git a/src/fastuidraw/painter/Rules.mk b/src/fastuidraw/painter/Rules.mk index f1f90ba87..846daefe9 100644 --- a/src/fastuidraw/painter/Rules.mk +++ b/src/fastuidraw/painter/Rules.mk @@ -21,6 +21,7 @@ FASTUIDRAW_SOURCES += $(call filelist, fill_rule.cpp \ painter_dashed_stroke_shader_set.cpp painter_stroke_shader.cpp \ painter_glyph_shader.cpp painter_blend_shader_set.cpp \ painter_fill_shader.cpp \ + stroked_caps_joins.cpp stroked_point.cpp \ stroked_path.cpp filled_path.cpp) # Begin standard footer diff --git a/src/fastuidraw/painter/filled_path.cpp b/src/fastuidraw/painter/filled_path.cpp index 577c156d5..dba657b24 100644 --- a/src/fastuidraw/painter/filled_path.cpp +++ b/src/fastuidraw/painter/filled_path.cpp @@ -151,7 +151,7 @@ namespace { public: unsigned int m_start, m_end, m_next; - bool m_draw_edge, m_draw_bevel; + bool m_draw_edge, m_draw_join; unsigned int num_attributes(void) const @@ -159,7 +159,7 @@ namespace unsigned int e, b; e = (m_draw_edge) ? 4 : 0; - b = (m_draw_bevel) ? 3 : 0; + b = (m_draw_join) ? 3 : 0; return e + b; } @@ -169,7 +169,7 @@ namespace unsigned int e, b; e = (m_draw_edge) ? 6 : 0; - b = (m_draw_bevel) ? 3 : 0; + b = (m_draw_join) ? 3 : 0; return e + b; } }; @@ -1128,7 +1128,7 @@ add_edge(unsigned int p0, unsigned int p1, bool edge_drawn) { FASTUIDRAWassert(m_current.back().m_end == p0); m_current.back().m_next = p1; - m_current.back().m_draw_bevel = edge_drawn || m_current.back().m_draw_edge; + m_current.back().m_draw_join = edge_drawn || m_current.back().m_draw_edge; } E.m_start = p0; @@ -1148,11 +1148,11 @@ end_boundary(void) FASTUIDRAWassert(m_current.back().m_end == m_current.front().m_start); m_current.back().m_next = m_current.front().m_end; - m_current.back().m_draw_bevel = m_current.front().m_draw_edge || m_current.back().m_draw_edge; + m_current.back().m_draw_join = m_current.front().m_draw_edge || m_current.back().m_draw_edge; for(const Edge &e : m_current) { - if (e.m_draw_edge || e.m_draw_bevel) + if (e.m_draw_edge || e.m_draw_join) { m_edges.push_back(e); ++m_edge_count; @@ -2474,7 +2474,7 @@ fill_data(fastuidraw::c_array attributes, fastuidraw::c_array dst_attrib; fastuidraw::c_array dst_index; unsigned int num_attribute, num_indices; - unsigned int start_bevel_idx(0), start_bevel_attr(0); + unsigned int start_join_idx(0), start_join_attr(0); num_attribute = E.num_attributes(); num_indices = E.num_indices(); @@ -2494,15 +2494,15 @@ fill_data(fastuidraw::c_array attributes, dst_index[4] = a_tmp[ch] + 2; dst_index[5] = a_tmp[ch] + 3; - start_bevel_idx = 6; - start_bevel_attr = 4; + start_join_idx = 6; + start_join_attr = 4; } - if (E.m_draw_bevel) + if (E.m_draw_join) { for (unsigned int i = 0; i < 3; ++i) { - dst_index[start_bevel_idx + i] = a_tmp[ch] + start_bevel_attr + i; + dst_index[start_join_idx + i] = a_tmp[ch] + start_join_attr + i; } } @@ -2532,7 +2532,7 @@ pack_attribute(const Edge &E, -1.0f }; - FASTUIDRAWassert(E.m_draw_bevel || E.m_draw_edge); + FASTUIDRAWassert(E.m_draw_join || E.m_draw_edge); if (E.m_draw_edge) { @@ -2548,7 +2548,7 @@ pack_attribute(const Edge &E, } } - if (E.m_draw_bevel) + if (E.m_draw_join) { float s; fastuidraw::dvec2 t2, n2, p; diff --git a/src/fastuidraw/painter/packing/painter_backend.cpp b/src/fastuidraw/painter/packing/painter_backend.cpp index 79f46c327..0f20d9507 100644 --- a/src/fastuidraw/painter/packing/painter_backend.cpp +++ b/src/fastuidraw/painter/packing/painter_backend.cpp @@ -258,9 +258,7 @@ fastuidraw::PainterBackend:: register_shader(const PainterShaderSet &shaders) { register_shader(shaders.stroke_shader()); - register_shader(shaders.pixel_width_stroke_shader()); register_shader(shaders.dashed_stroke_shader()); - register_shader(shaders.pixel_width_dashed_stroke_shader()); register_shader(shaders.fill_shader()); register_shader(shaders.glyph_shader()); register_shader(shaders.glyph_shader_anisotropic()); diff --git a/src/fastuidraw/painter/painter.cpp b/src/fastuidraw/painter/painter.cpp index bd7a0a22c..13e9b445a 100644 --- a/src/fastuidraw/painter/painter.cpp +++ b/src/fastuidraw/painter/painter.cpp @@ -638,6 +638,8 @@ namespace std::vector m_stroke_index_adjusts; fastuidraw::StrokedPath::ChunkSet m_stroke_chunk_set; fastuidraw::StrokedPath::ScratchSpace m_stroked_path_scratch; + fastuidraw::StrokedCapsJoins::ChunkSet m_stroke_caps_joins_chunk_set; + fastuidraw::StrokedCapsJoins::ScratchSpace m_stroked_caps_joins_scratch; // common filling work room WindingSet m_fill_ws; @@ -1243,7 +1245,7 @@ select_stroked_path(const fastuidraw::Path &path, mag, m_curve_flatness); t = fastuidraw::t_min(thresh, m_curve_flatness / mag); const TessellatedPath *tess; - tess = path.tessellation(t, TessellatedPath::threshhold_curve_distance).get(); + tess = path.tessellation(t).get(); return tess->stroked().get(); } @@ -1256,7 +1258,7 @@ select_filled_path(const fastuidraw::Path &path) mag = compute_path_magnification(path); thresh = m_curve_flatness / mag; - return *path.tessellation(thresh, TessellatedPath::threshhold_curve_distance)->filled(); + return *path.tessellation(thresh)->filled(); } void @@ -1834,6 +1836,7 @@ stroke_path_common(const PainterStrokeShader &shader, const PainterData &draw, const PainterAttributeData *edge_data(nullptr), *cap_data(nullptr), *join_data(nullptr); bool is_miter_join; const PainterShaderData::DataBase *raw_data; + const StrokedCapsJoins &caps_joins(path.caps_joins()); raw_data = draw.m_item_shader_data.data().data_base(); edge_data = &path.edges(); @@ -1842,11 +1845,11 @@ stroke_path_common(const PainterStrokeShader &shader, const PainterData &draw, switch(cp) { case PainterEnums::rounded_caps: - cap_data = &path.rounded_caps(thresh); + cap_data = &caps_joins.rounded_caps(thresh); break; case PainterEnums::square_caps: - cap_data = &path.square_caps(); + cap_data = &caps_joins.square_caps(); break; case PainterEnums::flat_caps: @@ -1854,7 +1857,7 @@ stroke_path_common(const PainterStrokeShader &shader, const PainterData &draw, break; case PainterEnums::number_cap_styles: - cap_data = &path.adjustable_caps(); + cap_data = &caps_joins.adjustable_caps(); break; } } @@ -1863,27 +1866,27 @@ stroke_path_common(const PainterStrokeShader &shader, const PainterData &draw, { case PainterEnums::miter_clip_joins: is_miter_join = true; - join_data = &path.miter_clip_joins(); + join_data = &caps_joins.miter_clip_joins(); break; case PainterEnums::miter_bevel_joins: is_miter_join = true; - join_data = &path.miter_bevel_joins(); + join_data = &caps_joins.miter_bevel_joins(); break; case PainterEnums::miter_joins: is_miter_join = true; - join_data = &path.miter_joins(); + join_data = &caps_joins.miter_joins(); break; case PainterEnums::bevel_joins: is_miter_join = false; - join_data = &path.bevel_joins(); + join_data = &caps_joins.bevel_joins(); break; case PainterEnums::rounded_joins: is_miter_join = false; - join_data = &path.rounded_joins(thresh); + join_data = &caps_joins.rounded_joins(thresh); break; default: @@ -1893,8 +1896,8 @@ stroke_path_common(const PainterStrokeShader &shader, const PainterData &draw, float pixels_additional_room(0.0f), item_space_additional_room(0.0f); shader.stroking_data_selector()->stroking_distances(raw_data, &pixels_additional_room, &item_space_additional_room); + path.compute_chunks(d->m_work_room.m_stroked_path_scratch, - dash_evaluator, draw.m_item_shader_data.data().data_base(), d->m_clip_store.current(), d->m_clip_rect_state.item_matrix(), d->m_one_pixel_width, @@ -1903,13 +1906,25 @@ stroke_path_common(const PainterStrokeShader &shader, const PainterData &draw, close_contours, d->m_max_attribs_per_block, d->m_max_indices_per_block, - is_miter_join, d->m_work_room.m_stroke_chunk_set); + caps_joins.compute_chunks(d->m_work_room.m_stroked_caps_joins_scratch, + dash_evaluator, draw.m_item_shader_data.data().data_base(), + d->m_clip_store.current(), + d->m_clip_rect_state.item_matrix(), + d->m_one_pixel_width, + pixels_additional_room, + item_space_additional_room, + close_contours, + d->m_max_attribs_per_block, + d->m_max_indices_per_block, + is_miter_join, + d->m_work_room.m_stroke_caps_joins_chunk_set); + stroke_path(shader, draw, edge_data, d->m_work_room.m_stroke_chunk_set.edge_chunks(), - cap_data, d->m_work_room.m_stroke_chunk_set.cap_chunks(), - join_data, d->m_work_room.m_stroke_chunk_set.join_chunks(), + cap_data, d->m_work_room.m_stroke_caps_joins_chunk_set.cap_chunks(), + join_data, d->m_work_room.m_stroke_caps_joins_chunk_set.join_chunks(), with_anti_aliasing, call_back); } @@ -1956,17 +1971,6 @@ stroke_path(const PainterData &draw, const Path &path, close_contours, cp, js, with_anti_aliasing, call_back); } -void -fastuidraw::Painter:: -stroke_path_pixel_width(const PainterData &draw, const Path &path, - bool close_contours, enum PainterEnums::cap_style cp, enum PainterEnums::join_style js, - bool with_anti_aliasing, - const reference_counted_ptr &call_back) -{ - stroke_path(default_shaders().pixel_width_stroke_shader(), draw, path, - close_contours, cp, js, with_anti_aliasing, call_back); -} - void fastuidraw::Painter:: stroke_dashed_path(const PainterDashedStrokeShaderSet &shader, const PainterData &draw, @@ -2012,17 +2016,6 @@ stroke_dashed_path(const PainterData &draw, const Path &path, close_contours, cp, js, with_anti_aliasing, call_back); } -void -fastuidraw::Painter:: -stroke_dashed_path_pixel_width(const PainterData &draw, const Path &path, - bool close_contours, enum PainterEnums::cap_style cp, enum PainterEnums::join_style js, - bool with_anti_aliasing, - const reference_counted_ptr &call_back) -{ - stroke_dashed_path(default_shaders().pixel_width_dashed_stroke_shader(), draw, path, - close_contours, cp, js, with_anti_aliasing, call_back); -} - void fastuidraw::Painter:: fill_path(const PainterFillShader &shader, const PainterData &draw, diff --git a/src/fastuidraw/painter/painter_dashed_stroke_params.cpp b/src/fastuidraw/painter/painter_dashed_stroke_params.cpp index af345c70f..315d7fbe7 100644 --- a/src/fastuidraw/painter/painter_dashed_stroke_params.cpp +++ b/src/fastuidraw/painter/painter_dashed_stroke_params.cpp @@ -46,6 +46,7 @@ namespace float m_miter_limit; float m_radius; + enum fastuidraw::PainterStrokeParams::stroking_units_t m_stroking_units; float m_dash_offset; float m_total_length; float m_first_interval_start; @@ -57,8 +58,7 @@ namespace { public: explicit - DashEvaluator(bool pixel_width_stroking): - m_pixel_width_stroking(pixel_width_stroking) + DashEvaluator(void) {} virtual @@ -70,8 +70,6 @@ namespace bool close_to_boundary(float dist, fastuidraw::range_type interval); - - bool m_pixel_width_stroking; }; } @@ -81,6 +79,7 @@ PainterDashedStrokeParamsData:: PainterDashedStrokeParamsData(void): m_miter_limit(15.0f), m_radius(1.0f), + m_stroking_units(fastuidraw::PainterStrokeParams::path_stroking_units), m_dash_offset(0.0f), m_total_length(0.0f), m_first_interval_start(0.0f) @@ -109,7 +108,14 @@ pack_data(unsigned int alignment, fastuidraw::c_array using namespace fastuidraw; dst[PainterDashedStrokeParams::stroke_miter_limit_offset].f = m_miter_limit; - dst[PainterDashedStrokeParams::stroke_radius_offset].f = m_radius; + if (m_stroking_units == PainterStrokeParams::pixel_stroking_units) + { + dst[PainterDashedStrokeParams::stroke_radius_offset].f = -m_radius; + } + else + { + dst[PainterDashedStrokeParams::stroke_radius_offset].f = m_radius; + } dst[PainterDashedStrokeParams::stroke_dash_offset_offset].f = m_dash_offset; dst[PainterDashedStrokeParams::stroke_total_length_offset].f = m_total_length; dst[PainterDashedStrokeParams::stroke_first_interval_start_offset].f = m_first_interval_start; @@ -235,7 +241,7 @@ width(float f) PainterDashedStrokeParamsData *d; FASTUIDRAWassert(dynamic_cast(m_data) != nullptr); d = static_cast(m_data); - d->m_radius = 0.5f * f; + d->m_radius = (f > 0.0f) ? 0.5f * f : 0.0f; return *this; } @@ -256,7 +262,7 @@ radius(float f) PainterDashedStrokeParamsData *d; FASTUIDRAWassert(dynamic_cast(m_data) != nullptr); d = static_cast(m_data); - d->m_radius = f; + d->m_radius = (f > 0.0f) ? f : 0.0f; return *this; } @@ -299,8 +305,7 @@ dash_pattern(c_array f) FASTUIDRAWassert(dynamic_cast(m_data) != nullptr); d = static_cast(m_data); - /* skip to first element on f[] that is non-zero. - */ + /* skip to first element on f[] that is non-zero. */ while(f.front().m_draw_length <= 0.0f && f.front().m_space_length <= 0.0f) { f = f.sub_array(1); @@ -374,16 +379,37 @@ dash_pattern(c_array f) return *this; } +enum fastuidraw::PainterStrokeParams::stroking_units_t +fastuidraw::PainterDashedStrokeParams:: +stroking_units(void) const +{ + PainterDashedStrokeParamsData *d; + FASTUIDRAWassert(dynamic_cast(m_data) != nullptr); + d = static_cast(m_data); + return d->m_stroking_units; +} + +fastuidraw::PainterDashedStrokeParams& +fastuidraw::PainterDashedStrokeParams:: +stroking_units(enum PainterStrokeParams::stroking_units_t v) +{ + PainterDashedStrokeParamsData *d; + FASTUIDRAWassert(dynamic_cast(m_data) != nullptr); + d = static_cast(m_data); + d->m_stroking_units = v; + return *this; +} + fastuidraw::reference_counted_ptr fastuidraw::PainterDashedStrokeParams:: -dash_evaluator(bool pixel_width_stroking) +dash_evaluator(void) { - return FASTUIDRAWnew DashEvaluator(pixel_width_stroking); + return FASTUIDRAWnew DashEvaluator(); } fastuidraw::reference_counted_ptr fastuidraw::PainterDashedStrokeParams:: -stroking_data_selector(bool pixel_width_stroking) +stroking_data_selector(void) { - return PainterStrokeParams::stroking_data_selector(pixel_width_stroking); + return PainterStrokeParams::stroking_data_selector(); } diff --git a/src/fastuidraw/painter/painter_shader_set.cpp b/src/fastuidraw/painter/painter_shader_set.cpp index 8c183094c..bb87567ca 100644 --- a/src/fastuidraw/painter/painter_shader_set.cpp +++ b/src/fastuidraw/painter/painter_shader_set.cpp @@ -27,9 +27,7 @@ namespace public: fastuidraw::PainterGlyphShader m_glyph_shader, m_glyph_shader_anisotropic; fastuidraw::PainterStrokeShader m_stroke_shader; - fastuidraw::PainterStrokeShader m_pixel_width_stroke_shader; fastuidraw::PainterDashedStrokeShaderSet m_dashed_stroke_shader; - fastuidraw::PainterDashedStrokeShaderSet m_pixel_width_dashed_stroke_shader; fastuidraw::PainterFillShader m_fill_shader; fastuidraw::PainterBlendShaderSet m_blend_shaders; }; @@ -67,12 +65,8 @@ setget_implement(fastuidraw::PainterShaderSet, PainterShaderSetPrivate, const fastuidraw::PainterGlyphShader&, glyph_shader_anisotropic) setget_implement(fastuidraw::PainterShaderSet, PainterShaderSetPrivate, const fastuidraw::PainterStrokeShader&, stroke_shader) -setget_implement(fastuidraw::PainterShaderSet, PainterShaderSetPrivate, - const fastuidraw::PainterStrokeShader&, pixel_width_stroke_shader) setget_implement(fastuidraw::PainterShaderSet, PainterShaderSetPrivate, const fastuidraw::PainterDashedStrokeShaderSet&, dashed_stroke_shader) -setget_implement(fastuidraw::PainterShaderSet, PainterShaderSetPrivate, - const fastuidraw::PainterDashedStrokeShaderSet&, pixel_width_dashed_stroke_shader) setget_implement(fastuidraw::PainterShaderSet, PainterShaderSetPrivate, const fastuidraw::PainterFillShader&, fill_shader) setget_implement(fastuidraw::PainterShaderSet, PainterShaderSetPrivate, diff --git a/src/fastuidraw/painter/painter_stroke_params.cpp b/src/fastuidraw/painter/painter_stroke_params.cpp index 52279ceea..240af4c0a 100644 --- a/src/fastuidraw/painter/painter_stroke_params.cpp +++ b/src/fastuidraw/painter/painter_stroke_params.cpp @@ -28,7 +28,8 @@ namespace public: PainterStrokeParamsData(void): m_miter_limit(15.0f), - m_radius(1.0f) + m_radius(1.0f), + m_stroking_units(fastuidraw::PainterStrokeParams::path_stroking_units) {} virtual @@ -49,20 +50,30 @@ namespace void pack_data(unsigned int alignment, fastuidraw::c_array dst) const { + using namespace fastuidraw; FASTUIDRAWunused(alignment); - dst[fastuidraw::PainterStrokeParams::stroke_miter_limit_offset].f = m_miter_limit; - dst[fastuidraw::PainterStrokeParams::stroke_radius_offset].f = m_radius; + + dst[PainterStrokeParams::stroke_miter_limit_offset].f = m_miter_limit; + if (m_stroking_units == PainterStrokeParams::pixel_stroking_units) + { + dst[PainterStrokeParams::stroke_radius_offset].f = -m_radius; + } + else + { + dst[PainterStrokeParams::stroke_radius_offset].f = m_radius; + } } float m_miter_limit; float m_radius; + enum fastuidraw::PainterStrokeParams::stroking_units_t m_stroking_units; }; class StrokingDataSelector:public fastuidraw::StrokingDataSelectorBase { public: explicit - StrokingDataSelector(bool pixel_width); + StrokingDataSelector(void); virtual float @@ -73,9 +84,6 @@ namespace stroking_distances(const fastuidraw::PainterShaderData::DataBase *data, float *out_pixel_distance, float *out_item_space_distance) const; - - private: - bool m_pixel_width; }; } @@ -83,8 +91,7 @@ namespace //////////////////////////////// // StrokingDataSelector methods StrokingDataSelector:: -StrokingDataSelector(bool pixel_width): - m_pixel_width(pixel_width) +StrokingDataSelector(void) {} float @@ -108,7 +115,7 @@ compute_thresh(const fastuidraw::PainterShaderData::DataBase *data, float return_value; return_value = curve_flatness / d->m_radius; - if (!m_pixel_width) + if (d->m_stroking_units == fastuidraw::PainterStrokeParams::path_stroking_units) { return_value /= path_magnification; } @@ -125,15 +132,15 @@ stroking_distances(const fastuidraw::PainterShaderData::DataBase *data, const PainterStrokeParamsData *d; d = static_cast(data); - if (m_pixel_width) + if (d->m_stroking_units == fastuidraw::PainterStrokeParams::path_stroking_units) { - *out_pixel_distance = d->m_radius; - *out_item_space_distance = 0.0f; + *out_pixel_distance = 0.0f; + *out_item_space_distance = d->m_radius; } else { - *out_pixel_distance = 0.0f; - *out_item_space_distance = d->m_radius; + *out_pixel_distance = d->m_radius; + *out_item_space_distance = 0.0f; } } @@ -183,7 +190,7 @@ width(float f) PainterStrokeParamsData *d; FASTUIDRAWassert(dynamic_cast(m_data) != nullptr); d = static_cast(m_data); - d->m_radius = 0.5f * f; + d->m_radius = (f > 0.0f ) ? 0.5f * f : 0.0f; return *this; } @@ -204,13 +211,34 @@ radius(float f) PainterStrokeParamsData *d; FASTUIDRAWassert(dynamic_cast(m_data) != nullptr); d = static_cast(m_data); - d->m_radius = f; + d->m_radius = (f > 0.0f ) ? f : 0.0f; + return *this; +} + +enum fastuidraw::PainterStrokeParams::stroking_units_t +fastuidraw::PainterStrokeParams:: +stroking_units(void) const +{ + PainterStrokeParamsData *d; + FASTUIDRAWassert(dynamic_cast(m_data) != nullptr); + d = static_cast(m_data); + return d->m_stroking_units; +} + +fastuidraw::PainterStrokeParams& +fastuidraw::PainterStrokeParams:: +stroking_units(enum stroking_units_t v) +{ + PainterStrokeParamsData *d; + FASTUIDRAWassert(dynamic_cast(m_data) != nullptr); + d = static_cast(m_data); + d->m_stroking_units = v; return *this; } fastuidraw::reference_counted_ptr fastuidraw::PainterStrokeParams:: -stroking_data_selector(bool pixel_width_stroking) +stroking_data_selector(void) { - return FASTUIDRAWnew StrokingDataSelector(pixel_width_stroking); + return FASTUIDRAWnew StrokingDataSelector(); } diff --git a/src/fastuidraw/painter/stroked_caps_joins.cpp b/src/fastuidraw/painter/stroked_caps_joins.cpp new file mode 100644 index 000000000..b2931b7c0 --- /dev/null +++ b/src/fastuidraw/painter/stroked_caps_joins.cpp @@ -0,0 +1,3203 @@ +/*! + * \file stroked_caps_joins.cpp + * \brief file stroked_caps_joins.cpp + * + * Copyright 2018 by Intel. + * + * Contact: kevin.rogovin@intel.com + * + * This Source Code Form is subject to the + * terms of the Mozilla Public License, v. 2.0. + * If a copy of the MPL was not distributed with + * this file, You can obtain one at + * http://mozilla.org/MPL/2.0/. + * + * \author Kevin Rogovin + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "../private/util_private.hpp" +#include "../private/util_private_ostream.hpp" +#include "../private/bounding_box.hpp" +#include "../private/path_util_private.hpp" +#include "../private/clip.hpp" + + +namespace +{ + inline + uint32_t + pack_data(int on_boundary, + enum fastuidraw::StrokedPoint::offset_type_t pt, + uint32_t depth) + { + using namespace fastuidraw; + FASTUIDRAWassert(on_boundary == 0 || on_boundary == 1); + + uint32_t bb(on_boundary), pp(pt); + return pack_bits(StrokedPoint::offset_type_bit0, + StrokedPoint::offset_type_num_bits, pp) + | pack_bits(StrokedPoint::boundary_bit, 1u, bb) + | pack_bits(StrokedPoint::depth_bit0, + StrokedPoint::depth_num_bits, depth); + } + + inline + uint32_t + pack_data_join(int on_boundary, + enum fastuidraw::StrokedPoint::offset_type_t pt, + uint32_t depth) + { + return pack_data(on_boundary, pt, depth) | fastuidraw::StrokedPoint::join_mask; + } + + void + add_triangle_fan(unsigned int begin, unsigned int end, + fastuidraw::c_array indices, + unsigned int &index_offset) + { + for(unsigned int i = begin + 1; i < end - 1; ++i, index_offset += 3) + { + indices[index_offset + 0] = begin; + indices[index_offset + 1] = i; + indices[index_offset + 2] = i + 1; + } + } + + class PerJoinData + { + public: + PerJoinData(const fastuidraw::vec2 &p, + float distance_from_previous_join, + const fastuidraw::vec2 &tangent_into_join, + const fastuidraw::vec2 &tangent_leaving_join); + + const fastuidraw::vec2& + n0(void) const + { + return m_normal_into_join; + } + + const fastuidraw::vec2& + n1(void) const + { + return m_normal_leaving_join; + } + + void + set_distance_values(fastuidraw::StrokedPoint *pt) const + { + pt->m_distance_from_edge_start = m_distance_from_previous_join; + pt->m_edge_length = m_distance_from_previous_join; + pt->m_open_contour_length = m_open_contour_length; + pt->m_closed_contour_length = m_closed_contour_length; + pt->m_distance_from_contour_start = m_distance_from_contour_start; + } + + /* given values from creation */ + bool m_of_closing_edge; + fastuidraw::vec2 m_p; + fastuidraw::vec2 m_tangent_into_join, m_tangent_leaving_join; + float m_distance_from_previous_join; + float m_distance_from_contour_start; + float m_open_contour_length; + float m_closed_contour_length; + + /* derived values from creation */ + fastuidraw::vec2 m_normal_into_join, m_normal_leaving_join; + float m_det, m_lambda; + }; + + class PerCapData + { + public: + fastuidraw::vec2 m_tangent_into_cap; + fastuidraw::vec2 m_p; + float m_distance_from_edge_start; + float m_distance_from_contour_start; + float m_open_contour_length; + float m_closed_contour_length; + bool m_is_starting_cap; + + void + set_distance_values(fastuidraw::StrokedPoint *pt) const + { + pt->m_distance_from_edge_start = m_distance_from_edge_start; + pt->m_edge_length = m_distance_from_edge_start; + pt->m_open_contour_length = m_open_contour_length; + pt->m_closed_contour_length = m_closed_contour_length; + pt->m_distance_from_contour_start = m_distance_from_contour_start; + } + }; + + class PerContourData + { + public: + PerContourData(void); + + void + start(const fastuidraw::vec2 &p, + const fastuidraw::vec2 &tangent_along_curve); + + void + end(float distance_from_previous_join, + const fastuidraw::vec2 &direction_into_join); + + void + add_join(const fastuidraw::vec2 &p, + float distance_from_previous_join, + const fastuidraw::vec2 &tangent_into_join, + const fastuidraw::vec2 &tangent_leaving_join); + + const PerCapData& + cap(unsigned int I) const + { + FASTUIDRAWassert(m_cap_count == 2); + return m_caps[I]; + } + + fastuidraw::c_array + joins_of_non_closing_edges(void) const + { + FASTUIDRAWassert(m_cap_count == 2); + return m_joins_of_non_closing_edges; + } + + fastuidraw::c_array + joins_of_closing_edge(void) const + { + FASTUIDRAWassert(m_cap_count == 2); + return m_joins_of_closing_edge; + } + + private: + std::vector m_joins; + fastuidraw::c_array m_joins_of_non_closing_edges; + fastuidraw::c_array m_joins_of_closing_edge; + fastuidraw::vecN m_caps; + unsigned int m_cap_count; + }; + + class ContourData:fastuidraw::noncopyable + { + public: + fastuidraw::BoundingBox m_bounding_box; + std::vector m_per_contour_data; + }; + + template + class OrderingEntry:public T + { + public: + OrderingEntry(const T &src, unsigned int chunk): + T(src), + m_chunk(chunk), + m_depth(0) + {} + + unsigned int m_chunk; + unsigned int m_depth; + }; + + /* provides in what chunk each Join is located. */ + class JoinOrdering + { + public: + fastuidraw::c_array > + non_closing_edge(void) const + { + return fastuidraw::make_c_array(m_non_closing_edge); + } + + fastuidraw::c_array > + closing_edge(void) const + { + return fastuidraw::make_c_array(m_closing_edge); + } + + void + add_join(const PerJoinData &src, unsigned int &chunk); + + void + post_process_non_closing(fastuidraw::range_type join_range, + unsigned int &depth); + + void + post_process_closing(unsigned int non_closing_edge_chunk_count, + fastuidraw::range_type join_range, + unsigned int &depth); + + unsigned int + number_joins(bool include_joins_of_closing_edge) const + { + return include_joins_of_closing_edge ? + m_non_closing_edge.size() + m_closing_edge.size() : + m_non_closing_edge.size(); + } + + unsigned int + join_chunk(unsigned int J) const; + + private: + std::vector > m_non_closing_edge; + std::vector > m_closing_edge; + }; + + class CapOrdering + { + public: + fastuidraw::c_array > + caps(void) const + { + return fastuidraw::make_c_array(m_caps); + } + + void + add_cap(const PerCapData &src, unsigned int &chunk) + { + OrderingEntry C(src, chunk); + m_caps.push_back(C); + ++chunk; + } + + void + post_process(fastuidraw::range_type range, + unsigned int &depth); + + private: + std::vector > m_caps; + }; + + class PathData:fastuidraw::noncopyable + { + public: + fastuidraw::BoundingBox m_bounding_box; + + JoinOrdering m_join_ordering; + unsigned int m_number_join_chunks; + CapOrdering m_cap_ordering; + unsigned int m_number_cap_chunks; + }; + + /* A CullingHierarchy represents a hierarchy choice of + * what joins and caps in each element of a hierarchy. + */ + class CullingHierarchy:fastuidraw::noncopyable + { + public: + static + CullingHierarchy* + create(const ContourData &contour_data); + + ~CullingHierarchy(); + + bool + has_children(void) const + { + bool b0(m_children[0] != nullptr); + bool b1(m_children[1] != nullptr); + + FASTUIDRAWassert(b0 == b1); + FASTUIDRAWunused(b0); + FASTUIDRAWunused(b1); + return m_children[0] != nullptr; + } + + const CullingHierarchy* + child(unsigned int i) const + { + return m_children[i]; + } + + fastuidraw::c_array + non_closing_joins(void) const + { + return m_joins_non_closing_edge; + } + + fastuidraw::c_array + closing_joins(void) const + { + return m_joins_closing_edge; + } + + fastuidraw::c_array + caps(void) const + { + return fastuidraw::make_c_array(m_caps); + } + + const fastuidraw::BoundingBox& + bounding_box(void) const + { + return m_bb; + } + + private: + enum + { + splitting_threshhold = 10 + }; + + CullingHierarchy(const fastuidraw::BoundingBox &bb, + std::vector &joins, std::vector &caps); + + /* a value of -1 means to NOT split. + */ + static + int + choose_splitting_coordinate(const fastuidraw::BoundingBox &start_box, + fastuidraw::c_array joins, + fastuidraw::c_array caps, + float &split_value); + + /* children, if any */ + fastuidraw::vecN m_children; + + /* Caps inside, only non-empty if has no children. */ + std::vector m_caps; + + /* Joins inside, only non-empty if has no children. */ + std::vector m_joins; + fastuidraw::c_array m_joins_non_closing_edge; + fastuidraw::c_array m_joins_closing_edge; + + /* bounding box of this + */ + fastuidraw::BoundingBox m_bb; + }; + + class ScratchSpacePrivate:fastuidraw::noncopyable + { + public: + std::vector m_adjusted_clip_eqs; + std::vector m_clipped_rect; + + fastuidraw::vecN, 2> m_clip_scratch_vec2s; + std::vector m_clip_scratch_floats; + }; + + class RangeAndChunk + { + public: + /* what range of Joins/Caps hit by the chunk. + */ + fastuidraw::range_type m_elements; + + /* depth range of Joins/Caps hit + */ + fastuidraw::range_type m_depth_range; + + /* what the chunk is + */ + unsigned int m_chunk; + + bool + non_empty(void) const + { + return m_depth_range.m_end > m_depth_range.m_begin; + } + }; + + class ChunkSetPrivate:fastuidraw::noncopyable + { + public: + void + reset(void) + { + m_ignore_join_adds = false; + m_join_chunks.clear(); + m_join_ranges.clear(); + m_cap_chunks.clear(); + } + + void + add_join_chunk(const RangeAndChunk &j); + + void + add_cap_chunk(const RangeAndChunk &c); + + fastuidraw::c_array + join_chunks(void) const + { + return fastuidraw::make_c_array(m_join_chunks); + } + + fastuidraw::c_array + cap_chunks(void) const + { + return fastuidraw::make_c_array(m_cap_chunks); + } + + void + ignore_join_adds(void) + { + m_ignore_join_adds = true; + } + + void + handle_dashed_evaluator(const fastuidraw::DashEvaluatorBase *dash_evaluator, + const fastuidraw::PainterShaderData::DataBase *dash_data, + const fastuidraw::StrokedCapsJoins &path); + + private: + std::vector m_join_chunks, m_cap_chunks; + std::vector > m_join_ranges; + bool m_ignore_join_adds; + }; + + /* Subset of a StrokedCapsJoins. Edges are to be placed into + * the store as follows: + * 1. child0 + * 2. child1 + * 3. caps/joins (i.e. from CullingHierarchy) + */ + class SubsetPrivate + { + public: + + class CreationValues + { + public: + CreationValues(void): + m_non_closing_join_chunk_cnt(0), + m_closing_join_chunk_cnt(0), + m_cap_chunk_cnt(0) + {} + + unsigned int m_non_closing_join_chunk_cnt; + unsigned int m_closing_join_chunk_cnt; + + unsigned int m_cap_chunk_cnt; + }; + + static + SubsetPrivate* + create(const CullingHierarchy *src, + CreationValues &out_values, + JoinOrdering &join_ordering, + CapOrdering &cap_ordering); + + ~SubsetPrivate(); + + void + compute_chunks(bool include_closing_edge, + ScratchSpacePrivate &work_room, + fastuidraw::c_array clip_equations, + const fastuidraw::float3x3 &clip_matrix_local, + const fastuidraw::vec2 &recip_dimensions, + float pixels_additional_room, + float item_space_additional_room, + unsigned int max_attribute_cnt, + unsigned int max_index_cnt, + bool take_joins_outside_of_region, + ChunkSetPrivate &dst); + + bool + have_children(void) const + { + bool b0(m_children[0] != nullptr); + bool b1(m_children[1] != nullptr); + + FASTUIDRAWassert(b0 == b1); + FASTUIDRAWunused(b0); + FASTUIDRAWunused(b1); + return m_children[0] != nullptr; + } + + const SubsetPrivate* + child(unsigned int I) const + { + FASTUIDRAWassert(have_children()); + FASTUIDRAWassert(I == 0 || I == 1); + return m_children[I]; + } + + const RangeAndChunk& + non_closing_joins(void) const + { + return m_non_closing_joins; + } + + const RangeAndChunk& + closing_joins(void) const + { + return m_closing_joins; + } + + const RangeAndChunk& + caps(void) const + { + return m_caps; + } + + private: + class PostProcessVariables + { + public: + PostProcessVariables(void): + m_join_depth(0), + m_closing_join_depth(0), + m_cap_depth(0) + {} + + unsigned int m_join_depth, m_closing_join_depth; + unsigned int m_cap_depth; + }; + + SubsetPrivate(CreationValues &out_values, + JoinOrdering &join_ordering, + CapOrdering &cap_ordering, + const CullingHierarchy *src); + + void + compute_chunks_implement(bool include_closing_edge, + ScratchSpacePrivate &work_room, + float item_space_additional_room, + unsigned int max_attribute_cnt, + unsigned int max_index_cnt, + ChunkSetPrivate &dst); + + void + compute_chunks_take_all(bool include_closing_edge, + unsigned int max_attribute_cnt, + unsigned int max_index_cnt, + ChunkSetPrivate &dst); + void + post_process(PostProcessVariables &variables, + const CreationValues &constants, + JoinOrdering &join_ordering, + CapOrdering &cap_ordering); + + fastuidraw::vecN m_children; + + /* book keeping for joins */ + RangeAndChunk m_non_closing_joins, m_closing_joins; + + /* book keeping for caps */ + RangeAndChunk m_caps; + + fastuidraw::BoundingBox m_bb; + + bool m_empty_subset; + }; + + class JoinCount + { + public: + explicit + JoinCount(const JoinOrdering &J): + m_number_close_joins(J.closing_edge().size()), + m_number_non_close_joins(J.non_closing_edge().size()) + {} + + unsigned int m_number_close_joins; + unsigned int m_number_non_close_joins; + }; + + class JoinCreatorBase:public fastuidraw::PainterAttributeDataFiller + { + public: + explicit + JoinCreatorBase(const PathData &P, const SubsetPrivate *st); + + virtual + ~JoinCreatorBase() {} + + virtual + void + compute_sizes(unsigned int &num_attributes, + unsigned int &num_indices, + unsigned int &num_attribute_chunks, + unsigned int &num_index_chunks, + unsigned int &number_z_ranges) const; + + virtual + void + fill_data(fastuidraw::c_array attribute_data, + fastuidraw::c_array index_data, + fastuidraw::c_array > attribute_chunks, + fastuidraw::c_array > index_chunks, + fastuidraw::c_array > zranges, + fastuidraw::c_array index_adjusts) const; + + protected: + + void + post_ctor_initalize(void); + + private: + + void + fill_join(unsigned int join_id, const PerJoinData &join_data, + unsigned int chunk, unsigned int depth, + fastuidraw::c_array pts, + fastuidraw::c_array indices, + unsigned int &vertex_offset, unsigned int &index_offset, + fastuidraw::c_array > attribute_chunks, + fastuidraw::c_array > index_chunks, + fastuidraw::c_array > zranges, + fastuidraw::c_array index_adjusts, + fastuidraw::c_array > join_vertex_ranges, + fastuidraw::c_array > join_index_ranges) const; + + static + void + set_chunks(const SubsetPrivate *st, + fastuidraw::c_array > join_vertex_ranges, + fastuidraw::c_array > join_index_ranges, + fastuidraw::c_array attribute_data, + fastuidraw::c_array index_data, + fastuidraw::c_array > attribute_chunks, + fastuidraw::c_array > index_chunks, + fastuidraw::c_array > zranges, + fastuidraw::c_array index_adjusts); + + static + void + process_chunk(const RangeAndChunk &ch, + fastuidraw::c_array > join_vertex_ranges, + fastuidraw::c_array > join_index_ranges, + fastuidraw::c_array attribute_data, + fastuidraw::c_array index_data, + fastuidraw::c_array > attribute_chunks, + fastuidraw::c_array > index_chunks, + fastuidraw::c_array > zranges, + fastuidraw::c_array index_adjusts); + + virtual + void + add_join(unsigned int join_id, const PerJoinData &join, + unsigned int &vert_count, unsigned int &index_count) const = 0; + + virtual + void + fill_join_implement(unsigned int join_id, const PerJoinData &join, + fastuidraw::c_array pts, + unsigned int depth, + fastuidraw::c_array indices, + unsigned int &vertex_offset, unsigned int &index_offset) const = 0; + + const JoinOrdering &m_ordering; + const SubsetPrivate *m_st; + unsigned int m_num_verts, m_num_indices, m_num_chunks, m_num_joins; + bool m_post_ctor_initalized_called; + }; + + + class RoundedJoinCreator:public JoinCreatorBase + { + public: + RoundedJoinCreator(const PathData &P, + const SubsetPrivate *st, + float thresh); + + private: + + class PerRoundedJoin:public PerJoinData + { + public: + PerRoundedJoin(const PerJoinData &J, float thresh); + + void + add_data(unsigned int depth, + fastuidraw::c_array pts, + unsigned int &vertex_offset, + fastuidraw::c_array indices, + unsigned int &index_offset) const; + + std::complex m_arc_start; + float m_delta_theta; + unsigned int m_num_arc_points; + }; + + virtual + void + add_join(unsigned int join_id, const PerJoinData &join, + unsigned int &vert_count, unsigned int &index_count) const; + + virtual + void + fill_join_implement(unsigned int join_id, const PerJoinData &join, + fastuidraw::c_array pts, + unsigned int depth, + fastuidraw::c_array indices, + unsigned int &vertex_offset, unsigned int &index_offset) const; + + float m_thresh; + mutable std::vector m_per_join_data; + }; + + class BevelJoinCreator:public JoinCreatorBase + { + public: + explicit + BevelJoinCreator(const PathData &P, const SubsetPrivate *st); + + private: + + virtual + void + add_join(unsigned int join_id, const PerJoinData &join, + unsigned int &vert_count, unsigned int &index_count) const; + + virtual + void + fill_join_implement(unsigned int join_id, const PerJoinData &join, + fastuidraw::c_array pts, + unsigned int depth, + fastuidraw::c_array indices, + unsigned int &vertex_offset, unsigned int &index_offset) const; + }; + + class MiterClipJoinCreator:public JoinCreatorBase + { + public: + explicit + MiterClipJoinCreator(const PathData &P, const SubsetPrivate *st); + + private: + virtual + void + add_join(unsigned int join_id, const PerJoinData &join, + unsigned int &vert_count, unsigned int &index_count) const; + + virtual + void + fill_join_implement(unsigned int join_id, const PerJoinData &join, + fastuidraw::c_array pts, + unsigned int depth, + fastuidraw::c_array indices, + unsigned int &vertex_offset, unsigned int &index_offset) const; + }; + + template + class MiterJoinCreator:public JoinCreatorBase + { + public: + explicit + MiterJoinCreator(const PathData &P, const SubsetPrivate *st); + + private: + virtual + void + add_join(unsigned int join_id, const PerJoinData &join, + unsigned int &vert_count, unsigned int &index_count) const; + + virtual + void + fill_join_implement(unsigned int join_id, const PerJoinData &join, + fastuidraw::c_array pts, + unsigned int depth, + fastuidraw::c_array indices, + unsigned int &vertex_offset, unsigned int &index_offset) const; + }; + + class PointIndexCapSize + { + public: + PointIndexCapSize(void): + m_verts(0), + m_indices(0) + {} + + unsigned int m_verts, m_indices; + }; + + class CapCreatorBase:public fastuidraw::PainterAttributeDataFiller + { + public: + CapCreatorBase(const PathData &P, + const SubsetPrivate *st, + PointIndexCapSize sz); + + virtual + ~CapCreatorBase() + {} + + virtual + void + compute_sizes(unsigned int &num_attributes, + unsigned int &num_indices, + unsigned int &num_attribute_chunks, + unsigned int &num_index_chunks, + unsigned int &number_z_ranges) const; + + virtual + void + fill_data(fastuidraw::c_array attribute_data, + fastuidraw::c_array index_data, + fastuidraw::c_array > attribute_chunks, + fastuidraw::c_array > index_chunks, + fastuidraw::c_array > zranges, + fastuidraw::c_array index_adjusts) const; + + private: + virtual + void + add_cap(const PerCapData &C, unsigned int depth, + fastuidraw::c_array pts, + fastuidraw::c_array indices, + unsigned int &vertex_offset, + unsigned int &index_offset) const = 0; + + static + void + set_chunks(const SubsetPrivate *st, + fastuidraw::c_array > cap_vertex_ranges, + fastuidraw::c_array > cap_index_ranges, + fastuidraw::c_array attribute_data, + fastuidraw::c_array index_data, + fastuidraw::c_array > attribute_chunks, + fastuidraw::c_array > index_chunks, + fastuidraw::c_array > zranges, + fastuidraw::c_array index_adjusts); + + const CapOrdering &m_ordering; + const SubsetPrivate *m_st; + + unsigned int m_num_chunks; + PointIndexCapSize m_size; + }; + + class RoundedCapCreator:public CapCreatorBase + { + public: + RoundedCapCreator(const PathData &P, + const SubsetPrivate *st, + float thresh); + + private: + static + PointIndexCapSize + compute_size(const PathData &P, float thresh); + + virtual + void + add_cap(const PerCapData &C, unsigned int depth, + fastuidraw::c_array pts, + fastuidraw::c_array indices, + unsigned int &vertex_offset, + unsigned int &index_offset) const; + + float m_delta_theta; + unsigned int m_num_arc_points_per_cap; + }; + + class SquareCapCreator:public CapCreatorBase + { + public: + explicit + SquareCapCreator(const PathData &P, + const SubsetPrivate *st): + CapCreatorBase(P, st, compute_size(P)) + {} + + private: + static + PointIndexCapSize + compute_size(const PathData &P); + + void + add_cap(const PerCapData &C, unsigned int depth, + fastuidraw::c_array pts, + fastuidraw::c_array indices, + unsigned int &vertex_offset, + unsigned int &index_offset) const; + }; + + class AdjustableCapCreator:public CapCreatorBase + { + public: + AdjustableCapCreator(const PathData &P, + const SubsetPrivate *st): + CapCreatorBase(P, st, compute_size(P)) + {} + + private: + enum + { + number_points_per_fan = 6, + number_triangles_per_fan = number_points_per_fan - 2, + number_indices_per_fan = 3 * number_triangles_per_fan, + }; + + static + PointIndexCapSize + compute_size(const PathData &P); + + void + add_cap(const PerCapData &C, unsigned int depth, + fastuidraw::c_array pts, + fastuidraw::c_array indices, + unsigned int &vertex_offset, + unsigned int &index_offset) const; + }; + + class ThreshWithData + { + public: + ThreshWithData(void): + m_data(nullptr), + m_thresh(-1) + {} + + ThreshWithData(fastuidraw::PainterAttributeData *d, float t): + m_data(d), m_thresh(t) + {} + + static + bool + reverse_compare_against_thresh(const ThreshWithData &lhs, float rhs) + { + return lhs.m_thresh > rhs; + } + + fastuidraw::PainterAttributeData *m_data; + float m_thresh; + }; + + template + class PreparedAttributeData + { + public: + PreparedAttributeData(void): + m_ready(false) + {} + + /* must be called before the first call to data(). + */ + void + mark_as_empty(void) + { + m_ready = true; + } + + const fastuidraw::PainterAttributeData& + data(const PathData &P, const SubsetPrivate *st) + { + if (!m_ready) + { + m_data.set_data(T(P, st)); + m_ready = true; + } + return m_data; + } + + private: + fastuidraw::PainterAttributeData m_data; + bool m_ready; + }; + + class StrokedCapsJoinsPrivate:fastuidraw::noncopyable + { + public: + explicit + StrokedCapsJoinsPrivate(const ContourData &P); + ~StrokedCapsJoinsPrivate(); + + void + create_joins_caps(const ContourData &P); + + template + const fastuidraw::PainterAttributeData& + fetch_create(float thresh, + std::vector &values); + + SubsetPrivate* m_subset; + + PreparedAttributeData m_bevel_joins; + PreparedAttributeData m_miter_clip_joins; + PreparedAttributeData > m_miter_joins; + PreparedAttributeData > m_miter_bevel_joins; + PreparedAttributeData m_square_caps; + PreparedAttributeData m_adjustable_caps; + + PathData m_path_data; + fastuidraw::vecN m_chunk_of_joins; + unsigned int m_chunk_of_caps; + + std::vector m_rounded_joins; + std::vector m_rounded_caps; + + bool m_empty_path; + fastuidraw::PainterAttributeData m_empty_data; + }; + +} + +///////////////////////////////// +// PerContourData methods +PerContourData:: +PerContourData(void): + m_cap_count(0) +{} + +void +PerContourData:: +start(const fastuidraw::vec2 &p, + const fastuidraw::vec2 &tangent_along_curve) +{ + FASTUIDRAWassert(m_cap_count == 0); + m_caps[0].m_p = p; + m_caps[0].m_tangent_into_cap = -tangent_along_curve; + m_caps[0].m_distance_from_edge_start = 0.0f; + m_caps[0].m_distance_from_contour_start = 0.0f; + m_caps[0].m_is_starting_cap = true; + m_cap_count = 1; +} + +void +PerContourData:: +end(float distance_from_previous_join, + const fastuidraw::vec2 &direction_into_join) +{ + fastuidraw::vec2 end_cap_pt, end_cap_direction; + float end_cap_edge_distance; + + /* the last added join starts the closing edge */ + if (!m_joins.empty()) + { + end_cap_pt = m_joins.back().m_p; + end_cap_direction = m_joins.back().m_tangent_into_join; + end_cap_edge_distance = m_joins.back().m_distance_from_previous_join; + m_joins.back().m_of_closing_edge = true; + + /* add the join ending the closing edge */ + add_join(m_caps[0].m_p, distance_from_previous_join, + direction_into_join, + -m_caps[0].m_tangent_into_cap); + m_joins.back().m_of_closing_edge = true; + } + else + { + end_cap_edge_distance = 0; + end_cap_pt = m_caps[0].m_p; + end_cap_direction = m_caps[0].m_tangent_into_cap; + } + + /* compute the contour lengths */ + float d(0.0f); + for(unsigned int i = 0, endi = m_joins.size(); i < endi; ++i) + { + d += m_joins[i].m_distance_from_previous_join; + m_joins[i].m_distance_from_contour_start = d; + } + + for (PerJoinData &J : m_joins) + { + J.m_closed_contour_length = d; + J.m_open_contour_length = d - distance_from_previous_join; + } + + for (PerCapData &C : m_caps) + { + C.m_closed_contour_length = m_joins.empty() ? 0.0f : d; + C.m_open_contour_length = m_joins.empty() ? distance_from_previous_join : d - distance_from_previous_join; + } + + m_caps[1].m_p = end_cap_pt; + m_caps[1].m_tangent_into_cap = end_cap_direction; + m_caps[1].m_distance_from_edge_start = end_cap_edge_distance; + m_caps[1].m_distance_from_contour_start = m_caps[1].m_open_contour_length; + m_caps[1].m_is_starting_cap = false; + + m_cap_count = 2; + + fastuidraw::c_array all_joins; + all_joins = fastuidraw::make_c_array(m_joins); + if (all_joins.size() > 2) + { + m_joins_of_non_closing_edges = all_joins.sub_array(0, all_joins.size() - 2); + m_joins_of_closing_edge = all_joins.sub_array(all_joins.size() - 2, 2); + } + else + { + m_joins_of_closing_edge = all_joins; + } +} + +void +PerContourData:: +add_join(const fastuidraw::vec2 &p, + float distance_from_previous_join, + const fastuidraw::vec2 &tangent_into_join, + const fastuidraw::vec2 &tangent_leaving_join) +{ + FASTUIDRAWassert(m_cap_count == 1); + m_joins.push_back(PerJoinData(p, distance_from_previous_join, + tangent_into_join, tangent_leaving_join)); +} + +////////////////////////////////// +// JoinOrdering methods +void +JoinOrdering:: +add_join(const PerJoinData &src, unsigned int &chunk) +{ + OrderingEntry J(src, chunk); + if (src.m_of_closing_edge) + { + m_closing_edge.push_back(J); + } + else + { + m_non_closing_edge.push_back(J); + } + ++chunk; +} + +void +JoinOrdering:: +post_process_non_closing(fastuidraw::range_type join_range, + unsigned int &depth) +{ + unsigned int R, d; + + /* the depth values of the joins must come in reverse + * so that higher depth values occlude later elements + */ + R = join_range.difference(); + d = depth + R - 1; + + for(unsigned int i = join_range.m_begin; i < join_range.m_end; ++i, --d) + { + FASTUIDRAWassert(i < m_non_closing_edge.size()); + m_non_closing_edge[i].m_depth = d; + } + depth += R; +} + +void +JoinOrdering:: +post_process_closing(unsigned int non_closing_edge_chunk_count, + fastuidraw::range_type join_range, + unsigned int &depth) +{ + unsigned int R, d; + + /* the depth values of the joins must come in reverse + * so that higher depth values occlude later elements + */ + R = join_range.difference(); + d = depth + R - 1; + + for(unsigned int i = join_range.m_begin; i < join_range.m_end; ++i, --d) + { + FASTUIDRAWassert(i < m_closing_edge.size()); + m_closing_edge[i].m_depth = d; + m_closing_edge[i].m_chunk += non_closing_edge_chunk_count; + } + depth += R; +} + +unsigned int +JoinOrdering:: +join_chunk(unsigned int J) const +{ + FASTUIDRAWassert(J < number_joins(true)); + if (J >= m_non_closing_edge.size()) + { + J -= m_non_closing_edge.size(); + return m_closing_edge[J].m_chunk; + } + else + { + return m_non_closing_edge[J].m_chunk; + } +} + +/////////////////////////// +// CapOrdering methods +void +CapOrdering:: +post_process(fastuidraw::range_type range, + unsigned int &depth) +{ + unsigned int R, d; + + /* the depth values of the caps must come in reverse + * so that higher depth values occlude later elements + */ + R = range.difference(); + d = depth + R - 1; + for(unsigned int i = range.m_begin; i < range.m_end; ++i, --d) + { + FASTUIDRAWassert(i < m_caps.size()); + m_caps[i].m_depth = d; + } + depth += R; +} + +///////////////////////////////// +// CullingHierarchy methods +CullingHierarchy* +CullingHierarchy:: +create(const ContourData &path_data) +{ + std::vector joins; + std::vector caps; + CullingHierarchy *return_value; + + /* fill joins and caps from the data in path_data */ + for(const PerContourData &C : path_data.m_per_contour_data) + { + caps.push_back(C.cap(0)); + caps.push_back(C.cap(1)); + /* add all but the joins of the closing edge of C, i.e. all but + * the last two joins + */ + for(const PerJoinData &J : C.joins_of_non_closing_edges()) + { + joins.push_back(J); + } + } + + /* now add the joins from the closing edges */ + for(const PerContourData &C : path_data.m_per_contour_data) + { + /* add all but the joins of the closing edge of C, i.e. all but + * the last two joins + */ + for(const PerJoinData &J : C.joins_of_closing_edge()) + { + joins.push_back(J); + } + } + + return_value = FASTUIDRAWnew CullingHierarchy(path_data.m_bounding_box, joins, caps); + return return_value; +} + +CullingHierarchy:: +CullingHierarchy(const fastuidraw::BoundingBox &start_box, + std::vector &joins, std::vector &caps): + m_children(nullptr, nullptr), + m_bb(start_box) +{ + int c; + float mid_point; + + FASTUIDRAWassert(!start_box.empty()); + c = choose_splitting_coordinate(start_box, + fastuidraw::make_c_array(joins), + fastuidraw::make_c_array(caps), + mid_point); + if (c != -1) + { + fastuidraw::vecN, 2> child_boxes; + fastuidraw::vecN, 2> child_joins; + fastuidraw::vecN, 2> child_caps; + + /* NOTE: since the joins come in the order of non-closing first + * and we process them in that order, the non-closing joins + * for child_joins[] will also come first + */ + for(const PerJoinData &J : joins) + { + bool s; + s = J.m_p[c] < mid_point; + child_joins[s].push_back(J); + child_boxes[s].union_point(J.m_p); + } + + for(const PerCapData &C : caps) + { + bool s; + s = C.m_p[c] < mid_point; + child_caps[s].push_back(C); + child_boxes[s].union_point(C.m_p); + } + FASTUIDRAWassert(child_joins[0].size() + child_joins[1].size() == joins.size()); + FASTUIDRAWassert(child_caps[0].size() + child_caps[1].size() == caps.size()); + + if (child_joins[0].size() + child_caps[0].size() < joins.size() + caps.size()) + { + m_children[0] = FASTUIDRAWnew CullingHierarchy(child_boxes[0], child_joins[0], child_caps[0]); + m_children[1] = FASTUIDRAWnew CullingHierarchy(child_boxes[1], child_joins[1], child_caps[1]); + } + } + + if (m_children[0] == nullptr) + { + FASTUIDRAWassert(m_children[1] == nullptr); + + /* steal the data */ + m_caps.swap(caps); + m_joins.swap(joins); + + fastuidraw::c_array all_joins; + all_joins = fastuidraw::make_c_array(m_joins); + + unsigned int non_closing_cnt; + for(non_closing_cnt = 0; + non_closing_cnt < m_joins.size() && !m_joins[non_closing_cnt].m_of_closing_edge; + ++non_closing_cnt) + {} + m_joins_non_closing_edge = all_joins.sub_array(0, non_closing_cnt); + m_joins_closing_edge = all_joins.sub_array(non_closing_cnt); + } +} + +CullingHierarchy:: +~CullingHierarchy(void) +{ + if (m_children[0] != nullptr) + { + FASTUIDRAWdelete(m_children[0]); + } + + if (m_children[1] != nullptr) + { + FASTUIDRAWdelete(m_children[1]); + } +} + +int +CullingHierarchy:: +choose_splitting_coordinate(const fastuidraw::BoundingBox &start_box, + fastuidraw::c_array joins, + fastuidraw::c_array caps, + float &split_value) +{ + if (joins.size() + caps.size() < splitting_threshhold) + { + return -1; + } + + std::vector qs; + fastuidraw::vec2 sz; + int c; + + /* the dimension we choose is whever is larger */ + sz = start_box.max_point() - start_box.min_point(); + c = (sz.x() > sz.y()) ? 0 : 1; + + qs.reserve(joins.size() + caps.size()); + for(const PerJoinData &d : joins) + { + qs.push_back(d.m_p[c]); + } + for(const PerCapData & d : caps) + { + qs.push_back(d.m_p[c]); + } + std::sort(qs.begin(), qs.end()); + + split_value = (qs[qs.size() / 2] + qs[qs.size() / 2 + 1]) * 0.5f; + return c; +} + +////////////////////////////////////////// +// SubsetPrivate methods +SubsetPrivate:: +~SubsetPrivate() +{ + if (m_children[0] != nullptr) + { + FASTUIDRAWdelete(m_children[0]); + } + if (m_children[1] != nullptr) + { + FASTUIDRAWdelete(m_children[1]); + } +} + +SubsetPrivate* +SubsetPrivate:: +create(const CullingHierarchy *src, + CreationValues &out_values, + JoinOrdering &join_ordering, + CapOrdering &cap_ordering) +{ + SubsetPrivate *return_value; + PostProcessVariables vars; + + return_value = FASTUIDRAWnew SubsetPrivate(out_values, join_ordering, cap_ordering, src); + return_value->post_process(vars, out_values, join_ordering, cap_ordering); + return return_value; +} + +void +SubsetPrivate:: +post_process(PostProcessVariables &variables, + const CreationValues &constants, + JoinOrdering &join_ordering, + CapOrdering &cap_ordering) +{ + /* We want the depth to go in the reverse order as the + * draw order. The Draw order is child(0), child(1) + * Thus, we first handle depth child(1) and then child(0). + */ + m_non_closing_joins.m_depth_range.m_begin = variables.m_join_depth; + m_closing_joins.m_depth_range.m_begin = variables.m_closing_join_depth; + + m_caps.m_depth_range.m_begin = variables.m_cap_depth; + + if (have_children()) + { + FASTUIDRAWassert(m_children[0] != nullptr); + FASTUIDRAWassert(m_children[1] != nullptr); + + m_children[1]->post_process(variables, constants, join_ordering, cap_ordering); + m_children[0]->post_process(variables, constants, join_ordering, cap_ordering); + } + else + { + FASTUIDRAWassert(m_children[0] == nullptr); + FASTUIDRAWassert(m_children[1] == nullptr); + + join_ordering.post_process_non_closing(m_non_closing_joins.m_elements, + variables.m_join_depth); + + join_ordering.post_process_closing(constants.m_non_closing_join_chunk_cnt, + m_closing_joins.m_elements, + variables.m_closing_join_depth); + + cap_ordering.post_process(m_caps.m_elements, variables.m_cap_depth); + } + + m_non_closing_joins.m_depth_range.m_end = variables.m_join_depth; + m_closing_joins.m_depth_range.m_end = variables.m_closing_join_depth; + + m_caps.m_depth_range.m_end = variables.m_cap_depth; + + FASTUIDRAWassert(m_non_closing_joins.m_elements.difference() == m_non_closing_joins.m_depth_range.difference()); + FASTUIDRAWassert(m_closing_joins.m_elements.difference() == m_closing_joins.m_depth_range.difference()); + + /* the joins are ordered so that the joins of the non-closing + * edges appear first. + */ + m_closing_joins.m_elements += join_ordering.non_closing_edge().size(); + + /* make the chunks of closing edges come AFTER + * chunks of non-closing edge + */ + m_closing_joins.m_chunk += constants.m_non_closing_join_chunk_cnt; + + m_empty_subset = !m_non_closing_joins.non_empty() + && !m_closing_joins.non_empty() + && !m_caps.non_empty(); +} + +SubsetPrivate:: +SubsetPrivate(CreationValues &out_values, + JoinOrdering &join_ordering, CapOrdering &cap_ordering, + const CullingHierarchy *src): + m_children(nullptr, nullptr), + m_bb(src->bounding_box()) +{ + /* Draw order is: + * child(0) + * child(1) + */ + m_non_closing_joins.m_elements.m_begin = join_ordering.non_closing_edge().size(); + m_closing_joins.m_elements.m_begin = join_ordering.closing_edge().size(); + m_caps.m_elements.m_begin = cap_ordering.caps().size(); + + if (src->has_children()) + { + FASTUIDRAWassert(src->caps().empty()); + FASTUIDRAWassert(src->non_closing_joins().empty()); + FASTUIDRAWassert(src->closing_joins().empty()); + for(unsigned int i = 0; i < 2; ++i) + { + FASTUIDRAWassert(src->child(i) != nullptr); + m_children[i] = FASTUIDRAWnew SubsetPrivate(out_values, join_ordering, cap_ordering, src->child(i)); + } + } + else + { + for(const PerJoinData &J : src->non_closing_joins()) + { + join_ordering.add_join(J, out_values.m_non_closing_join_chunk_cnt); + } + + for(const PerJoinData &J : src->closing_joins()) + { + join_ordering.add_join(J, out_values.m_closing_join_chunk_cnt); + } + + for(const PerCapData &C : src->caps()) + { + cap_ordering.add_cap(C, out_values.m_cap_chunk_cnt); + } + } + + m_non_closing_joins.m_elements.m_end = join_ordering.non_closing_edge().size(); + m_closing_joins.m_elements.m_end = join_ordering.closing_edge().size(); + m_caps.m_elements.m_end = cap_ordering.caps().size(); + + m_non_closing_joins.m_chunk = out_values.m_non_closing_join_chunk_cnt; + m_closing_joins.m_chunk = out_values.m_closing_join_chunk_cnt; + m_caps.m_chunk = out_values.m_cap_chunk_cnt; + + ++out_values.m_non_closing_join_chunk_cnt; + ++out_values.m_closing_join_chunk_cnt; + ++out_values.m_cap_chunk_cnt; +} + +void +SubsetPrivate:: +compute_chunks(bool include_closing_edge, + ScratchSpacePrivate &scratch, + fastuidraw::c_array clip_equations, + const fastuidraw::float3x3 &clip_matrix_local, + const fastuidraw::vec2 &recip_dimensions, + float pixels_additional_room, + float item_space_additional_room, + unsigned int max_attribute_cnt, + unsigned int max_index_cnt, + bool take_joins_outside_of_region, + ChunkSetPrivate &dst) +{ + scratch.m_adjusted_clip_eqs.resize(clip_equations.size()); + for(unsigned int i = 0; i < clip_equations.size(); ++i) + { + fastuidraw::vec3 c(clip_equations[i]); + float f; + + /* make "w" larger by the named number of pixels. + */ + f = fastuidraw::t_abs(c.x()) * recip_dimensions.x() + + fastuidraw::t_abs(c.y()) * recip_dimensions.y(); + + c.z() += pixels_additional_room * f; + + /* transform clip equations from clip coordinates to + * local coordinates. + */ + scratch.m_adjusted_clip_eqs[i] = c * clip_matrix_local; + } + + dst.reset(); + if (take_joins_outside_of_region) + { + dst.add_join_chunk(m_non_closing_joins); + if (include_closing_edge) + { + dst.add_join_chunk(m_closing_joins); + } + dst.ignore_join_adds(); + } + compute_chunks_implement(include_closing_edge, + scratch, item_space_additional_room, + max_attribute_cnt, max_index_cnt, dst); +} + +void +SubsetPrivate:: +compute_chunks_take_all(bool include_closing_edge, + unsigned int max_attribute_cnt, + unsigned int max_index_cnt, + ChunkSetPrivate &dst) +{ + if (m_empty_subset) + { + return; + } + + /* + * TODO: take into account max_attribute_cnt/max_indent + * ISSUE: rounded joins/caps have variable attribue/index count, + * solve this by giving cap coefficient and join coefficient + * and multiplying the number of caps and joins by their + * coefficients to get the correct value. + */ + FASTUIDRAWunused(max_attribute_cnt); + FASTUIDRAWunused(max_index_cnt); + + dst.add_join_chunk(m_non_closing_joins); + if (include_closing_edge) + { + dst.add_join_chunk(m_closing_joins); + } + else + { + dst.add_cap_chunk(m_caps); + } +} + +void +SubsetPrivate:: +compute_chunks_implement(bool include_closing_edge, + ScratchSpacePrivate &scratch, + float item_space_additional_room, + unsigned int max_attribute_cnt, + unsigned int max_index_cnt, + ChunkSetPrivate &dst) +{ + using namespace fastuidraw; + using namespace fastuidraw::detail; + + if (m_bb.empty() || m_empty_subset) + { + return; + } + + /* clip the bounding box of this SubsetPrivate */ + vecN bb; + bool unclipped; + + m_bb.inflated_polygon(bb, item_space_additional_room); + unclipped = clip_against_planes(make_c_array(scratch.m_adjusted_clip_eqs), + bb, scratch.m_clipped_rect, + scratch.m_clip_scratch_floats, + scratch.m_clip_scratch_vec2s); + //completely unclipped. + if (unclipped || !have_children()) + { + compute_chunks_take_all(include_closing_edge, max_attribute_cnt, max_index_cnt, dst); + return; + } + + //completely clipped + if (scratch.m_clipped_rect.empty()) + { + return; + } + + FASTUIDRAWassert(have_children()); + + FASTUIDRAWassert(m_children[0] != nullptr); + FASTUIDRAWassert(m_children[1] != nullptr); + m_children[0]->compute_chunks_implement(include_closing_edge, + scratch, item_space_additional_room, + max_attribute_cnt, max_index_cnt, + dst); + m_children[1]->compute_chunks_implement(include_closing_edge, + scratch, item_space_additional_room, + max_attribute_cnt, max_index_cnt, + dst); +} + +//////////////////////////////////////////////// +// PerJoinData methods +PerJoinData:: +PerJoinData(const fastuidraw::vec2 &p, + float distance_from_previous_join, + const fastuidraw::vec2 &tangent_into_join, + const fastuidraw::vec2 &tangent_leaving_join): + m_of_closing_edge(false), + m_p(p), + m_tangent_into_join(tangent_into_join), + m_tangent_leaving_join(tangent_leaving_join), + m_distance_from_previous_join(distance_from_previous_join), + m_distance_from_contour_start(0.0f), + m_open_contour_length(0.0f), + m_closed_contour_length(0.0f), + m_normal_into_join(-tangent_into_join.y(), tangent_into_join.x()), + m_normal_leaving_join(-tangent_leaving_join.y(), tangent_leaving_join.x()) +{ + /* Explanation: + * We have two curves, a(t) and b(t) with a(1) = b(0) + * The point p0 represents the end of a(t) and the + * point p1 represents the start of b(t). + * + * When stroking we have four auxiliary curves: + * a0(t) = a(t) + w * a_n(t) + * a1(t) = a(t) - w * a_n(t) + * b0(t) = b(t) + w * b_n(t) + * b1(t) = b(t) - w * b_n(t) + * where + * w = width of stroking + * a_n(t) = J( a'(t) ) / || a'(t) || + * b_n(t) = J( b'(t) ) / || b'(t) || + * when + * J(x, y) = (-y, x). + * + * A Bevel join is a triangle that connects + * consists of p, A and B where p is a(1)=b(0), + * A is one of a0(1) or a1(1) and B is one + * of b0(0) or b1(0). Now if we use a0(1) for + * A then we will use b0(0) for B because + * the normals are generated the same way for + * a(t) and b(t). Then, the questions comes + * down to, do we wish to add or subtract the + * normal. That value is represented by m_lambda. + * + * Now to figure out m_lambda. Let q0 be a point + * on a(t) before p=a(1). The q0 is given by + * + * q0 = p - s * m_v0 + * + * and let q1 be a point on b(t) after p=b(0), + * + * q1 = p + t * m_v1 + * + * where both s, t are positive. Let + * + * z = (q0+q1) / 2 + * + * the point z is then on the side of the join + * of the acute angle of the join. + * + * With this in mind, if either of + * or is positive then we want + * to add by -w * n rather than w * n. + * + * Note that: + * + * = 0.5 * < -s * m_v0 + t * m_v1, m_n1 > + * = -0.5 * s * + 0.5 * t * + * = -0.5 * s * + * = -0.5 * s * + * + * and + * + * = 0.5 * < -s * m_v0 + t * m_v1, m_n0 > + * = -0.5 * s * + 0.5 * t * + * = 0.5 * t * + * = 0.5 * t * + * = -0.5 * t * + * + * (the last line because transpose(J) = -J). Notice + * that the sign of and the sign of + * is then the same. + * + * thus m_lambda is positive if is negative. + */ + + m_det = fastuidraw::dot(m_tangent_leaving_join, m_normal_into_join); + if (m_det > 0.0f) + { + m_lambda = -1.0f; + } + else + { + m_lambda = 1.0f; + } +} + +///////////////////////////////////////////////// +// JoinCreatorBase methods +JoinCreatorBase:: +JoinCreatorBase(const PathData &P, const SubsetPrivate *st): + m_ordering(P.m_join_ordering), + m_st(st), + m_num_verts(0), + m_num_indices(0), + m_num_chunks(P.m_number_join_chunks), + m_num_joins(0), + m_post_ctor_initalized_called(false) +{} + +void +JoinCreatorBase:: +post_ctor_initalize(void) +{ + FASTUIDRAWassert(!m_post_ctor_initalized_called); + m_post_ctor_initalized_called = true; + + for(const PerJoinData &J : m_ordering.non_closing_edge()) + { + add_join(m_num_joins, J, m_num_verts, m_num_indices); + ++m_num_joins; + } + + for(const PerJoinData &J : m_ordering.closing_edge()) + { + add_join(m_num_joins, J, m_num_verts, m_num_indices); + ++m_num_joins; + } +} + +void +JoinCreatorBase:: +compute_sizes(unsigned int &num_attributes, + unsigned int &num_indices, + unsigned int &num_attribute_chunks, + unsigned int &num_index_chunks, + unsigned int &number_z_ranges) const +{ + FASTUIDRAWassert(m_post_ctor_initalized_called); + num_attributes = m_num_verts; + num_indices = m_num_indices; + number_z_ranges = num_attribute_chunks = num_index_chunks = m_num_chunks; +} + +void +JoinCreatorBase:: +fill_join(unsigned int join_id, + const PerJoinData &join_data, + unsigned int chunk, unsigned int depth, + fastuidraw::c_array pts, + fastuidraw::c_array indices, + unsigned int &vertex_offset, unsigned int &index_offset, + fastuidraw::c_array > attribute_chunks, + fastuidraw::c_array > index_chunks, + fastuidraw::c_array > zranges, + fastuidraw::c_array index_adjusts, + fastuidraw::c_array > join_vertex_ranges, + fastuidraw::c_array > join_index_ranges) const +{ + unsigned int v(vertex_offset), i(index_offset); + + FASTUIDRAWassert(join_id < m_num_joins); + + fill_join_implement(join_id, join_data, pts, depth, indices, vertex_offset, index_offset); + + join_vertex_ranges[join_id].m_begin = v; + join_vertex_ranges[join_id].m_end = vertex_offset; + + join_index_ranges[join_id].m_begin = i; + join_index_ranges[join_id].m_end = index_offset; + + attribute_chunks[chunk] = pts.sub_array(v, vertex_offset - v); + index_chunks[chunk] = indices.sub_array(i, index_offset - i); + index_adjusts[chunk] = -int(v); + zranges[chunk] = fastuidraw::range_type(depth, depth + 1); +} + +void +JoinCreatorBase:: +fill_data(fastuidraw::c_array attribute_data, + fastuidraw::c_array index_data, + fastuidraw::c_array > attribute_chunks, + fastuidraw::c_array > index_chunks, + fastuidraw::c_array > zranges, + fastuidraw::c_array index_adjusts) const +{ + unsigned int vertex_offset(0), index_offset(0), join_id(0); + std::vector > jvr(m_num_joins), jir(m_num_joins); + fastuidraw::c_array > join_vertex_ranges, join_index_ranges; + + join_vertex_ranges = fastuidraw::make_c_array(jvr); + join_index_ranges = fastuidraw::make_c_array(jir); + + FASTUIDRAWassert(attribute_data.size() == m_num_verts); + FASTUIDRAWassert(index_data.size() == m_num_indices); + FASTUIDRAWassert(attribute_chunks.size() == m_num_chunks); + FASTUIDRAWassert(index_chunks.size() == m_num_chunks); + FASTUIDRAWassert(zranges.size() == m_num_chunks); + FASTUIDRAWassert(index_adjusts.size() == m_num_chunks); + + /* Note that we reverse the the depth value, we need to do this because + * we want the joins draw first to obscure the joins drawn later. + */ + for(const OrderingEntry &J : m_ordering.non_closing_edge()) + { + fill_join(join_id, J, J.m_chunk, J.m_depth, + attribute_data, index_data, + vertex_offset, index_offset, + attribute_chunks, index_chunks, + zranges, index_adjusts, + join_vertex_ranges, + join_index_ranges); + ++join_id; + } + + for(const OrderingEntry &J : m_ordering.closing_edge()) + { + fill_join(join_id, J, J.m_chunk, J.m_depth, + attribute_data, index_data, + vertex_offset, index_offset, + attribute_chunks, index_chunks, + zranges, index_adjusts, + join_vertex_ranges, + join_index_ranges); + ++join_id; + } + + FASTUIDRAWassert(vertex_offset == m_num_verts); + FASTUIDRAWassert(index_offset == m_num_indices); + set_chunks(m_st, + join_vertex_ranges, join_index_ranges, + attribute_data, index_data, + attribute_chunks, index_chunks, + zranges, index_adjusts); +} + +void +JoinCreatorBase:: +set_chunks(const SubsetPrivate *st, + fastuidraw::c_array > join_vertex_ranges, + fastuidraw::c_array > join_index_ranges, + fastuidraw::c_array attribute_data, + fastuidraw::c_array index_data, + fastuidraw::c_array > attribute_chunks, + fastuidraw::c_array > index_chunks, + fastuidraw::c_array > zranges, + fastuidraw::c_array index_adjusts) +{ + process_chunk(st->non_closing_joins(), + join_vertex_ranges, join_index_ranges, + attribute_data, index_data, + attribute_chunks, index_chunks, + zranges, index_adjusts); + + process_chunk(st->closing_joins(), + join_vertex_ranges, join_index_ranges, + attribute_data, index_data, + attribute_chunks, index_chunks, + zranges, index_adjusts); + + if (st->have_children()) + { + set_chunks(st->child(0), + join_vertex_ranges, join_index_ranges, + attribute_data, index_data, + attribute_chunks, index_chunks, + zranges, index_adjusts); + + set_chunks(st->child(1), + join_vertex_ranges, join_index_ranges, + attribute_data, index_data, + attribute_chunks, index_chunks, + zranges, index_adjusts); + } +} + +void +JoinCreatorBase:: +process_chunk(const RangeAndChunk &ch, + fastuidraw::c_array > join_vertex_ranges, + fastuidraw::c_array > join_index_ranges, + fastuidraw::c_array attribute_data, + fastuidraw::c_array index_data, + fastuidraw::c_array > attribute_chunks, + fastuidraw::c_array > index_chunks, + fastuidraw::c_array > zranges, + fastuidraw::c_array index_adjusts) +{ + unsigned int K; + fastuidraw::range_type vr, ir; + + if (ch.m_elements.m_begin < ch.m_elements.m_end) + { + vr.m_begin = join_vertex_ranges[ch.m_elements.m_begin].m_begin; + vr.m_end = join_vertex_ranges[ch.m_elements.m_end - 1].m_end; + + ir.m_begin = join_index_ranges[ch.m_elements.m_begin].m_begin; + ir.m_end = join_index_ranges[ch.m_elements.m_end - 1].m_end; + } + else + { + vr.m_begin = vr.m_end = 0; + ir.m_begin = ir.m_end = 0; + } + + K = ch.m_chunk; + attribute_chunks[K] = attribute_data.sub_array(vr); + index_chunks[K] = index_data.sub_array(ir); + zranges[K] = fastuidraw::range_type(ch.m_depth_range.m_begin, ch.m_depth_range.m_end); + index_adjusts[K] = -int(vr.m_begin); +} + +///////////////////////////////////////////////// +// RoundedJoinCreator::PerRoundedJoin methods +RoundedJoinCreator::PerRoundedJoin:: +PerRoundedJoin(const PerJoinData &J, float thresh): + PerJoinData(J) +{ + /* n0z represents the start point of the rounded join in the complex plane + * as if the join was at the origin, n1z represents the end point of the + * rounded join in the complex plane as if the join was at the origin. + */ + std::complex n0z(m_lambda * n0().x(), m_lambda * n0().y()); + std::complex n1z(m_lambda * n1().x(), m_lambda * n1().y()); + + /* n1z_times_conj_n0z satisfies: + * n1z = n1z_times_conj_n0z * n0z + * i.e. it represents the arc-movement from n0z to n1z + */ + std::complex n1z_times_conj_n0z(n1z * std::conj(n0z)); + + m_arc_start = n0z; + m_delta_theta = std::atan2(n1z_times_conj_n0z.imag(), n1z_times_conj_n0z.real()); + m_num_arc_points = fastuidraw::detail::number_segments_for_tessellation(m_delta_theta, thresh); + m_delta_theta /= static_cast(m_num_arc_points - 1); +} + +void +RoundedJoinCreator::PerRoundedJoin:: +add_data(unsigned int depth, + fastuidraw::c_array pts, + unsigned int &vertex_offset, + fastuidraw::c_array indices, + unsigned int &index_offset) const +{ + unsigned int i, first; + float theta; + fastuidraw::StrokedPoint pt; + + first = vertex_offset; + set_distance_values(&pt); + + pt.m_position = m_p; + pt.m_pre_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_packed_data = pack_data_join(0, fastuidraw::StrokedPoint::offset_shared_with_edge, depth); + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + pt.m_position = m_p; + pt.m_pre_offset = m_lambda * n0(); + pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_packed_data = pack_data_join(1, fastuidraw::StrokedPoint::offset_shared_with_edge, depth); + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + for(i = 1, theta = m_delta_theta; i < m_num_arc_points - 1; ++i, theta += m_delta_theta, ++vertex_offset) + { + float t, c, s; + std::complex cs_as_complex; + + t = static_cast(i) / static_cast(m_num_arc_points - 1); + c = std::cos(theta); + s = std::sin(theta); + cs_as_complex = std::complex(c, s) * m_arc_start; + + pt.m_position = m_p; + pt.m_pre_offset = m_lambda * fastuidraw::vec2(n0().x(), n1().x()); + pt.m_auxiliary_offset = fastuidraw::vec2(t, cs_as_complex.real()); + pt.m_packed_data = pack_data_join(1, fastuidraw::StrokedPoint::offset_rounded_join, depth); + + if (m_lambda * n0().y() < 0.0f) + { + pt.m_packed_data |= fastuidraw::StrokedPoint::normal0_y_sign_mask; + } + + if (m_lambda * n1().y() < 0.0f) + { + pt.m_packed_data |= fastuidraw::StrokedPoint::normal1_y_sign_mask; + } + + if (cs_as_complex.imag() < 0.0f) + { + pt.m_packed_data |= fastuidraw::StrokedPoint::sin_sign_mask; + } + pt.pack_point(&pts[vertex_offset]); + } + + pt.m_position = m_p; + pt.m_pre_offset = m_lambda * n1(); + pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_packed_data = pack_data_join(1, fastuidraw::StrokedPoint::offset_shared_with_edge, depth); + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + add_triangle_fan(first, vertex_offset, indices, index_offset); +} + +/////////////////////////////////////////////////// +// RoundedJoinCreator methods +RoundedJoinCreator:: +RoundedJoinCreator(const PathData &P, + const SubsetPrivate *st, + float thresh): + JoinCreatorBase(P, st), + m_thresh(thresh) +{ + JoinCount J(P.m_join_ordering); + m_per_join_data.reserve(J.m_number_close_joins + J.m_number_non_close_joins); + post_ctor_initalize(); +} + +void +RoundedJoinCreator:: +add_join(unsigned int join_id, const PerJoinData &join, + unsigned int &vert_count, unsigned int &index_count) const +{ + FASTUIDRAWunused(join_id); + PerRoundedJoin J(join, m_thresh); + + m_per_join_data.push_back(J); + + /* a triangle fan centered at J.m_p with + * J.m_num_arc_points along an edge + */ + vert_count += (1 + J.m_num_arc_points); + index_count += 3 * (J.m_num_arc_points - 1); +} + +void +RoundedJoinCreator:: +fill_join_implement(unsigned int join_id, const PerJoinData &join, + fastuidraw::c_array pts, + unsigned int depth, + fastuidraw::c_array indices, + unsigned int &vertex_offset, unsigned int &index_offset) const +{ + FASTUIDRAWunused(join); + FASTUIDRAWassert(join_id < m_per_join_data.size()); + m_per_join_data[join_id].add_data(depth, pts, vertex_offset, indices, index_offset); +} + + + +/////////////////////////////////////////////////// +// BevelJoinCreator methods +BevelJoinCreator:: +BevelJoinCreator(const PathData &P, const SubsetPrivate *st): + JoinCreatorBase(P, st) +{ + post_ctor_initalize(); +} + +void +BevelJoinCreator:: +add_join(unsigned int join_id, const PerJoinData &join, + unsigned int &vert_count, unsigned int &index_count) const +{ + FASTUIDRAWunused(join_id); + FASTUIDRAWunused(join); + + /* one triangle per bevel join */ + vert_count += 3; + index_count += 3; +} + +void +BevelJoinCreator:: +fill_join_implement(unsigned int join_id, const PerJoinData &J, + fastuidraw::c_array pts, + unsigned int depth, + fastuidraw::c_array indices, + unsigned int &vertex_offset, unsigned int &index_offset) const +{ + FASTUIDRAWunused(join_id); + + fastuidraw::StrokedPoint pt; + J.set_distance_values(&pt); + + pt.m_position = J.m_p; + pt.m_pre_offset = J.m_lambda * J.n0(); + pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_packed_data = pack_data_join(1, fastuidraw::StrokedPoint::offset_shared_with_edge, depth); + pt.pack_point(&pts[vertex_offset + 0]); + + pt.m_position = J.m_p; + pt.m_pre_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_packed_data = pack_data_join(0, fastuidraw::StrokedPoint::offset_shared_with_edge, depth); + pt.pack_point(&pts[vertex_offset + 1]); + + pt.m_position = J.m_p; + pt.m_pre_offset = J.m_lambda * J.n1(); + pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_packed_data = pack_data_join(1, fastuidraw::StrokedPoint::offset_shared_with_edge, depth); + pt.pack_point(&pts[vertex_offset + 2]); + + add_triangle_fan(vertex_offset, vertex_offset + 3, indices, index_offset); + + vertex_offset += 3; +} + + + +/////////////////////////////////////////////////// +// MiterClipJoinCreator methods +MiterClipJoinCreator:: +MiterClipJoinCreator(const PathData &P, const SubsetPrivate *st): + JoinCreatorBase(P, st) +{ + post_ctor_initalize(); +} + +void +MiterClipJoinCreator:: +add_join(unsigned int join_id, const PerJoinData &join, + unsigned int &vert_count, unsigned int &index_count) const +{ + FASTUIDRAWunused(join_id); + FASTUIDRAWunused(join); + + /* Each join is a triangle fan from 5 points + * (thus 3 triangles, which is 9 indices) + */ + vert_count += 5; + index_count += 9; +} + + +void +MiterClipJoinCreator:: +fill_join_implement(unsigned int join_id, const PerJoinData &J, + fastuidraw::c_array pts, + unsigned int depth, + fastuidraw::c_array indices, + unsigned int &vertex_offset, unsigned int &index_offset) const +{ + FASTUIDRAWunused(join_id); + fastuidraw::StrokedPoint pt; + unsigned int first; + + /* The miter point is given by where the two boundary + * curves intersect. The two curves are given by: + * + * a(t) = J.m_p0 + stroke_width * J.m_lamba * J.m_n0 + t * J.m_v0 + * b(s) = J.m_p1 + stroke_width * J.m_lamba * J.m_n1 - s * J.m_v1 + * + * With J.m_0 is the location of the join. + * + * We need to solve a(t) = b(s) and compute that location. + * Linear algebra gives us that: + * + * t = - stroke_width * J.m_lamba * r + * s = - stroke_width * J.m_lamba * r + * where + * r = ( - 1) / + * + * thus + * + * a(t) = J.m_p + stroke_width * ( J.m_lamba * J.m_n0 - r * J.m_lamba * J.m_v0) + * = b(s) + * = J.m_p + stroke_width * ( J.m_lamba * J.m_n1 + r * J.m_lamba * J.m_v1) + */ + + first = vertex_offset; + J.set_distance_values(&pt); + + // join center point. + pt.m_position = J.m_p; + pt.m_pre_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_packed_data = pack_data_join(0, fastuidraw::StrokedPoint::offset_shared_with_edge, depth); + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + // join point from curve into join + pt.m_position = J.m_p; + pt.m_pre_offset = J.m_lambda * J.n0(); + pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_packed_data = pack_data_join(1, fastuidraw::StrokedPoint::offset_shared_with_edge, depth); + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + // miter point A + pt.m_position = J.m_p; + pt.m_pre_offset = J.n0(); + pt.m_auxiliary_offset = J.n1(); + pt.m_packed_data = pack_data_join(1, fastuidraw::StrokedPoint::offset_miter_clip_join, depth); + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + // miter point B + pt.m_position = J.m_p; + pt.m_pre_offset = J.n1(); + pt.m_auxiliary_offset = J.n0(); + pt.m_packed_data = pack_data_join(1, fastuidraw::StrokedPoint::offset_miter_clip_join, depth); + pt.m_packed_data |= fastuidraw::StrokedPoint::lambda_negated_mask; + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + // join point from curve out from join + pt.m_position = J.m_p; + pt.m_pre_offset = J.m_lambda * J.n1(); + pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_packed_data = pack_data_join(1, fastuidraw::StrokedPoint::offset_shared_with_edge, depth); + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + add_triangle_fan(first, vertex_offset, indices, index_offset); +} + + +//////////////////////////////////// +// MiterJoinCreator methods +template +MiterJoinCreator:: +MiterJoinCreator(const PathData &P, const SubsetPrivate *st): + JoinCreatorBase(P, st) +{ + post_ctor_initalize(); +} + +template +void +MiterJoinCreator:: +add_join(unsigned int join_id, const PerJoinData &J, + unsigned int &vert_count, unsigned int &index_count) const +{ + /* Each join is a triangle fan from 4 points + * (thus 2 triangles, which is 6 indices) + */ + FASTUIDRAWunused(join_id); + FASTUIDRAWunused(J); + + vert_count += 4; + index_count += 6; +} + +template +void +MiterJoinCreator:: +fill_join_implement(unsigned int join_id, const PerJoinData &J, + fastuidraw::c_array pts, + unsigned int depth, + fastuidraw::c_array indices, + unsigned int &vertex_offset, unsigned int &index_offset) const +{ + FASTUIDRAWunused(join_id); + + fastuidraw::StrokedPoint pt; + unsigned int first; + + first = vertex_offset; + J.set_distance_values(&pt); + + // join center point. + pt.m_position = J.m_p; + pt.m_pre_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_packed_data = pack_data_join(0, fastuidraw::StrokedPoint::offset_shared_with_edge, depth); + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + // join point from curve into join + pt.m_position = J.m_p; + pt.m_pre_offset = J.m_lambda * J.n0(); + pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_packed_data = pack_data_join(1, fastuidraw::StrokedPoint::offset_shared_with_edge, depth); + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + // miter point + pt.m_position = J.m_p; + pt.m_pre_offset = J.n0(); + pt.m_auxiliary_offset = J.n1(); + pt.m_packed_data = pack_data_join(1, tp, depth); + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + // join point from curve out from join + pt.m_position = J.m_p; + pt.m_pre_offset = J.m_lambda * J.n1(); + pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_packed_data = pack_data_join(1, fastuidraw::StrokedPoint::offset_shared_with_edge, depth); + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + add_triangle_fan(first, vertex_offset, indices, index_offset); +} + + +/////////////////////////////////////////////// +// CapCreatorBase methods +CapCreatorBase:: +CapCreatorBase(const PathData &P, + const SubsetPrivate *st, + PointIndexCapSize sz): + m_ordering(P.m_cap_ordering), + m_st(st), + m_num_chunks(P.m_number_cap_chunks), + m_size(sz) +{ +} + +void +CapCreatorBase:: +compute_sizes(unsigned int &num_attributes, + unsigned int &num_indices, + unsigned int &num_attribute_chunks, + unsigned int &num_index_chunks, + unsigned int &number_z_ranges) const +{ + num_attributes = m_size.m_verts; + num_indices = m_size.m_indices; + number_z_ranges = num_attribute_chunks = num_index_chunks = m_num_chunks; +} + +void +CapCreatorBase:: +fill_data(fastuidraw::c_array attribute_data, + fastuidraw::c_array index_data, + fastuidraw::c_array > attribute_chunks, + fastuidraw::c_array > index_chunks, + fastuidraw::c_array > zranges, + fastuidraw::c_array index_adjusts) const +{ + unsigned int vertex_offset(0u), index_offset(0u), cap_id(0u); + std::vector > cvr(m_num_chunks), cir(m_num_chunks); + + for(const OrderingEntry &C : m_ordering.caps()) + { + unsigned int v(vertex_offset), i(index_offset); + + add_cap(C, C.m_depth, attribute_data, index_data, vertex_offset, index_offset); + + cvr[cap_id].m_begin = v; + cvr[cap_id].m_end = vertex_offset; + + cir[cap_id].m_begin = i; + cir[cap_id].m_end = index_offset; + + attribute_chunks[C.m_chunk] = attribute_data.sub_array(cvr[cap_id]); + index_chunks[C.m_chunk] = index_data.sub_array(cir[cap_id]); + zranges[C.m_chunk] = fastuidraw::range_type(C.m_depth, C.m_depth + 1); + index_adjusts[C.m_chunk] = -int(v); + + ++cap_id; + } + + FASTUIDRAWassert(vertex_offset == m_size.m_verts); + FASTUIDRAWassert(index_offset == m_size.m_indices); + + set_chunks(m_st, + fastuidraw::make_c_array(cvr), + fastuidraw::make_c_array(cir), + attribute_data, index_data, + attribute_chunks, index_chunks, + zranges, index_adjusts); +} + +void +CapCreatorBase:: +set_chunks(const SubsetPrivate *st, + fastuidraw::c_array > cap_vertex_ranges, + fastuidraw::c_array > cap_index_ranges, + fastuidraw::c_array attribute_data, + fastuidraw::c_array index_data, + fastuidraw::c_array > attribute_chunks, + fastuidraw::c_array > index_chunks, + fastuidraw::c_array > zranges, + fastuidraw::c_array index_adjusts) +{ + const RangeAndChunk &ch(st->caps()); + fastuidraw::range_type vr, ir; + unsigned int K; + + if (ch.m_elements.m_begin < ch.m_elements.m_end) + { + vr.m_begin = cap_vertex_ranges[ch.m_elements.m_begin].m_begin; + vr.m_end = cap_vertex_ranges[ch.m_elements.m_end - 1].m_end; + + ir.m_begin = cap_index_ranges[ch.m_elements.m_begin].m_begin; + ir.m_end = cap_index_ranges[ch.m_elements.m_end - 1].m_end; + + FASTUIDRAWassert(ch.m_depth_range.m_begin < ch.m_depth_range.m_end); + } + else + { + vr.m_begin = vr.m_end = 0; + ir.m_begin = ir.m_end = 0; + FASTUIDRAWassert(ch.m_depth_range.m_begin == ch.m_depth_range.m_end); + } + + K = ch.m_chunk; + attribute_chunks[K] = attribute_data.sub_array(vr); + index_chunks[K] = index_data.sub_array(ir); + zranges[K] = fastuidraw::range_type(ch.m_depth_range.m_begin, ch.m_depth_range.m_end); + index_adjusts[K] = -int(vr.m_begin); + + if (st->have_children()) + { + set_chunks(st->child(0), + cap_vertex_ranges, cap_index_ranges, + attribute_data, index_data, + attribute_chunks, index_chunks, + zranges, index_adjusts); + + set_chunks(st->child(1), + cap_vertex_ranges, cap_index_ranges, + attribute_data, index_data, + attribute_chunks, index_chunks, + zranges, index_adjusts); + } +} + +/////////////////////////////////////////////////// +// RoundedCapCreator methods +RoundedCapCreator:: +RoundedCapCreator(const PathData &P, + const SubsetPrivate *st, + float thresh): + CapCreatorBase(P, st, compute_size(P, thresh)) +{ + m_num_arc_points_per_cap = fastuidraw::detail::number_segments_for_tessellation(M_PI, thresh); + m_delta_theta = static_cast(M_PI) / static_cast(m_num_arc_points_per_cap - 1); +} + +PointIndexCapSize +RoundedCapCreator:: +compute_size(const PathData &P, float thresh) +{ + unsigned int num_caps, num_arc_points_per_cap; + PointIndexCapSize return_value; + + num_arc_points_per_cap = fastuidraw::detail::number_segments_for_tessellation(M_PI, thresh); + + /* each cap is a triangle fan centered at the cap point. */ + num_caps = P.m_cap_ordering.caps().size(); + return_value.m_verts = (1 + num_arc_points_per_cap) * num_caps; + return_value.m_indices = 3 * (num_arc_points_per_cap - 1) * num_caps; + + return return_value; +} + +void +RoundedCapCreator:: +add_cap(const PerCapData &C, unsigned int depth, + fastuidraw::c_array pts, + fastuidraw::c_array indices, + unsigned int &vertex_offset, + unsigned int &index_offset) const +{ + fastuidraw::vec2 n, v; + unsigned int first, i; + float theta; + fastuidraw::StrokedPoint pt; + + first = vertex_offset; + v = C.m_tangent_into_cap; + n = fastuidraw::vec2(-v.y(), v.x()); + C.set_distance_values(&pt); + + pt.m_position = C.m_p; + pt.m_pre_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_packed_data = pack_data(0, fastuidraw::StrokedPoint::offset_shared_with_edge, depth); + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + pt.m_position = C.m_p; + pt.m_pre_offset = n; + pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_packed_data = pack_data(1, fastuidraw::StrokedPoint::offset_shared_with_edge, depth); + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + for(i = 1, theta = m_delta_theta; i < m_num_arc_points_per_cap - 1; ++i, theta += m_delta_theta, ++vertex_offset) + { + float s, c; + + s = std::sin(theta); + c = std::cos(theta); + pt.m_position = C.m_p; + pt.m_pre_offset = n; + pt.m_auxiliary_offset = fastuidraw::vec2(s, c); + pt.m_packed_data = pack_data(1, fastuidraw::StrokedPoint::offset_rounded_cap, depth); + pt.pack_point(&pts[vertex_offset]); + } + + pt.m_position = C.m_p; + pt.m_pre_offset = -n; + pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_packed_data = pack_data(1, fastuidraw::StrokedPoint::offset_shared_with_edge, depth); + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + add_triangle_fan(first, vertex_offset, indices, index_offset); +} + +/////////////////////////////////////////////////// +// SquareCapCreator methods +PointIndexCapSize +SquareCapCreator:: +compute_size(const PathData &P) +{ + PointIndexCapSize return_value; + unsigned int num_caps; + + /* each square cap generates 5 new points + * and 3 triangles (= 9 indices) + */ + num_caps = P.m_cap_ordering.caps().size(); + return_value.m_verts = 5 * num_caps; + return_value.m_indices = 9 * num_caps; + + return return_value; +} + +void +SquareCapCreator:: +add_cap(const PerCapData &C, unsigned int depth, + fastuidraw::c_array pts, + fastuidraw::c_array indices, + unsigned int &vertex_offset, + unsigned int &index_offset) const +{ + unsigned int first; + fastuidraw::vec2 n, v; + fastuidraw::StrokedPoint pt; + + first = vertex_offset; + v = C.m_tangent_into_cap; + n = fastuidraw::vec2(-v.y(), v.x()); + C.set_distance_values(&pt); + + pt.m_position = C.m_p; + pt.m_pre_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_packed_data = pack_data(0, fastuidraw::StrokedPoint::offset_shared_with_edge, depth); + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + pt.m_position = C.m_p; + pt.m_pre_offset = n; + pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_packed_data = pack_data(1, fastuidraw::StrokedPoint::offset_shared_with_edge, depth); + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + pt.m_position = C.m_p; + pt.m_pre_offset = n; + pt.m_auxiliary_offset = v; + pt.m_packed_data = pack_data(1, fastuidraw::StrokedPoint::offset_square_cap, depth); + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + pt.m_position = C.m_p; + pt.m_pre_offset = -n; + pt.m_auxiliary_offset = v; + pt.m_packed_data = pack_data(1, fastuidraw::StrokedPoint::offset_square_cap, depth); + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + pt.m_position = C.m_p; + pt.m_pre_offset = -n; + pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_packed_data = pack_data(1, fastuidraw::StrokedPoint::offset_shared_with_edge, depth); + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + add_triangle_fan(first, vertex_offset, indices, index_offset); +} + +////////////////////////////////////// +// AdjustableCapCreator methods +PointIndexCapSize +AdjustableCapCreator:: +compute_size(const PathData &P) +{ + PointIndexCapSize return_value; + unsigned int num_caps; + + num_caps = P.m_cap_ordering.caps().size(); + return_value.m_verts = number_points_per_fan * num_caps; + return_value.m_indices = number_indices_per_fan * num_caps; + + return return_value; +} + +void +AdjustableCapCreator:: +add_cap(const PerCapData &C, unsigned int depth, + fastuidraw::c_array pts, + fastuidraw::c_array indices, + unsigned int &vertex_offset, + unsigned int &index_offset) const +{ + enum fastuidraw::StrokedPoint::offset_type_t type; + fastuidraw::vec2 n, v; + unsigned int first; + fastuidraw::StrokedPoint pt; + + type = (C.m_is_starting_cap) ? + fastuidraw::StrokedPoint::offset_adjustable_cap_contour_start : + fastuidraw::StrokedPoint::offset_adjustable_cap_contour_end; + + first = vertex_offset; + v = C.m_tangent_into_cap; + n = fastuidraw::vec2(-v.y(), v.x()); + C.set_distance_values(&pt); + + pt.m_position = C.m_p; + pt.m_pre_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_auxiliary_offset = v; + pt.m_packed_data = pack_data(0, type, depth); + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + pt.m_position = C.m_p; + pt.m_pre_offset = n; + pt.m_auxiliary_offset = v; + pt.m_packed_data = pack_data(1, type, depth); + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + pt.m_position = C.m_p; + pt.m_pre_offset = n; + pt.m_auxiliary_offset = v; + pt.m_packed_data = pack_data(1, type, depth) | fastuidraw::StrokedPoint::adjustable_cap_ending_mask; + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + pt.m_position = C.m_p; + pt.m_pre_offset = fastuidraw::vec2(0.0f, 0.0f); + pt.m_auxiliary_offset = v; + pt.m_packed_data = pack_data(0, type, depth) | fastuidraw::StrokedPoint::adjustable_cap_ending_mask; + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + pt.m_position = C.m_p; + pt.m_pre_offset = -n; + pt.m_auxiliary_offset = v; + pt.m_packed_data = pack_data(1, type, depth) | fastuidraw::StrokedPoint::adjustable_cap_ending_mask; + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + pt.m_position = C.m_p; + pt.m_pre_offset = -n; + pt.m_auxiliary_offset = v; + pt.m_packed_data = pack_data(1, type, depth); + pt.pack_point(&pts[vertex_offset]); + ++vertex_offset; + + add_triangle_fan(first, vertex_offset, indices, index_offset); +} + +///////////////////////////////////////////// +// StrokedCapsJoinsPrivate methods +StrokedCapsJoinsPrivate:: +StrokedCapsJoinsPrivate(const ContourData &P): + m_subset(nullptr) +{ + if (!P.m_per_contour_data.empty()) + { + m_empty_path = false; + create_joins_caps(P); + } + else + { + m_empty_path = true; + m_bevel_joins.mark_as_empty(); + m_miter_clip_joins.mark_as_empty(); + m_miter_joins.mark_as_empty(); + m_miter_bevel_joins.mark_as_empty(); + m_square_caps.mark_as_empty(); + m_adjustable_caps.mark_as_empty(); + std::fill(m_chunk_of_joins.begin(), m_chunk_of_joins.end(), 0); + m_chunk_of_caps = 0; + } +} + +StrokedCapsJoinsPrivate:: +~StrokedCapsJoinsPrivate() +{ + for(unsigned int i = 0, endi = m_rounded_joins.size(); i < endi; ++i) + { + FASTUIDRAWdelete(m_rounded_joins[i].m_data); + } + + for(unsigned int i = 0, endi = m_rounded_caps.size(); i < endi; ++i) + { + FASTUIDRAWdelete(m_rounded_caps[i].m_data); + } + + if (!m_empty_path) + { + FASTUIDRAWdelete(m_subset); + } +} + +void +StrokedCapsJoinsPrivate:: +create_joins_caps(const ContourData &P) +{ + CullingHierarchy *s; + SubsetPrivate::CreationValues cnts; + + FASTUIDRAWassert(!m_empty_path); + s = CullingHierarchy::create(P); + m_subset = SubsetPrivate::create(s, cnts, m_path_data.m_join_ordering, m_path_data.m_cap_ordering); + + m_path_data.m_number_join_chunks = cnts.m_non_closing_join_chunk_cnt + cnts.m_closing_join_chunk_cnt; + m_path_data.m_number_cap_chunks = cnts.m_cap_chunk_cnt; + + /* the chunks of the root element have the data for everything. */ + m_chunk_of_joins[fastuidraw::StrokedCapsJoins::all_non_closing] = m_subset->non_closing_joins().m_chunk; + m_chunk_of_joins[fastuidraw::StrokedCapsJoins::all_closing] = m_subset->closing_joins().m_chunk; + + m_chunk_of_caps = m_subset->caps().m_chunk; + + FASTUIDRAWdelete(s); +} + +template +const fastuidraw::PainterAttributeData& +StrokedCapsJoinsPrivate:: +fetch_create(float thresh, std::vector &values) +{ + if (values.empty()) + { + fastuidraw::PainterAttributeData *newD; + newD = FASTUIDRAWnew fastuidraw::PainterAttributeData(); + newD->set_data(T(m_path_data, m_subset, 1.0f)); + values.push_back(ThreshWithData(newD, 1.0f)); + } + + /* we set a hard tolerance of 1e-6. Should we + * set it as a ratio of the bounding box of + * the underlying tessellated path? + */ + thresh = fastuidraw::t_max(thresh, float(1e-6)); + if (values.back().m_thresh <= thresh) + { + std::vector::const_iterator iter; + iter = std::lower_bound(values.begin(), values.end(), thresh, + ThreshWithData::reverse_compare_against_thresh); + FASTUIDRAWassert(iter != values.end()); + FASTUIDRAWassert(iter->m_thresh <= thresh); + FASTUIDRAWassert(iter->m_data != nullptr); + return *iter->m_data; + } + else + { + float t; + t = values.back().m_thresh; + while(t > thresh) + { + fastuidraw::PainterAttributeData *newD; + + t *= 0.5f; + newD = FASTUIDRAWnew fastuidraw::PainterAttributeData(); + newD->set_data(T(m_path_data, m_subset, t)); + values.push_back(ThreshWithData(newD, t)); + } + return *values.back().m_data; + } +} + +////////////////////////////////////////////// +// fastuidraw::StrokedCapsJoins::ScratchSpace methods +fastuidraw::StrokedCapsJoins::ScratchSpace:: +ScratchSpace(void) +{ + m_d = FASTUIDRAWnew ScratchSpacePrivate(); +} + +fastuidraw::StrokedCapsJoins::ScratchSpace:: +~ScratchSpace(void) +{ + ScratchSpacePrivate *d; + d = static_cast(m_d); + FASTUIDRAWdelete(d); + m_d = nullptr; +} + +/////////////////////////////////////// +// ChunkSetPrivate methods +void +ChunkSetPrivate:: +add_join_chunk(const RangeAndChunk &j) +{ + if (j.non_empty() && !m_ignore_join_adds) + { + m_join_chunks.push_back(j.m_chunk); + m_join_ranges.push_back(j.m_elements); + } +} + +void +ChunkSetPrivate:: +add_cap_chunk(const RangeAndChunk &c) +{ + if (c.non_empty()) + { + m_cap_chunks.push_back(c.m_chunk); + } +} + +void +ChunkSetPrivate:: +handle_dashed_evaluator(const fastuidraw::DashEvaluatorBase *dash_evaluator, + const fastuidraw::PainterShaderData::DataBase *dash_data, + const fastuidraw::StrokedCapsJoins &path) +{ + if (dash_evaluator != nullptr) + { + const fastuidraw::PainterAttributeData &joins(path.bevel_joins()); + unsigned int cnt(0); + + m_join_chunks.clear(); + for(const fastuidraw::range_type &R : m_join_ranges) + { + for(unsigned int J = R.m_begin; J < R.m_end; ++J, ++cnt) + { + unsigned int chunk; + fastuidraw::c_array attribs; + + chunk = path.join_chunk(J); + attribs = joins.attribute_data_chunk(chunk); + FASTUIDRAWassert(!attribs.empty()); + FASTUIDRAWassert(attribs.size() == 3); + if (dash_evaluator->covered_by_dash_pattern(dash_data, attribs[0])) + { + m_join_chunks.push_back(chunk); + } + } + } + } +} + +////////////////////////////////////////// +// fastuidraw::StrokedCapsJoins::ChunkSet methods +fastuidraw::StrokedCapsJoins::ChunkSet:: +ChunkSet(void) +{ + m_d = FASTUIDRAWnew ChunkSetPrivate(); +} + +fastuidraw::StrokedCapsJoins::ChunkSet:: +~ChunkSet(void) +{ + ChunkSetPrivate *d; + d = static_cast(m_d); + FASTUIDRAWdelete(d); +} + +fastuidraw::c_array +fastuidraw::StrokedCapsJoins::ChunkSet:: +join_chunks(void) const +{ + ChunkSetPrivate *d; + d = static_cast(m_d); + return d->join_chunks(); +} + +fastuidraw::c_array +fastuidraw::StrokedCapsJoins::ChunkSet:: +cap_chunks(void) const +{ + ChunkSetPrivate *d; + d = static_cast(m_d); + return d->cap_chunks(); +} + +//////////////////////////////////////// +// fastuidraw::StrokedCapsJoins::Builder methods +fastuidraw::StrokedCapsJoins::Builder:: +Builder(void) +{ + m_d = FASTUIDRAWnew ContourData(); +} + +fastuidraw::StrokedCapsJoins::Builder:: +~Builder() +{ + ContourData *d; + d = static_cast(m_d); + FASTUIDRAWdelete(d); +} + +void +fastuidraw::StrokedCapsJoins::Builder:: +begin_contour(const vec2 &start_pt, + const vec2 &path_direction) +{ + ContourData *d; + d = static_cast(m_d); + + d->m_bounding_box.union_point(start_pt); + d->m_per_contour_data.push_back(PerContourData()); + d->m_per_contour_data.back().start(start_pt, path_direction); +} + +void +fastuidraw::StrokedCapsJoins::Builder:: +add_join(const vec2 &join_pt, + float distance_from_previous_join, + const vec2 &direction_into_join, + const vec2 &direction_leaving_join) +{ + ContourData *d; + d = static_cast(m_d); + + FASTUIDRAWassert(!d->m_per_contour_data.empty()); + d->m_bounding_box.union_point(join_pt); + d->m_per_contour_data.back().add_join(join_pt, distance_from_previous_join, + direction_into_join, direction_leaving_join); +} + +void +fastuidraw::StrokedCapsJoins::Builder:: +end_contour(float distance_from_previous_join, + const vec2 &tangent_along_curve) +{ + ContourData *d; + d = static_cast(m_d); + + FASTUIDRAWassert(!d->m_per_contour_data.empty()); + d->m_per_contour_data.back().end(distance_from_previous_join, + tangent_along_curve); +} + +////////////////////////////////////////////////////////////// +// fastuidraw::StrokedCapsJoins methods +fastuidraw::StrokedCapsJoins:: +StrokedCapsJoins(const Builder &B) +{ + ContourData *C; + C = static_cast(B.m_d); + + m_d = FASTUIDRAWnew StrokedCapsJoinsPrivate(*C); +} + +fastuidraw::StrokedCapsJoins:: +~StrokedCapsJoins() +{ + StrokedCapsJoinsPrivate *d; + d = static_cast(m_d); + FASTUIDRAWdelete(d); + m_d = nullptr; +} + +void +fastuidraw::StrokedCapsJoins:: +compute_chunks(ScratchSpace &scratch_space, + const DashEvaluatorBase *dash_evaluator, + const PainterShaderData::DataBase *dash_data, + c_array clip_equations, + const float3x3 &clip_matrix_local, + const vec2 &recip_dimensions, + float pixels_additional_room, + float item_space_additional_room, + bool include_closing_edges, + unsigned int max_attribute_cnt, + unsigned int max_index_cnt, + bool take_joins_outside_of_region, + ChunkSet &dst) const +{ + StrokedCapsJoinsPrivate *d; + ScratchSpacePrivate *scratch_space_ptr; + ChunkSetPrivate *chunk_set_ptr; + + d = static_cast(m_d); + scratch_space_ptr = static_cast(scratch_space.m_d); + chunk_set_ptr = static_cast(dst.m_d); + + if (d->m_empty_path) + { + chunk_set_ptr->reset(); + return; + } + + d->m_subset->compute_chunks(include_closing_edges, + *scratch_space_ptr, + clip_equations, + clip_matrix_local, + recip_dimensions, + pixels_additional_room, + item_space_additional_room, + max_attribute_cnt, + max_index_cnt, + take_joins_outside_of_region, + *chunk_set_ptr); + chunk_set_ptr->handle_dashed_evaluator(dash_evaluator, dash_data, *this); +} + +unsigned int +fastuidraw::StrokedCapsJoins:: +number_joins(bool include_joins_of_closing_edge) const +{ + StrokedCapsJoinsPrivate *d; + d = static_cast(m_d); + return d->m_path_data.m_join_ordering.number_joins(include_joins_of_closing_edge); +} + +unsigned int +fastuidraw::StrokedCapsJoins:: +join_chunk(unsigned int J) const +{ + StrokedCapsJoinsPrivate *d; + d = static_cast(m_d); + return d->m_path_data.m_join_ordering.join_chunk(J); +} + +unsigned int +fastuidraw::StrokedCapsJoins:: +chunk_of_joins(enum chunk_selection c) const +{ + StrokedCapsJoinsPrivate *d; + d = static_cast(m_d); + return d->m_chunk_of_joins[c]; +} + +unsigned int +fastuidraw::StrokedCapsJoins:: +chunk_of_caps(void) const +{ + StrokedCapsJoinsPrivate *d; + d = static_cast(m_d); + return d->m_chunk_of_caps; +} + +const fastuidraw::PainterAttributeData& +fastuidraw::StrokedCapsJoins:: +square_caps(void) const +{ + StrokedCapsJoinsPrivate *d; + d = static_cast(m_d); + return d->m_square_caps.data(d->m_path_data, d->m_subset); +} + +const fastuidraw::PainterAttributeData& +fastuidraw::StrokedCapsJoins:: +adjustable_caps(void) const +{ + StrokedCapsJoinsPrivate *d; + d = static_cast(m_d); + return d->m_adjustable_caps.data(d->m_path_data, d->m_subset); +} + +const fastuidraw::PainterAttributeData& +fastuidraw::StrokedCapsJoins:: +bevel_joins(void) const +{ + StrokedCapsJoinsPrivate *d; + d = static_cast(m_d); + return d->m_bevel_joins.data(d->m_path_data, d->m_subset); +} + +const fastuidraw::PainterAttributeData& +fastuidraw::StrokedCapsJoins:: +miter_clip_joins(void) const +{ + StrokedCapsJoinsPrivate *d; + d = static_cast(m_d); + return d->m_miter_clip_joins.data(d->m_path_data, d->m_subset); +} + +const fastuidraw::PainterAttributeData& +fastuidraw::StrokedCapsJoins:: +miter_bevel_joins(void) const +{ + StrokedCapsJoinsPrivate *d; + d = static_cast(m_d); + return d->m_miter_bevel_joins.data(d->m_path_data, d->m_subset); +} + +const fastuidraw::PainterAttributeData& +fastuidraw::StrokedCapsJoins:: +miter_joins(void) const +{ + StrokedCapsJoinsPrivate *d; + d = static_cast(m_d); + return d->m_miter_joins.data(d->m_path_data, d->m_subset); +} + +const fastuidraw::PainterAttributeData& +fastuidraw::StrokedCapsJoins:: +rounded_joins(float thresh) const +{ + StrokedCapsJoinsPrivate *d; + d = static_cast(m_d); + + return (!d->m_empty_path) ? + d->fetch_create(thresh, d->m_rounded_joins) : + d->m_empty_data; +} + +const fastuidraw::PainterAttributeData& +fastuidraw::StrokedCapsJoins:: +rounded_caps(float thresh) const +{ + StrokedCapsJoinsPrivate *d; + d = static_cast(m_d); + return (!d->m_empty_path) ? + d->fetch_create(thresh, d->m_rounded_caps) : + d->m_empty_data; +} diff --git a/src/fastuidraw/painter/stroked_path.cpp b/src/fastuidraw/painter/stroked_path.cpp index 751c6373c..6c729ff9f 100644 --- a/src/fastuidraw/painter/stroked_path.cpp +++ b/src/fastuidraw/painter/stroked_path.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -37,66 +38,20 @@ namespace inline uint32_t pack_data(int on_boundary, - enum fastuidraw::StrokedPath::point::offset_type_t pt, + enum fastuidraw::StrokedPoint::offset_type_t pt, uint32_t depth) { using namespace fastuidraw; FASTUIDRAWassert(on_boundary == 0 || on_boundary == 1); uint32_t bb(on_boundary), pp(pt); - return pack_bits(StrokedPath::point::offset_type_bit0, - StrokedPath::point::offset_type_num_bits, pp) - | pack_bits(StrokedPath::point::boundary_bit, 1u, bb) - | pack_bits(StrokedPath::point::depth_bit0, - StrokedPath::point::depth_num_bits, depth); + return pack_bits(StrokedPoint::offset_type_bit0, + StrokedPoint::offset_type_num_bits, pp) + | pack_bits(StrokedPoint::boundary_bit, 1u, bb) + | pack_bits(StrokedPoint::depth_bit0, + StrokedPoint::depth_num_bits, depth); } - inline - uint32_t - pack_data_join(int on_boundary, - enum fastuidraw::StrokedPath::point::offset_type_t pt, - uint32_t depth) - { - return pack_data(on_boundary, pt, depth) | fastuidraw::StrokedPath::point::join_mask; - } - - void - add_triangle_fan(unsigned int begin, unsigned int end, - fastuidraw::c_array indices, - unsigned int &index_offset) - { - for(unsigned int i = begin + 1; i < end - 1; ++i, index_offset += 3) - { - indices[index_offset + 0] = begin; - indices[index_offset + 1] = i; - indices[index_offset + 2] = i + 1; - } - } - - class JoinSource - { - public: - fastuidraw::vec2 m_pt; - unsigned int m_contour; - unsigned int m_edge_going_into_join; - bool m_of_closing_edge; - }; - - class CapSource - { - public: - fastuidraw::vec2 m_pt; - unsigned int m_contour; - bool m_is_start_cap; - }; - - class PerEdgeData - { - public: - fastuidraw::vec2 m_begin_normal, m_end_normal; - fastuidraw::TessellatedPath::point m_start_pt, m_end_pt; - }; - template class OrderingEntry:public T { @@ -111,201 +66,6 @@ namespace unsigned int m_depth; }; - /* provides in what chunk each Join is located. - */ - class JoinOrdering - { - public: - fastuidraw::c_array > - non_closing_edge(void) const - { - return fastuidraw::make_c_array(m_non_closing_edge); - } - - fastuidraw::c_array > - closing_edge(void) const - { - return fastuidraw::make_c_array(m_closing_edge); - } - - void - add_join(const JoinSource &src, unsigned int &chunk) - { - OrderingEntry J(src, chunk); - if (src.m_of_closing_edge) - { - m_closing_edge.push_back(J); - } - else - { - m_non_closing_edge.push_back(J); - } - ++chunk; - } - - void - post_process_non_closing(fastuidraw::range_type join_range, - unsigned int &depth) - { - unsigned int R, d; - - /* the depth values of the joins must come in reverse - * so that higher depth values occlude later elements - */ - R = join_range.difference(); - d = depth + R - 1; - - for(unsigned int i = join_range.m_begin; i < join_range.m_end; ++i, --d) - { - FASTUIDRAWassert(i < m_non_closing_edge.size()); - m_non_closing_edge[i].m_depth = d; - } - depth += R; - } - - void - post_process_closing(unsigned int non_closing_edge_chunk_count, - fastuidraw::range_type join_range, - unsigned int &depth) - { - unsigned int R, d; - - /* the depth values of the joins must come in reverse - * so that higher depth values occlude later elements - */ - R = join_range.difference(); - d = depth + R - 1; - - for(unsigned int i = join_range.m_begin; i < join_range.m_end; ++i, --d) - { - FASTUIDRAWassert(i < m_closing_edge.size()); - m_closing_edge[i].m_depth = d; - m_closing_edge[i].m_chunk += non_closing_edge_chunk_count; - } - depth += R; - } - - unsigned int - number_joins(bool include_joins_of_closing_edge) const - { - return include_joins_of_closing_edge ? - m_non_closing_edge.size() + m_closing_edge.size() : - m_non_closing_edge.size(); - } - - unsigned int - join_chunk(unsigned int J) const - { - FASTUIDRAWassert(J < number_joins(true)); - if (J >= m_non_closing_edge.size()) - { - J -= m_non_closing_edge.size(); - return m_closing_edge[J].m_chunk; - } - else - { - return m_non_closing_edge[J].m_chunk; - } - } - - private: - std::vector > m_non_closing_edge; - std::vector > m_closing_edge; - }; - - class CapOrdering - { - public: - fastuidraw::c_array > - caps(void) const - { - return fastuidraw::make_c_array(m_caps); - } - - void - add_cap(const CapSource &src, unsigned int &chunk) - { - OrderingEntry C(src, chunk); - m_caps.push_back(C); - ++chunk; - } - - void - post_process(fastuidraw::range_type range, - unsigned int &depth) - { - unsigned int R, d; - - /* the depth values of the caps must come in reverse - * so that higher depth values occlude later elements - */ - R = range.difference(); - d = depth + R - 1; - for(unsigned int i = range.m_begin; i < range.m_end; ++i, --d) - { - FASTUIDRAWassert(i < m_caps.size()); - m_caps[i].m_depth = d; - } - depth += R; - } - - private: - std::vector > m_caps; - }; - - class PerContourData - { - public: - const PerEdgeData& - edge_data(unsigned int E) const - { - return (E == m_edge_data_store.size()) ? - m_edge_data_store[0]: - m_edge_data_store[E]; - } - - PerEdgeData& - write_edge_data(unsigned int E) - { - FASTUIDRAWassert(E < m_edge_data_store.size()); - return m_edge_data_store[E]; - } - - fastuidraw::vec2 m_begin_cap_normal, m_end_cap_normal; - fastuidraw::TessellatedPath::point m_start_contour_pt, m_end_contour_pt; - std::vector m_edge_data_store; - }; - - class ContourData:fastuidraw::noncopyable - { - public: - unsigned int - number_contours(void) const - { - return m_per_contour_data.size(); - } - - unsigned int - number_edges(unsigned int C) const - { - FASTUIDRAWassert(C < m_per_contour_data.size()); - return m_per_contour_data[C].m_edge_data_store.size(); - } - - std::vector m_per_contour_data; - }; - - class PathData:fastuidraw::noncopyable - { - public: - ContourData m_contour_data; - - JoinOrdering m_join_ordering; - unsigned int m_number_join_chunks; - CapOrdering m_cap_ordering; - unsigned int m_number_cap_chunks; - }; - class SingleSubEdge { public: @@ -348,7 +108,7 @@ namespace public: static SubEdgeCullingHierarchy* - create(const fastuidraw::TessellatedPath &P, ContourData &contour_data); + create(const fastuidraw::TessellatedPath &P); ~SubEdgeCullingHierarchy(); @@ -382,24 +142,6 @@ namespace return m_sub_edges.m_closing; } - fastuidraw::c_array - non_closing_joins(void) const - { - return m_joins.m_non_closing; - } - - fastuidraw::c_array - closing_joins(void) const - { - return m_joins.m_closing; - } - - fastuidraw::c_array - caps(void) const - { - return fastuidraw::make_c_array(m_caps); - } - const fastuidraw::BoundingBox& bounding_box(void) const { @@ -444,9 +186,7 @@ namespace }; SubEdgeCullingHierarchy(const fastuidraw::BoundingBox &start_box, - std::vector &data, unsigned int num_non_closing_edges, - std::vector &joins, unsigned int num_non_closing_joins, - std::vector &caps); + std::vector &data, unsigned int num_non_closing_edges); /* a value of -1 means to NOT split. */ @@ -458,15 +198,13 @@ namespace static void - create_lists(const fastuidraw::TessellatedPath &P, ContourData &contour_data, + create_lists(const fastuidraw::TessellatedPath &P, std::vector &data, unsigned int &num_non_closing_edges, - fastuidraw::BoundingBox &bx, - std::vector &joins, unsigned int &num_non_closing_joins, - std::vector &caps); + fastuidraw::BoundingBox &bx); static void - process_edge(const fastuidraw::TessellatedPath &P, ContourData &contour_data, + process_edge(const fastuidraw::TessellatedPath &P, unsigned int contour, unsigned int edge, std::vector &dst, fastuidraw::BoundingBox &bx); @@ -476,6 +214,17 @@ namespace void check_closing_at_end(const std::vector &data, unsigned int num_non_closing); + static + float + compute_lambda(const fastuidraw::vec2 &n0, const fastuidraw::vec2 &n1) + { + fastuidraw::vec2 v1(n1.y(), -n1.x()); + float d; + + d = fastuidraw::dot(v1, n0); + return (d > 0.0f) ? -1.0f : 1.0f; + } + /* tolerance for normal computation when creating edge list. */ static const float sm_mag_tol; @@ -484,14 +233,6 @@ namespace */ fastuidraw::vecN m_children; - /* Caps inside, only non-empty if has no children. - */ - std::vector m_caps; - - /* Joins inside, only non-empty if has no children. - */ - PartitionedData m_joins; - /* what edges of this, only non-empty if has no children. */ PartitionedData m_sub_edges; @@ -549,83 +290,28 @@ namespace } }; - class RangeAndChunk - { - public: - /* what range of Joins/Caps hit by the chunk. - */ - fastuidraw::range_type m_elements; - - /* depth range of Joins/Caps hit - */ - fastuidraw::range_type m_depth_range; - - /* what the chunk is - */ - unsigned int m_chunk; - - bool - non_empty(void) const - { - return m_depth_range.m_end > m_depth_range.m_begin; - } - }; - class ChunkSetPrivate:fastuidraw::noncopyable { public: void reset(void) { - m_ignore_join_adds = false; m_edge_chunks.clear(); - m_join_chunks.clear(); - m_join_ranges.clear(); - m_cap_chunks.clear(); } void add_edge_chunk(const EdgeRanges &ed); - void - add_join_chunk(const RangeAndChunk &j); - - void - add_cap_chunk(const RangeAndChunk &c); - fastuidraw::c_array edge_chunks(void) const { return fastuidraw::make_c_array(m_edge_chunks); } - fastuidraw::c_array - join_chunks(void) const - { - return fastuidraw::make_c_array(m_join_chunks); - } - - fastuidraw::c_array - cap_chunks(void) const - { - return fastuidraw::make_c_array(m_cap_chunks); - } - - void - ignore_join_adds(void) - { - m_ignore_join_adds = true; - } - - void - handle_dashed_evaluator(const fastuidraw::DashEvaluatorBase *dash_evaluator, - const fastuidraw::PainterShaderData::DataBase *dash_data, - const fastuidraw::StrokedPath &path); + fastuidraw::StrokedCapsJoins::ChunkSet m_caps_joins; private: - std::vector m_edge_chunks, m_join_chunks, m_cap_chunks; - std::vector > m_join_ranges; - bool m_ignore_join_adds; + std::vector m_edge_chunks; }; /* Subset of a StrokedPath. Edges are to be placed into @@ -655,10 +341,7 @@ namespace m_non_closing_edge_chunk_cnt(0), m_closing_edge_vertex_cnt(0), m_closing_edge_index_cnt(0), - m_closing_edge_chunk_cnt(0), - m_non_closing_join_chunk_cnt(0), - m_closing_join_chunk_cnt(0), - m_cap_chunk_cnt(0) + m_closing_edge_chunk_cnt(0) {} unsigned int m_non_closing_edge_vertex_cnt; @@ -668,19 +351,12 @@ namespace unsigned int m_closing_edge_vertex_cnt; unsigned int m_closing_edge_index_cnt; unsigned int m_closing_edge_chunk_cnt; - - unsigned int m_non_closing_join_chunk_cnt; - unsigned int m_closing_join_chunk_cnt; - - unsigned int m_cap_chunk_cnt; }; static StrokedPathSubset* create(const SubEdgeCullingHierarchy *src, - CreationValues &out_values, - JoinOrdering &join_ordering, - CapOrdering &cap_ordering); + CreationValues &out_values); ~StrokedPathSubset(); @@ -694,7 +370,6 @@ namespace float item_space_additional_room, unsigned int max_attribute_cnt, unsigned int max_index_cnt, - bool take_joins_outside_of_region, ChunkSetPrivate &dst); bool @@ -729,44 +404,19 @@ namespace return m_closing_edges; } - const RangeAndChunk& - non_closing_joins(void) const - { - return m_non_closing_joins; - } - - const RangeAndChunk& - closing_joins(void) const - { - return m_closing_joins; - } - - const RangeAndChunk& - caps(void) const - { - return m_caps; - } - private: class PostProcessVariables { public: PostProcessVariables(void): m_edge_depth(0), - m_closing_edge_depth(0), - m_join_depth(0), - m_closing_join_depth(0), - m_cap_depth(0) + m_closing_edge_depth(0) {} unsigned int m_edge_depth, m_closing_edge_depth; - unsigned int m_join_depth, m_closing_join_depth; - unsigned int m_cap_depth; }; StrokedPathSubset(CreationValues &out_values, - JoinOrdering &join_ordering, - CapOrdering &cap_ordering, const SubEdgeCullingHierarchy *src); void @@ -790,24 +440,13 @@ namespace unsigned int &index_cnt); void post_process(PostProcessVariables &variables, - const CreationValues &constants, - JoinOrdering &join_ordering, - CapOrdering &cap_ordering); + const CreationValues &constants); fastuidraw::vecN m_children; - /* book keeping for edges. - */ + /* book keeping for edges. */ EdgeRanges m_non_closing_edges, m_closing_edges; - /* book keeping for joins - */ - RangeAndChunk m_non_closing_joins, m_closing_joins; - - /* book keeping for caps - */ - RangeAndChunk m_caps; - fastuidraw::BoundingBox m_bb; bool m_empty_subset; @@ -868,552 +507,68 @@ namespace unsigned int m_total_number_chunks; }; - class JoinCount + template + class PreparedAttributeData { public: - explicit - JoinCount(const ContourData &P); + PreparedAttributeData(void): + m_ready(false) + {} - unsigned int m_number_close_joins; - unsigned int m_number_non_close_joins; - }; + /* must be called before the first call to data(). + */ + void + mark_as_empty(void) + { + m_ready = true; + } - class CommonJoinData - { - public: - CommonJoinData(const fastuidraw::vec2 &p0, - const fastuidraw::vec2 &n0, - const fastuidraw::vec2 &p1, - const fastuidraw::vec2 &n1, - float distance_from_edge_start, - float distance_from_contour_start, - float edge_length, - float open_contour_length, - float closed_contour_length); - - float m_det, m_lambda; - fastuidraw::vec2 m_p0, m_v0, m_n0; - fastuidraw::vec2 m_p1, m_v1, m_n1; - float m_distance_from_edge_start; - float m_distance_from_contour_start; - float m_edge_length; - float m_open_contour_length; - float m_closed_contour_length; + const fastuidraw::PainterAttributeData& + data(const StrokedPathSubset *st) + { + if (!m_ready) + { + m_data.set_data(T(st)); + m_ready = true; + } + return m_data; + } - static - float - compute_lambda(const fastuidraw::vec2 &n0, const fastuidraw::vec2 &n1); + private: + fastuidraw::PainterAttributeData m_data; + bool m_ready; }; - class JoinCreatorBase:public fastuidraw::PainterAttributeDataFiller + class StrokedPathPrivate:fastuidraw::noncopyable { public: explicit - JoinCreatorBase(const PathData &P, - const StrokedPathSubset *st); - - virtual - ~JoinCreatorBase() {} - - virtual - void - compute_sizes(unsigned int &num_attributes, - unsigned int &num_indices, - unsigned int &num_attribute_chunks, - unsigned int &num_index_chunks, - unsigned int &number_z_ranges) const; - - virtual - void - fill_data(fastuidraw::c_array attribute_data, - fastuidraw::c_array index_data, - fastuidraw::c_array > attribute_chunks, - fastuidraw::c_array > index_chunks, - fastuidraw::c_array > zranges, - fastuidraw::c_array index_adjusts) const; - - protected: - - void - post_ctor_initalize(void); - - private: + StrokedPathPrivate(const fastuidraw::TessellatedPath &P, + const fastuidraw::StrokedCapsJoins::Builder &b); + ~StrokedPathPrivate(); void - fill_join(unsigned int join_id, - const PerEdgeData &into_join, - const PerEdgeData &leaving_join, - unsigned int chunk, unsigned int depth, - fastuidraw::c_array pts, - fastuidraw::c_array indices, - unsigned int &vertex_offset, unsigned int &index_offset, - fastuidraw::c_array > attribute_chunks, - fastuidraw::c_array > index_chunks, - fastuidraw::c_array > zranges, - fastuidraw::c_array index_adjusts, - fastuidraw::c_array > join_vertex_ranges, - fastuidraw::c_array > join_index_ranges) const; + create_edges(const fastuidraw::TessellatedPath &P); static void - set_chunks(const StrokedPathSubset *st, - fastuidraw::c_array > join_vertex_ranges, - fastuidraw::c_array > join_index_ranges, - fastuidraw::c_array attribute_data, - fastuidraw::c_array index_data, - fastuidraw::c_array > attribute_chunks, - fastuidraw::c_array > index_chunks, - fastuidraw::c_array > zranges, - fastuidraw::c_array index_adjusts); + ready_builder(const fastuidraw::TessellatedPath *tess, + fastuidraw::StrokedCapsJoins::Builder &b); static void - process_chunk(const RangeAndChunk &ch, - fastuidraw::c_array > join_vertex_ranges, - fastuidraw::c_array > join_index_ranges, - fastuidraw::c_array attribute_data, - fastuidraw::c_array index_data, - fastuidraw::c_array > attribute_chunks, - fastuidraw::c_array > index_chunks, - fastuidraw::c_array > zranges, - fastuidraw::c_array index_adjusts); + ready_builder_contour(const fastuidraw::TessellatedPath *tess, + unsigned int contour, + fastuidraw::StrokedCapsJoins::Builder &b); - virtual - void - add_join(unsigned int join_id, - const PerEdgeData &into_join, - const PerEdgeData &leaving_join, - unsigned int &vert_count, unsigned int &index_count) const = 0; + fastuidraw::StrokedCapsJoins m_caps_joins; + StrokedPathSubset* m_subset; + fastuidraw::PainterAttributeData m_edges; - virtual - void - fill_join_implement(unsigned int join_id, - const PerEdgeData &into_join, - const PerEdgeData &leaving_join, - fastuidraw::c_array pts, - unsigned int depth, - fastuidraw::c_array indices, - unsigned int &vertex_offset, unsigned int &index_offset) const = 0; - - const ContourData &m_P; - const JoinOrdering &m_ordering; - const StrokedPathSubset *m_st; - unsigned int m_num_verts, m_num_indices, m_num_chunks, m_num_joins; - bool m_post_ctor_initalized_called; - }; - - - class RoundedJoinCreator:public JoinCreatorBase - { - public: - RoundedJoinCreator(const PathData &P, - const StrokedPathSubset *st, - float thresh); - - private: - - class PerJoinData:public CommonJoinData - { - public: - PerJoinData(const fastuidraw::TessellatedPath::point &p0, - const fastuidraw::TessellatedPath::point &p1, - const fastuidraw::vec2 &n0_from_stroking, - const fastuidraw::vec2 &n1_from_stroking, - float thresh); - - void - add_data(unsigned int depth, - fastuidraw::c_array pts, - unsigned int &vertex_offset, - fastuidraw::c_array indices, - unsigned int &index_offset) const; - - std::complex m_arc_start; - float m_delta_theta; - unsigned int m_num_arc_points; - }; - - virtual - void - add_join(unsigned int join_id, - const PerEdgeData &into_join, - const PerEdgeData &leaving_join, - unsigned int &vert_count, unsigned int &index_count) const; - - virtual - void - fill_join_implement(unsigned int join_id, - const PerEdgeData &into_join, - const PerEdgeData &leaving_join, - fastuidraw::c_array pts, - unsigned int depth, - fastuidraw::c_array indices, - unsigned int &vertex_offset, unsigned int &index_offset) const; - - float m_thresh; - mutable std::vector m_per_join_data; - }; - - class BevelJoinCreator:public JoinCreatorBase - { - public: - explicit - BevelJoinCreator(const PathData &P, - const StrokedPathSubset *st); - - private: - - virtual - void - add_join(unsigned int join_id, - const PerEdgeData &into_join, - const PerEdgeData &leaving_join, - unsigned int &vert_count, unsigned int &index_count) const; - - virtual - void - fill_join_implement(unsigned int join_id, - const PerEdgeData &into_join, - const PerEdgeData &leaving_join, - fastuidraw::c_array pts, - unsigned int depth, - fastuidraw::c_array indices, - unsigned int &vertex_offset, unsigned int &index_offset) const; - - mutable std::vector m_n0, m_n1; - }; - - class MiterClipJoinCreator:public JoinCreatorBase - { - public: - explicit - MiterClipJoinCreator(const PathData &P, - const StrokedPathSubset *st); - - private: - virtual - void - add_join(unsigned int join_id, - const PerEdgeData &into_join, - const PerEdgeData &leaving_join, - unsigned int &vert_count, unsigned int &index_count) const; - - virtual - void - fill_join_implement(unsigned int join_id, - const PerEdgeData &into_join, - const PerEdgeData &leaving_join, - fastuidraw::c_array pts, - unsigned int depth, - fastuidraw::c_array indices, - unsigned int &vertex_offset, unsigned int &index_offset) const; - - mutable std::vector m_n0, m_n1; - }; - - template - class MiterJoinCreator:public JoinCreatorBase - { - public: - explicit - MiterJoinCreator(const PathData &P, - const StrokedPathSubset *st); - - private: - virtual - void - add_join(unsigned int join_id, - const PerEdgeData &into_join, - const PerEdgeData &leaving_join, - unsigned int &vert_count, unsigned int &index_count) const; - - virtual - void - fill_join_implement(unsigned int join_id, - const PerEdgeData &into_join, - const PerEdgeData &leaving_join, - fastuidraw::c_array pts, - unsigned int depth, - fastuidraw::c_array indices, - unsigned int &vertex_offset, unsigned int &index_offset) const; - - mutable std::vector m_n0, m_n1; - }; - - class PointIndexCapSize - { - public: - PointIndexCapSize(void): - m_verts(0), - m_indices(0) - {} - - unsigned int m_verts, m_indices; - }; - - class CommonCapData - { - public: - CommonCapData(bool is_start_cap, - const fastuidraw::vec2 &src_pt, - const fastuidraw::vec2 &normal_from_stroking): - m_is_start_cap(is_start_cap), - m_lambda((is_start_cap) ? -1.0f : 1.0f), - m_p(src_pt), - m_n(normal_from_stroking) - { - //caps at the start are on the "other side" - m_v = fastuidraw::vec2(m_n.y(), -m_n.x()); - m_v *= m_lambda; - m_n *= m_lambda; - } - - bool m_is_start_cap; - float m_lambda; - fastuidraw::vec2 m_p, m_n, m_v; - }; - - class CapCreatorBase:public fastuidraw::PainterAttributeDataFiller - { - public: - CapCreatorBase(const PathData &P, - const StrokedPathSubset *st, - PointIndexCapSize sz); - - virtual - ~CapCreatorBase() - {} - - virtual - void - compute_sizes(unsigned int &num_attributes, - unsigned int &num_indices, - unsigned int &num_attribute_chunks, - unsigned int &num_index_chunks, - unsigned int &number_z_ranges) const; - - virtual - void - fill_data(fastuidraw::c_array attribute_data, - fastuidraw::c_array index_data, - fastuidraw::c_array > attribute_chunks, - fastuidraw::c_array > index_chunks, - fastuidraw::c_array > zranges, - fastuidraw::c_array index_adjusts) const; - - private: - virtual - void - add_cap(const fastuidraw::vec2 &normal_from_stroking, - bool is_starting_cap, unsigned int depth, - const fastuidraw::TessellatedPath::point &p0, - fastuidraw::c_array pts, - fastuidraw::c_array indices, - unsigned int &vertex_offset, - unsigned int &index_offset) const = 0; - - static - void - set_chunks(const StrokedPathSubset *st, - fastuidraw::c_array > cap_vertex_ranges, - fastuidraw::c_array > cap_index_ranges, - fastuidraw::c_array attribute_data, - fastuidraw::c_array index_data, - fastuidraw::c_array > attribute_chunks, - fastuidraw::c_array > index_chunks, - fastuidraw::c_array > zranges, - fastuidraw::c_array index_adjusts); - - const ContourData &m_P; - const CapOrdering &m_ordering; - const StrokedPathSubset *m_st; - - unsigned int m_num_chunks; - PointIndexCapSize m_size; - }; - - class RoundedCapCreator:public CapCreatorBase - { - public: - RoundedCapCreator(const PathData &P, - const StrokedPathSubset *st, - float thresh); - - private: - static - PointIndexCapSize - compute_size(const ContourData &P, float thresh); - - virtual - void - add_cap(const fastuidraw::vec2 &normal_from_stroking, - bool is_starting_cap, unsigned int depth, - const fastuidraw::TessellatedPath::point &p0, - fastuidraw::c_array pts, - fastuidraw::c_array indices, - unsigned int &vertex_offset, - unsigned int &index_offset) const; - - float m_delta_theta; - unsigned int m_num_arc_points_per_cap; - }; - - class SquareCapCreator:public CapCreatorBase - { - public: - explicit - SquareCapCreator(const PathData &P, - const StrokedPathSubset *st): - CapCreatorBase(P, st, compute_size(P.m_contour_data)) - {} - - private: - static - PointIndexCapSize - compute_size(const ContourData &P); - - void - add_cap(const fastuidraw::vec2 &normal_from_stroking, - bool is_starting_cap, unsigned int depth, - const fastuidraw::TessellatedPath::point &p0, - fastuidraw::c_array pts, - fastuidraw::c_array indices, - unsigned int &vertex_offset, - unsigned int &index_offset) const; - }; - - class AdjustableCapCreator:public CapCreatorBase - { - public: - AdjustableCapCreator(const PathData &P, - const StrokedPathSubset *st): - CapCreatorBase(P, st, compute_size(P.m_contour_data)) - {} - - private: - enum - { - number_points_per_fan = 6, - number_triangles_per_fan = number_points_per_fan - 2, - number_indices_per_fan = 3 * number_triangles_per_fan, - }; - - static - void - pack_fan(bool entering_contour, - enum fastuidraw::StrokedPath::point::offset_type_t type, - const fastuidraw::TessellatedPath::point &edge_pt, - const fastuidraw::vec2 &stroking_normal, - unsigned int depth, - fastuidraw::c_array pts, - unsigned int &vertex_offset, - fastuidraw::c_array indices, - unsigned int &index_offset); - - static - PointIndexCapSize - compute_size(const ContourData &P); - - void - add_cap(const fastuidraw::vec2 &normal_from_stroking, - bool is_starting_cap, unsigned int depth, - const fastuidraw::TessellatedPath::point &p0, - fastuidraw::c_array pts, - fastuidraw::c_array indices, - unsigned int &vertex_offset, - unsigned int &index_offset) const; - }; - - class ThreshWithData - { - public: - ThreshWithData(void): - m_data(nullptr), - m_thresh(-1) - {} - - ThreshWithData(fastuidraw::PainterAttributeData *d, float t): - m_data(d), m_thresh(t) - {} - - static - bool - reverse_compare_against_thresh(const ThreshWithData &lhs, float rhs) - { - return lhs.m_thresh > rhs; - } - - fastuidraw::PainterAttributeData *m_data; - float m_thresh; - }; - - template - class PreparedAttributeData - { - public: - PreparedAttributeData(void): - m_ready(false) - {} - - /* must be called before the first call to data(). - */ - void - mark_as_empty(void) - { - m_ready = true; - } - - const fastuidraw::PainterAttributeData& - data(const PathData &P, const StrokedPathSubset *st) - { - if (!m_ready) - { - m_data.set_data(T(P, st)); - m_ready = true; - } - return m_data; - } - - private: - fastuidraw::PainterAttributeData m_data; - bool m_ready; - }; - - class StrokedPathPrivate:fastuidraw::noncopyable - { - public: - explicit - StrokedPathPrivate(const fastuidraw::TessellatedPath &P); - ~StrokedPathPrivate(); - - void - create_edges(const fastuidraw::TessellatedPath &P); - - template - const fastuidraw::PainterAttributeData& - fetch_create(float thresh, - std::vector &values); - - StrokedPathSubset* m_subset; - fastuidraw::PainterAttributeData m_edges; - - PreparedAttributeData m_bevel_joins; - PreparedAttributeData m_miter_clip_joins; - PreparedAttributeData > m_miter_joins; - PreparedAttributeData > m_miter_bevel_joins; - PreparedAttributeData m_square_caps; - PreparedAttributeData m_adjustable_caps; - - PathData m_path_data; - fastuidraw::vecN m_chunk_of_joins; - fastuidraw::vecN m_chunk_of_edges; - unsigned int m_chunk_of_caps; - - std::vector m_rounded_joins; - std::vector m_rounded_caps; - - bool m_empty_path; - fastuidraw::PainterAttributeData m_empty_data; + fastuidraw::vecN m_chunk_of_edges; + + bool m_empty_path; + fastuidraw::PainterAttributeData m_empty_data; }; } @@ -1482,102 +637,46 @@ const float SubEdgeCullingHierarchy::sm_mag_tol = 0.000001f; SubEdgeCullingHierarchy* SubEdgeCullingHierarchy:: -create(const fastuidraw::TessellatedPath &P, ContourData &path_data) +create(const fastuidraw::TessellatedPath &P) { std::vector data; - std::vector joins; - std::vector caps; fastuidraw::BoundingBox bx; - unsigned int num_non_closing_edges, num_non_closing_joins; + unsigned int num_non_closing_edges; SubEdgeCullingHierarchy *return_value; - create_lists(P, path_data, - data, num_non_closing_edges, bx, - joins, num_non_closing_joins, caps); - return_value = FASTUIDRAWnew SubEdgeCullingHierarchy(bx, data, num_non_closing_edges, - joins, num_non_closing_joins, caps); + create_lists(P, data, num_non_closing_edges, bx); + return_value = FASTUIDRAWnew SubEdgeCullingHierarchy(bx, data, num_non_closing_edges); return return_value; } void SubEdgeCullingHierarchy:: -create_lists(const fastuidraw::TessellatedPath &P, ContourData &path_data, +create_lists(const fastuidraw::TessellatedPath &P, std::vector &data, unsigned int &num_non_closing_edges, - fastuidraw::BoundingBox &bx, - std::vector &joins, unsigned int &num_non_closing_joins, - std::vector &caps) + fastuidraw::BoundingBox &bx) { - path_data.m_per_contour_data.resize(P.number_contours()); - - /* place data of closing sub-edges at the tail of the lists. - */ + /* place data of closing sub-edges at the tail of the lists. */ for(unsigned int o = 0; o < P.number_contours(); ++o) { - path_data.m_per_contour_data[o].m_edge_data_store.resize(P.number_edges(o)); - path_data.m_per_contour_data[o].m_start_contour_pt = P.unclosed_contour_point_data(o).front(); - path_data.m_per_contour_data[o].m_end_contour_pt = P.unclosed_contour_point_data(o).back(); for(unsigned int e = 0, ende = P.number_edges(o); e + 1 < ende; ++e) { - process_edge(P, path_data, o, e, data, bx); - - if (e + 2 != ende) - { - JoinSource J; - J.m_contour = o; - J.m_edge_going_into_join = e; - J.m_of_closing_edge = false; - J.m_pt = path_data.m_per_contour_data[o].m_edge_data_store[e].m_end_pt.m_p; - joins.push_back(J); - } + process_edge(P, o, e, data, bx); } } num_non_closing_edges = data.size(); - num_non_closing_joins = joins.size(); - for(unsigned int o = 0; o < P.number_contours(); ++o) { if (P.number_edges(o) > 0) { - process_edge(P, path_data, o, P.number_edges(o) - 1, data, bx); - - if (P.number_edges(o) >= 2) - { - JoinSource J; - unsigned int e; - - e = P.number_edges(o) - 2; - J.m_contour = o; - J.m_edge_going_into_join = e; - J.m_of_closing_edge = true; - J.m_pt = path_data.m_per_contour_data[o].m_edge_data_store[e].m_end_pt.m_p; - joins.push_back(J); - - e = P.number_edges(o) - 1; - J.m_contour = o; - J.m_edge_going_into_join = e; - J.m_of_closing_edge = true; - J.m_pt = path_data.m_per_contour_data[o].m_edge_data_store[e].m_end_pt.m_p; - joins.push_back(J); - } + process_edge(P, o, P.number_edges(o) - 1, data, bx); } - - CapSource C0, C1; - C0.m_contour = o; - C0.m_is_start_cap = true; - C0.m_pt = P.unclosed_contour_point_data(o).front().m_p; - caps.push_back(C0); - - C1.m_contour = o; - C1.m_is_start_cap = false; - C1.m_pt = P.unclosed_contour_point_data(o).back().m_p; - caps.push_back(C1); } } void SubEdgeCullingHierarchy:: -process_edge(const fastuidraw::TessellatedPath &P, ContourData &path_data, +process_edge(const fastuidraw::TessellatedPath &P, unsigned int contour, unsigned int edge, std::vector &dst, fastuidraw::BoundingBox &bx) { @@ -1611,16 +710,10 @@ process_edge(const fastuidraw::TessellatedPath &P, ContourData &path_data, sub_edge.m_bevel_lambda = 0.0f; sub_edge.m_has_bevel = false; sub_edge.m_bevel_normal = fastuidraw::vec2(0.0f, 0.0f); - path_data.m_per_contour_data[contour].write_edge_data(edge).m_begin_normal = normal; - path_data.m_per_contour_data[contour].write_edge_data(edge).m_start_pt = src_pts[i]; - if (edge == 0) - { - path_data.m_per_contour_data[contour].m_begin_cap_normal = normal; - } } else { - sub_edge.m_bevel_lambda = CommonJoinData::compute_lambda(last_normal, normal); + sub_edge.m_bevel_lambda = compute_lambda(last_normal, normal); sub_edge.m_has_bevel = true; sub_edge.m_bevel_normal = last_normal; } @@ -1637,23 +730,6 @@ process_edge(const fastuidraw::TessellatedPath &P, ContourData &path_data, last_normal = normal; } - - if (R.m_begin + 1 >= R.m_end) - { - path_data.m_per_contour_data[contour].write_edge_data(edge).m_begin_normal = normal; - path_data.m_per_contour_data[contour].write_edge_data(edge).m_start_pt = src_pts[R.m_begin]; - if (edge == 0) - { - path_data.m_per_contour_data[contour].m_begin_cap_normal = normal; - } - } - - path_data.m_per_contour_data[contour].write_edge_data(edge).m_end_normal = normal; - path_data.m_per_contour_data[contour].write_edge_data(edge).m_end_pt = src_pts[R.m_end - 1]; - if (edge + 2 == P.number_edges(contour)) - { - path_data.m_per_contour_data[contour].m_end_cap_normal = normal; - } } template @@ -1683,9 +759,7 @@ check_closing_at_end(const std::vector &data, unsigned int num_non_closing) SubEdgeCullingHierarchy:: SubEdgeCullingHierarchy(const fastuidraw::BoundingBox &start_box, - std::vector &edges, unsigned int num_non_closing_edges, - std::vector &joins, unsigned int num_non_closing_joins, - std::vector &caps): + std::vector &edges, unsigned int num_non_closing_edges): m_children(nullptr, nullptr), m_bb(start_box) { @@ -1694,36 +768,15 @@ SubEdgeCullingHierarchy(const fastuidraw::BoundingBox &start_box, FASTUIDRAWassert(!start_box.empty()); check_closing_at_end(edges, num_non_closing_edges); - check_closing_at_end(joins, num_non_closing_joins); c = choose_splitting_coordinate(start_box, fastuidraw::make_c_array(edges), mid_point); if (c != -1) { fastuidraw::vecN, 2> child_boxes; fastuidraw::vecN, 2> child_sub_edges; - fastuidraw::vecN, 2> child_joins; - fastuidraw::vecN, 2> child_caps; fastuidraw::vecN child_num_non_closing_edges(0, 0); fastuidraw::vecN child_num_non_closing_joins(0, 0); - for(const JoinSource &J : joins) - { - bool s; - s = J.m_pt[c] < mid_point; - child_joins[s].push_back(J); - if (!J.m_of_closing_edge) - { - ++child_num_non_closing_joins[s]; - } - } - - for(const CapSource &C : caps) - { - bool s; - s = C.m_pt[c] < mid_point; - child_caps[s].push_back(C); - } - for(const SingleSubEdge &sub_edge : edges) { bool sA, sB; @@ -1758,21 +811,13 @@ SubEdgeCullingHierarchy(const fastuidraw::BoundingBox &start_box, } } - m_children[0] = FASTUIDRAWnew SubEdgeCullingHierarchy(child_boxes[0], - child_sub_edges[0], child_num_non_closing_edges[0], - child_joins[0], child_num_non_closing_joins[0], - child_caps[0]); - m_children[1] = FASTUIDRAWnew SubEdgeCullingHierarchy(child_boxes[1], - child_sub_edges[1], child_num_non_closing_edges[1], - child_joins[1], child_num_non_closing_joins[1], - child_caps[1]); + m_children[0] = FASTUIDRAWnew SubEdgeCullingHierarchy(child_boxes[0], child_sub_edges[0], child_num_non_closing_edges[0]); + m_children[1] = FASTUIDRAWnew SubEdgeCullingHierarchy(child_boxes[1], child_sub_edges[1], child_num_non_closing_edges[1]); } else { /* steal the data */ - m_caps.swap(caps); m_sub_edges.init(edges, num_non_closing_edges); - m_joins.init(joins, num_non_closing_joins); } } @@ -1874,24 +919,19 @@ StrokedPathSubset:: StrokedPathSubset* StrokedPathSubset:: create(const SubEdgeCullingHierarchy *src, - CreationValues &out_values, - JoinOrdering &join_ordering, - CapOrdering &cap_ordering) + CreationValues &out_values) { StrokedPathSubset *return_value; PostProcessVariables vars; - return_value = FASTUIDRAWnew StrokedPathSubset(out_values, join_ordering, cap_ordering, src); - return_value->post_process(vars, out_values, join_ordering, cap_ordering); + return_value = FASTUIDRAWnew StrokedPathSubset(out_values, src); + return_value->post_process(vars, out_values); return return_value; } void StrokedPathSubset:: -post_process(PostProcessVariables &variables, - const CreationValues &constants, - JoinOrdering &join_ordering, - CapOrdering &cap_ordering) +post_process(PostProcessVariables &variables, const CreationValues &constants) { /* We want the depth to go in the reverse order as the * draw order. The Draw order is child(0), child(1) @@ -1900,11 +940,6 @@ post_process(PostProcessVariables &variables, m_non_closing_edges.m_depth_range.m_begin = variables.m_edge_depth; m_closing_edges.m_depth_range.m_begin = variables.m_closing_edge_depth; - m_non_closing_joins.m_depth_range.m_begin = variables.m_join_depth; - m_closing_joins.m_depth_range.m_begin = variables.m_closing_join_depth; - - m_caps.m_depth_range.m_begin = variables.m_cap_depth; - if (have_children()) { FASTUIDRAWassert(m_children[0] != nullptr); @@ -1912,8 +947,8 @@ post_process(PostProcessVariables &variables, FASTUIDRAWassert(m_non_closing_edges.m_src.empty()); FASTUIDRAWassert(m_closing_edges.m_src.empty()); - m_children[1]->post_process(variables, constants, join_ordering, cap_ordering); - m_children[0]->post_process(variables, constants, join_ordering, cap_ordering); + m_children[1]->post_process(variables, constants); + m_children[0]->post_process(variables, constants); } else { @@ -1922,27 +957,10 @@ post_process(PostProcessVariables &variables, variables.m_edge_depth += m_non_closing_edges.m_src.size(); variables.m_closing_edge_depth += m_closing_edges.m_src.size(); - - join_ordering.post_process_non_closing(m_non_closing_joins.m_elements, - variables.m_join_depth); - - join_ordering.post_process_closing(constants.m_non_closing_join_chunk_cnt, - m_closing_joins.m_elements, - variables.m_closing_join_depth); - - cap_ordering.post_process(m_caps.m_elements, variables.m_cap_depth); } m_non_closing_edges.m_depth_range.m_end = variables.m_edge_depth; m_closing_edges.m_depth_range.m_end = variables.m_closing_edge_depth; - m_non_closing_joins.m_depth_range.m_end = variables.m_join_depth; - m_closing_joins.m_depth_range.m_end = variables.m_closing_join_depth; - - m_caps.m_depth_range.m_end = variables.m_cap_depth; - - FASTUIDRAWassert(m_non_closing_joins.m_elements.difference() == m_non_closing_joins.m_depth_range.difference()); - FASTUIDRAWassert(m_closing_joins.m_elements.difference() == m_closing_joins.m_depth_range.difference()); - /* make the closing edge chunks start after the * non-closing edge chunks. */ @@ -1954,26 +972,12 @@ post_process(PostProcessVariables &variables, m_closing_edges.m_vertex_data_range += constants.m_non_closing_edge_vertex_cnt; m_closing_edges.m_index_data_range += constants.m_non_closing_edge_index_cnt; - /* the joins are ordered so that the joins of the non-closing - * edges appear first. - */ - m_closing_joins.m_elements += join_ordering.non_closing_edge().size(); - - /* make the chunks of closing edges come AFTER - * chunks of non-closing edge - */ - m_closing_joins.m_chunk += constants.m_non_closing_join_chunk_cnt; - m_empty_subset = !m_non_closing_edges.non_empty() - && !m_non_closing_joins.non_empty() - && !m_closing_edges.non_empty() - && !m_closing_joins.non_empty(); + && !m_closing_edges.non_empty(); } StrokedPathSubset:: -StrokedPathSubset(CreationValues &out_values, - JoinOrdering &join_ordering, CapOrdering &cap_ordering, - const SubEdgeCullingHierarchy *src): +StrokedPathSubset(CreationValues &out_values, const SubEdgeCullingHierarchy *src): m_children(nullptr, nullptr), m_bb(src->bounding_box()) { @@ -1987,19 +991,12 @@ StrokedPathSubset(CreationValues &out_values, m_closing_edges.m_vertex_data_range.m_begin = out_values.m_closing_edge_vertex_cnt; m_closing_edges.m_index_data_range.m_begin = out_values.m_closing_edge_index_cnt; - m_non_closing_joins.m_elements.m_begin = join_ordering.non_closing_edge().size(); - m_closing_joins.m_elements.m_begin = join_ordering.closing_edge().size(); - m_caps.m_elements.m_begin = cap_ordering.caps().size(); - if (src->has_children()) { - FASTUIDRAWassert(src->caps().empty()); - FASTUIDRAWassert(src->non_closing_joins().empty()); - FASTUIDRAWassert(src->closing_joins().empty()); for(unsigned int i = 0; i < 2; ++i) { FASTUIDRAWassert(src->child(i) != nullptr); - m_children[i] = FASTUIDRAWnew StrokedPathSubset(out_values, join_ordering, cap_ordering, src->child(i)); + m_children[i] = FASTUIDRAWnew StrokedPathSubset(out_values, src->child(i)); } } else @@ -2013,21 +1010,6 @@ StrokedPathSubset(CreationValues &out_values, increment_vertices_indices(m_closing_edges.m_src, out_values.m_closing_edge_vertex_cnt, out_values.m_closing_edge_index_cnt); - - for(const JoinSource &J : src->non_closing_joins()) - { - join_ordering.add_join(J, out_values.m_non_closing_join_chunk_cnt); - } - - for(const JoinSource &J : src->closing_joins()) - { - join_ordering.add_join(J, out_values.m_closing_join_chunk_cnt); - } - - for(const CapSource &C : src->caps()) - { - cap_ordering.add_cap(C, out_values.m_cap_chunk_cnt); - } } m_non_closing_edges.m_vertex_data_range.m_end = out_values.m_non_closing_edge_vertex_cnt; @@ -2038,19 +1020,8 @@ StrokedPathSubset(CreationValues &out_values, m_non_closing_edges.m_chunk = out_values.m_non_closing_edge_chunk_cnt; m_closing_edges.m_chunk = out_values.m_closing_edge_chunk_cnt; - m_non_closing_joins.m_elements.m_end = join_ordering.non_closing_edge().size(); - m_closing_joins.m_elements.m_end = join_ordering.closing_edge().size(); - m_caps.m_elements.m_end = cap_ordering.caps().size(); - - m_non_closing_joins.m_chunk = out_values.m_non_closing_join_chunk_cnt; - m_closing_joins.m_chunk = out_values.m_closing_join_chunk_cnt; - m_caps.m_chunk = out_values.m_cap_chunk_cnt; - ++out_values.m_non_closing_edge_chunk_cnt; ++out_values.m_closing_edge_chunk_cnt; - ++out_values.m_non_closing_join_chunk_cnt; - ++out_values.m_closing_join_chunk_cnt; - ++out_values.m_cap_chunk_cnt; } void @@ -2083,7 +1054,6 @@ compute_chunks(bool include_closing_edge, float item_space_additional_room, unsigned int max_attribute_cnt, unsigned int max_index_cnt, - bool take_joins_outside_of_region, ChunkSetPrivate &dst) { scratch.m_adjusted_clip_eqs.resize(clip_equations.size()); @@ -2106,15 +1076,6 @@ compute_chunks(bool include_closing_edge, } dst.reset(); - if (take_joins_outside_of_region) - { - dst.add_join_chunk(m_non_closing_joins); - if (include_closing_edge) - { - dst.add_join_chunk(m_closing_joins); - } - dst.ignore_join_adds(); - } compute_chunks_implement(include_closing_edge, scratch, item_space_additional_room, max_attribute_cnt, max_index_cnt, dst); @@ -2136,16 +1097,9 @@ compute_chunks_take_all(bool include_closing_edge, && (!include_closing_edge || m_closing_edges.chunk_fits(max_attribute_cnt, max_index_cnt))) { dst.add_edge_chunk(m_non_closing_edges); - dst.add_join_chunk(m_non_closing_joins); - if (include_closing_edge) { dst.add_edge_chunk(m_closing_edges); - dst.add_join_chunk(m_closing_joins); - } - else - { - dst.add_cap_chunk(m_caps); } } else if (have_children()) @@ -2178,8 +1132,7 @@ compute_chunks_implement(bool include_closing_edge, return; } - /* clip the bounding box of this StrokedPathSubset - */ + /* clip the bounding box of this StrokedPathSubset */ vecN bb; bool unclipped; @@ -2218,17 +1171,11 @@ compute_chunks_implement(bool include_closing_edge, { FASTUIDRAWassert(m_non_closing_edges.chunk_fits(max_attribute_cnt, max_index_cnt)); dst.add_edge_chunk(m_non_closing_edges); - dst.add_join_chunk(m_non_closing_joins); if (include_closing_edge) { FASTUIDRAWassert(m_closing_edges.chunk_fits(max_attribute_cnt, max_index_cnt)); dst.add_edge_chunk(m_closing_edges); - dst.add_join_chunk(m_closing_joins); - } - else - { - dst.add_cap_chunk(m_caps); } } } @@ -2347,8 +1294,8 @@ build_chunk(const EdgeRanges &edge, { for(unsigned int v = edge.m_vertex_data_range.m_begin; v < edge.m_vertex_data_range.m_end; ++v) { - fastuidraw::StrokedPath::point P; - fastuidraw::StrokedPath::point::unpack_point(&P, attribute_data[v]); + fastuidraw::StrokedPoint P; + fastuidraw::StrokedPoint::unpack_point(&P, attribute_data[v]); FASTUIDRAWassert(P.depth() >= edge.m_depth_range.m_begin); FASTUIDRAWassert(P.depth() < edge.m_depth_range.m_end); } @@ -2365,7 +1312,7 @@ process_sub_edge(const SingleSubEdge &sub_edge, unsigned int depth, { const int boundary_values[3] = { 1, 1, 0 }; const float normal_sign[3] = { 1.0f, -1.0f, 0.0f }; - fastuidraw::vecN pts; + fastuidraw::vecN pts; if (sub_edge.m_has_bevel) { @@ -2386,20 +1333,20 @@ process_sub_edge(const SingleSubEdge &sub_edge, unsigned int depth, } pts[0].m_pre_offset = fastuidraw::vec2(0.0f, 0.0f); - pts[0].m_packed_data = pack_data(0, fastuidraw::StrokedPath::point::offset_start_sub_edge, depth) - | fastuidraw::StrokedPath::point::bevel_edge_mask; + pts[0].m_packed_data = pack_data(0, fastuidraw::StrokedPoint::offset_start_sub_edge, depth) + | fastuidraw::StrokedPoint::bevel_edge_mask; pts[1].m_pre_offset = sub_edge.m_bevel_lambda * sub_edge.m_bevel_normal; - pts[1].m_packed_data = pack_data(1, fastuidraw::StrokedPath::point::offset_start_sub_edge, depth) - | fastuidraw::StrokedPath::point::bevel_edge_mask; + pts[1].m_packed_data = pack_data(1, fastuidraw::StrokedPoint::offset_start_sub_edge, depth) + | fastuidraw::StrokedPoint::bevel_edge_mask; pts[2].m_pre_offset = sub_edge.m_bevel_lambda * sub_edge.m_normal; - pts[2].m_packed_data = pack_data(1, fastuidraw::StrokedPath::point::offset_start_sub_edge, depth) - | fastuidraw::StrokedPath::point::bevel_edge_mask; + pts[2].m_packed_data = pack_data(1, fastuidraw::StrokedPoint::offset_start_sub_edge, depth) + | fastuidraw::StrokedPoint::bevel_edge_mask; for(unsigned int i = 0; i < 3; ++i) { - pts[i].fastuidraw::StrokedPath::point::pack_point(&attribute_data[vert_offset + i]); + pts[i].fastuidraw::StrokedPoint::pack_point(&attribute_data[vert_offset + i]); } vert_offset += 3; @@ -2428,7 +1375,7 @@ process_sub_edge(const SingleSubEdge &sub_edge, unsigned int depth, pts[k].m_pre_offset = normal_sign[k] * sub_edge.m_normal; pts[k].m_auxiliary_offset = sub_edge.m_delta; pts[k].m_packed_data = pack_data(boundary_values[k], - fastuidraw::StrokedPath::point::offset_start_sub_edge, + fastuidraw::StrokedPoint::offset_start_sub_edge, depth); pts[k + 3].m_position = sub_edge.m_pt1.m_pt; @@ -2440,13 +1387,13 @@ process_sub_edge(const SingleSubEdge &sub_edge, unsigned int depth, pts[k + 3].m_pre_offset = normal_sign[k] * sub_edge.m_normal; pts[k + 3].m_auxiliary_offset = -sub_edge.m_delta; pts[k + 3].m_packed_data = pack_data(boundary_values[k], - fastuidraw::StrokedPath::point::offset_end_sub_edge, + fastuidraw::StrokedPoint::offset_end_sub_edge, depth); } for(unsigned int i = 0; i < 6; ++i) { - pts[i].fastuidraw::StrokedPath::point::pack_point(&attribute_data[vert_offset + i]); + pts[i].fastuidraw::StrokedPoint::pack_point(&attribute_data[vert_offset + i]); } indices[index_offset + 0] = vert_offset + 0; @@ -2467,1863 +1414,257 @@ process_sub_edge(const SingleSubEdge &sub_edge, unsigned int depth, vert_offset += StrokedPathSubset::points_per_segment; } - -////////////////////////////////////////////// -// JoinCount methods -JoinCount:: -JoinCount(const ContourData &P): - m_number_close_joins(0), - m_number_non_close_joins(0) -{ - for(unsigned int o = 0; o < P.number_contours(); ++o) - { - if (P.number_edges(o) >= 2) - { - m_number_non_close_joins += P.number_edges(o) - 2; - m_number_close_joins += 2; - } - } -} - -//////////////////////////////////////////////// -// CommonJoinData methods -CommonJoinData:: -CommonJoinData(const fastuidraw::vec2 &p0, - const fastuidraw::vec2 &n0, - const fastuidraw::vec2 &p1, - const fastuidraw::vec2 &n1, - float distance_from_edge_start, - float distance_from_contour_start, - float edge_length, - float open_contour_length, - float closed_contour_length): - m_distance_from_edge_start(distance_from_edge_start), - m_distance_from_contour_start(distance_from_contour_start), - m_edge_length(edge_length), - m_open_contour_length(open_contour_length), - m_closed_contour_length(closed_contour_length) -{ - /* Explanation: - * We have two curves, a(t) and b(t) with a(1) = b(0) - * The point p0 represents the end of a(t) and the - * point p1 represents the start of b(t). - * - * When stroking we have four auxiliary curves: - * a0(t) = a(t) + w * a_n(t) - * a1(t) = a(t) - w * a_n(t) - * b0(t) = b(t) + w * b_n(t) - * b1(t) = b(t) - w * b_n(t) - * where - * w = width of stroking - * a_n(t) = J( a'(t) ) / || a'(t) || - * b_n(t) = J( b'(t) ) / || b'(t) || - * when - * J(x, y) = (-y, x). - * - * A Bevel join is a triangle that connects - * consists of p, A and B where p is a(1)=b(0), - * A is one of a0(1) or a1(1) and B is one - * of b0(0) or b1(0). Now if we use a0(1) for - * A then we will use b0(0) for B because - * the normals are generated the same way for - * a(t) and b(t). Then, the questions comes - * down to, do we wish to add or subtract the - * normal. That value is represented by m_lambda. - * - * Now to figure out m_lambda. Let q0 be a point - * on a(t) before p=a(1). The q0 is given by - * - * q0 = p - s * m_v0 - * - * and let q1 be a point on b(t) after p=b(0), - * - * q1 = p + t * m_v1 - * - * where both s, t are positive. Let - * - * z = (q0+q1) / 2 - * - * the point z is then on the side of the join - * of the acute angle of the join. - * - * With this in mind, if either of - * or is positive then we want - * to add by -w * n rather than w * n. - * - * Note that: - * - * = 0.5 * < -s * m_v0 + t * m_v1, m_n1 > - * = -0.5 * s * + 0.5 * t * - * = -0.5 * s * - * = -0.5 * s * - * - * and - * - * = 0.5 * < -s * m_v0 + t * m_v1, m_n0 > - * = -0.5 * s * + 0.5 * t * - * = 0.5 * t * - * = 0.5 * t * - * = -0.5 * t * - * - * (the last line because transpose(J) = -J). Notice - * that the sign of and the sign of - * is then the same. - * - * thus m_lambda is positive if is negative. - */ - m_p0 = p0; - m_n0 = n0; - m_v0 = fastuidraw::vec2(m_n0.y(), -m_n0.x()); - - m_p1 = p1; - m_n1 = n1; - m_v1 = fastuidraw::vec2(m_n1.y(), -m_n1.x()); - - m_det = fastuidraw::dot(m_v1, m_n0); - if (m_det > 0.0f) - { - m_lambda = -1.0f; - } - else - { - m_lambda = 1.0f; - } -} - -float -CommonJoinData:: -compute_lambda(const fastuidraw::vec2 &n0, const fastuidraw::vec2 &n1) +///////////////////////////////////////////// +// StrokedPathPrivate methods +StrokedPathPrivate:: +StrokedPathPrivate(const fastuidraw::TessellatedPath &P, + const fastuidraw::StrokedCapsJoins::Builder &b): + m_caps_joins(b), + m_subset(nullptr) { - fastuidraw::vec2 v1; - float d; - - v1 = fastuidraw::vec2(n1.y(), -n1.x()); - d = fastuidraw::dot(v1, n0); - if (d > 0.0f) + if (!P.point_data().empty()) { - return -1.0f; + m_empty_path = false; + create_edges(P); } else { - return 1.0f; + m_empty_path = true; + std::fill(m_chunk_of_edges.begin(), m_chunk_of_edges.end(), 0); } } -///////////////////////////////////////////////// -// JoinCreatorBase methods -JoinCreatorBase:: -JoinCreatorBase(const PathData &P, - const StrokedPathSubset *st): - m_P(P.m_contour_data), - m_ordering(P.m_join_ordering), - m_st(st), - m_num_verts(0), - m_num_indices(0), - m_num_chunks(P.m_number_join_chunks), - m_num_joins(0), - m_post_ctor_initalized_called(false) -{} - -void -JoinCreatorBase:: -post_ctor_initalize(void) +StrokedPathPrivate:: +~StrokedPathPrivate() { - FASTUIDRAWassert(!m_post_ctor_initalized_called); - m_post_ctor_initalized_called = true; - - for(const JoinSource &J : m_ordering.non_closing_edge()) - { - add_join(m_num_joins, - m_P.m_per_contour_data[J.m_contour].edge_data(J.m_edge_going_into_join), - m_P.m_per_contour_data[J.m_contour].edge_data(J.m_edge_going_into_join + 1), - m_num_verts, m_num_indices); - ++m_num_joins; - } - - for(const JoinSource &J : m_ordering.closing_edge()) + if (!m_empty_path) { - add_join(m_num_joins, - m_P.m_per_contour_data[J.m_contour].edge_data(J.m_edge_going_into_join), - m_P.m_per_contour_data[J.m_contour].edge_data(J.m_edge_going_into_join + 1), - m_num_verts, m_num_indices); - ++m_num_joins; + FASTUIDRAWdelete(m_subset); } } void -JoinCreatorBase:: -compute_sizes(unsigned int &num_attributes, - unsigned int &num_indices, - unsigned int &num_attribute_chunks, - unsigned int &num_index_chunks, - unsigned int &number_z_ranges) const -{ - FASTUIDRAWassert(m_post_ctor_initalized_called); - num_attributes = m_num_verts; - num_indices = m_num_indices; - number_z_ranges = num_attribute_chunks = num_index_chunks = m_num_chunks; -} - -void -JoinCreatorBase:: -fill_join(unsigned int join_id, - const PerEdgeData &into_join, - const PerEdgeData &leaving_join, - unsigned int chunk, unsigned int depth, - fastuidraw::c_array pts, - fastuidraw::c_array indices, - unsigned int &vertex_offset, unsigned int &index_offset, - fastuidraw::c_array > attribute_chunks, - fastuidraw::c_array > index_chunks, - fastuidraw::c_array > zranges, - fastuidraw::c_array index_adjusts, - fastuidraw::c_array > join_vertex_ranges, - fastuidraw::c_array > join_index_ranges) const -{ - unsigned int v(vertex_offset), i(index_offset); - - FASTUIDRAWassert(join_id < m_num_joins); - - fill_join_implement(join_id, into_join, leaving_join, - pts, depth, indices, vertex_offset, index_offset); - - join_vertex_ranges[join_id].m_begin = v; - join_vertex_ranges[join_id].m_end = vertex_offset; - - join_index_ranges[join_id].m_begin = i; - join_index_ranges[join_id].m_end = index_offset; - - attribute_chunks[chunk] = pts.sub_array(v, vertex_offset - v); - index_chunks[chunk] = indices.sub_array(i, index_offset - i); - index_adjusts[chunk] = -int(v); - zranges[chunk] = fastuidraw::range_type(depth, depth + 1); -} - -void -JoinCreatorBase:: -fill_data(fastuidraw::c_array attribute_data, - fastuidraw::c_array index_data, - fastuidraw::c_array > attribute_chunks, - fastuidraw::c_array > index_chunks, - fastuidraw::c_array > zranges, - fastuidraw::c_array index_adjusts) const +StrokedPathPrivate:: +ready_builder(const fastuidraw::TessellatedPath *tess, + fastuidraw::StrokedCapsJoins::Builder &b) { - unsigned int vertex_offset(0), index_offset(0), join_id(0); - std::vector > jvr(m_num_joins), jir(m_num_joins); - fastuidraw::c_array > join_vertex_ranges, join_index_ranges; - - join_vertex_ranges = fastuidraw::make_c_array(jvr); - join_index_ranges = fastuidraw::make_c_array(jir); - - FASTUIDRAWassert(attribute_data.size() == m_num_verts); - FASTUIDRAWassert(index_data.size() == m_num_indices); - FASTUIDRAWassert(attribute_chunks.size() == m_num_chunks); - FASTUIDRAWassert(index_chunks.size() == m_num_chunks); - FASTUIDRAWassert(zranges.size() == m_num_chunks); - FASTUIDRAWassert(index_adjusts.size() == m_num_chunks); - - /* Note that we reverse the the depth value, we need to do this because - * we want the joins draw first to obscure the joins drawn later. - */ - for(const OrderingEntry &J : m_ordering.non_closing_edge()) - { - fill_join(join_id, - m_P.m_per_contour_data[J.m_contour].edge_data(J.m_edge_going_into_join), - m_P.m_per_contour_data[J.m_contour].edge_data(J.m_edge_going_into_join + 1), - J.m_chunk, J.m_depth, - attribute_data, index_data, - vertex_offset, index_offset, - attribute_chunks, index_chunks, - zranges, index_adjusts, - join_vertex_ranges, - join_index_ranges); - ++join_id; - } - - for(const OrderingEntry &J : m_ordering.closing_edge()) + for(unsigned int c = 0; c < tess->number_contours(); ++c) { - fill_join(join_id, - m_P.m_per_contour_data[J.m_contour].edge_data(J.m_edge_going_into_join), - m_P.m_per_contour_data[J.m_contour].edge_data(J.m_edge_going_into_join + 1), - J.m_chunk, J.m_depth, - attribute_data, index_data, - vertex_offset, index_offset, - attribute_chunks, index_chunks, - zranges, index_adjusts, - join_vertex_ranges, - join_index_ranges); - ++join_id; + ready_builder_contour(tess, c, b); } - - FASTUIDRAWassert(vertex_offset == m_num_verts); - FASTUIDRAWassert(index_offset == m_num_indices); - set_chunks(m_st, - join_vertex_ranges, join_index_ranges, - attribute_data, index_data, - attribute_chunks, index_chunks, - zranges, index_adjusts); } void -JoinCreatorBase:: -set_chunks(const StrokedPathSubset *st, - fastuidraw::c_array > join_vertex_ranges, - fastuidraw::c_array > join_index_ranges, - fastuidraw::c_array attribute_data, - fastuidraw::c_array index_data, - fastuidraw::c_array > attribute_chunks, - fastuidraw::c_array > index_chunks, - fastuidraw::c_array > zranges, - fastuidraw::c_array index_adjusts) +StrokedPathPrivate:: +ready_builder_contour(const fastuidraw::TessellatedPath *tess, + unsigned int c, + fastuidraw::StrokedCapsJoins::Builder &b) { - process_chunk(st->non_closing_joins(), - join_vertex_ranges, join_index_ranges, - attribute_data, index_data, - attribute_chunks, index_chunks, - zranges, index_adjusts); - - process_chunk(st->closing_joins(), - join_vertex_ranges, join_index_ranges, - attribute_data, index_data, - attribute_chunks, index_chunks, - zranges, index_adjusts); - - if (st->have_children()) - { - set_chunks(st->child(0), - join_vertex_ranges, join_index_ranges, - attribute_data, index_data, - attribute_chunks, index_chunks, - zranges, index_adjusts); - - set_chunks(st->child(1), - join_vertex_ranges, join_index_ranges, - attribute_data, index_data, - attribute_chunks, index_chunks, - zranges, index_adjusts); - } -} + fastuidraw::c_array last_pts; + fastuidraw::vec2 delta, last_direction; + float delta_mag; + const float tol(0.000001f); -void -JoinCreatorBase:: -process_chunk(const RangeAndChunk &ch, - fastuidraw::c_array > join_vertex_ranges, - fastuidraw::c_array > join_index_ranges, - fastuidraw::c_array attribute_data, - fastuidraw::c_array index_data, - fastuidraw::c_array > attribute_chunks, - fastuidraw::c_array > index_chunks, - fastuidraw::c_array > zranges, - fastuidraw::c_array index_adjusts) -{ - unsigned int K; - fastuidraw::range_type vr, ir; + last_pts = tess->edge_point_data(c, 0); - if (ch.m_elements.m_begin < ch.m_elements.m_end) + delta = last_pts[1].m_p - last_pts[0].m_p; + delta_mag = delta.magnitude(); + if (delta_mag < tol) { - vr.m_begin = join_vertex_ranges[ch.m_elements.m_begin].m_begin; - vr.m_end = join_vertex_ranges[ch.m_elements.m_end - 1].m_end; - - ir.m_begin = join_index_ranges[ch.m_elements.m_begin].m_begin; - ir.m_end = join_index_ranges[ch.m_elements.m_end - 1].m_end; + delta = fastuidraw::vec2(1.0f, 0.0f); } else { - vr.m_begin = vr.m_end = 0; - ir.m_begin = ir.m_end = 0; + delta /= delta_mag; } + last_direction = delta; + b.begin_contour(last_pts[0].m_p, delta); - K = ch.m_chunk; - attribute_chunks[K] = attribute_data.sub_array(vr); - index_chunks[K] = index_data.sub_array(ir); - zranges[K] = fastuidraw::range_type(ch.m_depth_range.m_begin, ch.m_depth_range.m_end); - index_adjusts[K] = -int(vr.m_begin); -} - -///////////////////////////////////////////////// -// RoundedJoinCreator::PerJoinData methods -RoundedJoinCreator::PerJoinData:: -PerJoinData(const fastuidraw::TessellatedPath::point &p0, - const fastuidraw::TessellatedPath::point &p1, - const fastuidraw::vec2 &n0_from_stroking, - const fastuidraw::vec2 &n1_from_stroking, - float thresh): - CommonJoinData(p0.m_p, n0_from_stroking, p1.m_p, n1_from_stroking, - p0.m_distance_from_edge_start, p0.m_distance_from_contour_start, - p0.m_edge_length, p0.m_open_contour_length, p0.m_closed_contour_length) -{ - /* n0z represents the start point of the rounded join in the complex plane - * as if the join was at the origin, n1z represents the end point of the - * rounded join in the complex plane as if the join was at the origin. - */ - std::complex n0z(m_lambda * m_n0.x(), m_lambda * m_n0.y()); - std::complex n1z(m_lambda * m_n1.x(), m_lambda * m_n1.y()); - - /* n1z_times_conj_n0z satisfies: - * n1z = n1z_times_conj_n0z * n0z - * i.e. it represents the arc-movement from n0z to n1z - */ - std::complex n1z_times_conj_n0z(n1z * std::conj(n0z)); - - m_arc_start = n0z; - m_delta_theta = std::atan2(n1z_times_conj_n0z.imag(), n1z_times_conj_n0z.real()); - m_num_arc_points = fastuidraw::detail::number_segments_for_tessellation(m_delta_theta, thresh); - m_delta_theta /= static_cast(m_num_arc_points - 1); -} - -void -RoundedJoinCreator::PerJoinData:: -add_data(unsigned int depth, - fastuidraw::c_array pts, - unsigned int &vertex_offset, - fastuidraw::c_array indices, - unsigned int &index_offset) const -{ - unsigned int i, first; - float theta; - fastuidraw::StrokedPath::point pt; - - first = vertex_offset; - - pt.m_position = m_p0; - pt.m_pre_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_distance_from_edge_start = m_distance_from_edge_start; - pt.m_distance_from_contour_start = m_distance_from_contour_start; - pt.m_edge_length = m_edge_length; - pt.m_open_contour_length = m_open_contour_length; - pt.m_closed_contour_length = m_closed_contour_length; - pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_packed_data = pack_data_join(0, fastuidraw::StrokedPath::point::offset_shared_with_edge, depth); - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - pt.m_position = m_p0; - pt.m_pre_offset = m_lambda * m_n0; - pt.m_distance_from_edge_start = m_distance_from_edge_start; - pt.m_distance_from_contour_start = m_distance_from_contour_start; - pt.m_edge_length = m_edge_length; - pt.m_open_contour_length = m_open_contour_length; - pt.m_closed_contour_length = m_closed_contour_length; - pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_packed_data = pack_data_join(1, fastuidraw::StrokedPath::point::offset_shared_with_edge, depth); - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - for(i = 1, theta = m_delta_theta; i < m_num_arc_points - 1; ++i, theta += m_delta_theta, ++vertex_offset) + for(unsigned int e = 1, ende = tess->number_edges(c); e < ende; ++e) { - float t, c, s; - std::complex cs_as_complex; - - t = static_cast(i) / static_cast(m_num_arc_points - 1); - c = std::cos(theta); - s = std::sin(theta); - cs_as_complex = std::complex(c, s) * m_arc_start; - - pt.m_position = m_p0; - pt.m_pre_offset = m_lambda * fastuidraw::vec2(m_n0.x(), m_n1.x()); - pt.m_auxiliary_offset = fastuidraw::vec2(t, cs_as_complex.real()); - pt.m_distance_from_edge_start = m_distance_from_edge_start; - pt.m_distance_from_contour_start = m_distance_from_contour_start; - pt.m_edge_length = m_edge_length; - pt.m_open_contour_length = m_open_contour_length; - pt.m_closed_contour_length = m_closed_contour_length; - pt.m_packed_data = pack_data_join(1, fastuidraw::StrokedPath::point::offset_rounded_join, depth); - - if (m_lambda * m_n0.y() < 0.0f) + fastuidraw::c_array pts; + fastuidraw::vec2 delta_into, delta_leaving; + float mag; + + pts = tess->edge_point_data(c, e); + delta_into = last_pts[last_pts.size() - 1].m_p - last_pts[last_pts.size() - 2].m_p; + mag = delta_into.magnitude(); + if (mag < tol) { - pt.m_packed_data |= fastuidraw::StrokedPath::point::normal0_y_sign_mask; + delta_into = last_direction; } - - if (m_lambda * m_n1.y() < 0.0f) + else { - pt.m_packed_data |= fastuidraw::StrokedPath::point::normal1_y_sign_mask; + delta_into /= mag; + last_direction = delta_into; } - if (cs_as_complex.imag() < 0.0f) + delta_leaving = pts[1].m_p - pts[0].m_p; + mag = delta_leaving.magnitude(); + if (mag < tol) { - pt.m_packed_data |= fastuidraw::StrokedPath::point::sin_sign_mask; + delta_leaving = last_direction; + } + else + { + delta_leaving /= mag; + last_direction = delta_leaving; } - pt.pack_point(&pts[vertex_offset]); - } - pt.m_position = m_p1; - pt.m_pre_offset = m_lambda * m_n1; - pt.m_distance_from_edge_start = m_distance_from_edge_start; - pt.m_distance_from_contour_start = m_distance_from_contour_start; - pt.m_edge_length = m_edge_length; - pt.m_open_contour_length = m_open_contour_length; - pt.m_closed_contour_length = m_closed_contour_length; - pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_packed_data = pack_data_join(1, fastuidraw::StrokedPath::point::offset_shared_with_edge, depth); - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - add_triangle_fan(first, vertex_offset, indices, index_offset); -} + b.add_join(pts[0].m_p, + last_pts.back().m_edge_length, + delta_into, delta_leaving); + last_pts = pts; + } -/////////////////////////////////////////////////// -// RoundedJoinCreator methods -RoundedJoinCreator:: -RoundedJoinCreator(const PathData &P, - const StrokedPathSubset *st, - float thresh): - JoinCreatorBase(P, st), - m_thresh(thresh) -{ - JoinCount J(P.m_contour_data); - m_per_join_data.reserve(J.m_number_close_joins + J.m_number_non_close_joins); - post_ctor_initalize(); + delta = last_pts[last_pts.size() - 1].m_p - last_pts[last_pts.size() - 2].m_p; + delta_mag = delta.magnitude(); + if (delta_mag < tol) + { + delta = last_direction; + } + else + { + delta /= delta_mag; + } + b.end_contour(last_pts.back().m_edge_length, delta); } void -RoundedJoinCreator:: -add_join(unsigned int join_id, - const PerEdgeData &into_join, - const PerEdgeData &leaving_join, - unsigned int &vert_count, unsigned int &index_count) const +StrokedPathPrivate:: +create_edges(const fastuidraw::TessellatedPath &P) { - FASTUIDRAWunused(join_id); - PerJoinData J(into_join.m_end_pt, leaving_join.m_start_pt, - into_join.m_end_normal, leaving_join.m_begin_normal, - m_thresh); + SubEdgeCullingHierarchy *s; + StrokedPathSubset::CreationValues cnts; - m_per_join_data.push_back(J); + FASTUIDRAWassert(!m_empty_path); + s = SubEdgeCullingHierarchy::create(P); + m_subset = StrokedPathSubset::create(s, cnts); + m_edges.set_data(EdgeAttributeFiller(m_subset, P, cnts)); - /* a triangle fan centered at p0 = p1 with - * m_num_arc_points along an edge + /* the chunks of the root element have the data for everything. */ - vert_count += (1 + J.m_num_arc_points); - index_count += 3 * (J.m_num_arc_points - 1); -} + m_chunk_of_edges[fastuidraw::StrokedPath::all_non_closing] = m_subset->non_closing_edges().m_chunk; + m_chunk_of_edges[fastuidraw::StrokedPath::all_closing] = m_subset->closing_edges().m_chunk; + FASTUIDRAWdelete(s); +} -void -RoundedJoinCreator:: -fill_join_implement(unsigned int join_id, - const PerEdgeData &into_join, - const PerEdgeData &leaving_join, - fastuidraw::c_array pts, - unsigned int depth, - fastuidraw::c_array indices, - unsigned int &vertex_offset, unsigned int &index_offset) const +////////////////////////////////////////////// +// fastuidraw::StrokedPath::ScratchSpace methods +fastuidraw::StrokedPath::ScratchSpace:: +ScratchSpace(void) { - FASTUIDRAWunused(into_join); - FASTUIDRAWunused(leaving_join); - FASTUIDRAWassert(join_id < m_per_join_data.size()); - m_per_join_data[join_id].add_data(depth, pts, vertex_offset, indices, index_offset); + m_d = FASTUIDRAWnew ScratchSpacePrivate(); } -/////////////////////////////////////////////////// -// BevelJoinCreator methods -BevelJoinCreator:: -BevelJoinCreator(const PathData &P, - const StrokedPathSubset *st): - JoinCreatorBase(P, st) +fastuidraw::StrokedPath::ScratchSpace:: +~ScratchSpace(void) { - post_ctor_initalize(); + ScratchSpacePrivate *d; + d = static_cast(m_d); + FASTUIDRAWdelete(d); + m_d = nullptr; } +/////////////////////////////////////// +// ChunkSetPrivate methods void -BevelJoinCreator:: -add_join(unsigned int join_id, - const PerEdgeData &into_join, - const PerEdgeData &leaving_join, - unsigned int &vert_count, unsigned int &index_count) const +ChunkSetPrivate:: +add_edge_chunk(const EdgeRanges &ed) { - FASTUIDRAWunused(join_id); - - /* one triangle per bevel join - */ - vert_count += 3; - index_count += 3; - - m_n0.push_back(into_join.m_end_normal); - m_n1.push_back(leaving_join.m_begin_normal); + if (ed.non_empty()) + { + m_edge_chunks.push_back(ed.m_chunk); + } } -void -BevelJoinCreator:: -fill_join_implement(unsigned int join_id, - const PerEdgeData &into_join, - const PerEdgeData &leaving_join, - fastuidraw::c_array pts, - unsigned int depth, - fastuidraw::c_array indices, - unsigned int &vertex_offset, unsigned int &index_offset) const +////////////////////////////////////////// +// fastuidraw::StrokedPath::ChunkSet methods +fastuidraw::StrokedPath::ChunkSet:: +ChunkSet(void) { - const fastuidraw::TessellatedPath::point &prev_pt(into_join.m_end_pt); - const fastuidraw::TessellatedPath::point &next_pt(leaving_join.m_start_pt); - fastuidraw::StrokedPath::point pt; - - CommonJoinData J(prev_pt.m_p, m_n0[join_id], - next_pt.m_p, m_n1[join_id], - prev_pt.m_distance_from_edge_start, - prev_pt.m_distance_from_contour_start, - //using p0 to decide the edge length, as - //we think of the join as ending an edge. - prev_pt.m_edge_length, - prev_pt.m_open_contour_length, - prev_pt.m_closed_contour_length); - - pt.m_position = J.m_p0; - pt.m_pre_offset = J.m_lambda * J.m_n0; - pt.m_distance_from_edge_start = J.m_distance_from_edge_start; - pt.m_distance_from_contour_start = J.m_distance_from_contour_start; - pt.m_edge_length = J.m_edge_length; - pt.m_open_contour_length = J.m_open_contour_length; - pt.m_closed_contour_length = J.m_closed_contour_length; - pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_packed_data = pack_data_join(1, fastuidraw::StrokedPath::point::offset_shared_with_edge, depth); - pt.pack_point(&pts[vertex_offset + 0]); - - pt.m_position = J.m_p0; - pt.m_pre_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_distance_from_edge_start = J.m_distance_from_edge_start; - pt.m_distance_from_contour_start = J.m_distance_from_contour_start; - pt.m_edge_length = J.m_edge_length; - pt.m_open_contour_length = J.m_open_contour_length; - pt.m_closed_contour_length = J.m_closed_contour_length; - pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_packed_data = pack_data_join(0, fastuidraw::StrokedPath::point::offset_shared_with_edge, depth); - pt.pack_point(&pts[vertex_offset + 1]); - - pt.m_position = J.m_p1; - pt.m_pre_offset = J.m_lambda * J.m_n1; - pt.m_distance_from_edge_start = J.m_distance_from_edge_start; - pt.m_distance_from_contour_start = J.m_distance_from_contour_start; - pt.m_edge_length = J.m_edge_length; - pt.m_open_contour_length = J.m_open_contour_length; - pt.m_closed_contour_length = J.m_closed_contour_length; - pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_packed_data = pack_data_join(1, fastuidraw::StrokedPath::point::offset_shared_with_edge, depth); - pt.pack_point(&pts[vertex_offset + 2]); - - add_triangle_fan(vertex_offset, vertex_offset + 3, indices, index_offset); - - vertex_offset += 3; + m_d = FASTUIDRAWnew ChunkSetPrivate(); } -/////////////////////////////////////////////////// -// MiterClipJoinCreator methods -MiterClipJoinCreator:: -MiterClipJoinCreator(const PathData &P, - const StrokedPathSubset *st): - JoinCreatorBase(P, st) +fastuidraw::StrokedPath::ChunkSet:: +~ChunkSet(void) { - post_ctor_initalize(); + ChunkSetPrivate *d; + d = static_cast(m_d); + FASTUIDRAWdelete(d); } -void -MiterClipJoinCreator:: -add_join(unsigned int join_id, - const PerEdgeData &into_join, - const PerEdgeData &leaving_join, - unsigned int &vert_count, unsigned int &index_count) const +fastuidraw::c_array +fastuidraw::StrokedPath::ChunkSet:: +edge_chunks(void) const { - FASTUIDRAWunused(join_id); - /* Each join is a triangle fan from 5 points - * (thus 3 triangles, which is 9 indices) - */ - vert_count += 5; - index_count += 9; - - m_n0.push_back(into_join.m_end_normal); - m_n1.push_back(leaving_join.m_begin_normal); + ChunkSetPrivate *d; + d = static_cast(m_d); + return d->edge_chunks(); } - -void -MiterClipJoinCreator:: -fill_join_implement(unsigned int join_id, - const PerEdgeData &into_join, - const PerEdgeData &leaving_join, - fastuidraw::c_array pts, - unsigned int depth, - fastuidraw::c_array indices, - unsigned int &vertex_offset, unsigned int &index_offset) const +////////////////////////////////////////////////////////////// +// fastuidraw::StrokedPath methods +fastuidraw::StrokedPath:: +StrokedPath(const fastuidraw::TessellatedPath &P) { - FASTUIDRAWunused(join_id); - - const fastuidraw::TessellatedPath::point &prev_pt(into_join.m_end_pt); - const fastuidraw::TessellatedPath::point &next_pt(leaving_join.m_start_pt); - fastuidraw::StrokedPath::point pt; - unsigned int first; - - CommonJoinData J(prev_pt.m_p, m_n0[join_id], - next_pt.m_p, m_n1[join_id], - prev_pt.m_distance_from_edge_start, - prev_pt.m_distance_from_contour_start, - //using p0 to decide the edge length, as - //we think of the join as ending an edge. - prev_pt.m_edge_length, - prev_pt.m_open_contour_length, - prev_pt.m_closed_contour_length); - - /* The miter point is given by where the two boundary - * curves intersect. The two curves are given by: - * - * a(t) = J.m_p0 + stroke_width * J.m_lamba * J.m_n0 + t * J.m_v0 - * b(s) = J.m_p1 + stroke_width * J.m_lamba * J.m_n1 - s * J.m_v1 - * - * With J.m_p0 is the same value as J.m_p1, the location - * of the join. - * - * We need to solve a(t) = b(s) and compute that location. - * Linear algebra gives us that: - * - * t = - stroke_width * J.m_lamba * r - * s = - stroke_width * J.m_lamba * r - * where - * r = ( - 1) / - * - * thus - * - * a(t) = J.m_p0 + stroke_width * ( J.m_lamba * J.m_n0 - r * J.m_lamba * J.m_v0) - * = b(s) - * = J.m_p1 + stroke_width * ( J.m_lamba * J.m_n1 + r * J.m_lamba * J.m_v1) - */ - - first = vertex_offset; - - // join center point. - pt.m_position = J.m_p0; - pt.m_pre_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_distance_from_edge_start = J.m_distance_from_edge_start; - pt.m_distance_from_contour_start = J.m_distance_from_contour_start; - pt.m_edge_length = J.m_edge_length; - pt.m_open_contour_length = J.m_open_contour_length; - pt.m_closed_contour_length = J.m_closed_contour_length; - pt.m_packed_data = pack_data_join(0, fastuidraw::StrokedPath::point::offset_shared_with_edge, depth); - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - // join point from curve into join - pt.m_position = J.m_p0; - pt.m_pre_offset = J.m_lambda * J.m_n0; - pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_distance_from_edge_start = J.m_distance_from_edge_start; - pt.m_distance_from_contour_start = J.m_distance_from_contour_start; - pt.m_edge_length = J.m_edge_length; - pt.m_open_contour_length = J.m_open_contour_length; - pt.m_closed_contour_length = J.m_closed_contour_length; - pt.m_packed_data = pack_data_join(1, fastuidraw::StrokedPath::point::offset_shared_with_edge, depth); - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - // miter point A - pt.m_position = J.m_p0; - pt.m_pre_offset = J.m_n0; - pt.m_auxiliary_offset = J.m_n1; - pt.m_distance_from_edge_start = J.m_distance_from_edge_start; - pt.m_distance_from_contour_start = J.m_distance_from_contour_start; - pt.m_edge_length = J.m_edge_length; - pt.m_open_contour_length = J.m_open_contour_length; - pt.m_closed_contour_length = J.m_closed_contour_length; - pt.m_packed_data = pack_data_join(1, fastuidraw::StrokedPath::point::offset_miter_clip_join, depth); - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - // miter point B - pt.m_position = J.m_p1; - pt.m_pre_offset = J.m_n1; - pt.m_auxiliary_offset = J.m_n0; - pt.m_distance_from_edge_start = J.m_distance_from_edge_start; - pt.m_distance_from_contour_start = J.m_distance_from_contour_start; - pt.m_edge_length = J.m_edge_length; - pt.m_open_contour_length = J.m_open_contour_length; - pt.m_closed_contour_length = J.m_closed_contour_length; - pt.m_packed_data = pack_data_join(1, fastuidraw::StrokedPath::point::offset_miter_clip_join_lambda_negated, depth); - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - // join point from curve out from join - pt.m_position = J.m_p1; - pt.m_pre_offset = J.m_lambda * J.m_n1; - pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_distance_from_edge_start = J.m_distance_from_edge_start; - pt.m_distance_from_contour_start = J.m_distance_from_contour_start; - pt.m_edge_length = J.m_edge_length; - pt.m_open_contour_length = J.m_open_contour_length; - pt.m_closed_contour_length = J.m_closed_contour_length; - pt.m_packed_data = pack_data_join(1, fastuidraw::StrokedPath::point::offset_shared_with_edge, depth); - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - add_triangle_fan(first, vertex_offset, indices, index_offset); + StrokedCapsJoins::Builder b; + StrokedPathPrivate::ready_builder(&P, b); + m_d = FASTUIDRAWnew StrokedPathPrivate(P, b); } -//////////////////////////////////// -// MiterJoinCreator methods -template -MiterJoinCreator:: -MiterJoinCreator(const PathData &P, - const StrokedPathSubset *st): - JoinCreatorBase(P, st) +fastuidraw::StrokedPath:: +~StrokedPath() { - post_ctor_initalize(); + StrokedPathPrivate *d; + d = static_cast(m_d); + FASTUIDRAWdelete(d); + m_d = nullptr; } -template void -MiterJoinCreator:: -add_join(unsigned int join_id, - const PerEdgeData &into_join, - const PerEdgeData &leaving_join, - unsigned int &vert_count, unsigned int &index_count) const +fastuidraw::StrokedPath:: +compute_chunks(ScratchSpace &scratch_space, + c_array clip_equations, + const float3x3 &clip_matrix_local, + const vec2 &recip_dimensions, + float pixels_additional_room, + float item_space_additional_room, + bool include_closing_edges, + unsigned int max_attribute_cnt, + unsigned int max_index_cnt, + ChunkSet &dst) const { - /* Each join is a triangle fan from 4 points - * (thus 2 triangles, which is 6 indices) - */ - FASTUIDRAWunused(join_id); + StrokedPathPrivate *d; + ScratchSpacePrivate *scratch_space_ptr; + ChunkSetPrivate *chunk_set_ptr; + + d = static_cast(m_d); + scratch_space_ptr = static_cast(scratch_space.m_d); + chunk_set_ptr = static_cast(dst.m_d); - vert_count += 4; - index_count += 6; + if (d->m_empty_path) + { + chunk_set_ptr->reset(); + return; + } - m_n0.push_back(into_join.m_end_normal); - m_n1.push_back(leaving_join.m_begin_normal); + d->m_subset->compute_chunks(include_closing_edges, + *scratch_space_ptr, + clip_equations, + clip_matrix_local, + recip_dimensions, + pixels_additional_room, + item_space_additional_room, + max_attribute_cnt, + max_index_cnt, + *chunk_set_ptr); } -template -void -MiterJoinCreator:: -fill_join_implement(unsigned int join_id, - const PerEdgeData &into_join, - const PerEdgeData &leaving_join, - fastuidraw::c_array pts, - unsigned int depth, - fastuidraw::c_array indices, - unsigned int &vertex_offset, unsigned int &index_offset) const -{ - FASTUIDRAWunused(join_id); - - const fastuidraw::TessellatedPath::point &prev_pt(into_join.m_end_pt); - const fastuidraw::TessellatedPath::point &next_pt(leaving_join.m_start_pt); - fastuidraw::StrokedPath::point pt; - unsigned int first; - - CommonJoinData J(prev_pt.m_p, m_n0[join_id], - next_pt.m_p, m_n1[join_id], - prev_pt.m_distance_from_edge_start, - prev_pt.m_distance_from_contour_start, - //using p0 to decide the edge length, as - //we think of the join as ending an edge. - prev_pt.m_edge_length, - prev_pt.m_open_contour_length, - prev_pt.m_closed_contour_length); - - /* The miter point is given by where the two boundary - * curves intersect. The two curves are given by: - * - * a(t) = J.m_p0 + stroke_width * J.m_lamba * J.m_n0 + t * J.m_v0 - * b(s) = J.m_p1 + stroke_width * J.m_lamba * J.m_n1 - s * J.m_v1 - * - * With J.m_p0 is the same value as J.m_p1, the location - * of the join. - * - * We need to solve a(t) = b(s) and compute that location. - * Linear algebra gives us that: - * - * t = - stroke_width * J.m_lamba * r - * s = - stroke_width * J.m_lamba * r - * where - * r = ( - 1) / - * - * thus - * - * a(t) = J.m_p0 + stroke_width * ( J.m_lamba * J.m_n0 - r * J.m_lamba * J.m_v0) - * = b(s) - * = J.m_p1 + stroke_width * ( J.m_lamba * J.m_n1 + r * J.m_lamba * J.m_v1) - */ - - first = vertex_offset; - - // join center point. - pt.m_position = J.m_p0; - pt.m_pre_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_distance_from_edge_start = J.m_distance_from_edge_start; - pt.m_distance_from_contour_start = J.m_distance_from_contour_start; - pt.m_edge_length = J.m_edge_length; - pt.m_open_contour_length = J.m_open_contour_length; - pt.m_closed_contour_length = J.m_closed_contour_length; - pt.m_packed_data = pack_data_join(0, fastuidraw::StrokedPath::point::offset_shared_with_edge, depth); - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - // join point from curve into join - pt.m_position = J.m_p0; - pt.m_pre_offset = J.m_lambda * J.m_n0; - pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_distance_from_edge_start = J.m_distance_from_edge_start; - pt.m_distance_from_contour_start = J.m_distance_from_contour_start; - pt.m_edge_length = J.m_edge_length; - pt.m_open_contour_length = J.m_open_contour_length; - pt.m_closed_contour_length = J.m_closed_contour_length; - pt.m_packed_data = pack_data_join(1, fastuidraw::StrokedPath::point::offset_shared_with_edge, depth); - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - // miter point - pt.m_position = J.m_p0; - pt.m_pre_offset = J.m_n0; - pt.m_auxiliary_offset = J.m_n1; - pt.m_distance_from_edge_start = J.m_distance_from_edge_start; - pt.m_distance_from_contour_start = J.m_distance_from_contour_start; - pt.m_edge_length = J.m_edge_length; - pt.m_open_contour_length = J.m_open_contour_length; - pt.m_closed_contour_length = J.m_closed_contour_length; - pt.m_packed_data = pack_data_join(1, tp, depth); - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - // join point from curve out from join - pt.m_position = J.m_p1; - pt.m_pre_offset = J.m_lambda * J.m_n1; - pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_distance_from_edge_start = J.m_distance_from_edge_start; - pt.m_distance_from_contour_start = J.m_distance_from_contour_start; - pt.m_edge_length = J.m_edge_length; - pt.m_open_contour_length = J.m_open_contour_length; - pt.m_closed_contour_length = J.m_closed_contour_length; - pt.m_packed_data = pack_data_join(1, fastuidraw::StrokedPath::point::offset_shared_with_edge, depth); - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - add_triangle_fan(first, vertex_offset, indices, index_offset); -} - -/////////////////////////////////////////////// -// CapCreatorBase methods -CapCreatorBase:: -CapCreatorBase(const PathData &P, - const StrokedPathSubset *st, - PointIndexCapSize sz): - m_P(P.m_contour_data), - m_ordering(P.m_cap_ordering), - m_st(st), - m_num_chunks(P.m_number_cap_chunks), - m_size(sz) -{ -} - -void -CapCreatorBase:: -compute_sizes(unsigned int &num_attributes, - unsigned int &num_indices, - unsigned int &num_attribute_chunks, - unsigned int &num_index_chunks, - unsigned int &number_z_ranges) const -{ - num_attributes = m_size.m_verts; - num_indices = m_size.m_indices; - number_z_ranges = num_attribute_chunks = num_index_chunks = m_num_chunks; -} - -void -CapCreatorBase:: -fill_data(fastuidraw::c_array attribute_data, - fastuidraw::c_array index_data, - fastuidraw::c_array > attribute_chunks, - fastuidraw::c_array > index_chunks, - fastuidraw::c_array > zranges, - fastuidraw::c_array index_adjusts) const -{ - unsigned int vertex_offset(0u), index_offset(0u), cap_id(0u); - std::vector > cvr(m_num_chunks), cir(m_num_chunks); - - for(const OrderingEntry C : m_ordering.caps()) - { - unsigned int v(vertex_offset), i(index_offset); - const fastuidraw::vec2 *normal; - const fastuidraw::TessellatedPath::point *pt; - - normal = C.m_is_start_cap ? - &m_P.m_per_contour_data[C.m_contour].m_begin_cap_normal: - &m_P.m_per_contour_data[C.m_contour].m_end_cap_normal; - - pt = C.m_is_start_cap ? - &m_P.m_per_contour_data[C.m_contour].m_start_contour_pt: - &m_P.m_per_contour_data[C.m_contour].m_end_contour_pt; - - add_cap(*normal, C.m_is_start_cap, C.m_depth, *pt, - attribute_data, index_data, vertex_offset, index_offset); - - cvr[cap_id].m_begin = v; - cvr[cap_id].m_end = vertex_offset; - - cir[cap_id].m_begin = i; - cir[cap_id].m_end = index_offset; - - attribute_chunks[C.m_chunk] = attribute_data.sub_array(cvr[cap_id]); - index_chunks[C.m_chunk] = index_data.sub_array(cir[cap_id]); - zranges[C.m_chunk] = fastuidraw::range_type(C.m_depth, C.m_depth + 1); - index_adjusts[C.m_chunk] = -int(v); - - ++cap_id; - } - - FASTUIDRAWassert(vertex_offset == m_size.m_verts); - FASTUIDRAWassert(index_offset == m_size.m_indices); - - set_chunks(m_st, - fastuidraw::make_c_array(cvr), - fastuidraw::make_c_array(cir), - attribute_data, index_data, - attribute_chunks, index_chunks, - zranges, index_adjusts); -} - -void -CapCreatorBase:: -set_chunks(const StrokedPathSubset *st, - fastuidraw::c_array > cap_vertex_ranges, - fastuidraw::c_array > cap_index_ranges, - fastuidraw::c_array attribute_data, - fastuidraw::c_array index_data, - fastuidraw::c_array > attribute_chunks, - fastuidraw::c_array > index_chunks, - fastuidraw::c_array > zranges, - fastuidraw::c_array index_adjusts) -{ - const RangeAndChunk &ch(st->caps()); - fastuidraw::range_type vr, ir; - unsigned int K; - - if (ch.m_elements.m_begin < ch.m_elements.m_end) - { - vr.m_begin = cap_vertex_ranges[ch.m_elements.m_begin].m_begin; - vr.m_end = cap_vertex_ranges[ch.m_elements.m_end - 1].m_end; - - ir.m_begin = cap_index_ranges[ch.m_elements.m_begin].m_begin; - ir.m_end = cap_index_ranges[ch.m_elements.m_end - 1].m_end; - - FASTUIDRAWassert(ch.m_depth_range.m_begin < ch.m_depth_range.m_end); - } - else - { - vr.m_begin = vr.m_end = 0; - ir.m_begin = ir.m_end = 0; - FASTUIDRAWassert(ch.m_depth_range.m_begin == ch.m_depth_range.m_end); - } - - K = ch.m_chunk; - attribute_chunks[K] = attribute_data.sub_array(vr); - index_chunks[K] = index_data.sub_array(ir); - zranges[K] = fastuidraw::range_type(ch.m_depth_range.m_begin, ch.m_depth_range.m_end); - index_adjusts[K] = -int(vr.m_begin); - - if (st->have_children()) - { - set_chunks(st->child(0), - cap_vertex_ranges, cap_index_ranges, - attribute_data, index_data, - attribute_chunks, index_chunks, - zranges, index_adjusts); - - set_chunks(st->child(1), - cap_vertex_ranges, cap_index_ranges, - attribute_data, index_data, - attribute_chunks, index_chunks, - zranges, index_adjusts); - } -} - -/////////////////////////////////////////////////// -// RoundedCapCreator methods -RoundedCapCreator:: -RoundedCapCreator(const PathData &P, - const StrokedPathSubset *st, - float thresh): - CapCreatorBase(P, st, compute_size(P.m_contour_data, thresh)) -{ - m_num_arc_points_per_cap = fastuidraw::detail::number_segments_for_tessellation(M_PI, thresh); - m_delta_theta = static_cast(M_PI) / static_cast(m_num_arc_points_per_cap - 1); -} - -PointIndexCapSize -RoundedCapCreator:: -compute_size(const ContourData &P, float thresh) -{ - unsigned int num_caps, num_arc_points_per_cap; - PointIndexCapSize return_value; - - num_arc_points_per_cap = fastuidraw::detail::number_segments_for_tessellation(M_PI, thresh); - - /* each cap is a triangle fan centered at the cap point. - */ - num_caps = 2 * P.number_contours(); - return_value.m_verts = (1 + num_arc_points_per_cap) * num_caps; - return_value.m_indices = 3 * (num_arc_points_per_cap - 1) * num_caps; - - return return_value; -} - -void -RoundedCapCreator:: -add_cap(const fastuidraw::vec2 &normal_from_stroking, - bool is_starting_cap, unsigned int depth, - const fastuidraw::TessellatedPath::point &p, - fastuidraw::c_array pts, - fastuidraw::c_array indices, - unsigned int &vertex_offset, - unsigned int &index_offset) const -{ - CommonCapData C(is_starting_cap, p.m_p, normal_from_stroking); - unsigned int first, i; - float theta; - fastuidraw::StrokedPath::point pt; - - first = vertex_offset; - - pt.m_position = C.m_p; - pt.m_pre_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_distance_from_edge_start = p.m_distance_from_edge_start; - pt.m_edge_length = p.m_edge_length; - pt.m_open_contour_length = p.m_open_contour_length; - pt.m_closed_contour_length = p.m_closed_contour_length; - pt.m_distance_from_contour_start = p.m_distance_from_contour_start; - pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_packed_data = pack_data(0, fastuidraw::StrokedPath::point::offset_shared_with_edge, depth); - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - pt.m_position = C.m_p; - pt.m_pre_offset = C.m_n; - pt.m_distance_from_edge_start = p.m_distance_from_edge_start; - pt.m_distance_from_contour_start = p.m_distance_from_contour_start; - pt.m_edge_length = p.m_edge_length; - pt.m_open_contour_length = p.m_open_contour_length; - pt.m_closed_contour_length = p.m_closed_contour_length; - pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_packed_data = pack_data(1, fastuidraw::StrokedPath::point::offset_shared_with_edge, depth); - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - for(i = 1, theta = m_delta_theta; i < m_num_arc_points_per_cap - 1; ++i, theta += m_delta_theta, ++vertex_offset) - { - float s, c; - - s = std::sin(theta); - c = std::cos(theta); - pt.m_position = C.m_p; - pt.m_pre_offset = C.m_n; - pt.m_auxiliary_offset = fastuidraw::vec2(s, c); - pt.m_distance_from_edge_start = p.m_distance_from_edge_start; - pt.m_distance_from_contour_start = p.m_distance_from_contour_start; - pt.m_edge_length = p.m_edge_length; - pt.m_open_contour_length = p.m_open_contour_length; - pt.m_closed_contour_length = p.m_closed_contour_length; - pt.m_packed_data = pack_data(1, fastuidraw::StrokedPath::point::offset_rounded_cap, depth); - pt.pack_point(&pts[vertex_offset]); - } - - pt.m_position = C.m_p; - pt.m_pre_offset = -C.m_n; - pt.m_distance_from_edge_start = p.m_distance_from_edge_start; - pt.m_distance_from_contour_start = p.m_distance_from_contour_start; - pt.m_edge_length = p.m_edge_length; - pt.m_open_contour_length = p.m_open_contour_length; - pt.m_closed_contour_length = p.m_closed_contour_length; - pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_packed_data = pack_data(1, fastuidraw::StrokedPath::point::offset_shared_with_edge, depth); - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - add_triangle_fan(first, vertex_offset, indices, index_offset); -} - - -/////////////////////////////////////////////////// -// SquareCapCreator methods -PointIndexCapSize -SquareCapCreator:: -compute_size(const ContourData &P) -{ - PointIndexCapSize return_value; - unsigned int num_caps; - - /* each square cap generates 5 new points - * and 3 triangles (= 9 indices) - */ - num_caps = 2 * P.number_contours(); - return_value.m_verts = 5 * num_caps; - return_value.m_indices = 9 * num_caps; - - return return_value; -} - -void -SquareCapCreator:: -add_cap(const fastuidraw::vec2 &normal_from_stroking, - bool is_starting_cap, unsigned int depth, - const fastuidraw::TessellatedPath::point &p, - fastuidraw::c_array pts, - fastuidraw::c_array indices, - unsigned int &vertex_offset, - unsigned int &index_offset) const -{ - CommonCapData C(is_starting_cap, p.m_p, normal_from_stroking); - unsigned int first; - fastuidraw::StrokedPath::point pt; - - first = vertex_offset; - - pt.m_position = C.m_p; - pt.m_pre_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_distance_from_edge_start = p.m_distance_from_edge_start; - pt.m_distance_from_contour_start = p.m_distance_from_contour_start; - pt.m_edge_length = p.m_edge_length; - pt.m_open_contour_length = p.m_open_contour_length; - pt.m_closed_contour_length = p.m_closed_contour_length; - pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_packed_data = pack_data(0, fastuidraw::StrokedPath::point::offset_shared_with_edge, depth); - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - pt.m_position = C.m_p; - pt.m_pre_offset = C.m_n; - pt.m_distance_from_edge_start = p.m_distance_from_edge_start; - pt.m_distance_from_contour_start = p.m_distance_from_contour_start; - pt.m_edge_length = p.m_edge_length; - pt.m_open_contour_length = p.m_open_contour_length; - pt.m_closed_contour_length = p.m_closed_contour_length; - pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_packed_data = pack_data(1, fastuidraw::StrokedPath::point::offset_shared_with_edge, depth); - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - pt.m_position = C.m_p; - pt.m_pre_offset = C.m_n; - pt.m_distance_from_edge_start = p.m_distance_from_edge_start; - pt.m_distance_from_contour_start = p.m_distance_from_contour_start; - pt.m_edge_length = p.m_edge_length; - pt.m_open_contour_length = p.m_open_contour_length; - pt.m_closed_contour_length = p.m_closed_contour_length; - pt.m_auxiliary_offset = C.m_v; - pt.m_packed_data = pack_data(1, fastuidraw::StrokedPath::point::offset_square_cap, depth); - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - pt.m_position = C.m_p; - pt.m_pre_offset = -C.m_n; - pt.m_distance_from_edge_start = p.m_distance_from_edge_start; - pt.m_distance_from_contour_start = p.m_distance_from_contour_start; - pt.m_edge_length = p.m_edge_length; - pt.m_open_contour_length = p.m_open_contour_length; - pt.m_closed_contour_length = p.m_closed_contour_length; - pt.m_auxiliary_offset = C.m_v; - pt.m_packed_data = pack_data(1, fastuidraw::StrokedPath::point::offset_square_cap, depth); - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - pt.m_position = C.m_p; - pt.m_pre_offset = -C.m_n; - pt.m_distance_from_edge_start = p.m_distance_from_edge_start; - pt.m_distance_from_contour_start = p.m_distance_from_contour_start; - pt.m_edge_length = p.m_edge_length; - pt.m_open_contour_length = p.m_open_contour_length; - pt.m_closed_contour_length = p.m_closed_contour_length; - pt.m_auxiliary_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_packed_data = pack_data(1, fastuidraw::StrokedPath::point::offset_shared_with_edge, depth); - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - add_triangle_fan(first, vertex_offset, indices, index_offset); -} - -////////////////////////////////////// -// AdjustableCapCreator methods -PointIndexCapSize -AdjustableCapCreator:: -compute_size(const ContourData &P) -{ - PointIndexCapSize return_value; - unsigned int num_caps; - - num_caps = 2 * P.number_contours(); - return_value.m_verts = number_points_per_fan * num_caps; - return_value.m_indices = number_indices_per_fan * num_caps; - - return return_value; -} - -void -AdjustableCapCreator:: -pack_fan(bool entering_contour, - enum fastuidraw::StrokedPath::point::offset_type_t type, - const fastuidraw::TessellatedPath::point &p, - const fastuidraw::vec2 &stroking_normal, - unsigned int depth, - fastuidraw::c_array pts, - unsigned int &vertex_offset, - fastuidraw::c_array indices, - unsigned int &index_offset) -{ - CommonCapData C(entering_contour, p.m_p, stroking_normal); - unsigned int first(vertex_offset); - fastuidraw::StrokedPath::point pt; - - pt.m_position = C.m_p; - pt.m_pre_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_auxiliary_offset = C.m_v; - pt.m_distance_from_edge_start = p.m_distance_from_edge_start; - pt.m_distance_from_contour_start = p.m_distance_from_contour_start; - pt.m_edge_length = p.m_edge_length; - pt.m_open_contour_length = p.m_open_contour_length; - pt.m_closed_contour_length = p.m_closed_contour_length; - pt.m_packed_data = pack_data(0, type, depth); - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - pt.m_position = C.m_p; - pt.m_pre_offset = C.m_n; - pt.m_auxiliary_offset = C.m_v; - pt.m_distance_from_edge_start = p.m_distance_from_edge_start; - pt.m_distance_from_contour_start = p.m_distance_from_contour_start; - pt.m_edge_length = p.m_edge_length; - pt.m_open_contour_length = p.m_open_contour_length; - pt.m_closed_contour_length = p.m_closed_contour_length; - pt.m_packed_data = pack_data(1, type, depth); - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - pt.m_position = C.m_p; - pt.m_pre_offset = C.m_n; - pt.m_auxiliary_offset = C.m_v; - pt.m_distance_from_edge_start = p.m_distance_from_edge_start; - pt.m_distance_from_contour_start = p.m_distance_from_contour_start; - pt.m_edge_length = p.m_edge_length; - pt.m_open_contour_length = p.m_open_contour_length; - pt.m_closed_contour_length = p.m_closed_contour_length; - pt.m_packed_data = pack_data(1, type, depth) | fastuidraw::StrokedPath::point::adjustable_cap_ending_mask; - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - pt.m_position = C.m_p; - pt.m_pre_offset = fastuidraw::vec2(0.0f, 0.0f); - pt.m_auxiliary_offset = C.m_v; - pt.m_distance_from_edge_start = p.m_distance_from_edge_start; - pt.m_distance_from_contour_start = p.m_distance_from_contour_start; - pt.m_edge_length = p.m_edge_length; - pt.m_open_contour_length = p.m_open_contour_length; - pt.m_closed_contour_length = p.m_closed_contour_length; - pt.m_packed_data = pack_data(0, type, depth) | fastuidraw::StrokedPath::point::adjustable_cap_ending_mask; - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - pt.m_position = C.m_p; - pt.m_pre_offset = -C.m_n; - pt.m_auxiliary_offset = C.m_v; - pt.m_distance_from_edge_start = p.m_distance_from_edge_start; - pt.m_distance_from_contour_start = p.m_distance_from_contour_start; - pt.m_edge_length = p.m_edge_length; - pt.m_open_contour_length = p.m_open_contour_length; - pt.m_closed_contour_length = p.m_closed_contour_length; - pt.m_packed_data = pack_data(1, type, depth) | fastuidraw::StrokedPath::point::adjustable_cap_ending_mask; - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - pt.m_position = C.m_p; - pt.m_pre_offset = -C.m_n; - pt.m_auxiliary_offset = C.m_v; - pt.m_distance_from_edge_start = p.m_distance_from_edge_start; - pt.m_distance_from_contour_start = p.m_distance_from_contour_start; - pt.m_edge_length = p.m_edge_length; - pt.m_open_contour_length = p.m_open_contour_length; - pt.m_closed_contour_length = p.m_closed_contour_length; - pt.m_packed_data = pack_data(1, type, depth); - pt.pack_point(&pts[vertex_offset]); - ++vertex_offset; - - add_triangle_fan(first, vertex_offset, indices, index_offset); -} - -void -AdjustableCapCreator:: -add_cap(const fastuidraw::vec2 &normal_from_stroking, - bool is_starting_cap, unsigned int depth, - const fastuidraw::TessellatedPath::point &p0, - fastuidraw::c_array pts, - fastuidraw::c_array indices, - unsigned int &vertex_offset, - unsigned int &index_offset) const -{ - enum fastuidraw::StrokedPath::point::offset_type_t tp; - tp = (is_starting_cap) ? - fastuidraw::StrokedPath::point::offset_adjustable_cap_contour_start : - fastuidraw::StrokedPath::point::offset_adjustable_cap_contour_end; - - pack_fan(is_starting_cap, tp, - p0, normal_from_stroking, depth, - pts, vertex_offset, indices, index_offset); -} - -///////////////////////////////////////////// -// StrokedPathPrivate methods -StrokedPathPrivate:: -StrokedPathPrivate(const fastuidraw::TessellatedPath &P): - m_subset(nullptr) -{ - if (!P.point_data().empty()) - { - m_empty_path = false; - create_edges(P); - } - else - { - m_empty_path = true; - m_bevel_joins.mark_as_empty(); - m_miter_clip_joins.mark_as_empty(); - m_miter_joins.mark_as_empty(); - m_miter_bevel_joins.mark_as_empty(); - m_square_caps.mark_as_empty(); - m_adjustable_caps.mark_as_empty(); - std::fill(m_chunk_of_joins.begin(), m_chunk_of_joins.end(), 0); - std::fill(m_chunk_of_edges.begin(), m_chunk_of_edges.end(), 0); - m_chunk_of_caps = 0; - } -} - -StrokedPathPrivate:: -~StrokedPathPrivate() -{ - for(unsigned int i = 0, endi = m_rounded_joins.size(); i < endi; ++i) - { - FASTUIDRAWdelete(m_rounded_joins[i].m_data); - } - - for(unsigned int i = 0, endi = m_rounded_caps.size(); i < endi; ++i) - { - FASTUIDRAWdelete(m_rounded_caps[i].m_data); - } - - if (!m_empty_path) - { - FASTUIDRAWdelete(m_subset); - } -} - -void -StrokedPathPrivate:: -create_edges(const fastuidraw::TessellatedPath &P) -{ - SubEdgeCullingHierarchy *s; - StrokedPathSubset::CreationValues cnts; - - FASTUIDRAWassert(!m_empty_path); - s = SubEdgeCullingHierarchy::create(P, m_path_data.m_contour_data); - m_subset = StrokedPathSubset::create(s, cnts, m_path_data.m_join_ordering, m_path_data.m_cap_ordering); - m_edges.set_data(EdgeAttributeFiller(m_subset, P, cnts)); - - m_path_data.m_number_join_chunks = cnts.m_non_closing_join_chunk_cnt + cnts.m_closing_join_chunk_cnt; - m_path_data.m_number_cap_chunks = cnts.m_cap_chunk_cnt; - - /* the chunks of the root element have the data for everything. - */ - m_chunk_of_edges[fastuidraw::StrokedPath::all_non_closing] = m_subset->non_closing_edges().m_chunk; - m_chunk_of_joins[fastuidraw::StrokedPath::all_non_closing] = m_subset->non_closing_joins().m_chunk; - - m_chunk_of_edges[fastuidraw::StrokedPath::all_closing] = m_subset->closing_edges().m_chunk; - m_chunk_of_joins[fastuidraw::StrokedPath::all_closing] = m_subset->closing_joins().m_chunk; - - m_chunk_of_caps = m_subset->caps().m_chunk; - - FASTUIDRAWdelete(s); -} - -template -const fastuidraw::PainterAttributeData& -StrokedPathPrivate:: -fetch_create(float thresh, std::vector &values) -{ - if (values.empty()) - { - fastuidraw::PainterAttributeData *newD; - newD = FASTUIDRAWnew fastuidraw::PainterAttributeData(); - newD->set_data(T(m_path_data, m_subset, 1.0f)); - values.push_back(ThreshWithData(newD, 1.0f)); - } - - /* we set a hard tolerance of 1e-6. Should we - * set it as a ratio of the bounding box of - * the underlying tessellated path? - */ - thresh = fastuidraw::t_max(thresh, float(1e-6)); - if (values.back().m_thresh <= thresh) - { - std::vector::const_iterator iter; - iter = std::lower_bound(values.begin(), values.end(), thresh, - ThreshWithData::reverse_compare_against_thresh); - FASTUIDRAWassert(iter != values.end()); - FASTUIDRAWassert(iter->m_thresh <= thresh); - FASTUIDRAWassert(iter->m_data != nullptr); - return *iter->m_data; - } - else - { - float t; - t = values.back().m_thresh; - while(t > thresh) - { - fastuidraw::PainterAttributeData *newD; - - t *= 0.5f; - newD = FASTUIDRAWnew fastuidraw::PainterAttributeData(); - newD->set_data(T(m_path_data, m_subset, t)); - values.push_back(ThreshWithData(newD, t)); - } - return *values.back().m_data; - } -} - -////////////////////////////////////// -// fastuidraw::StrokedPath::point methods -void -fastuidraw::StrokedPath::point:: -pack_point(PainterAttribute *dst) const -{ - dst->m_attrib0 = pack_vec4(m_position.x(), - m_position.y(), - m_pre_offset.x(), - m_pre_offset.y()); - - dst->m_attrib1 = pack_vec4(m_distance_from_edge_start, - m_distance_from_contour_start, - m_auxiliary_offset.x(), - m_auxiliary_offset.y()); - - dst->m_attrib2 = uvec4(m_packed_data, - pack_float(m_edge_length), - pack_float(m_open_contour_length), - pack_float(m_closed_contour_length)); -} - -void -fastuidraw::StrokedPath::point:: -unpack_point(point *dst, const PainterAttribute &a) -{ - dst->m_position.x() = unpack_float(a.m_attrib0.x()); - dst->m_position.y() = unpack_float(a.m_attrib0.y()); - - dst->m_pre_offset.x() = unpack_float(a.m_attrib0.z()); - dst->m_pre_offset.y() = unpack_float(a.m_attrib0.w()); - - dst->m_distance_from_edge_start = unpack_float(a.m_attrib1.x()); - dst->m_distance_from_contour_start = unpack_float(a.m_attrib1.y()); - dst->m_auxiliary_offset.x() = unpack_float(a.m_attrib1.z()); - dst->m_auxiliary_offset.y() = unpack_float(a.m_attrib1.w()); - - dst->m_packed_data = a.m_attrib2.x(); - dst->m_edge_length = unpack_float(a.m_attrib2.y()); - dst->m_open_contour_length = unpack_float(a.m_attrib2.z()); - dst->m_closed_contour_length = unpack_float(a.m_attrib2.w()); -} - -fastuidraw::vec2 -fastuidraw::StrokedPath::point:: -offset_vector(void) -{ - enum offset_type_t tp; - - tp = offset_type(); - - switch(tp) - { - case offset_start_sub_edge: - case offset_end_sub_edge: - case offset_shared_with_edge: - return m_pre_offset; - - case offset_square_cap: - return m_pre_offset + m_auxiliary_offset; - - case offset_rounded_cap: - { - vec2 n(m_pre_offset), v(n.y(), -n.x()); - return m_auxiliary_offset.x() * v + m_auxiliary_offset.y() * n; - } - - case offset_miter_clip_join: - case offset_miter_clip_join_lambda_negated: - { - vec2 n0(m_pre_offset), Jn0(n0.y(), -n0.x()); - vec2 n1(m_auxiliary_offset), Jn1(n1.y(), -n1.x()); - float r, det, lambda; - - det = dot(Jn1, n0); - lambda = -t_sign(det); - r = (det != 0.0f) ? (dot(n0, n1) - 1.0f) / det : 0.0f; - - if (tp == offset_miter_clip_join_lambda_negated) - { - lambda = -lambda; - } - - return lambda * (n0 + r * Jn0); - } - - case offset_miter_bevel_join: - case offset_miter_join: - { - vec2 n0(m_pre_offset), Jn0(n0.y(), -n0.x()); - vec2 n1(m_auxiliary_offset), Jn1(n1.y(), -n1.x()); - float r, lambda, den; - - lambda = t_sign(dot(Jn0, n1)); - den = 1.0f + dot(n0, n1); - r = (den != 0.0f) ? lambda / den : 0.0f; - return r * (n0 + n1); - } - - case offset_rounded_join: - { - vec2 cs; - - cs.x() = m_auxiliary_offset.y(); - cs.y() = sqrt(1.0 - cs.x() * cs.x()); - if (m_packed_data & sin_sign_mask) - { - cs.y() = -cs.y(); - } - return cs; - } - - default: - return vec2(0.0f, 0.0f); - } -} - -float -fastuidraw::StrokedPath::point:: -miter_distance(void) const -{ - enum offset_type_t tp; - tp = offset_type(); - - switch(tp) - { - case offset_miter_clip_join: - case offset_miter_clip_join_lambda_negated: - { - vec2 n0(m_pre_offset), Jn0(n0.y(), -n0.x()); - vec2 n1(m_auxiliary_offset), Jn1(n1.y(), -n1.x()); - float r, det; - - det = dot(Jn1, n0); - r = (det != 0.0f) ? (dot(n0, n1) - 1.0f) / det : 0.0f; - return t_sqrt(1.0f + r * r); - } - - case offset_miter_bevel_join: - case offset_miter_join: - { - vec2 n0(m_pre_offset), n1(m_auxiliary_offset); - vec2 n0_plus_n1(n0 + n1); - float den; - - den = 1.0f + dot(n0, n1); - return den != 0.0f ? - n0_plus_n1.magnitude() / den : - 0.0f; - } - - default: - return 0.0f; - } -} - -////////////////////////////////////////////// -// fastuidraw::StrokedPath::ScratchSpace methods -fastuidraw::StrokedPath::ScratchSpace:: -ScratchSpace(void) -{ - m_d = FASTUIDRAWnew ScratchSpacePrivate(); -} - -fastuidraw::StrokedPath::ScratchSpace:: -~ScratchSpace(void) -{ - ScratchSpacePrivate *d; - d = static_cast(m_d); - FASTUIDRAWdelete(d); - m_d = nullptr; -} - -/////////////////////////////////////// -// ChunkSetPrivate methods -void -ChunkSetPrivate:: -add_edge_chunk(const EdgeRanges &ed) -{ - if (ed.non_empty()) - { - m_edge_chunks.push_back(ed.m_chunk); - } -} - -void -ChunkSetPrivate:: -add_join_chunk(const RangeAndChunk &j) -{ - if (j.non_empty() && !m_ignore_join_adds) - { - m_join_chunks.push_back(j.m_chunk); - m_join_ranges.push_back(j.m_elements); - } -} - -void -ChunkSetPrivate:: -add_cap_chunk(const RangeAndChunk &c) -{ - if (c.non_empty()) - { - m_cap_chunks.push_back(c.m_chunk); - } -} - -void -ChunkSetPrivate:: -handle_dashed_evaluator(const fastuidraw::DashEvaluatorBase *dash_evaluator, - const fastuidraw::PainterShaderData::DataBase *dash_data, - const fastuidraw::StrokedPath &path) -{ - if (dash_evaluator != nullptr) - { - const fastuidraw::PainterAttributeData &joins(path.bevel_joins()); - unsigned int cnt(0); - - m_join_chunks.clear(); - for(const fastuidraw::range_type &R : m_join_ranges) - { - for(unsigned int J = R.m_begin; J < R.m_end; ++J, ++cnt) - { - unsigned int chunk; - fastuidraw::c_array attribs; - - chunk = path.join_chunk(J); - attribs = joins.attribute_data_chunk(chunk); - FASTUIDRAWassert(!attribs.empty()); - FASTUIDRAWassert(attribs.size() == 3); - if (dash_evaluator->covered_by_dash_pattern(dash_data, attribs[0])) - { - m_join_chunks.push_back(chunk); - } - } - } - } -} - -////////////////////////////////////////// -// fastuidraw::StrokedPath::ChunkSet methods -fastuidraw::StrokedPath::ChunkSet:: -ChunkSet(void) -{ - m_d = FASTUIDRAWnew ChunkSetPrivate(); -} - -fastuidraw::StrokedPath::ChunkSet:: -~ChunkSet(void) -{ - ChunkSetPrivate *d; - d = static_cast(m_d); - FASTUIDRAWdelete(d); -} - -fastuidraw::c_array -fastuidraw::StrokedPath::ChunkSet:: -edge_chunks(void) const -{ - ChunkSetPrivate *d; - d = static_cast(m_d); - return d->edge_chunks(); -} - -fastuidraw::c_array -fastuidraw::StrokedPath::ChunkSet:: -join_chunks(void) const -{ - ChunkSetPrivate *d; - d = static_cast(m_d); - return d->join_chunks(); -} - -fastuidraw::c_array -fastuidraw::StrokedPath::ChunkSet:: -cap_chunks(void) const -{ - ChunkSetPrivate *d; - d = static_cast(m_d); - return d->cap_chunks(); -} - - -////////////////////////////////////////////////////////////// -// fastuidraw::StrokedPath methods -fastuidraw::StrokedPath:: -StrokedPath(const fastuidraw::TessellatedPath &P) -{ - FASTUIDRAWassert(point::number_offset_types < FASTUIDRAW_MAX_VALUE_FROM_NUM_BITS(point::offset_type_num_bits)); - m_d = FASTUIDRAWnew StrokedPathPrivate(P); -} - -fastuidraw::StrokedPath:: -~StrokedPath() -{ - StrokedPathPrivate *d; - d = static_cast(m_d); - FASTUIDRAWdelete(d); - m_d = nullptr; -} - -void -fastuidraw::StrokedPath:: -compute_chunks(ScratchSpace &scratch_space, - const DashEvaluatorBase *dash_evaluator, - const PainterShaderData::DataBase *dash_data, - c_array clip_equations, - const float3x3 &clip_matrix_local, - const vec2 &recip_dimensions, - float pixels_additional_room, - float item_space_additional_room, - bool include_closing_edges, - unsigned int max_attribute_cnt, - unsigned int max_index_cnt, - bool take_joins_outside_of_region, - ChunkSet &dst) const -{ - StrokedPathPrivate *d; - ScratchSpacePrivate *scratch_space_ptr; - ChunkSetPrivate *chunk_set_ptr; - - d = static_cast(m_d); - scratch_space_ptr = static_cast(scratch_space.m_d); - chunk_set_ptr = static_cast(dst.m_d); - - if (d->m_empty_path) - { - chunk_set_ptr->reset(); - return; - } - - d->m_subset->compute_chunks(include_closing_edges, - *scratch_space_ptr, - clip_equations, - clip_matrix_local, - recip_dimensions, - pixels_additional_room, - item_space_additional_room, - max_attribute_cnt, - max_index_cnt, - take_joins_outside_of_region, - *chunk_set_ptr); - chunk_set_ptr->handle_dashed_evaluator(dash_evaluator, dash_data, *this); -} - -const fastuidraw::PainterAttributeData& -fastuidraw::StrokedPath:: -edges(void) const +const fastuidraw::PainterAttributeData& +fastuidraw::StrokedPath:: +edges(void) const { StrokedPathPrivate *d; d = static_cast(m_d); @@ -4339,115 +1680,11 @@ chunk_of_edges(enum chunk_selection c) const return d->m_chunk_of_edges[c]; } -unsigned int -fastuidraw::StrokedPath:: -number_joins(bool include_joins_of_closing_edge) const -{ - StrokedPathPrivate *d; - d = static_cast(m_d); - return d->m_path_data.m_join_ordering.number_joins(include_joins_of_closing_edge); -} - -unsigned int -fastuidraw::StrokedPath:: -join_chunk(unsigned int J) const -{ - StrokedPathPrivate *d; - d = static_cast(m_d); - return d->m_path_data.m_join_ordering.join_chunk(J); -} - -unsigned int -fastuidraw::StrokedPath:: -chunk_of_joins(enum chunk_selection c) const -{ - StrokedPathPrivate *d; - d = static_cast(m_d); - return d->m_chunk_of_joins[c]; -} - -unsigned int -fastuidraw::StrokedPath:: -chunk_of_caps(void) const -{ - StrokedPathPrivate *d; - d = static_cast(m_d); - return d->m_chunk_of_caps; -} - -const fastuidraw::PainterAttributeData& -fastuidraw::StrokedPath:: -square_caps(void) const -{ - StrokedPathPrivate *d; - d = static_cast(m_d); - return d->m_square_caps.data(d->m_path_data, d->m_subset); -} - -const fastuidraw::PainterAttributeData& -fastuidraw::StrokedPath:: -adjustable_caps(void) const -{ - StrokedPathPrivate *d; - d = static_cast(m_d); - return d->m_adjustable_caps.data(d->m_path_data, d->m_subset); -} - -const fastuidraw::PainterAttributeData& -fastuidraw::StrokedPath:: -bevel_joins(void) const -{ - StrokedPathPrivate *d; - d = static_cast(m_d); - return d->m_bevel_joins.data(d->m_path_data, d->m_subset); -} - -const fastuidraw::PainterAttributeData& -fastuidraw::StrokedPath:: -miter_clip_joins(void) const -{ - StrokedPathPrivate *d; - d = static_cast(m_d); - return d->m_miter_clip_joins.data(d->m_path_data, d->m_subset); -} - -const fastuidraw::PainterAttributeData& -fastuidraw::StrokedPath:: -miter_bevel_joins(void) const -{ - StrokedPathPrivate *d; - d = static_cast(m_d); - return d->m_miter_bevel_joins.data(d->m_path_data, d->m_subset); -} - -const fastuidraw::PainterAttributeData& -fastuidraw::StrokedPath:: -miter_joins(void) const -{ - StrokedPathPrivate *d; - d = static_cast(m_d); - return d->m_miter_joins.data(d->m_path_data, d->m_subset); -} - -const fastuidraw::PainterAttributeData& -fastuidraw::StrokedPath:: -rounded_joins(float thresh) const -{ - StrokedPathPrivate *d; - d = static_cast(m_d); - - return (!d->m_empty_path) ? - d->fetch_create(thresh, d->m_rounded_joins) : - d->m_empty_data; -} - -const fastuidraw::PainterAttributeData& +const fastuidraw::StrokedCapsJoins& fastuidraw::StrokedPath:: -rounded_caps(float thresh) const +caps_joins(void) const { StrokedPathPrivate *d; d = static_cast(m_d); - return (!d->m_empty_path) ? - d->fetch_create(thresh, d->m_rounded_caps) : - d->m_empty_data; + return d->m_caps_joins; } diff --git a/src/fastuidraw/painter/stroked_point.cpp b/src/fastuidraw/painter/stroked_point.cpp new file mode 100644 index 000000000..92dcadf7d --- /dev/null +++ b/src/fastuidraw/painter/stroked_point.cpp @@ -0,0 +1,82 @@ +#include + +////////////////////////////////////// +// fastuidraw::StrokedPoint methods +void +fastuidraw::StrokedPoint:: +pack_point(PainterAttribute *dst) const +{ + dst->m_attrib0 = pack_vec4(m_position.x(), + m_position.y(), + m_pre_offset.x(), + m_pre_offset.y()); + + dst->m_attrib1 = pack_vec4(m_distance_from_edge_start, + m_distance_from_contour_start, + m_auxiliary_offset.x(), + m_auxiliary_offset.y()); + + dst->m_attrib2 = uvec4(m_packed_data, + pack_float(m_edge_length), + pack_float(m_open_contour_length), + pack_float(m_closed_contour_length)); +} + +void +fastuidraw::StrokedPoint:: +unpack_point(StrokedPoint *dst, const PainterAttribute &a) +{ + dst->m_position.x() = unpack_float(a.m_attrib0.x()); + dst->m_position.y() = unpack_float(a.m_attrib0.y()); + + dst->m_pre_offset.x() = unpack_float(a.m_attrib0.z()); + dst->m_pre_offset.y() = unpack_float(a.m_attrib0.w()); + + dst->m_distance_from_edge_start = unpack_float(a.m_attrib1.x()); + dst->m_distance_from_contour_start = unpack_float(a.m_attrib1.y()); + dst->m_auxiliary_offset.x() = unpack_float(a.m_attrib1.z()); + dst->m_auxiliary_offset.y() = unpack_float(a.m_attrib1.w()); + + dst->m_packed_data = a.m_attrib2.x(); + dst->m_edge_length = unpack_float(a.m_attrib2.y()); + dst->m_open_contour_length = unpack_float(a.m_attrib2.z()); + dst->m_closed_contour_length = unpack_float(a.m_attrib2.w()); +} + +float +fastuidraw::StrokedPoint:: +miter_distance(void) const +{ + enum offset_type_t tp; + tp = offset_type(); + + switch(tp) + { + case offset_miter_clip_join: + { + vec2 n0(m_pre_offset), Jn0(n0.y(), -n0.x()); + vec2 n1(m_auxiliary_offset), Jn1(n1.y(), -n1.x()); + float r, det; + + det = dot(Jn1, n0); + r = (det != 0.0f) ? (dot(n0, n1) - 1.0f) / det : 0.0f; + return t_sqrt(1.0f + r * r); + } + + case offset_miter_bevel_join: + case offset_miter_join: + { + vec2 n0(m_pre_offset), n1(m_auxiliary_offset); + vec2 n0_plus_n1(n0 + n1); + float den; + + den = 1.0f + dot(n0, n1); + return den != 0.0f ? + n0_plus_n1.magnitude() / den : + 0.0f; + } + + default: + return 0.0f; + } +} diff --git a/src/fastuidraw/path.cpp b/src/fastuidraw/path.cpp index 0629b0e20..56cc53d07 100644 --- a/src/fastuidraw/path.cpp +++ b/src/fastuidraw/path.cpp @@ -24,23 +24,11 @@ #include #include "private/util_private.hpp" #include "private/path_util_private.hpp" +#include "private/util_private_ostream.hpp" +#include "private/bounding_box.hpp" namespace { - class analytic_point_data:public fastuidraw::TessellatedPath::point - { - public: - analytic_point_data(float t, const fastuidraw::vec2 &p); - - bool - operator<(const analytic_point_data &rhs) const - { - return m_time < rhs.m_time; - } - - float m_time; - }; - inline float compute_distance(const fastuidraw::vec2 &a, @@ -62,51 +50,90 @@ namespace class Tessellator:fastuidraw::noncopyable { public: - Tessellator(const fastuidraw::TessellatedPath::TessellationParams &tess_params, - const fastuidraw::PathContour::interpolator_generic *h): + typedef fastuidraw::PathContour PathContour; + typedef PathContour::interpolator_generic interpolator_generic; + typedef interpolator_generic::tessellated_region tessellated_region; + typedef fastuidraw::TessellatedPath TessellatedPath; + typedef TessellatedPath::TessellationParams TessellationParams; + + Tessellator(const TessellationParams &tess_params, + const interpolator_generic *h): m_h(h), m_thresh(tess_params), - m_max_recursion(fastuidraw::uint32_log2(tess_params.m_max_segments)), - m_max_size(tess_params.m_max_segments + 1) + m_minimum_tessellation_recursion(m_h->minimum_tessellation_recursion()) { } - virtual - ~Tessellator() - {} - unsigned int - dump(fastuidraw::c_array out_data, - fastuidraw::c_array out_threshholds); + dump(TessellatedPath::PointStorage *out_data, + float *out_threshhold) const; private: + unsigned int + tessellation_worker(unsigned int recurse_level, + TessellatedPath::PointStorage *out_data, + fastuidraw::reference_counted_ptr in_src, + float *out_threshhold) const; + void - tessellation_worker(unsigned int idx_p, unsigned int idx_q, - unsigned int recursion_level, - fastuidraw::PathContour::interpolator_generic::tessellated_region *in_src, - fastuidraw::c_array out_threshholds); + add_point(TessellatedPath::PointStorage *out_data, + const fastuidraw::vec2 &pt) const + { + TessellatedPath::point P; + P.m_p = pt; + out_data->add_point(P); + } - unsigned int - fill_data(fastuidraw::c_array out_data, - fastuidraw::c_array out_threshholds); + const interpolator_generic *m_h; + TessellationParams m_thresh; + unsigned int m_minimum_tessellation_recursion; + }; - bool - tessellation_satsified(unsigned int recurse_level, - fastuidraw::c_array threshholds) + class ArcTessellator + { + public: + typedef fastuidraw::PathContour PathContour; + typedef PathContour::interpolator_generic interpolator_generic; + typedef interpolator_generic::tessellated_region tessellated_region; + typedef fastuidraw::ArcTessellatedPath ArcTessellatedPath; + typedef ArcTessellatedPath::TessellationParams TessellationParams; + + ArcTessellator(const TessellationParams &tess_params, + const interpolator_generic *h): + m_h(h), + m_thresh(tess_params), + m_minimum_tessellation_recursion(m_h->minimum_tessellation_recursion()) { - return (threshholds[m_thresh.m_threshhold_type] < m_thresh.m_threshhold); } - void - complete_threshholds(unsigned int idx_start, - unsigned int idx_mid, - unsigned int idx_end, - fastuidraw::c_array out_threshholds); - - const fastuidraw::PathContour::interpolator_generic *m_h; - fastuidraw::TessellatedPath::TessellationParams m_thresh; - unsigned int m_max_recursion, m_max_size; - std::vector m_data; + unsigned int + dump(ArcTessellatedPath::SegmentStorage *out_data, + float *out_threshhold) const; + + private: + unsigned int + tessellation_worker(unsigned int recurse_level, + ArcTessellatedPath::SegmentStorage *out_data, + fastuidraw::reference_counted_ptr in_L, + fastuidraw::reference_counted_ptr in_R, + fastuidraw::vec2 start, fastuidraw::vec2 mid, fastuidraw::vec2 end, + float *out_threshhold) const; + + unsigned int + recurse(unsigned int recurse_level, + ArcTessellatedPath::SegmentStorage *out_data, + fastuidraw::reference_counted_ptr L0, + fastuidraw::reference_counted_ptr L1, + fastuidraw::reference_counted_ptr R0, + fastuidraw::reference_counted_ptr R1, + fastuidraw::vec2 start, fastuidraw::vec2 midL, + fastuidraw::vec2 mid, fastuidraw::vec2 midR, + fastuidraw::vec2 end, + float *out_threshhold) const; + + const interpolator_generic *m_h; + TessellationParams m_thresh; + unsigned int m_minimum_tessellation_recursion; }; class InterpolatorBasePrivate @@ -177,7 +204,7 @@ namespace init(void); fastuidraw::vec2 m_min_bb, m_max_bb; - BezierTessRegion m_start_region; + fastuidraw::reference_counted_ptr m_start_region; std::vector m_pts; fastuidraw::vecN, 2> m_work_room; }; @@ -189,6 +216,37 @@ namespace float m_start_angle; fastuidraw::vec2 m_center; fastuidraw::vec2 m_min_bb, m_max_bb; + + void + compute_bb(void) + { + using namespace fastuidraw; + + vec2 p0, p1, z; + float d; + + p0 = vec2(t_cos(m_start_angle), + t_sin(m_start_angle)); + + p1 = vec2(t_cos(m_start_angle + m_angle_speed), + t_sin(m_start_angle + m_angle_speed)); + + d = 1.0f - t_cos(m_angle_speed * 0.5f); + + /* + * bb represents the bounding box of an arc + * [m_start_angle, m_start_angle + m_angle_speed) + * with radius one centered at the origin + */ + BoundingBox bb; + bb.union_point(p0); + bb.union_point(p1); + bb.union_point(p0 + d * z); + bb.union_point(p1 + d * z); + + m_min_bb = m_center + m_radius * bb.min_point(); + m_max_bb = m_center + m_radius * bb.max_point(); + } }; class PathContourPrivate @@ -209,41 +267,49 @@ namespace class PathPrivate; + template class TessellatedPathList { public: - typedef fastuidraw::TessellatedPath TessellatedPath; - typedef fastuidraw::reference_counted_ptr tessellated_path_ref; + typedef pTessedClass TessedPath; + typedef typename TessedPath::TessellationParams TessParams; + typedef fastuidraw::reference_counted_ptr TessedPathRef; explicit - TessellatedPathList(enum fastuidraw::TessellatedPath::threshhold_type_t tp): - m_type(tp), + TessellatedPathList(void): m_done(false) {} - const tessellated_path_ref& - tessellation(PathPrivate &p, float thresh); + const TessedPathRef& + tessellation(const fastuidraw::Path &path, float thresh); void clear(void) { - m_tesses.clear(); + m_data.clear(); m_done = false; } - void - add_tess(const tessellated_path_ref &tess); - - enum fastuidraw::TessellatedPath::threshhold_type_t - type(void) const + private: + class reverse_compare_thresh { - return m_type; - } + public: + bool + operator()(const TessedPathRef &lhs, float rhs) const + { + return lhs->effective_threshhold() > rhs; + } + + bool + operator()(const TessedPathRef &lhs, + const TessedPathRef &rhs) const + { + return lhs->effective_threshhold() > rhs->effective_threshhold(); + } + }; - private: - enum fastuidraw::TessellatedPath::threshhold_type_t m_type; bool m_done; - std::vector m_tesses; + std::vector m_data; }; class PathPrivate:fastuidraw::noncopyable @@ -268,11 +334,9 @@ namespace void clear_tesses(void); - void - add_tess(const TessellatedPathList::tessellated_path_ref &tess); - std::vector > m_contours; - std::vector m_tesses; + TessellatedPathList m_tess_list; + TessellatedPathList m_arc_tess_list; /* m_start_check_bb gives the index into m_contours that * have not had their bounding box absorbed into @@ -283,144 +347,210 @@ namespace bool m_is_flat; fastuidraw::Path *m_p; }; - - class reverse_compare_thresh - { - public: - explicit - reverse_compare_thresh(enum fastuidraw::TessellatedPath::threshhold_type_t tp): - m_tp(tp) - {} - - bool - operator()(const TessellatedPathList::tessellated_path_ref &lhs, float rhs) const - { - return lhs->effective_threshhold(m_tp) > rhs; - } - - bool - operator()(const TessellatedPathList::tessellated_path_ref &lhs, - const TessellatedPathList::tessellated_path_ref &rhs) const - { - return lhs->effective_threshhold(m_tp) > rhs->effective_threshhold(m_tp); - } - - private: - enum fastuidraw::TessellatedPath::threshhold_type_t m_tp; - }; -} - -//////////////////////////////////// -// analytic_point_data methods -analytic_point_data:: -analytic_point_data(float t, const fastuidraw::vec2 &p): - m_time(t) -{ - m_p = p; } ///////////////////////////////// // Tessellator methods unsigned int Tessellator:: -dump(fastuidraw::c_array out_data, - fastuidraw::c_array out_threshholds) +dump(TessellatedPath::PointStorage *out_data, + float *out_threshhold) const { unsigned int return_value; - std::fill(out_threshholds.begin(), out_threshholds.end(), -1.0f); - return_value = fill_data(out_data, out_threshholds); - out_data = out_data.sub_array(0, return_value); - - /* enforce start and end point values */ - out_data.front().m_p = m_h->start_pt(); - out_data.back().m_p = m_h->end_pt(); - - /* compute distance values along edge */ - out_data[0].m_distance_from_edge_start = 0.0f; - for(unsigned int i = 1, endi = out_data.size(); i < endi; ++i) - { - fastuidraw::vec2 delta; - - delta = out_data[i].m_p - out_data[i-1].m_p; - out_data[i].m_distance_from_edge_start = delta.magnitude() - + out_data[i-1].m_distance_from_edge_start; - } + *out_threshhold = -1.0f; + /* + * tessellate: note that we add the start point, then tessellate + * and then at the end add the end point. By doing so, the points + * are added to m_data in order of time automatically. + */ + add_point(out_data, m_h->start_pt()); + return_value = tessellation_worker(0, out_data, nullptr, out_threshhold); + add_point(out_data, m_h->end_pt()); return return_value; } unsigned int Tessellator:: -fill_data(fastuidraw::c_array out_data, - fastuidraw::c_array out_threshholds) +tessellation_worker(unsigned int recurse_level, + TessellatedPath::PointStorage *out_data, + fastuidraw::reference_counted_ptr in_src, + float *out_threshhold) const { - /* initialize m_data with start and end point data. - */ - m_data.push_back(analytic_point_data(0.0f, m_h->start_pt())); - m_data.push_back(analytic_point_data(1.0f, m_h->end_pt())); + using namespace fastuidraw; + + reference_counted_ptr rgnA, rgnB; + vec2 p; - /* tessellate */ - tessellation_worker(0, 1, 0, nullptr, out_threshholds); + m_h->tessellate(in_src, &rgnA, &rgnB, &p, out_threshhold); - /* sort the values by time */ - std::sort(m_data.begin(), m_data.end()); + if (recurse_level < m_minimum_tessellation_recursion + || (recurse_level <= m_thresh.m_max_recursion + && *out_threshhold > m_thresh.m_threshhold)) + { + float tmpA(-1.0f), tmpB(-1.0f); + unsigned int vA, vB; + + /* NOTE the order of recursing and adding to m_data: + * - first we recurse into the left side + * - second we add the mid-point + * - last we recurse into the right side + * By doing so, we keep the order in time of the points, + * since all points on the left side come before the + * mid point, and all points in the right side come + * after the midpoint. + */ + vA = tessellation_worker(recurse_level + 1, out_data, rgnA, &tmpA); + add_point(out_data, p); + vB = tessellation_worker(recurse_level + 1, out_data, rgnB, &tmpB); - FASTUIDRAWassert(m_data.size() <= out_data.size()); - std::copy(m_data.begin(), m_data.end(), out_data.begin()); - return m_data.size(); + *out_threshhold = t_max(tmpA, tmpB); + return t_max(vA, vB); + } + else + { + add_point(out_data, p); + return recurse_level; + } } -void -Tessellator:: -complete_threshholds(unsigned int idx_start, - unsigned int idx_mid, - unsigned int idx_end, - fastuidraw::c_array out_threshholds) +/////////////////////////////////////// +// ArcTessellator methods +unsigned int +ArcTessellator:: +dump(ArcTessellatedPath::SegmentStorage *out_data, + float *out_threshhold) const { using namespace fastuidraw; - FASTUIDRAWassert(out_threshholds[TessellatedPath::threshhold_curve_distance] >= 0.0f); - FASTUIDRAWunused(idx_start); - FASTUIDRAWunused(idx_mid); - FASTUIDRAWunused(idx_end); - FASTUIDRAWunused(out_threshholds); + reference_counted_ptr L, R; + vec2 mid; + + m_h->tessellate(nullptr, &L, &R, &mid, nullptr); + return tessellation_worker(1, out_data, L, R, m_h->start_pt(), mid, m_h->end_pt(), out_threshhold); } -void -Tessellator:: -tessellation_worker(unsigned int idx_start, unsigned int idx_end, - unsigned int recurse_level, - fastuidraw::PathContour::interpolator_generic::tessellated_region *in_src, - fastuidraw::c_array out_threshholds) +unsigned int +ArcTessellator:: +recurse(unsigned int recurse_level, + ArcTessellatedPath::SegmentStorage *out_data, + fastuidraw::reference_counted_ptr L0, + fastuidraw::reference_counted_ptr L1, + fastuidraw::reference_counted_ptr R0, + fastuidraw::reference_counted_ptr R1, + fastuidraw::vec2 start, fastuidraw::vec2 midL, + fastuidraw::vec2 mid, fastuidraw::vec2 midR, + fastuidraw::vec2 end, + float *out_threshhold) const +{ + float threshL, threshR; + unsigned int vL, vR; + + vL = tessellation_worker(recurse_level + 1u, out_data, L0, L1, + start, midL, mid, &threshL); + vR = tessellation_worker(recurse_level + 1u, out_data, R0, R1, + mid, midR, end, &threshR); + *out_threshhold = fastuidraw::t_max(threshL, threshR); + return fastuidraw::t_max(vL, vR); +} + +unsigned int +ArcTessellator:: +tessellation_worker(unsigned int recurse_level, + ArcTessellatedPath::SegmentStorage *out_data, + fastuidraw::reference_counted_ptr inL, + fastuidraw::reference_counted_ptr inR, + fastuidraw::vec2 start, fastuidraw::vec2 mid, fastuidraw::vec2 end, + float *out_threshhold) const { using namespace fastuidraw; + reference_counted_ptr L0, L1, R0, R1; + vec2 midL, midR; + static float tol(0.00001f); + float threshL, threshR; - unsigned int idx_mid(m_data.size()); - PathContour::interpolator_generic::tessellated_region *rgnA, *rgnB; - vec2 p;; - float t; + m_h->tessellate(inL, &L0, &L1, &midL, nullptr); + m_h->tessellate(inR, &R0, &R1, &midR, nullptr); - m_h->tessellate(in_src, &rgnA, &rgnB, - &t, &p, out_threshholds); + if (recurse_level < m_minimum_tessellation_recursion) + { + return recurse(recurse_level, out_data, + L0, L1, R0, R1, + start, midL, mid, midR, end, + out_threshhold); + } + + /* compute the circle going through start, mid, end */ + vec2 c, v0, v1, n0, n1, p0, p1; + float s, det, r; - complete_threshholds(idx_start, idx_mid, idx_end, out_threshholds); - m_data.push_back(analytic_point_data(t, p)); + p0 = 0.5f * (start + mid); + p1 = 0.5f * (mid + end); - if (recurse_level + 1u < m_max_recursion - && !tessellation_satsified(recurse_level, out_threshholds)) + v0 = start - mid; + n0 = vec2(-v0.y(), v0.x()); + + v1 = mid - end; + n1 = vec2(-v1.y(), v1.x()); + + det = n1.y() * n0.x() - n0.y() * n1.x(); + if (t_abs(det) < tol) { - vecN tmpA(-1.0f), tmpB(-1.0f); - tessellation_worker(idx_start, idx_mid, recurse_level + 1, rgnA, tmpA); - tessellation_worker(idx_mid, idx_end, recurse_level + 1, rgnB, tmpB); - for (unsigned int i = 0; i < TessellatedPath::number_threshholds; ++i) + if (recurse_level <= m_thresh.m_max_recursion) { - out_threshholds[i] = t_max(tmpA[i], tmpB[i]); + return recurse(recurse_level, out_data, + L0, L1, R0, R1, + start, midL, mid, midR, end, + out_threshhold); } + else + { + /* practically flat anyways, just add a line segment */ + ArcTessellatedPath::segment S; + + S.m_type = ArcTessellatedPath::line_segment; + S.m_p = start; + S.m_data = end; + S.m_radius = 0.0f; + out_data->add_segment(S); + + *out_threshhold = 0.0f; + return recurse_level; + } + } + + s = dot(v1, p1 - p0) / det; + c = p0 + s * n0; + r = (c - mid).magnitude(); + + /* we are done if both midL and midR are close enough to C */ + threshL = t_abs(r - (c - midL).magnitude()); + threshR = t_abs(r - (c - midR).magnitude()); + *out_threshhold = t_max(threshL, threshR); + + if (recurse_level <= m_thresh.m_max_recursion + && *out_threshhold > m_thresh.m_threshhold) + { + return recurse(recurse_level, out_data, + L0, L1, R0, R1, + start, midL, mid, midR, end, + out_threshhold); } + else + { + ArcTessellatedPath::segment S; + + S.m_type = ArcTessellatedPath::arc_segment; + S.m_p = c; + S.m_radius = r; - FASTUIDRAWdelete(rgnA); - FASTUIDRAWdelete(rgnB); + S.m_data.x() = std::atan2(start.y() - c.y(), start.x() - c.x()); + S.m_data.y() = std::atan2(end.y() - c.y(), end.x() - c.x()); + out_data->add_segment(S); + + return recurse_level; + } } //////////////////////////////////////// @@ -445,7 +575,8 @@ init(void) m_max_bb.x() = fastuidraw::t_max(m_max_bb.x(), m_pts[i].x()); m_max_bb.y() = fastuidraw::t_max(m_max_bb.y(), m_pts[i].y()); } - m_start_region.m_pts = m_pts; //original region uses original points. + m_start_region = FASTUIDRAWnew BezierTessRegion(); + m_start_region->m_pts = m_pts; //original region uses original points. m_work_room[0].resize(m_pts.size()); m_work_room[1].resize(m_pts.size()); @@ -504,13 +635,22 @@ end_pt(void) const unsigned int fastuidraw::PathContour::interpolator_generic:: produce_tessellation(const TessellatedPath::TessellationParams &tess_params, - c_array out_data, - c_array out_threshholds) const + TessellatedPath::PointStorage *out_data, + float *out_threshhold) const { Tessellator tesser(tess_params, this); - return tesser.dump(out_data, out_threshholds); + return tesser.dump(out_data, out_threshhold); } +unsigned int +fastuidraw::PathContour::interpolator_generic:: +produce_tessellation(const ArcTessellatedPath::TessellationParams &tess_params, + ArcTessellatedPath::SegmentStorage *out_data, + float *out_threshhold) const +{ + ArcTessellator tesser(tess_params, this); + return tesser.dump(out_data, out_threshhold); +} //////////////////////////////////// // fastuidraw::PathContour::bezier methods @@ -599,24 +739,24 @@ approximate_bounding_box(vec2 *out_min_bb, vec2 *out_max_bb) const void fastuidraw::PathContour::bezier:: -tessellate(tessellated_region *in_region, - tessellated_region **out_regionA, tessellated_region **out_regionB, - float *out_t, vec2 *out_p, - c_array out_threshholds) const +tessellate(reference_counted_ptr in_region, + reference_counted_ptr *out_regionA, + reference_counted_ptr *out_regionB, + vec2 *out_p, float *out_threshhold) const { BezierPrivate *d; d = static_cast(m_d); - if (in_region == nullptr) + if (!in_region) { - in_region = &d->m_start_region; + in_region = d->m_start_region; } BezierTessRegion *in_region_casted; - FASTUIDRAWassert(dynamic_cast(in_region) != nullptr); - in_region_casted = static_cast(in_region); + FASTUIDRAWassert(dynamic_cast(in_region.get()) != nullptr); + in_region_casted = static_cast(in_region.get()); - BezierTessRegion *newA, *newB; + reference_counted_ptr newA, newB; newA = FASTUIDRAWnew BezierTessRegion(in_region_casted, true); newB = FASTUIDRAWnew BezierTessRegion(in_region_casted, false); @@ -656,11 +796,12 @@ tessellate(tessellated_region *in_region, *out_regionA = newA; *out_regionB = newB; - *out_t = newA->m_end; *out_p = newA->m_pts.back(); - out_threshholds[TessellatedPath::threshhold_curve_distance] - = t_max(newA->compute_curve_distance(), newB->compute_curve_distance()); + if (out_threshhold) + { + *out_threshhold = t_max(newA->compute_curve_distance(), newB->compute_curve_distance()); + } } fastuidraw::PathContour::interpolator_base* @@ -670,6 +811,16 @@ deep_copy(const reference_counted_ptr &prev) const return FASTUIDRAWnew bezier(*this, prev); } +unsigned int +fastuidraw::PathContour::bezier:: +minimum_tessellation_recursion(void) const +{ + BezierPrivate *d; + d = static_cast(m_d); + + return 1 + uint32_log2(d->m_pts.size()); +} + ////////////////////////////////////// // fastuidraw::PathContour::flat methods bool @@ -682,21 +833,43 @@ is_flat(void) const unsigned int fastuidraw::PathContour::flat:: produce_tessellation(const TessellatedPath::TessellationParams&, - c_array out_data, - c_array out_threshholds) const + TessellatedPath::PointStorage *out_data, + float *out_threshhold) const { + TessellatedPath::point p0, p1; vec2 delta(end_pt() - start_pt()); float mag(delta.magnitude()); - out_data[0].m_p = start_pt(); - out_data[0].m_distance_from_edge_start = 0.0f; + p0.m_p = start_pt(); + p0.m_distance_from_edge_start = 0.0f; + out_data->add_point(p0); + + p1.m_p = end_pt(); + p1.m_distance_from_edge_start = mag; + out_data->add_point(p1); + + *out_threshhold = 0.0f; + return 0; +} + +unsigned int +fastuidraw::PathContour::flat:: +produce_tessellation(const ArcTessellatedPath::TessellationParams &tess_params, + ArcTessellatedPath::SegmentStorage *out_data, + float *out_threshhold) const +{ + FASTUIDRAWunused(tess_params); - out_data[1].m_p = end_pt(); - out_data[1].m_distance_from_edge_start = mag; + ArcTessellatedPath::segment S; - std::fill(out_threshholds.begin(), out_threshholds.end(), 0.0f); + S.m_type = ArcTessellatedPath::line_segment; + S.m_p = start_pt(); + S.m_data = end_pt(); + S.m_radius = 0.0f; + out_data->add_segment(S); - return 2; + *out_threshhold = 0.0f; + return 0; } fastuidraw::PathContour::interpolator_base* @@ -765,18 +938,30 @@ arc(const reference_counted_ptr &start, float angle, co d->m_start_angle = std::atan2(start_center.y(), start_center.x()); d->m_angle_speed = angle_coeff_dir * angle; - vec2 p0, p1; - p0 = vec2(std::cos(d->m_start_angle), std::sin(d->m_start_angle)); - p1 = vec2(std::cos(d->m_start_angle + d->m_angle_speed), std::sin(d->m_start_angle + d->m_angle_speed)); + d->compute_bb(); +} + +fastuidraw::PathContour::arc:: +arc(const reference_counted_ptr &start, + const vec2 ¢er, const vec2 &end): + fastuidraw::PathContour::interpolator_base(start, end) +{ + ArcPrivate *d; + d = FASTUIDRAWnew ArcPrivate(); + m_d = d; + + vec2 start_center(start_pt() - center); + vec2 end_center(end - center); + float end_angle; - d->m_min_bb.x() = fastuidraw::t_min(p0.x(), p1.x()); - d->m_min_bb.y() = fastuidraw::t_min(p0.y(), p1.y()); + end_angle = std::atan2(end_center.y(), end_center.x()); - d->m_max_bb.x() = fastuidraw::t_max(p0.x(), p1.x()); - d->m_max_bb.y() = fastuidraw::t_max(p0.y(), p1.y()); + d->m_center = center; + d->m_radius = start_center.magnitude(); + d->m_start_angle = std::atan2(start_center.y(), start_center.x()); + d->m_angle_speed = end_angle - d->m_start_angle; - d->m_min_bb = d->m_center + d->m_radius * d->m_min_bb; - d->m_max_bb = d->m_center + d->m_radius * d->m_max_bb; + d->compute_bb(); } fastuidraw::PathContour::arc:: @@ -807,13 +992,12 @@ is_flat(void) const unsigned int fastuidraw::PathContour::arc:: produce_tessellation(const TessellatedPath::TessellationParams &tess_params, - c_array out_data, - c_array out_threshholds) const + TessellatedPath::PointStorage *out_data, + float *out_threshhold) const { ArcPrivate *d; d = static_cast(m_d); - unsigned int return_value; - float s, c, a, da; + float a, da; unsigned int needed_size; float delta_angle; @@ -824,18 +1008,53 @@ produce_tessellation(const TessellatedPath::TessellationParams &tess_params, da = 0.0f; for(unsigned int i = 0; i <= needed_size; ++i, a += delta_angle, da += delta_angle) { - s = d->m_radius * std::sin(a); - c = d->m_radius * std::cos(a); - out_data[i].m_p = d->m_center + vec2(c, s); - out_data[i].m_distance_from_edge_start = t_abs(da) * d->m_radius; + TessellatedPath::point p; + + if (i == 0) + { + p.m_p = start_pt(); + } + else if (i == needed_size) + { + p.m_p = end_pt(); + } + else + { + float c, s; + + c = d->m_radius * std::cos(a); + s = d->m_radius * std::sin(a); + p.m_p = d->m_center + vec2(c, s); + } + + p.m_distance_from_edge_start = t_abs(da) * d->m_radius; + out_data->add_point(p); } - out_data[0].m_p = start_pt(); - out_data[needed_size].m_p = end_pt(); - out_threshholds[TessellatedPath::threshhold_curve_distance] = d->m_radius * (1.0f - t_cos(delta_angle * 0.5f)); + *out_threshhold = d->m_radius * (1.0f - t_cos(delta_angle * 0.5f)); + return 0; +} - return_value = needed_size + 1; - return return_value; +unsigned int +fastuidraw::PathContour::arc:: +produce_tessellation(const ArcTessellatedPath::TessellationParams &tess_params, + ArcTessellatedPath::SegmentStorage *out_data, + float *out_threshhold) const +{ + ArcPrivate *d; + ArcTessellatedPath::segment S; + d = static_cast(m_d); + + FASTUIDRAWunused(tess_params); + S.m_type = ArcTessellatedPath::arc_segment; + S.m_p = d->m_center; + S.m_data.x() = d->m_start_angle; + S.m_data.y() = d->m_start_angle + d->m_angle_speed; + S.m_radius = d->m_radius; + out_data->add_segment(S); + + *out_threshhold = 0.0f; + return 0; } void @@ -1174,33 +1393,22 @@ approximate_bounding_box(vec2 *out_min_bb, vec2 *out_max_bb) const ///////////////////////////////// // TessellatedPathList methods -void -TessellatedPathList:: -add_tess(const tessellated_path_ref &tess) -{ - if (m_tesses.empty() - || m_tesses.back()->effective_threshhold(m_type) > tess->effective_threshhold(m_type)) - { - m_tesses.push_back(tess); - } -} - -const TessellatedPathList::tessellated_path_ref& -TessellatedPathList:: -tessellation(PathPrivate &path, float thresh) +template +const typename TessellatedPathList::TessedPathRef& +TessellatedPathList:: +tessellation(const fastuidraw::Path &path, float thresh) { using namespace fastuidraw; - if (m_tesses.empty()) + if (m_data.empty()) { - TessellatedPath::TessellationParams params; + TessParams params; vec2 bb_min, bb_max, bb_size; - /* always prefer to use distance threshholds over - * curvature; use the size of the bounding box times + /* use the size of the bounding box times * 1/500 as teh default tessellation factor. */ - if (path.m_p->approximate_bounding_box(&bb_min, &bb_max)) + if (path.approximate_bounding_box(&bb_min, &bb_max)) { vec2 bb_size; float d; @@ -1208,78 +1416,82 @@ tessellation(PathPrivate &path, float thresh) d = t_max(bb_size.x(), bb_size.y()); if (d > 0.0f) { - params.curve_distance_tessellate(d / 500.0f); + params.threshhold(d / 500.0f); } } - path.add_tess(FASTUIDRAWnew TessellatedPath(*path.m_p, params)); + m_data.push_back(FASTUIDRAWnew TessedPath(path, params)); } - if (thresh <= 0.0 || path.m_p->is_flat()) + if (thresh <= 0.0 || path.is_flat()) { - return m_tesses.front(); + return m_data.front(); } - if (m_tesses.back()->effective_threshhold(m_type) <= thresh) + if (m_data.back()->effective_threshhold() <= thresh) { - std::vector::const_iterator iter; - iter = std::lower_bound(m_tesses.begin(), - m_tesses.end(), + typename std::vector::const_iterator iter; + iter = std::lower_bound(m_data.begin(), + m_data.end(), thresh, - reverse_compare_thresh(m_type)); + reverse_compare_thresh()); - FASTUIDRAWassert(iter != m_tesses.end()); + FASTUIDRAWassert(iter != m_data.end()); FASTUIDRAWassert(*iter); - FASTUIDRAWassert((*iter)->effective_threshhold(m_type) <= thresh); + FASTUIDRAWassert((*iter)->effective_threshhold() <= thresh); return *iter; } if (m_done) { - return m_tesses.back(); + return m_data.back(); } - tessellated_path_ref prev_ref, ref; - TessellatedPath::TessellationParams params; + TessedPathRef prev_ref, ref; + TessParams params; - ref = m_tesses.back(); + ref = m_data.back(); params - .max_segments(ref->max_segments()) - .threshhold_type(m_type) - .threshhold(ref->effective_threshhold(m_type)); + .max_recursion(ref->max_recursion()) + .threshhold(ref->effective_threshhold()); - while(!m_done && ref->effective_threshhold(m_type) > thresh) + while(!m_done && ref->effective_threshhold() > thresh) { params.m_threshhold *= 0.5f; while(!m_done - && ref->effective_threshhold(m_type) > params.m_threshhold) + && ref->effective_threshhold() > params.m_threshhold) { float last_tess; - params.m_max_segments *= 2; - last_tess = ref->effective_threshhold(m_type); - ref = FASTUIDRAWnew TessellatedPath(*path.m_p, params); - if (last_tess > ref->effective_threshhold(m_type)) + ++params.m_max_recursion; + last_tess = ref->effective_threshhold(); + ref = FASTUIDRAWnew TessedPath(path, params); + + if (last_tess > ref->effective_threshhold()) { - path.add_tess(ref); - FASTUIDRAWassert(m_tesses.back() == ref); + m_data.push_back(ref); + + /* + std::cout << "Add type = " << typeid(TessedPath).name() + << "(max_segs = " << ref->max_segments() << ", tess_factor = " + << ref->effective_threshhold() + << ")\n"; + */ } else { m_done = true; /* - *std::cout << "Tapped out on type = " - * << m_type << " (max_segs = " - * << ref->max_segments() << ", tess_factor = " - * << ref->effective_threshhold(m_type) - * << ", num_points = " << ref->point_data().size() - * << ")\n"; - */ + std::cout << "Tapped out on type = " << typeid(TessedPath).name() + << "(max_segs = " << ref->max_segments() << ", tess_factor = " + << ref->effective_threshhold() + << ")\n"; + */ } } } - return m_tesses.back(); + return m_data.back(); } ///////////////////////////////// @@ -1290,18 +1502,13 @@ PathPrivate(fastuidraw::Path *p): m_is_flat(true), m_p(p) { - for(int i = 0; i < fastuidraw::TessellatedPath::number_threshholds; ++i) - { - enum fastuidraw::TessellatedPath::threshhold_type_t tp; - tp = static_cast(i); - m_tesses.push_back(TessellatedPathList(tp)); - } } PathPrivate:: PathPrivate(fastuidraw::Path *p, const PathPrivate &obj): m_contours(obj.m_contours), - m_tesses(obj.m_tesses), + m_tess_list(obj.m_tess_list), + m_arc_tess_list(obj.m_arc_tess_list), m_start_check_bb(obj.m_start_check_bb), m_max_bb(obj.m_max_bb), m_min_bb(obj.m_min_bb), @@ -1311,7 +1518,7 @@ PathPrivate(fastuidraw::Path *p, const PathPrivate &obj): /* if the last contour is not ended, we need to do a * deep copy on it. */ - if (!m_contours.back()->ended()) + if (!m_contours.empty() && !m_contours.back()->ended()) { m_contours.back() = m_contours.back()->deep_copy(); m_is_flat = m_is_flat && m_contours.back()->is_flat(); @@ -1335,20 +1542,8 @@ void PathPrivate:: clear_tesses(void) { - for(TessellatedPathList &T : m_tesses) - { - T.clear(); - } -} - -void -PathPrivate:: -add_tess(const TessellatedPathList::tessellated_path_ref &tess) -{ - for(TessellatedPathList &T : m_tesses) - { - T.add_tess(tess); - } + m_tess_list.clear(); + m_arc_tess_list.clear(); } ///////////////////////////////////////// @@ -1542,13 +1737,27 @@ tessellation(void) const const fastuidraw::reference_counted_ptr& fastuidraw::Path:: -tessellation(float thresh, enum TessellatedPath::threshhold_type_t tp) const +tessellation(float thresh) const { PathPrivate *d; d = static_cast(m_d); + return d->m_tess_list.tessellation(*this, thresh); +} - FASTUIDRAWassert(d->m_tesses[tp].type() == tp); - return d->m_tesses[tp].tessellation(*d, thresh); +const fastuidraw::reference_counted_ptr& +fastuidraw::Path:: +arc_tessellation(void) const +{ + return arc_tessellation(-1.0f); +} + +const fastuidraw::reference_counted_ptr& +fastuidraw::Path:: +arc_tessellation(float thresh) const +{ + PathPrivate *d; + d = static_cast(m_d); + return d->m_arc_tess_list.tessellation(*this, thresh); } bool diff --git a/src/fastuidraw/private/bounding_box.hpp b/src/fastuidraw/private/bounding_box.hpp index 8c1800113..0aaae4888 100644 --- a/src/fastuidraw/private/bounding_box.hpp +++ b/src/fastuidraw/private/bounding_box.hpp @@ -33,6 +33,8 @@ namespace fastuidraw typedef vecN pt_type; BoundingBox(void): + m_min(T(0), T(0)), + m_max(T(0), T(0)), m_empty(true) {} diff --git a/src/fastuidraw/private/path_util_private.cpp b/src/fastuidraw/private/path_util_private.cpp index 1ef1a3e29..52a7c68fa 100644 --- a/src/fastuidraw/private/path_util_private.cpp +++ b/src/fastuidraw/private/path_util_private.cpp @@ -25,18 +25,7 @@ fastuidraw::detail:: number_segments_for_tessellation(float radius, float arc_angle, const TessellatedPath::TessellationParams &P) { - float d, needed_sizef, theta; - unsigned int needed_size; - - d = t_max(1.0f - P.m_threshhold / radius, 0.5f); - theta = t_max(0.00001f, 0.5f * std::acos(d)); - needed_sizef = t_abs(arc_angle) / theta; - - /* we ask for one more than necessary, to ensure that we BEAT - * the tessellation requirement. - */ - needed_size = 1 + fastuidraw::t_max(3u, static_cast(needed_sizef)); - return fastuidraw::t_min(needed_size, P.m_max_segments); + return number_segments_for_tessellation(arc_angle, P.m_threshhold / radius); } unsigned int @@ -54,3 +43,16 @@ number_segments_for_tessellation(float arc_angle, float distance_thresh) */ return 1 + fastuidraw::t_max(3u, static_cast(needed_sizef)); } + +float +fastuidraw::detail:: +distance_to_line(const vec2 &q, const vec2 &p1, const vec2 &p2) +{ + vec2 delta(p2 - p1); + float num, den; + + num = delta.y() * q.x() - delta.x() * q.y() - p2.x() * p1.y() - p1.x() * p2.y(); + den = delta.magnitudeSq(); + + return t_sqrt(num / den); +} diff --git a/src/fastuidraw/private/path_util_private.hpp b/src/fastuidraw/private/path_util_private.hpp index 0eda81440..676ae9a48 100644 --- a/src/fastuidraw/private/path_util_private.hpp +++ b/src/fastuidraw/private/path_util_private.hpp @@ -31,5 +31,8 @@ namespace fastuidraw unsigned int number_segments_for_tessellation(float arc_angle, float distance_thresh); + + float + distance_to_line(const vec2 &q, const vec2 &p0, const vec2 &p1); } } diff --git a/src/fastuidraw/tessellated_path.cpp b/src/fastuidraw/tessellated_path.cpp index 9090cc064..913aff009 100644 --- a/src/fastuidraw/tessellated_path.cpp +++ b/src/fastuidraw/tessellated_path.cpp @@ -24,6 +24,7 @@ #include #include #include "private/util_private.hpp" +#include "private/bounding_box.hpp" namespace { @@ -35,10 +36,10 @@ namespace std::vector > > m_edge_ranges; std::vector m_point_data; - fastuidraw::vec2 m_box_min, m_box_max; + fastuidraw::BoundingBox m_bounding_box; fastuidraw::TessellatedPath::TessellationParams m_params; - fastuidraw::vecN m_effective_threshholds; - unsigned int m_max_segments; + float m_effective_threshhold; + unsigned int m_max_segments, m_max_recursion; fastuidraw::reference_counted_ptr m_stroked; fastuidraw::reference_counted_ptr m_filled; }; @@ -50,78 +51,93 @@ TessellatedPathPrivate:: TessellatedPathPrivate(const fastuidraw::Path &input, fastuidraw::TessellatedPath::TessellationParams TP): m_edge_ranges(input.number_contours()), - m_box_min(0.0f, 0.0f), - m_box_max(0.0f, 0.0f), m_params(TP), - m_effective_threshholds(0.0f), - m_max_segments(0u) + m_effective_threshhold(0.0f), + m_max_segments(0u), + m_max_recursion(0u) { - using namespace fastuidraw; +} + +////////////////////////////////////////////////////////// +// fastuidraw::TessellatedPath::PointStorage methods +void +fastuidraw::TessellatedPath::PointStorage:: +add_point(const point &p) +{ + std::vector *d; + d = static_cast*>(m_d); + d->push_back(p); +} + +////////////////////////////////////// +// fastuidraw::TessellatedPath methods +fastuidraw::TessellatedPath:: +TessellatedPath(const Path &input, + fastuidraw::TessellatedPath::TessellationParams TP) +{ + TessellatedPathPrivate *d; + m_d = d = FASTUIDRAWnew TessellatedPathPrivate(input, TP); if (input.number_contours() > 0) { - std::list > temp; - std::vector work_room; - std::list >::const_iterator iter, end_iter; + std::list > temp; + std::vector work_room; + std::list >::const_iterator iter, end_iter; for(unsigned int loc = 0, o = 0, endo = input.number_contours(); o < endo; ++o) { reference_counted_ptr contour(input.contour(o)); float contour_length(0.0f), open_contour_length(0.0f), closed_contour_length(0.0f); - std::list >::iterator start_contour; + std::list >::iterator start_contour; - m_edge_ranges[o].resize(contour->number_points()); + d->m_edge_ranges[o].resize(contour->number_points()); for(unsigned int e = 0, ende = contour->number_points(); e < ende; ++e) { - unsigned int needed; - vecN tmp; - - work_room.resize(m_params.m_max_segments + 1); - temp.push_back(std::vector()); - needed = contour->interpolator(e)->produce_tessellation(m_params, - make_c_array(work_room), - tmp); - m_edge_ranges[o][e] = range_type(loc, loc + needed); + unsigned int needed, recurse_depth; + float tmp; + PointStorage point_storage; + + FASTUIDRAWassert(work_room.empty()); + point_storage.m_d = &work_room; + recurse_depth = contour->interpolator(e)->produce_tessellation(d->m_params, &point_storage, &tmp); + d->m_max_recursion = t_max(d->m_max_recursion, recurse_depth); + + needed = work_room.size(); + d->m_edge_ranges[o][e] = range_type(loc, loc + needed); loc += needed; FASTUIDRAWassert(needed > 0u); - m_max_segments = t_max(m_max_segments, needed - 1); - for (unsigned int i = 0; i < TessellatedPath::number_threshholds; ++i) - { - m_effective_threshholds[i] = t_max(m_effective_threshholds[i], tmp[i]); - } + d->m_max_segments = t_max(d->m_max_segments, needed - 1); + d->m_effective_threshhold = t_max(d->m_effective_threshhold, tmp); - work_room.resize(needed); for(unsigned int n = 0; n < work_room.size(); ++n) { - const vec2 &pt(work_room[n].m_p); - - work_room[n].m_distance_from_contour_start - = contour_length + work_room[n].m_distance_from_edge_start; + d->m_bounding_box.union_point(work_room[n].m_p); - if (o == 0 && e == 0 && n == 0) + if (n != 0) { - m_box_min = pt; - m_box_max = pt; + vec2 delta; + float l; + + delta = work_room[n].m_p - work_room[n - 1].m_p; + l = delta.magnitude(); + + work_room[n].m_distance_from_edge_start = l + work_room[n - 1].m_distance_from_edge_start; + contour_length += l; } else { - m_box_min.x() = t_min(m_box_min.x(), pt.x()); - m_box_min.y() = t_min(m_box_min.y(), pt.y()); - m_box_max.x() = t_max(m_box_max.x(), pt.x()); - m_box_max.y() = t_max(m_box_max.y(), pt.y()); + work_room[n].m_distance_from_edge_start = 0.0f; } + + work_room[n].m_distance_from_contour_start = contour_length; } - std::list >::iterator t; - t = temp.insert(temp.end(), work_room); - if (e == 0) + for(unsigned int n = 0; n < needed; ++n) { - start_contour = t; + work_room[n].m_edge_length = work_room[needed - 1].m_distance_from_edge_start; } - contour_length = temp.back().back().m_distance_from_contour_start; - if (e + 2 == ende) { open_contour_length = contour_length; @@ -131,13 +147,16 @@ TessellatedPathPrivate(const fastuidraw::Path &input, closed_contour_length = contour_length; } - for(unsigned int n = 0; n < needed; ++n) + std::list >::iterator t; + t = temp.insert(temp.end(), std::vector()); + t->swap(work_room); + if (e == 0) { - (*t)[n].m_edge_length = (*t)[needed - 1].m_distance_from_edge_start; + start_contour = t; } } - for(std::list >::iterator + for(std::list >::iterator t = start_contour, endt = temp.end(); t != endt; ++t) { for(unsigned int e = 0, ende = t->size(); e < ende; ++e) @@ -150,27 +169,14 @@ TessellatedPathPrivate(const fastuidraw::Path &input, unsigned int total_needed; - total_needed = m_edge_ranges.back().back().m_end; - m_point_data.reserve(total_needed); + total_needed = d->m_edge_ranges.back().back().m_end; + d->m_point_data.reserve(total_needed); for(iter = temp.begin(), end_iter = temp.end(); iter != end_iter; ++iter) { - std::copy(iter->begin(), iter->end(), std::back_inserter(m_point_data)); + std::copy(iter->begin(), iter->end(), std::back_inserter(d->m_point_data)); } - FASTUIDRAWassert(total_needed == m_point_data.size()); + FASTUIDRAWassert(total_needed == d->m_point_data.size()); } - else - { - m_box_min = m_box_max = fastuidraw::vec2(0.0f, 0.0f); - } -} - -////////////////////////////////////// -// fastuidraw::TessellatedPath methods -fastuidraw::TessellatedPath:: -TessellatedPath(const Path &input, - fastuidraw::TessellatedPath::TessellationParams TP) -{ - m_d = FASTUIDRAWnew TessellatedPathPrivate(input, TP); } fastuidraw::TessellatedPath:: @@ -220,12 +226,11 @@ tessellation_parameters(void) const float fastuidraw::TessellatedPath:: -effective_threshhold(enum threshhold_type_t tp) const +effective_threshhold(void) const { TessellatedPathPrivate *d; d = static_cast(m_d); - return tp < d->m_effective_threshholds.size() ? - d->m_effective_threshholds[tp] : 0.0f; + return d->m_effective_threshhold; } unsigned int @@ -237,6 +242,15 @@ max_segments(void) const return d->m_max_segments; } +unsigned int +fastuidraw::TessellatedPath:: +max_recursion(void) const +{ + TessellatedPathPrivate *d; + d = static_cast(m_d); + return d->m_max_recursion; +} + fastuidraw::c_array fastuidraw::TessellatedPath:: point_data(void) const @@ -343,7 +357,7 @@ bounding_box_min(void) const TessellatedPathPrivate *d; d = static_cast(m_d); - return d->m_box_min; + return d->m_bounding_box.min_point(); } fastuidraw::vec2 @@ -353,7 +367,7 @@ bounding_box_max(void) const TessellatedPathPrivate *d; d = static_cast(m_d); - return d->m_box_max; + return d->m_bounding_box.max_point(); } fastuidraw::vec2 @@ -363,5 +377,5 @@ bounding_box_size(void) const TessellatedPathPrivate *d; d = static_cast(m_d); - return d->m_box_max - d->m_box_min; + return d->m_bounding_box.size(); }