1#include <iostream>
2#include <sstream>
3#include <fstream>
4#include <algorithm>
5#include <bitset>
6#include <math.h>
7
8#include <fastuidraw/util/util.hpp>
9#include <fastuidraw/path_dash_effect.hpp>
10#include <fastuidraw/painter/attribute_data/stroked_point.hpp>
11#include <fastuidraw/painter/attribute_data/arc_stroked_point.hpp>
12
13#include "sdl_painter_demo.hpp"
14#include "simple_time.hpp"
15#include "PanZoomTracker.hpp"
16#include "read_path.hpp"
17#include "path_util.hpp"
18#include "read_dash_pattern.hpp"
19#include "ImageLoader.hpp"
20#include "colorstop_command_line.hpp"
21#include "cycle_value.hpp"
22#include "ostream_utility.hpp"
23#include "text_helper.hpp"
24#include "command_line_list.hpp"
25
26using namespace fastuidraw;
27
28c_string
29on_off(bool v)
30{
31 return v ? "ON" : "OFF";
32}
33
34class WavyEffect
35{
36public:
37 float m_domain_coeff, m_phase;
38 vecN<float, 4> m_cos_coeffs;
39 vecN<float, 4> m_sin_coeffs;
40};
41
42template<typename T>
43class ExampleItemData:public PainterItemShaderData
44{
45public:
46 virtual
47 void
48 pack_data(c_array<uvec4> dst) const override
49 {
50 float sum(0.0f);
51
52 for (int i = 0; i < 4; ++i)
53 {
54 sum += t_abs(m_wavy_effect.m_cos_coeffs[i]);
55 sum += t_abs(m_wavy_effect.m_sin_coeffs[i]);
56 }
57
58 dst[0] = pack_vec4(m_wavy_effect.m_cos_coeffs);
59 dst[1] = pack_vec4(m_wavy_effect.m_sin_coeffs);
60 dst[2].x() = pack_float(m_wavy_effect.m_domain_coeff);
61 dst[2].y() = pack_float(1.0f / sum);
62 dst[2].z() = pack_float(m_wavy_effect.m_phase);
63 dst[2].w() = pack_float(m_stroke_params.width());
64 m_stroke_params.pack_data(dst.sub_array(3));
65 }
66
67 virtual
68 unsigned int
69 data_size(void) const override
70 {
71 return 3 + m_stroke_params.data_size();
72 }
73
74 WavyEffect m_wavy_effect;
75 T m_stroke_params;
76};
77
78reference_counted_ptr<glsl::PainterItemShaderGLSL>
79call_ctor(bool uses_discard,
80 const glsl::ShaderSource &vert_src,
81 const glsl::ShaderSource &frag_src,
82 const glsl::PainterItemShaderGLSL::DependencyList deps,
83 unsigned int num_subshaders)
84{
85 return FASTUIDRAWnew glsl::PainterItemShaderGLSL(uses_discard,
86 vert_src,
87 frag_src,
88 glsl::varying_list(),
89 deps,
90 num_subshaders);
91}
92
93reference_counted_ptr<glsl::PainterItemCoverageShaderGLSL>
94call_ctor(bool,
95 const glsl::ShaderSource &vert_src,
96 const glsl::ShaderSource &frag_src,
97 const glsl::PainterItemCoverageShaderGLSL::DependencyList deps,
98 unsigned int num_subshaders)
99{
100 return FASTUIDRAWnew glsl::PainterItemCoverageShaderGLSL(vert_src,
101 frag_src,
102 glsl::varying_list(),
103 deps,
104 num_subshaders);
105}
106
107bool
108uses_discard(const reference_counted_ptr<glsl::PainterItemShaderGLSL> &sh,
109 bool apply_wavy_effect)
110{
111 return apply_wavy_effect || sh->uses_discard();
112}
113
114bool
115uses_discard(const reference_counted_ptr<glsl::PainterItemCoverageShaderGLSL>&,
116 bool)
117{
118 return false;
119}
120
121template<typename T>
122reference_counted_ptr<T>
123create_custom_shader(const reference_counted_ptr<T> &stroking_shader,
124 bool add_wavy_effect)
125{
126 using namespace fastuidraw;
127 using namespace glsl;
128
129 typename T::DependencyList deps;
130 deps.add_shader("stroke_shader", stroking_shader);
131
132 const char *custom_vert_shader =
133 "void\n"
134 "fastuidraw_gl_vert_main(in uint sub_shader,\n"
135 " in uvec4 in_attrib0,\n"
136 " in uvec4 in_attrib1,\n"
137 " in uvec4 in_attrib2,\n"
138 " inout uint shader_data_block,\n"
139 " #ifdef FASTUIDRAW_RENDER_TO_COLOR_BUFFER\n"
140 " out int z_add,\n"
141 " out vec2 out_brush_p,\n"
142 " #endif\n"
143 " out vec3 out_clip_p)\n"
144 "{\n"
145 " shader_data_block += 3u;\n"
146 " stroke_shader(sub_shader, in_attrib0, in_attrib1, in_attrib2,\n"
147 " shader_data_block,\n"
148 " #ifdef FASTUIDRAW_RENDER_TO_COLOR_BUFFER\n"
149 " z_add, out_brush_p,\n"
150 " #endif\n"
151 " out_clip_p);\n"
152 "}\n";
153
154 const char *custom_wavy_frag_shader =
155 "#ifdef FASTUIDRAW_RENDER_TO_COLOR_BUFFER\n"
156 "#define return_type vec4\n"
157 "#else\n"
158 "#define return_type float\n"
159 "#endif\n"
160 "return_type\n"
161 "fastuidraw_gl_frag_main(in uint sub_shader,\n"
162 " inout uint shader_data_block)\n"
163 "{\n"
164 " vec4 cos_coeffs, sin_coeffs;\n"
165 " uvec4 tmp;\n"
166 " float coeff, inverse_sum, phase, width;\n"
167 " return_type return_value;\n"
168 " cos_coeffs = uintBitsToFloat(fastuidraw_fetch_data(shader_data_block));\n"
169 " sin_coeffs = uintBitsToFloat(fastuidraw_fetch_data(shader_data_block + 1u));\n"
170 " tmp = fastuidraw_fetch_data(shader_data_block + 2u);\n"
171 " coeff = uintBitsToFloat(tmp.x);\n"
172 " inverse_sum = uintBitsToFloat(tmp.y);\n"
173 " phase = uintBitsToFloat(tmp.z);\n"
174 " width = uintBitsToFloat(tmp.w);\n"
175 " shader_data_block += 3u;\n"
176 "\n"
177 " return_value = stroke_shader(sub_shader, shader_data_block);\n"
178 // use the symbols fastuidraw_stroking_relative_distance_from_center
179 // and fastuidraw_stroking_distance to know where and how far the fragment
180 // is from the path and how far along it is on the path
181 " float a, r;\n"
182 " vec4 cos_tuple, sin_tuple;\n"
183 " r = coeff * stroke_shader::fastuidraw_stroking_distance + phase;\n"
184 " cos_tuple = vec4(cos(r), cos(2.0 * r), cos(3.0 * r), cos(4.0 * r));\n"
185 " sin_tuple = vec4(sin(r), sin(2.0 * r), sin(3.0 * r), sin(4.0 * r));\n"
186 " a = inverse_sum * (dot(cos_coeffs, cos_tuple) + dot(sin_coeffs, sin_tuple));\n"
187 " a = abs(a);\n"
188 "#ifdef FASTUIDRAW_RENDER_TO_COLOR_BUFFER\n"
189 " if (a < stroke_shader::fastuidraw_stroking_relative_distance_from_center)\n"
190 " FASTUIDRAW_DISCARD;\n"
191 "#else\n"
192 " float q, dd;\n"
193 // we want a coverage value so that if a < stroke_shader::fastuidraw_stroking_relative_distance_from_center
194 // then we get zero, but we want to apply some anti-aliasing as well.
195 " q = max(0.0, a - stroke_shader::fastuidraw_stroking_relative_distance_from_center);\n"
196 " dd = max(q, stroke_shader::fastuidraw_stroking_relative_distance_from_center_fwidth);\n"
197 " return_value *= q / dd;\n"
198 "#endif\n"
199 " return return_value;\n"
200 "}\n"
201 "#undef return_type\n";
202
203 const char *custom_pass_through_frag_shader =
204 "#ifdef FASTUIDRAW_RENDER_TO_COLOR_BUFFER\n"
205 "vec4\n"
206 "#else\n"
207 "float\n"
208 "#endif\n"
209 "fastuidraw_gl_frag_main(in uint sub_shader,\n"
210 " inout uint shader_data_block)\n"
211 "{\n"
212 " shader_data_block += 3u;"
213 " return stroke_shader(sub_shader, shader_data_block);\n"
214 "}\n";
215
216 const char *custom_frag_shader;
217
218 custom_frag_shader = (add_wavy_effect) ?
219 custom_wavy_frag_shader:
220 custom_pass_through_frag_shader;
221
222 return call_ctor(uses_discard(stroking_shader, add_wavy_effect),
223 ShaderSource().add_source(custom_vert_shader, ShaderSource::from_string),
224 ShaderSource().add_source(custom_frag_shader, ShaderSource::from_string),
225 deps,
226 stroking_shader->number_sub_shaders());
227}
228
229template<typename T>
230class CustomShaderGenerator:noncopyable
231{
232public:
233 typedef reference_counted_ptr<T> shader_ref;
234 typedef std::map<shader_ref, shader_ref> shader_map;
235
236 shader_ref
237 fetch_generate_custom_shader(const shader_ref &src, bool add_wavy_effect)
238 {
239 typename shader_map::iterator iter;
240 shader_ref return_value;
241
242 iter = m_shaders.find(src);
243 if (iter != m_shaders.end())
244 {
245 return_value = iter->second;
246 }
247 else
248 {
249 return_value = create_custom_shader<T>(src, add_wavy_effect);
250 m_shaders[src] = return_value;
251 }
252 return return_value;
253 }
254
255private:
256 shader_map m_shaders;
257};
258
259reference_counted_ptr<PainterItemShader>
260generate_item_shader(CustomShaderGenerator<glsl::PainterItemShaderGLSL> &item_shaders,
261 CustomShaderGenerator<glsl::PainterItemCoverageShaderGLSL> &cvg_shaders,
262 reference_counted_ptr<PainterItemShader> src)
263{
264 using namespace fastuidraw;
265 using namespace glsl;
266
267 reference_counted_ptr<PainterItemCoverageShader> src_cvg, use_cvg;
268 reference_counted_ptr<PainterItemShaderGLSL> src_glsl, use;
269
270 /* Get the actual item shader that implements the code */
271 if (src->parent())
272 {
273 src_glsl = src->parent().dynamic_cast_ptr<PainterItemShaderGLSL>();
274 }
275 else
276 {
277 src_glsl = src.dynamic_cast_ptr<PainterItemShaderGLSL>();
278 }
279 FASTUIDRAWassert(src_glsl);
280
281 /* Generate the parent shader(s) for the returned value from
282 * the source item shader that implements the code
283 */
284 src_cvg = src->coverage_shader();
285 if (src_cvg)
286 {
287 reference_counted_ptr<PainterItemCoverageShaderGLSL> src_cvg_glsl;
288
289 if (src_cvg->parent())
290 {
291 src_cvg_glsl = src_cvg->parent().dynamic_cast_ptr<PainterItemCoverageShaderGLSL>();
292 }
293 else
294 {
295 src_cvg_glsl = src_cvg.dynamic_cast_ptr<PainterItemCoverageShaderGLSL>();
296 }
297 FASTUIDRAWassert(src_cvg_glsl);
298
299 /* the coverage shader does the pixel coverage */
300 use_cvg = cvg_shaders.fetch_generate_custom_shader(src_cvg_glsl, true);
301 use = item_shaders.fetch_generate_custom_shader(src_glsl, false);
302
303 /* use correct sub-shader from use_cvg */
304 use_cvg = FASTUIDRAWnew PainterItemCoverageShader(use_cvg, src_cvg->sub_shader());
305 }
306 else
307 {
308 /* Fragment's item shader does the pixel computation */
309 use = item_shaders.fetch_generate_custom_shader(src_glsl, true);
310 }
311
312 return FASTUIDRAWnew PainterItemShader(use, src->sub_shader(), use_cvg);
313}
314
315void
316set_shader(PainterStrokeShader &dst,
317 CustomShaderGenerator<glsl::PainterItemShaderGLSL> &item_shaders,
318 CustomShaderGenerator<glsl::PainterItemCoverageShaderGLSL> &cvg_shaders,
319 const PainterStrokeShader &src,
320 enum Painter::stroking_method_t tp,
321 enum PainterStrokeShader::shader_type_t sh)
322{
323 reference_counted_ptr<PainterItemShader> new_shader, src_shader;
324
325 src_shader = src.shader(tp, sh);
326 new_shader = generate_item_shader(item_shaders, cvg_shaders, src_shader);
327 FASTUIDRAWassert(new_shader);
328
329 dst.shader(tp, sh, new_shader);
330}
331
332class CustomStrokingDataSelector:public StrokingDataSelectorBase
333{
334public:
335 explicit
336 CustomStrokingDataSelector(const reference_counted_ptr<const StrokingDataSelectorBase> &base):
337 m_base(base)
338 {}
339
340 virtual
341 float
342 compute_thresh(c_array<const uvec4> data,
343 float path_magnification,
344 float curve_flatness) const override
345 {
346 return m_base->compute_thresh(data.sub_array(3),
347 path_magnification,
348 curve_flatness);
349 }
350
351 virtual
352 void
353 stroking_distances(c_array<const uvec4 > data,
354 c_array<float> out_values) const override
355 {
356 m_base->stroking_distances(data.sub_array(3), out_values);
357 }
358
359 virtual
360 bool
361 arc_stroking_possible(c_array<const uvec4 > data) const override
362 {
363 return m_base->arc_stroking_possible(data.sub_array(3));
364 }
365
366 virtual
367 bool
368 data_compatible(c_array<const uvec4 > data) const override
369 {
370 return m_base->data_compatible(data.sub_array(3));
371 }
372
373 reference_counted_ptr<const StrokingDataSelectorBase> m_base;
374};
375
376PainterStrokeShader
377generate_stroke_shader(const PainterStrokeShader &src,
378 CustomShaderGenerator<glsl::PainterItemShaderGLSL> &item_shaders,
379 CustomShaderGenerator<glsl::PainterItemCoverageShaderGLSL> &cvg_shaders)
380{
381 using namespace fastuidraw;
382 using namespace glsl;
383
384 PainterStrokeShader return_value;
385
386 set_shader(return_value, item_shaders, cvg_shaders,
387 src, Painter::stroking_method_linear,
388 PainterStrokeShader::non_aa_shader);
389
390 set_shader(return_value, item_shaders, cvg_shaders,
391 src, Painter::stroking_method_arc,
392 PainterStrokeShader::non_aa_shader);
393
394 set_shader(return_value, item_shaders, cvg_shaders,
395 src, Painter::stroking_method_linear,
396 PainterStrokeShader::aa_shader);
397
398 set_shader(return_value, item_shaders, cvg_shaders,
399 src, Painter::stroking_method_arc,
400 PainterStrokeShader::aa_shader);
401
402 return_value
403 .stroking_data_selector(FASTUIDRAWnew CustomStrokingDataSelector(src.stroking_data_selector()))
404 .fastest_non_anti_aliased_stroking_method(src.fastest_non_anti_aliased_stroking_method())
405 .fastest_anti_aliased_stroking_method(src.fastest_anti_aliased_stroking_method());
406
407 return return_value;
408}
409
410void
411set_shader(PainterDashedStrokeShaderSet &dst,
412 CustomShaderGenerator<glsl::PainterItemShaderGLSL> &item_shaders,
413 CustomShaderGenerator<glsl::PainterItemCoverageShaderGLSL> &cvg_shaders,
414 const PainterDashedStrokeShaderSet &src,
415 enum Painter::cap_style st)
416{
417 dst.shader(st, generate_stroke_shader(src.shader(st), item_shaders, cvg_shaders));
418}
419
420PainterDashedStrokeShaderSet
421generate_dashed_stroke_shader(const PainterDashedStrokeShaderSet &src,
422 CustomShaderGenerator<glsl::PainterItemShaderGLSL> &item_shaders,
423 CustomShaderGenerator<glsl::PainterItemCoverageShaderGLSL> &cvg_shaders)
424{
425 PainterDashedStrokeShaderSet return_value;
426
427 set_shader(return_value, item_shaders, cvg_shaders, src, Painter::flat_caps);
428 set_shader(return_value, item_shaders, cvg_shaders, src, Painter::rounded_caps);
429 set_shader(return_value, item_shaders, cvg_shaders, src, Painter::square_caps);
430
431 return return_value;
432}
433
434class DashPatternList:public command_line_argument
435{
436public:
437 DashPatternList(command_line_register &parent):
438 command_line_argument(parent)
439 {
440 m_desc = produce_formatted_detailed_description("add_dash_pattern filename",
441 "Adds a dash pattern to use source from a file");
442 }
443
444 virtual
445 int
446 check_arg(const std::vector<std::string> &args, int location)
447 {
448 if (static_cast<unsigned int>(location + 1) < args.size() && args[location] == "add_dash_pattern")
449 {
450 m_files.push_back(args[location + 1]);
451 std::cout << "\nAdded dash pattern from file " << args[location + 1];
452 return 2;
453 }
454 return 0;
455 }
456
457 virtual
458 void
459 print_command_line_description(std::ostream &ostr) const
460 {
461 ostr << "[add_dash_pattern file]";
462 }
463
464 virtual
465 void
466 print_detailed_description(std::ostream &ostr) const
467 {
468 ostr << m_desc;
469 }
470
471 std::vector<std::string> m_files;
472
473private:
474 std::string m_desc;
475};
476
477class character_code_range:public command_line_argument
478{
479public:
480 explicit
481 character_code_range(command_line_list<uint32_t> *p):
482 command_line_argument(*p->parent()),
483 m_p(p)
484 {
485 m_desc = produce_formatted_detailed_description("add_path_character_codes first last",
486 "add a set of paths from an inclusive range of character codes");
487 }
488
489 virtual
490 int
491 check_arg(const std::vector<std::string> &args, int location)
492 {
493 if (static_cast<unsigned int>(location + 2) < args.size() && args[location] == "add_path_character_codes")
494 {
495 uint32_t first, last;
496
497 readvalue_from_string(first, args[location + 1]);
498 readvalue_from_string(last, args[location + 2]);
499 for (; first != last + 1; ++first)
500 {
501 m_p->insert(first);
502 }
503 return 3;
504 }
505 return 0;
506 }
507
508 virtual
509 void
510 print_command_line_description(std::ostream &ostr) const
511 {
512 ostr << "[add_path_character_codes first last] ";
513 }
514
515 virtual
516 void
517 print_detailed_description(std::ostream &ostr) const
518 {
519 ostr << m_desc;
520 }
521
522private:
523 std::string m_desc;
524 command_line_list<uint32_t> *m_p;
525};
526
527class WindingValueFillRule:public CustomFillRuleBase
528{
529public:
530 WindingValueFillRule(int v = 0):
531 m_winding_number(v)
532 {}
533
534 bool
535 operator()(int w) const
536 {
537 return w == m_winding_number;
538 }
539
540private:
541 int m_winding_number;
542};
543
544bool
545everything_filled(int)
546{
547 return true;
548}
549
550#ifndef FASTUIDRAW_GL_USE_GLES
551
552class EnableWireFrameAction:public PainterDrawBreakAction
553{
554public:
555 explicit
556 EnableWireFrameAction(bool b):
557 m_lines(b)
558 {}
559
560 virtual
561 gpu_dirty_state
562 execute(PainterBackend*) const
563 {
564 if (m_lines)
565 {
566 fastuidraw_glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
567 fastuidraw_glLineWidth(4.0);
568 }
569 else
570 {
571 fastuidraw_glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
572 }
573 return 0u;
574 }
575
576private:
577 bool m_lines;
578};
579
580#endif
581
582class PerPath:public reference_counted<PerPath>::non_concurrent
583{
584public:
585 PerPath(Path &path, const std::string &label,
586 int w, int h, bool from_gylph);
587
588 PerPath(const Path *path, const std::string &label,
589 int w, int h, bool from_gylph);
590
591 const Path&
592 path(void) const
593 {
594 return *m_path_ptr;
595 }
596
597 std::string m_path_string;
598 std::vector<vec2> m_pts, m_ctl_pts, m_arc_center_pts;
599 std::string m_label;
600 bool m_from_glyph;
601 unsigned int m_fill_rule;
602 unsigned int m_end_fill_rule;
603 vec2 m_shear, m_shear2;
604 float m_angle;
605 PanZoomTrackerSDLEvent m_path_zoomer;
606
607 bool m_matrix_brush;
608
609 vec2 m_gradient_p0, m_gradient_p1;
610 float m_gradient_r0, m_gradient_r1;
611 float m_sweep_repeat_factor;
612 enum PainterBrush::spread_type_t m_gradient_spread_type;
613
614 bool m_repeat_window;
615 vec2 m_repeat_xy, m_repeat_wh;
616 enum PainterBrush::spread_type_t m_repeat_window_spread_type_x;
617 enum PainterBrush::spread_type_t m_repeat_window_spread_type_y;
618
619 bool m_clipping_window;
620 vec2 m_clipping_xy, m_clipping_wh;
621
622private:
623 void
624 common_init(int w, int h);
625
626 Path m_path_v;
627 const Path *m_path_ptr;
628};
629
630class painter_stroke_test:public sdl_painter_demo
631{
632public:
633 painter_stroke_test(void);
634
635protected:
636
637 void
638 derived_init(int w, int h);
639
640 void
641 draw_frame(void);
642
643 void
644 handle_event(const SDL_Event &ev);
645
646private:
647
648 enum gradient_draw_mode
649 {
650 draw_no_gradient,
651 draw_linear_gradient,
652 draw_radial_gradient,
653 draw_sweep_gradient,
654
655 number_gradient_draw_modes
656 };
657
658 enum image_filter
659 {
660 no_image,
661 image_nearest_filter,
662 image_linear_filter,
663 image_cubic_filter,
664
665 number_image_filter_modes
666 };
667
668 enum fill_mode_t
669 {
670 draw_fill_path,
671 draw_fill_path_occludes_stroking,
672 dont_draw_fill_path,
673
674 number_fill_modes,
675 };
676
677 enum fill_by_mode_t
678 {
679 fill_by_filled_path,
680 fill_by_clipping,
681 fill_by_shader_filled_path,
682
683 fill_by_number_modes
684 };
685
686 typedef std::pair<std::string,
687 reference_counted_ptr<ColorStopSequence> > named_color_stop;
688
689 void
690 construct_paths(int w, int h);
691
692 void
693 per_path_processing(void);
694
695 void
696 construct_color_stops(void);
697
698 void
699 construct_dash_patterns(void);
700
701 vec2
702 item_coordinates(ivec2 c)
703 {
704 return item_coordinates(vec2(c));
705 }
706
707 vec2
708 item_coordinates(vec2 c);
709
710 vec2
711 brush_item_coordinate(ivec2 c);
712
713 void
714 update_cts_params(void);
715
716 PanZoomTrackerSDLEvent&
717 zoomer(void)
718 {
719 return m_paths[m_selected_path]->m_path_zoomer;
720 }
721
722 const Path&
723 path(void)
724 {
725 return m_paths[m_selected_path]->path();
726 }
727
728 unsigned int&
729 current_fill_rule(void)
730 {
731 return m_paths[m_selected_path]->m_fill_rule;
732 }
733
734 unsigned int
735 current_end_fill_rule(void)
736 {
737 return m_paths[m_selected_path]->m_end_fill_rule;
738 }
739
740 vec2&
741 shear(void)
742 {
743 return m_paths[m_selected_path]->m_shear;
744 }
745
746 vec2&
747 shear2(void)
748 {
749 return m_paths[m_selected_path]->m_shear2;
750 }
751
752 float&
753 angle(void)
754 {
755 return m_paths[m_selected_path]->m_angle;
756 }
757
758 vec2&
759 gradient_p0(void)
760 {
761 return m_paths[m_selected_path]->m_gradient_p0;
762 }
763
764 vec2&
765 gradient_p1(void)
766 {
767 return m_paths[m_selected_path]->m_gradient_p1;
768 }
769
770 float&
771 gradient_r0(void)
772 {
773 return m_paths[m_selected_path]->m_gradient_r0;
774 }
775
776 float&
777 gradient_r1(void)
778 {
779 return m_paths[m_selected_path]->m_gradient_r1;
780 }
781
782 float&
783 sweep_repeat_factor(void)
784 {
785 return m_paths[m_selected_path]->m_sweep_repeat_factor;
786 }
787
788 enum PainterBrush::spread_type_t&
789 gradient_spread_type(void)
790 {
791 return m_paths[m_selected_path]->m_gradient_spread_type;
792 }
793
794 enum PainterBrush::spread_type_t&
795 repeat_window_spread_type_x(void)
796 {
797 return m_paths[m_selected_path]->m_repeat_window_spread_type_x;
798 }
799
800 enum PainterBrush::spread_type_t&
801 repeat_window_spread_type_y(void)
802 {
803 return m_paths[m_selected_path]->m_repeat_window_spread_type_y;
804 }
805
806 bool&
807 matrix_brush(void)
808 {
809 return m_paths[m_selected_path]->m_matrix_brush;
810 }
811
812 bool&
813 repeat_window(void)
814 {
815 return m_paths[m_selected_path]->m_repeat_window;
816 }
817
818 vec2&
819 repeat_xy(void)
820 {
821 return m_paths[m_selected_path]->m_repeat_xy;
822 }
823
824 vec2&
825 repeat_wh(void)
826 {
827 return m_paths[m_selected_path]->m_repeat_wh;
828 }
829
830 bool&
831 clipping_window(void)
832 {
833 return m_paths[m_selected_path]->m_clipping_window;
834 }
835
836 vec2&
837 clipping_xy(void)
838 {
839 return m_paths[m_selected_path]->m_clipping_xy;
840 }
841
842 vec2&
843 clipping_wh(void)
844 {
845 return m_paths[m_selected_path]->m_clipping_wh;
846 }
847
848 void
849 fill_centered_rect(const vec2 &pt, float r, const PainterData &d);
850
851 void
852 draw_scene(bool drawing_wire_frame);
853
854 void
855 fill_way_effect(WavyEffect &data);
856
857 command_line_argument_value<float> m_change_miter_limit_rate;
858 command_line_argument_value<float> m_change_stroke_width_rate;
859 command_line_argument_value<float> m_window_change_rate;
860 command_line_argument_value<float> m_radial_gradient_change_rate;
861 command_line_argument_value<float> m_sweep_factor_gradient_change_rate;
862 command_line_list<std::string> m_path_file_list;
863 DashPatternList m_dash_pattern_files;
864 command_line_argument_value<bool> m_print_path;
865 color_stop_arguments m_color_stop_args;
866 command_line_argument_value<std::string> m_image_file;
867 command_line_argument_value<bool> m_use_atlas;
868 command_line_argument_value<int> m_sub_image_x, m_sub_image_y;
869 command_line_argument_value<int> m_sub_image_w, m_sub_image_h;
870 command_line_argument_value<std::string> m_font_file;
871 command_line_list<uint32_t> m_character_code_list;
872 character_code_range m_character_code_list_range_adder;
873 command_line_argument_value<float> m_stroke_red;
874 command_line_argument_value<float> m_stroke_green;
875 command_line_argument_value<float> m_stroke_blue;
876 command_line_argument_value<float> m_stroke_alpha;
877 command_line_argument_value<float> m_fill_red;
878 command_line_argument_value<float> m_fill_green;
879 command_line_argument_value<float> m_fill_blue;
880 command_line_argument_value<float> m_fill_alpha;
881 command_line_argument_value<float> m_draw_line_red;
882 command_line_argument_value<float> m_draw_line_green;
883 command_line_argument_value<float> m_draw_line_blue;
884 command_line_argument_value<float> m_draw_line_alpha;
885 command_line_argument_value<bool> m_init_pan_zoom;
886 command_line_argument_value<float> m_initial_zoom;
887 command_line_argument_value<float> m_initial_pan_x;
888 command_line_argument_value<float> m_initial_pan_y;
889
890 std::vector<reference_counted_ptr<PerPath> > m_paths;
891 reference_counted_ptr<Image> m_image;
892 uvec2 m_image_offset, m_image_size;
893 std::vector<named_color_stop> m_color_stops;
894 std::vector<std::vector<PainterDashedStrokeParams::DashPatternElement> > m_dash_patterns;
895 reference_counted_ptr<const FontBase> m_font;
896
897 vecN<std::string, number_gradient_draw_modes> m_gradient_mode_labels;
898 vecN<std::string, number_image_filter_modes> m_image_filter_mode_labels;
899 vecN<std::string, number_fill_modes> m_draw_fill_labels;
900 vecN<std::string, PainterBrush::number_spread_types> m_spread_type_labels;
901 vecN<std::string, fill_by_number_modes> m_fill_by_mode_labels;
902
903 PainterData::brush_value m_black_pen;
904 PainterData::brush_value m_white_pen;
905 PainterData::brush_value m_stroke_pen;
906 PainterData::brush_value m_draw_line_pen;
907 PainterData::brush_value m_blue_pen;
908 PainterData::brush_value m_red_pen;
909 PainterData::brush_value m_green_pen;
910
911 Path m_rect;
912
913 unsigned int m_selected_path;
914 enum Painter::join_style m_join_style;
915 enum Painter::cap_style m_cap_style;
916
917 /* m_dash pattern:
918 0 -> undashed stroking
919 [1, m_dash_patterns.size()] -> dashed stroking
920 */
921 unsigned int m_dash;
922
923 bool
924 is_dashed_stroking(void)
925 {
926 return m_dash != 0;
927 }
928
929 unsigned int
930 dash_pattern(void)
931 {
932 return m_dash - 1;
933 }
934
935 float m_miter_limit, m_stroke_width;
936 unsigned int m_draw_fill;
937 bool m_aa_stroke_mode;
938 enum Painter::stroking_method_t m_stroking_mode;
939 bool m_aa_fill_mode;
940 unsigned int m_active_color_stop;
941 unsigned int m_gradient_draw_mode;
942 unsigned int m_image_filter;
943 bool m_apply_mipmapping;
944 bool m_draw_stats;
945 float m_curve_flatness;
946 bool m_draw_path_pts;
947 bool m_use_path_effect;
948
949 bool m_wire_frame;
950 bool m_stroke_width_in_pixels;
951
952 int m_fill_by_mode;
953 bool m_draw_grid;
954
955 simple_time m_draw_timer, m_fps_timer, m_phase_timer;
956 Path m_grid_path;
957 bool m_grid_path_dirty;
958
959 Path m_clip_window_path;
960 bool m_clip_window_path_dirty;
961
962 float3x3 m_pixel_matrix;
963 int m_show_surface, m_last_shown_surface;
964
965 PathDashEffect m_current_effect;
966
967 bool m_use_shader_effect;
968 PainterStrokeShader m_stroke_shader;
969 PainterDashedStrokeShaderSet m_dashed_stroke_shader;
970};
971
972///////////////////////////////////
973// PerPath methods
974PerPath::
975PerPath(Path &path, const std::string &label, int w, int h, bool from_gylph):
976 m_label(label),
977 m_from_glyph(from_gylph),
978 m_fill_rule(Painter::odd_even_fill_rule),
979 m_end_fill_rule(Painter::number_fill_rule),
980 m_shear(1.0f, 1.0f),
981 m_shear2(1.0f, 1.0f),
982 m_angle(0.0f),
983 m_matrix_brush(false),
984 m_gradient_spread_type(PainterBrush::spread_repeat),
985 m_repeat_window(false),
986 m_repeat_window_spread_type_x(PainterBrush::spread_repeat),
987 m_repeat_window_spread_type_y(PainterBrush::spread_repeat),
988 m_clipping_window(false),
989 m_path_ptr(&m_path_v)
990{
991 m_path_v.swap(path);
992 common_init(w, h);
993}
994
995PerPath::
996PerPath(const Path *path, const std::string &label, int w, int h, bool from_gylph):
997 m_label(label),
998 m_from_glyph(from_gylph),
999 m_fill_rule(Painter::odd_even_fill_rule),
1000 m_end_fill_rule(Painter::number_fill_rule),
1001 m_shear(1.0f, 1.0f),
1002 m_shear2(1.0f, 1.0f),
1003 m_angle(0.0f),
1004 m_matrix_brush(false),
1005 m_gradient_spread_type(PainterBrush::spread_repeat),
1006 m_repeat_window(false),
1007 m_repeat_window_spread_type_x(PainterBrush::spread_repeat),
1008 m_repeat_window_spread_type_y(PainterBrush::spread_repeat),
1009 m_clipping_window(false),
1010 m_path_ptr(path)
1011{
1012 common_init(w, h);
1013}
1014
1015void
1016PerPath::
1017common_init(int w, int h)
1018{
1019 m_end_fill_rule =
1020 path().tessellation().filled().root_subset().winding_numbers().size() + Painter::number_fill_rule;
1021
1022 /* set transformation to center and contain path. */
1023 vec2 p0, p1, delta, dsp(w, h), ratio, mid;
1024 const Rect &R(path().tessellation().bounding_box());
1025 float mm;
1026
1027 p0 = R.m_min_point;
1028 p1 = R.m_max_point;
1029
1030 if (m_from_glyph)
1031 {
1032 /* the path is rendered y-flipped, so we need to adjust
1033 * p0 and p1 correctly for it.
1034 */
1035 p0.y() *= -1.0f;
1036 p1.y() *= -1.0f;
1037 std::swap(p0.y(), p1.y());
1038 }
1039
1040 delta = p1 - p0;
1041 ratio = delta / dsp;
1042 mm = t_max(0.00001f, t_max(ratio.x(), ratio.y()) );
1043 mid = 0.5 * (p1 + p0);
1044
1045 ScaleTranslate<float> sc, tr1, tr2;
1046
1047 tr1.translation(-mid);
1048 sc.scale(1.0f / mm);
1049 tr2.translation(dsp * 0.5f);
1050
1051 m_path_zoomer.transformation(tr2 * sc * tr1);
1052
1053 m_gradient_p0 = p0;
1054 m_gradient_p1 = p1;
1055
1056 m_gradient_r0 = 0.0f;
1057 m_gradient_r1 = 200.0f / m_path_zoomer.transformation().scale();
1058
1059 m_sweep_repeat_factor = 1.0f;
1060
1061 m_repeat_xy = vec2(0.0f, 0.0f);
1062 m_repeat_wh = p1 - p0;
1063
1064 m_clipping_xy = p0;
1065 m_clipping_wh = m_repeat_wh;
1066
1067 extract_path_info(path(), &m_pts, &m_ctl_pts, &m_arc_center_pts, &m_path_string);
1068}
1069
1070//////////////////////////////////////
1071// painter_stroke_test methods
1072painter_stroke_test::
1073painter_stroke_test(void):
1074 sdl_painter_demo("painter-stroke-test"),
1075 m_change_miter_limit_rate(1.0f, "miter_limit_rate",
1076 "rate of change in in stroke widths per second for "
1077 "changing the miter limit when the when key is down",
1078 *this),
1079 m_change_stroke_width_rate(10.0f, "change_stroke_width_rate",
1080 "rate of change in pixels/sec for changing stroke width "
1081 "when changing stroke when key is down",
1082 *this),
1083 m_window_change_rate(10.0f, "change_rate_brush_repeat_window",
1084 "rate of change in pixels/sec when changing the repeat window",
1085 *this),
1086 m_radial_gradient_change_rate(0.1f, "change_rate_brush_radial_gradient",
1087 "rate of change in pixels/sec when changing the radial gradient radius",
1088 *this),
1089 m_sweep_factor_gradient_change_rate(0.05f, "change_rate_brush_sweep_factor_gradient",
1090 "rate of change in units/sec when changing the sweep factor",
1091 *this),
1092 m_path_file_list("add_path_file",
1093 "add a path read from file to path list; if path list is empty then "
1094 "a default path will be used to render ",
1095 *this),
1096 m_dash_pattern_files(*this),
1097 m_print_path(false, "print_path",
1098 "If true, print the geometry data of the path drawn to stdout",
1099 *this),
1100 m_color_stop_args(*this),
1101 m_image_file("", "image", "if a valid file name, apply an image to drawing the fill", *this),
1102 m_use_atlas(true, "use_atlas",
1103 "If false, each image is realized as a texture; if "
1104 "GL_ARB_bindless_texture or GL_NV_bindless_texture "
1105 "is supported, the Image objects are realized as bindless "
1106 "texture, thus avoding draw breaks; if both of these "
1107 "extensions is not present, then images are realized as "
1108 "bound textures which means that a draw break will be present "
1109 "whenever the image changes, harming performance.",
1110 *this),
1111 m_sub_image_x(0, "sub_image_x",
1112 "x-coordinate of top left corner of sub-image rectange (negative value means no-subimage)",
1113 *this),
1114 m_sub_image_y(0, "sub_image_y",
1115 "y-coordinate of top left corner of sub-image rectange (negative value means no-subimage)",
1116 *this),
1117 m_sub_image_w(-1, "sub_image_w",
1118 "sub-image width of sub-image rectange (negative value means no-subimage)",
1119 *this),
1120 m_sub_image_h(-1, "sub_image_h",
1121 "sub-image height of sub-image rectange (negative value means no-subimage)",
1122 *this),
1123 m_font_file(default_font(), "font", "File from which to take font", *this),
1124 m_character_code_list("add_path_character_code",
1125 "add a path of a glyph selected via character code", *this),
1126 m_character_code_list_range_adder(&m_character_code_list),
1127 m_stroke_red(1.0f, "stroke_red", "red component of stroking pen color", *this),
1128 m_stroke_green(1.0f, "stroke_green", "green component of stroking pen color", *this),
1129 m_stroke_blue(1.0f, "stroke_blue", "blue component of stroking pen olor", *this),
1130 m_stroke_alpha(0.5f, "stroke_alpha", "alpha component of stroking pen color", *this),
1131 m_fill_red(1.0f, "fill_red", "red component of fill pen color", *this),
1132 m_fill_green(1.0f, "fill_green", "green component of fill pen color", *this),
1133 m_fill_blue(1.0f, "fill_blue", "blue component of fill pen color", *this),
1134 m_fill_alpha(1.0f, "fill_alpha", "alpha component of fill pen color", *this),
1135 m_draw_line_red(1.0f, "draw_line_red", "red component when showing line-rasterization", *this),
1136 m_draw_line_green(0.0f, "draw_line_green", "green component when showing line-rasterization", *this),
1137 m_draw_line_blue(0.0f, "draw_line_blue", "blue component when showing line-rasterization", *this),
1138 m_draw_line_alpha(0.4f, "draw_line_alpha", "alpha component when showing line-rasterization", *this),
1139 m_init_pan_zoom(false, "init_pan_zoom", "If true, initialize the view with values given by "
1140 "initial_zoom, initial_pan_x and initial_pan_y; if false initialize each path "
1141 "view so that the entire path just fits on screen", *this),
1142 m_initial_zoom(1.0f, "initial_zoom", "initial zoom for view if init_pan_zoom is true", *this),
1143 m_initial_pan_x(0.0f, "initial_pan_x", "initial x-offset for view if init_pan_zoom is true", *this),
1144 m_initial_pan_y(0.0f, "initial_pan_y", "initial y-offset for view if init_pan_zoom is true", *this),
1145 m_selected_path(0),
1146 m_join_style(Painter::rounded_joins),
1147 m_cap_style(Painter::flat_caps),
1148 m_dash(0),
1149 m_miter_limit(5.0f),
1150 m_stroke_width(10.0f),
1151 m_draw_fill(dont_draw_fill_path),
1152 m_aa_stroke_mode(true),
1153 m_stroking_mode(Painter::stroking_method_fastest),
1154 m_aa_fill_mode(true),
1155 m_active_color_stop(0),
1156 m_gradient_draw_mode(draw_no_gradient),
1157 m_image_filter(image_nearest_filter),
1158 m_apply_mipmapping(false),
1159 m_draw_stats(false),
1160 m_draw_path_pts(false),
1161 m_use_path_effect(false),
1162 m_wire_frame(false),
1163 m_stroke_width_in_pixels(false),
1164 m_fill_by_mode(fill_by_filled_path),
1165 m_draw_grid(false),
1166 m_grid_path_dirty(true),
1167 m_clip_window_path_dirty(true),
1168 m_show_surface(0),
1169 m_last_shown_surface(0),
1170 m_use_shader_effect(false)
1171{
1172 std::cout << "Controls:\n"
1173 << "\tv: cycle through stroking modes\n"
1174 << "\tk: select next path\n"
1175 << "\ta: cycle through anti-aliased modes for stroking\n"
1176 << "\tu: cycle through anti-aliased modes for filling\n"
1177 << "\tj: cycle through join styles for stroking\n"
1178 << "\tc: cycle through cap style for stroking\n"
1179 << "\td: cycle through dash patterns\n"
1180 << "\t[: decrease stroke width(hold left-shift for slower rate and right shift for faster)\n"
1181 << "\t]: increase stroke width(hold left-shift for slower rate and right shift for faster)\n"
1182 << "\tp: toggle stroke width in pixels or local coordinates\n"
1183 << "\tctrl-p: toggle showing points (blue), control pts(blue) and arc-center(green) of Path\n"
1184 << "\tshift-p: print current path to console\n"
1185 << "\t5: toggle drawing grid\n"
1186 << "\tq: reset shear to 1.0\n"
1187 << "\t6: x-shear (hold ctrl to decrease, hold enter for shear2)\n"
1188 << "\t7: y-shear (hold ctrl to decrease, hold enter for shear2)\n"
1189 << "\t0: Rotate left\n"
1190 << "\t9: Rotate right\n"
1191 << "\tb: decrease miter limit(hold left-shift for slower rate and right shift for faster)\n"
1192 << "\tn: increase miter limit(hold left-shift for slower rate and right shift for faster)\n"
1193 << "\tm: toggle miter limit enforced\n"
1194 << "\tf: cycle drawing path filled (not filled, filled, filled and occludes stroking)\n"
1195 << "\tr: cycle through fill rules\n"
1196 << "\te: toggle fill by drawing clip rect\n"
1197 << "\ti: cycle through image filter to apply to fill (no image, nearest, linear, cubic)\n"
1198 << "\tctrl-i: toggle mipmap filtering when applying an image\n"
1199 << "\ts: cycle through defined color stops for gradient\n"
1200 << "\tg: cycle through gradient types (linear or radial)\n"
1201 << "\th: cycle though gradient spead types\n"
1202 << "\ty: toggle applying matrix brush so that brush appears to be in pixel coordinates\n"
1203 << "\to: toggle clipping window\n"
1204 << "\tctrl-o: cycle through buffers to show\n"
1205 << "\tz: increase/decrease curve flatness\n"
1206 << "\t4,6,2,8 (number pad): change location of clipping window\n"
1207 << "\tctrl-4,6,2,8 (number pad): change size of clipping window\n"
1208 << "\tw: toggle brush repeat window active\n"
1209 << "\tshift-w: cycle though y-repeat window spread modes\n"
1210 << "\tctrl-w: cycle though y-repeat window spread modes\n"
1211 << "\tarrow keys: change location of brush repeat window\n"
1212 << "\tctrl-arrow keys: change size of brush repeat window\n"
1213 << "\tMiddle Mouse Draw: set p0(starting position top left) {drawn black with white inside} of gradient\n"
1214 << "\t1/2 : decrease/increase r0 of gradient(hold left-shift for slower rate and right shift for faster)\n"
1215 << "\t3/4 : decrease/increase r1 of gradient(hold left-shift for slower rate and right shift for faster)\n"
1216 << "\tl: draw Painter stats\n"
1217 << "\tx: toggle use path effect to perform dashing\n"
1218 << "\tctrl-x: toggle custom stroke shader effect\n"
1219 << "\tRight Mouse Draw: set p1(starting position bottom right) {drawn white with black inside} of gradient\n"
1220 << "\tLeft Mouse Drag: pan\n"
1221 << "\tHold Left Mouse, then drag up/down: zoom out/in\n";
1222
1223 m_gradient_mode_labels[draw_no_gradient] = "draw_no_gradient";
1224 m_gradient_mode_labels[draw_linear_gradient] = "draw_linear_gradient";
1225 m_gradient_mode_labels[draw_radial_gradient] = "draw_radial_gradient";
1226 m_gradient_mode_labels[draw_sweep_gradient] = "draw_sweep_gradient";
1227
1228 m_spread_type_labels[PainterBrush::spread_clamp] = "spread_clamp";
1229 m_spread_type_labels[PainterBrush::spread_repeat] = "spread_repeat";
1230 m_spread_type_labels[PainterBrush::spread_mirror_repeat] = "spread_mirror_repeat";
1231 m_spread_type_labels[PainterBrush::spread_mirror] = "spread_mirror";
1232
1233 m_fill_by_mode_labels[fill_by_filled_path] = "FilledPath";
1234 m_fill_by_mode_labels[fill_by_clipping] = "clipping against FilledPath";
1235 m_fill_by_mode_labels[fill_by_shader_filled_path] = "ShaderFilledPath";
1236
1237 m_image_filter_mode_labels[no_image] = "no_image";
1238 m_image_filter_mode_labels[image_nearest_filter] = "image_nearest_filter";
1239 m_image_filter_mode_labels[image_linear_filter] = "image_linear_filter";
1240 m_image_filter_mode_labels[image_cubic_filter] = "image_cubic_filter";
1241
1242 m_draw_fill_labels[draw_fill_path] = "draw_fill";
1243 m_draw_fill_labels[draw_fill_path_occludes_stroking] = "draw_fill_path_occludes_stroking";
1244 m_draw_fill_labels[dont_draw_fill_path] = "dont_draw_fill_path";
1245
1246 m_rect << vec2(-0.5f, -0.5f)
1247 << vec2(-0.5f, +0.5f)
1248 << vec2(+0.5f, +0.5f)
1249 << vec2(+0.5f, -0.5f)
1250 << Path::contour_close();
1251}
1252
1253
1254void
1255painter_stroke_test::
1256update_cts_params(void)
1257{
1258 const Uint8 *keyboard_state = SDL_GetKeyboardState(nullptr);
1259 FASTUIDRAWassert(keyboard_state != nullptr);
1260
1261 float speed = static_cast<float>(m_draw_timer.restart_us()), speed_stroke, speed_shear;
1262 speed /= 1000.0f;
1263
1264 if (keyboard_state[SDL_SCANCODE_LSHIFT])
1265 {
1266 speed *= 0.1f;
1267 }
1268 if (keyboard_state[SDL_SCANCODE_RSHIFT])
1269 {
1270 speed *= 10.0f;
1271 }
1272
1273 speed_shear = 0.01f * speed;
1274 if (keyboard_state[SDL_SCANCODE_LCTRL] || keyboard_state[SDL_SCANCODE_RCTRL])
1275 {
1276 speed_shear = -speed_shear;
1277 }
1278
1279 vec2 *pshear(&shear());
1280 c_string shear_txt = "";
1281 if (keyboard_state[SDL_SCANCODE_RETURN])
1282 {
1283 pshear = &shear2();
1284 shear_txt = "2";
1285 }
1286
1287 if (keyboard_state[SDL_SCANCODE_6])
1288 {
1289 pshear->x() += speed_shear;
1290 std::cout << "Shear" << shear_txt << " set to: " << *pshear << "\n";
1291 }
1292 if (keyboard_state[SDL_SCANCODE_7])
1293 {
1294 pshear->y() += speed_shear;
1295 std::cout << "Shear " << shear_txt << " set to: " << *pshear << "\n";
1296 }
1297
1298 if (keyboard_state[SDL_SCANCODE_9])
1299 {
1300 angle() += speed * 0.1f;
1301 std::cout << "Angle set to: " << angle() << "\n";
1302 }
1303 if (keyboard_state[SDL_SCANCODE_0])
1304 {
1305 angle() -= speed * 0.1f;
1306 std::cout << "Angle set to: " << angle() << "\n";
1307 }
1308
1309 speed_stroke = speed * m_change_stroke_width_rate.value();
1310 if (!m_stroke_width_in_pixels)
1311 {
1312 speed_stroke /= zoomer().transformation().scale();
1313 }
1314
1315 if (keyboard_state[SDL_SCANCODE_RIGHTBRACKET])
1316 {
1317 m_grid_path_dirty = true;
1318 m_stroke_width += speed_stroke;
1319 }
1320
1321 if (keyboard_state[SDL_SCANCODE_LEFTBRACKET] && m_stroke_width > 0.0f)
1322 {
1323 m_grid_path_dirty = true;
1324 m_stroke_width -= speed_stroke;
1325 m_stroke_width = t_max(m_stroke_width, 0.0f);
1326 }
1327
1328 if (keyboard_state[SDL_SCANCODE_RIGHTBRACKET] || keyboard_state[SDL_SCANCODE_LEFTBRACKET])
1329 {
1330 std::cout << "Stroke width set to: " << m_stroke_width << "\n";
1331 }
1332
1333 if (repeat_window())
1334 {
1335 vec2 *changer;
1336 float delta, delta_y;
1337
1338 delta = m_window_change_rate.value() * speed / zoomer().transformation().scale();
1339 if (keyboard_state[SDL_SCANCODE_LCTRL] || keyboard_state[SDL_SCANCODE_RCTRL])
1340 {
1341 changer = &repeat_wh();
1342 delta_y = delta;
1343 }
1344 else
1345 {
1346 changer = &repeat_xy();
1347 delta_y = -delta;
1348 }
1349
1350 if (keyboard_state[SDL_SCANCODE_UP])
1351 {
1352 changer->y() += delta_y;
1353 changer->y() = t_max(0.0f, changer->y());
1354 }
1355
1356 if (keyboard_state[SDL_SCANCODE_DOWN])
1357 {
1358 changer->y() -= delta_y;
1359 changer->y() = t_max(0.0f, changer->y());
1360 }
1361
1362 if (keyboard_state[SDL_SCANCODE_RIGHT])
1363 {
1364 changer->x() += delta;
1365 }
1366
1367 if (keyboard_state[SDL_SCANCODE_LEFT])
1368 {
1369 changer->x() -= delta;
1370 changer->x() = t_max(0.0f, changer->x());
1371 }
1372
1373 if (keyboard_state[SDL_SCANCODE_UP] || keyboard_state[SDL_SCANCODE_DOWN]
1374 || keyboard_state[SDL_SCANCODE_RIGHT] || keyboard_state[SDL_SCANCODE_LEFT])
1375 {
1376 std::cout << "Brush repeat window set to: xy = " << repeat_xy() << " wh = " << repeat_wh() << "\n";
1377 }
1378 }
1379
1380
1381 if (m_gradient_draw_mode == draw_radial_gradient)
1382 {
1383 float delta;
1384
1385 delta = m_radial_gradient_change_rate.value() * speed / zoomer().transformation().scale();
1386 if (keyboard_state[SDL_SCANCODE_1])
1387 {
1388 gradient_r0() -= delta;
1389 gradient_r0() = t_max(0.0f, gradient_r0());
1390 }
1391 if (keyboard_state[SDL_SCANCODE_2])
1392 {
1393 gradient_r0() += delta;
1394 }
1395
1396 if (keyboard_state[SDL_SCANCODE_3])
1397 {
1398 gradient_r1() -= delta;
1399 gradient_r1() = t_max(0.0f, gradient_r1());
1400 }
1401 if (keyboard_state[SDL_SCANCODE_4])
1402 {
1403 gradient_r1() += delta;
1404 }
1405
1406 if (keyboard_state[SDL_SCANCODE_1] || keyboard_state[SDL_SCANCODE_2]
1407 || keyboard_state[SDL_SCANCODE_3] || keyboard_state[SDL_SCANCODE_4])
1408 {
1409 std::cout << "Radial gradient values set to: r0 = "
1410 << gradient_r0() << " r1 = " << gradient_r1() << "\n";
1411 }
1412 }
1413
1414 if (m_gradient_draw_mode == draw_sweep_gradient)
1415 {
1416 float delta;
1417
1418 delta = m_sweep_factor_gradient_change_rate.value();
1419 if (keyboard_state[SDL_SCANCODE_1])
1420 {
1421 sweep_repeat_factor() -= delta;
1422 }
1423 if (keyboard_state[SDL_SCANCODE_2])
1424 {
1425 sweep_repeat_factor() += delta;
1426 }
1427
1428 if (keyboard_state[SDL_SCANCODE_1] || keyboard_state[SDL_SCANCODE_2])
1429 {
1430 std::cout << "Sweep Repeat factor set to: "
1431 << sweep_repeat_factor() << "\n";
1432 }
1433 }
1434
1435 if (Painter::is_miter_join(m_join_style))
1436 {
1437 if (keyboard_state[SDL_SCANCODE_N])
1438 {
1439 m_miter_limit += m_change_miter_limit_rate.value() * speed;
1440 }
1441
1442 if (keyboard_state[SDL_SCANCODE_B])
1443 {
1444 m_miter_limit -= m_change_miter_limit_rate.value() * speed;
1445 m_miter_limit = t_max(0.0f, m_miter_limit);
1446 }
1447
1448 if (keyboard_state[SDL_SCANCODE_N] || keyboard_state[SDL_SCANCODE_B])
1449 {
1450 std::cout << "Miter-limit set to: " << m_miter_limit << "\n";
1451 }
1452 }
1453
1454 if (clipping_window())
1455 {
1456 vec2 *changer;
1457 float delta, delta_y;
1458
1459 delta = m_window_change_rate.value() * speed / zoomer().transformation().scale();
1460 if (keyboard_state[SDL_SCANCODE_LCTRL] || keyboard_state[SDL_SCANCODE_RCTRL])
1461 {
1462 changer = &clipping_wh();
1463 delta_y = delta;
1464 }
1465 else
1466 {
1467 changer = &clipping_xy();
1468 delta_y = -delta;
1469 }
1470
1471 if (keyboard_state[SDL_SCANCODE_KP_8])
1472 {
1473 changer->y() += delta_y;
1474 }
1475
1476 if (keyboard_state[SDL_SCANCODE_KP_2])
1477 {
1478 changer->y() -= delta_y;
1479 }
1480
1481 if (keyboard_state[SDL_SCANCODE_KP_6])
1482 {
1483 changer->x() += delta;
1484 }
1485
1486 if (keyboard_state[SDL_SCANCODE_KP_4])
1487 {
1488 changer->x() -= delta;
1489 }
1490
1491 if (keyboard_state[SDL_SCANCODE_KP_2] || keyboard_state[SDL_SCANCODE_KP_4]
1492 || keyboard_state[SDL_SCANCODE_KP_6] || keyboard_state[SDL_SCANCODE_KP_8])
1493 {
1494 m_clip_window_path_dirty = true;
1495 std::cout << "Clipping window set to: xy = " << clipping_xy() << " wh = " << clipping_wh() << "\n";
1496 }
1497 }
1498}
1499
1500void
1501painter_stroke_test::
1502fill_centered_rect(const vec2 &pt, float r, const PainterData &draw)
1503{
1504 Rect rect;
1505 vec2 sz(0.5f * r);
1506
1507 rect.m_min_point = pt - sz;
1508 rect.m_max_point = pt + sz;
1509 m_painter->fill_rect(draw, rect, m_aa_fill_mode);
1510}
1511
1512vec2
1513painter_stroke_test::
1514brush_item_coordinate(ivec2 q)
1515{
1516 vec2 p;
1517
1518 if (matrix_brush())
1519 {
1520 p = vec2(q);
1521 }
1522 else
1523 {
1524 p = item_coordinates(q);
1525 }
1526 return p;
1527}
1528
1529vec2
1530painter_stroke_test::
1531item_coordinates(vec2 p)
1532{
1533 /* unapply zoomer()
1534 */
1535 p = zoomer().transformation().apply_inverse_to_point(p);
1536
1537 /* unapply shear()
1538 */
1539 p /= shear();
1540
1541 /* unapply rotation by angle()
1542 */
1543 float s, c, a;
1544 float2x2 tr;
1545 a = -angle() * FASTUIDRAW_PI / 180.0f;
1546 s = t_sin(a);
1547 c = t_cos(a);
1548
1549 tr(0, 0) = c;
1550 tr(1, 0) = s;
1551 tr(0, 1) = -s;
1552 tr(1, 1) = c;
1553
1554 p = tr * p;
1555
1556 /* unapply shear2()
1557 */
1558 p /= shear2();
1559
1560 /* unapply glyph-flip
1561 */
1562 if (m_paths[m_selected_path]->m_from_glyph)
1563 {
1564 p.y() *= -1.0f;
1565 }
1566
1567 return p;
1568}
1569
1570void
1571painter_stroke_test::
1572handle_event(const SDL_Event &ev)
1573{
1574 zoomer().handle_event(ev);
1575 switch(ev.type)
1576 {
1577 case SDL_QUIT:
1578 end_demo(0);
1579 break;
1580
1581 case SDL_WINDOWEVENT:
1582 if (ev.window.event == SDL_WINDOWEVENT_RESIZED)
1583 {
1584 m_grid_path_dirty = true;
1585 on_resize(ev.window.data1, ev.window.data2);
1586 }
1587 break;
1588
1589 case SDL_MOUSEMOTION:
1590 {
1591 ivec2 c(ev.motion.x + ev.motion.xrel,
1592 ev.motion.y + ev.motion.yrel);
1593
1594 if (ev.motion.state & SDL_BUTTON(SDL_BUTTON_MIDDLE))
1595 {
1596 gradient_p0() = brush_item_coordinate(c);
1597 }
1598 else if (ev.motion.state & SDL_BUTTON(SDL_BUTTON_RIGHT))
1599 {
1600 gradient_p1() = brush_item_coordinate(c);
1601 }
1602 }
1603 break;
1604
1605 case SDL_KEYUP:
1606 switch(ev.key.keysym.sym)
1607 {
1608 case SDLK_ESCAPE:
1609 end_demo(0);
1610 break;
1611
1612 case SDLK_v:
1613 cycle_value(m_stroking_mode, ev.key.keysym.mod & (KMOD_SHIFT|KMOD_CTRL|KMOD_ALT),
1614 Painter::number_stroking_methods);
1615 std::cout << "Stroking mode set to: " << Painter::label(m_stroking_mode) << "\n";
1616 break;
1617
1618 case SDLK_k:
1619 cycle_value(m_selected_path, ev.key.keysym.mod & (KMOD_SHIFT|KMOD_CTRL|KMOD_ALT), m_paths.size());
1620 std::cout << "Path " << m_paths[m_selected_path]->m_label << " selected\n";
1621 m_clip_window_path_dirty = true;
1622 break;
1623
1624 case SDLK_5:
1625 m_draw_grid = !m_draw_grid;
1626 if (m_draw_grid)
1627 {
1628 std::cout << "Draw grid\n";
1629 }
1630 else
1631 {
1632 std::cout << "Don't draw grid\n";
1633 }
1634 break;
1635
1636 case SDLK_q:
1637 shear() = shear2() = vec2(1.0f, 1.0f);
1638 break;
1639
1640 case SDLK_p:
1641 if (ev.key.keysym.mod & KMOD_CTRL)
1642 {
1643 m_draw_path_pts = !m_draw_path_pts;
1644 if (m_draw_path_pts)
1645 {
1646 std::cout << "Draw Path Points\n";
1647 }
1648 else
1649 {
1650 std::cout << "Do not draw Path Points\n";
1651 }
1652 }
1653 else if(ev.key.keysym.mod & KMOD_SHIFT)
1654 {
1655 std::cout << m_paths[m_selected_path]->m_path_string;
1656 }
1657 else
1658 {
1659 m_stroke_width_in_pixels = !m_stroke_width_in_pixels;
1660 if (m_stroke_width_in_pixels)
1661 {
1662 std::cout << "Stroke width specified in pixels\n";
1663 }
1664 else
1665 {
1666 std::cout << "Stroke width specified in local coordinates\n";
1667 }
1668 }
1669 break;
1670
1671 case SDLK_o:
1672 if (ev.key.keysym.mod & KMOD_CTRL)
1673 {
1674 if (ev.key.keysym.mod & (KMOD_SHIFT | KMOD_ALT))
1675 {
1676 if (m_show_surface > 0)
1677 {
1678 --m_show_surface;
1679 }
1680 }
1681 else
1682 {
1683 ++m_show_surface;
1684 }
1685 }
1686 else
1687 {
1688 clipping_window() = !clipping_window();
1689 std::cout << "Clipping window: " << on_off(clipping_window()) << "\n";
1690 }
1691 break;
1692
1693 case SDLK_w:
1694 if (!(ev.key.keysym.mod & (KMOD_SHIFT | KMOD_CTRL)))
1695 {
1696 repeat_window() = !repeat_window();
1697 std::cout << "Brush Repeat window: " << on_off(repeat_window()) << "\n";
1698 }
1699 else if (repeat_window())
1700 {
1701 if (ev.key.keysym.mod & KMOD_SHIFT)
1702 {
1703 cycle_value(repeat_window_spread_type_x(), false, PainterBrush::number_spread_types);
1704 std::cout << "Brush Repeat window x-spread-type set to "
1705 << m_spread_type_labels[repeat_window_spread_type_x()]
1706 << "\n";
1707 }
1708
1709 if (ev.key.keysym.mod & KMOD_CTRL)
1710 {
1711 cycle_value(repeat_window_spread_type_y(), false, PainterBrush::number_spread_types);
1712 std::cout << "Brush Repeat window y-spread-type set to "
1713 << m_spread_type_labels[repeat_window_spread_type_y()]
1714 << "\n";
1715 }
1716 }
1717 break;
1718
1719 case SDLK_y:
1720 matrix_brush() = !matrix_brush();
1721 std::cout << "Make brush appear as-if in pixel coordinates: " << on_off(matrix_brush()) << "\n";
1722 break;
1723
1724 case SDLK_h:
1725 if (m_gradient_draw_mode != draw_no_gradient)
1726 {
1727 cycle_value(gradient_spread_type(),
1728 ev.key.keysym.mod & KMOD_SHIFT,
1729 PainterBrush::number_spread_types);
1730 std::cout << "Gradient spread type set to : "
1731 << m_spread_type_labels[gradient_spread_type()]
1732 << "\n";
1733 }
1734 break;
1735
1736 case SDLK_i:
1737 if (m_image && m_draw_fill != dont_draw_fill_path)
1738 {
1739 if (ev.key.keysym.mod & (KMOD_CTRL|KMOD_ALT))
1740 {
1741 m_apply_mipmapping = !m_apply_mipmapping;
1742 std::cout << "Mipmapping ";
1743 if (!m_apply_mipmapping)
1744 {
1745 std::cout << "NOT ";
1746 }
1747 std::cout << "applied.\n";
1748 }
1749 else
1750 {
1751 cycle_value(m_image_filter, ev.key.keysym.mod & KMOD_SHIFT, number_image_filter_modes);
1752 std::cout << "Image filter mode set to: " << m_image_filter_mode_labels[m_image_filter] << "\n";
1753 }
1754 }
1755 break;
1756
1757 case SDLK_s:
1758 if (m_draw_fill != dont_draw_fill_path)
1759 {
1760 cycle_value(m_active_color_stop, ev.key.keysym.mod & (KMOD_SHIFT|KMOD_CTRL|KMOD_ALT), m_color_stops.size());
1761 std::cout << "Drawing color stop: " << m_color_stops[m_active_color_stop].first << "\n";
1762 }
1763 break;
1764
1765 case SDLK_x:
1766 if (ev.key.keysym.mod & KMOD_CTRL)
1767 {
1768 m_use_shader_effect = !m_use_shader_effect;
1769 std::cout << "Use shader effect set to: " << on_off(m_use_shader_effect) << "\n";
1770 }
1771 else
1772 {
1773 m_use_path_effect = !m_use_path_effect;
1774 std::cout << "Use path effect set to: " << on_off(m_use_path_effect) << "\n";
1775 }
1776 break;
1777
1778 case SDLK_g:
1779 if (m_draw_fill != dont_draw_fill_path)
1780 {
1781 cycle_value(m_gradient_draw_mode, ev.key.keysym.mod & (KMOD_SHIFT|KMOD_CTRL|KMOD_ALT), number_gradient_draw_modes);
1782 std::cout << "Gradient mode set to: " << m_gradient_mode_labels[m_gradient_draw_mode] << "\n";
1783 }
1784 break;
1785
1786 case SDLK_j:
1787 cycle_value(m_join_style, ev.key.keysym.mod & (KMOD_SHIFT|KMOD_CTRL|KMOD_ALT), Painter::number_join_styles);
1788 std::cout << "Join drawing mode set to: " << Painter::label(m_join_style) << "\n";
1789 break;
1790
1791 case SDLK_d:
1792 cycle_value(m_dash, ev.key.keysym.mod & (KMOD_SHIFT|KMOD_CTRL|KMOD_ALT), m_dash_patterns.size() + 1);
1793 m_current_effect.clear();
1794 if (is_dashed_stroking())
1795 {
1796 unsigned int P;
1797 P = dash_pattern();
1798 std::cout << "Set to stroke dashed with pattern: {";
1799 for(unsigned int i = 0; i < m_dash_patterns[P].size(); ++i)
1800 {
1801 if (i != 0)
1802 {
1803 std::cout << ", ";
1804 }
1805 std::cout << "Draw(" << m_dash_patterns[P][i].m_draw_length
1806 << "), Space(" << m_dash_patterns[P][i].m_space_length
1807 << ")";
1808
1809 m_current_effect.add_dash(m_dash_patterns[P][i].m_draw_length,
1810 m_dash_patterns[P][i].m_space_length);
1811 }
1812 std::cout << "}\n";
1813 }
1814 else
1815 {
1816 std::cout << "Set to stroke non-dashed\n";
1817 }
1818 break;
1819
1820 case SDLK_c:
1821 cycle_value(m_cap_style, ev.key.keysym.mod & (KMOD_SHIFT|KMOD_CTRL|KMOD_ALT), Painter::number_cap_styles);
1822 std::cout << "Cap drawing mode set to: " << Painter::label(m_cap_style) << "\n";
1823 break;
1824
1825 case SDLK_r:
1826 if (m_draw_fill != dont_draw_fill_path)
1827 {
1828 cycle_value(current_fill_rule(),
1829 ev.key.keysym.mod & (KMOD_SHIFT|KMOD_CTRL|KMOD_ALT),
1830 current_end_fill_rule() + 1);
1831 if (current_fill_rule() < Painter::number_fill_rule)
1832 {
1833 std::cout << "Fill rule set to: "
1834 << Painter::label(static_cast<enum Painter::fill_rule_t>(current_fill_rule()))
1835 << "\n";
1836 }
1837 else if (current_fill_rule() == current_end_fill_rule())
1838 {
1839 std::cout << "Fill rule set to custom fill rule: all winding numbers filled\n";
1840 }
1841 else
1842 {
1843 c_array<const int> wnd;
1844 int value;
1845 wnd = path().tessellation().filled().root_subset().winding_numbers();
1846 value = wnd[current_fill_rule() - Painter::number_fill_rule];
1847 std::cout << "Fill rule set to custom fill rule: winding_number == "
1848 << value << "\n";
1849 }
1850 }
1851 break;
1852
1853 case SDLK_e:
1854 if (m_draw_fill != dont_draw_fill_path)
1855 {
1856 cycle_value(m_fill_by_mode, ev.key.keysym.mod & (KMOD_SHIFT|KMOD_CTRL|KMOD_ALT), fill_by_number_modes);
1857 std::cout << "Set to fill by " << m_fill_by_mode_labels[m_fill_by_mode] << "\n";
1858 }
1859 break;
1860
1861 case SDLK_f:
1862 cycle_value(m_draw_fill,
1863 ev.key.keysym.mod & (KMOD_SHIFT|KMOD_CTRL|KMOD_ALT),
1864 number_fill_modes);
1865 std::cout << "Draw Fill by " << m_draw_fill_labels[m_draw_fill] << "\n";
1866 break;
1867
1868 case SDLK_u:
1869 if (m_draw_fill != dont_draw_fill_path)
1870 {
1871 m_aa_fill_mode = !m_aa_fill_mode;
1872 std::cout << "Filling anti-alias mode set to: "
1873 << on_off(m_aa_fill_mode) << "\n";
1874 }
1875 break;
1876
1877 case SDLK_a:
1878 if (m_stroke_width > 0.0f)
1879 {
1880 m_aa_stroke_mode = !m_aa_stroke_mode;
1881 std::cout << on_off(m_aa_stroke_mode) << "\n";
1882 }
1883 break;
1884
1885 case SDLK_SPACE:
1886 m_wire_frame = !m_wire_frame;
1887 std::cout << "Wire Frame = " << m_wire_frame << "\n";
1888 break;
1889
1890 case SDLK_l:
1891 m_draw_stats = !m_draw_stats;
1892 break;
1893
1894 case SDLK_z:
1895 if (ev.key.keysym.mod & (KMOD_SHIFT|KMOD_CTRL|KMOD_ALT))
1896 {
1897 m_curve_flatness *= 0.5f;
1898 }
1899 else
1900 {
1901 m_curve_flatness *= 2.0f;
1902 }
1903 std::cout << "Painter::curve_flatness set to " << m_curve_flatness << "\n";
1904 break;
1905 }
1906 break;
1907 };
1908}
1909
1910void
1911painter_stroke_test::
1912construct_paths(int w, int h)
1913{
1914 for(const std::string &file : m_path_file_list)
1915 {
1916 std::ifstream path_file(file.c_str());
1917 if (path_file)
1918 {
1919 std::stringstream buffer;
1920 Path P;
1921
1922 buffer << path_file.rdbuf();
1923 read_path(P, buffer.str());
1924 if (P.number_contours() > 0)
1925 {
1926 m_paths.push_back(FASTUIDRAWnew PerPath(P, file, w, h, false));
1927 }
1928 }
1929 }
1930
1931 for (uint32_t character_code : m_character_code_list)
1932 {
1933 uint32_t glyph_code;
1934 GlyphRenderer renderer(distance_field_glyph);
1935 Glyph g;
1936
1937 glyph_code = m_font->glyph_code(character_code);
1938 g = m_painter->glyph_cache().fetch_glyph(renderer, m_font.get(), glyph_code);
1939 if (g.valid() && g.path().number_contours() > 0)
1940 {
1941 std::ostringstream str;
1942 str << "character code:" << character_code;
1943 m_paths.push_back(FASTUIDRAWnew PerPath(&g.path(), str.str(), w, h, true));
1944 }
1945 }
1946
1947 if (m_paths.empty())
1948 {
1949 Path path;
1950
1951 path << vec2(50.0f, 35.0f)
1952 << Path::control_point(60.0f, 50.0f)
1953 << vec2(70.0f, 35.0f)
1954 << Path::arc_degrees(180.0, vec2(70.0f, -100.0f))
1955 << Path::control_point(60.0f, -150.0f)
1956 << Path::control_point(30.0f, -50.0f)
1957 << vec2(0.0f, -100.0f)
1958 << Path::contour_close_arc_degrees(90.0f)
1959 << vec2(200.0f, 200.0f)
1960 << vec2(400.0f, 200.0f)
1961 << vec2(400.0f, 400.0f)
1962 << vec2(200.0f, 400.0f)
1963 << Path::contour_close()
1964 << vec2(-50.0f, 100.0f)
1965 << vec2(0.0f, 200.0f)
1966 << vec2(100.0f, 300.0f)
1967 << vec2(150.0f, 325.0f)
1968 << vec2(150.0f, 100.0f)
1969 << Path::contour_close()
1970 << vec2(300.0f, 300.0f);
1971 m_paths.push_back(FASTUIDRAWnew PerPath(path, "Default Path", w, h, false));
1972 }
1973
1974 if (m_init_pan_zoom.value())
1975 {
1976 for (const auto &P : m_paths)
1977 {
1978 ScaleTranslate<float> v;
1979
1980 v.translation_x(m_initial_pan_x.value());
1981 v.translation_y(m_initial_pan_y.value());
1982 v.scale(m_initial_zoom.value());
1983 P->m_path_zoomer.transformation(v);
1984 }
1985 }
1986}
1987
1988void
1989painter_stroke_test::
1990per_path_processing(void)
1991{
1992 m_miter_limit = 0.0f;
1993 for(const auto &P : m_paths)
1994 {
1995 c_array<const TessellatedPath::join> joins;
1996 const TessellatedPath *tess;
1997
1998 tess = &P->path().tessellation(-1.0f);
1999 joins = tess->partitioned().joins();
2000 for(const TessellatedPath::join &J : joins)
2001 {
2002 /* TODO: if the join is so that the miter-distance
2003 * is infinity (because the join happens at anti-parallel
2004 * value), we should make m_miter_limit quite large.
2005 * The value of m_miter_distance() is -1 if the
2006 * miter-distance is (too floating point arithmatic)
2007 * infinity.
2008 */
2009 m_miter_limit = t_max(m_miter_limit, J.m_miter_distance);
2010 }
2011
2012 if (m_print_path.value())
2013 {
2014 std::cout << "Path \"" << P->m_label << "\" tessellated:\n";
2015 for(unsigned int c = 0; c < tess->number_contours(); ++c)
2016 {
2017 std::cout << "\tContour #" << c << "\n";
2018 for(unsigned int e = 0; e < tess->number_edges(c); ++e)
2019 {
2020 c_array<const TessellatedPath::segment> segs;
2021
2022 std::cout << "\t\tEdge #" << e << " has "
2023 << tess->edge_segment_data(c, e).size() << " segments\n";
2024 segs = tess->edge_segment_data(c, e);
2025 for(unsigned int i = 0; i < segs.size(); ++i)
2026 {
2027 std::cout << "\t\t\tSegment #" << i << ":\n"
2028 << "\t\t\t\tstart_p = " << segs[i].m_start_pt << "\n"
2029 << "\t\t\t\tend_p = " << segs[i].m_end_pt << "\n"
2030 << "\t\t\t\tlength = " << segs[i].m_length << "\n"
2031 << "\t\t\t\tedge_d = " << segs[i].m_distance_from_edge_start << "\n"
2032 << "\t\t\t\tcontour_d = " << segs[i].m_distance_from_contour_start << "\n"
2033 << "\t\t\t\tedge_l = " << segs[i].m_edge_length << "\n"
2034 << "\t\t\t\tcontour_l = " << segs[i].m_contour_length << "\n";
2035 }
2036 }
2037 }
2038 }
2039 }
2040 m_miter_limit = t_min(100.0f, m_miter_limit); //100 is an insane miter limit.
2041}
2042
2043void
2044painter_stroke_test::
2045construct_color_stops(void)
2046{
2047 for(color_stop_arguments::hoard::const_iterator
2048 iter = m_color_stop_args.values().begin(),
2049 end = m_color_stop_args.values().end();
2050 iter != end; ++iter)
2051 {
2052 reference_counted_ptr<ColorStopSequence> h;
2053 h = m_painter->colorstop_atlas().create(iter->second->m_stops,
2054 iter->second->m_discretization);
2055 m_color_stops.push_back(named_color_stop(iter->first, h));
2056 }
2057
2058 if (m_color_stops.empty())
2059 {
2060 ColorStopArray S;
2061 reference_counted_ptr<ColorStopSequence> h;
2062
2063 S.add(ColorStop(u8vec4(0, 255, 0, 255), 0.0f));
2064 S.add(ColorStop(u8vec4(0, 255, 255, 255), 0.33f));
2065 S.add(ColorStop(u8vec4(255, 255, 0, 255), 0.66f));
2066 S.add(ColorStop(u8vec4(255, 0, 0, 255), 1.0f));
2067 h = m_painter->colorstop_atlas().create(S, 8);
2068 m_color_stops.push_back(named_color_stop("Default ColorStop Sequence", h));
2069 }
2070}
2071
2072void
2073painter_stroke_test::
2074construct_dash_patterns(void)
2075{
2076 std::vector<PainterDashedStrokeParams::DashPatternElement> tmp;
2077 for(unsigned int i = 0, endi = m_dash_pattern_files.m_files.size(); i < endi; ++i)
2078 {
2079 std::ifstream file(m_dash_pattern_files.m_files[i].c_str());
2080
2081 read_dash_pattern(tmp, file);
2082 if (!tmp.empty())
2083 {
2084 m_dash_patterns.push_back(std::vector<PainterDashedStrokeParams::DashPatternElement>());
2085 std::swap(tmp, m_dash_patterns.back());
2086 }
2087 tmp.clear();
2088 }
2089
2090 if (m_dash_patterns.empty())
2091 {
2092 m_dash_patterns.push_back(std::vector<PainterDashedStrokeParams::DashPatternElement>());
2093 m_dash_patterns.back().push_back(PainterDashedStrokeParams::DashPatternElement(20.0f, 10.0f));
2094 m_dash_patterns.back().push_back(PainterDashedStrokeParams::DashPatternElement(15.0f, 10.0f));
2095 m_dash_patterns.back().push_back(PainterDashedStrokeParams::DashPatternElement(10.0f, 10.0f));
2096 m_dash_patterns.back().push_back(PainterDashedStrokeParams::DashPatternElement( 5.0f, 10.0f));
2097 }
2098}
2099
2100void
2101painter_stroke_test::
2102fill_way_effect(WavyEffect &data)
2103{
2104 uint32_t ms;
2105 float fc, fs, fc2, fs2;
2106
2107 ms = m_phase_timer.elapsed();
2108 ms = ms % 4000;
2109
2110 data.m_phase = static_cast<float>(ms) / 2000.0f * FASTUIDRAW_PI;
2111 fc = t_cos(data.m_phase);
2112 fs = t_sin(data.m_phase);
2113 fc2 = t_cos(2.0f * data.m_phase);
2114 fs2 = t_sin(2.0f * data.m_phase);
2115
2116 data.m_domain_coeff = 8.0f * FASTUIDRAW_PI / (m_stroke_width * 10.0f);
2117 data.m_cos_coeffs = vec4(+8.0f * fc,
2118 -4.0f * fs,
2119 +2.0f * fs2,
2120 -1.0f * fc2);
2121 data.m_sin_coeffs = vec4(+1.3f * fs,
2122 -2.0f * fc,
2123 +4.0f * fs2,
2124 -8.0f * fc2);
2125}
2126
2127void
2128painter_stroke_test::
2129draw_scene(bool drawing_wire_frame)
2130{
2131 m_painter->save();
2132 if (!m_draw_line_pen.packed())
2133 {
2134 PainterBrush br;
2135 br.color(m_draw_line_red.value(), m_draw_line_green.value(),
2136 m_draw_line_blue.value(), m_draw_line_alpha.value());
2137 m_draw_line_pen = m_painter->packed_value_pool().create_packed_brush(br);
2138 }
2139
2140 if (m_paths[m_selected_path]->m_from_glyph)
2141 {
2142 /* Glyphs have y-increasing upwards, rather than
2143 * downwards; so we reverse the y
2144 */
2145 m_painter->shear(1.0f, -1.0f);
2146 }
2147
2148 if (clipping_window() && !drawing_wire_frame)
2149 {
2150 if (m_clip_window_path_dirty)
2151 {
2152 Path clip_window_path;
2153 clip_window_path << clipping_xy()
2154 << vec2(clipping_xy().x(), clipping_xy().y() + clipping_wh().y())
2155 << clipping_xy() + clipping_wh()
2156 << vec2(clipping_xy().x() + clipping_wh().x(), clipping_xy().y())
2157 << Path::contour_close();
2158 m_clip_window_path.swap(clip_window_path);
2159 m_clip_window_path_dirty = false;
2160 }
2161
2162 PainterBrush white;
2163 white.color(1.0f, 1.0f, 1.0f, 1.0f);
2164 PainterStrokeParams st;
2165 st.miter_limit(-1.0f);
2166 st.width(4.0f);
2167 m_painter->save();
2168 m_painter->clip_out_path(m_clip_window_path, Painter::nonzero_fill_rule);
2169 m_painter->stroke_path(PainterData(&white, &st), m_clip_window_path,
2170 StrokingStyle()
2171 .join_style(Painter::miter_clip_joins),
2172 false);
2173 m_painter->restore();
2174 m_painter->clip_in_rect(Rect()
2175 .min_point(clipping_xy())
2176 .size(clipping_wh()));
2177 }
2178
2179 CustomFillRuleFunction fill_rule_function(everything_filled);
2180 WindingValueFillRule value_fill_rule;
2181 CustomFillRuleBase *fill_rule(&fill_rule_function);
2182
2183 if (m_draw_fill != dont_draw_fill_path)
2184 {
2185 PainterBrush fill_brush;
2186
2187 fill_brush.color(m_fill_red.value(), m_fill_green.value(),
2188 m_fill_blue.value(), m_fill_alpha.value());
2189
2190 if (matrix_brush())
2191 {
2192 /* We want the effect that the brush is in the pixel-coordinates
2193 * so we make the transformation the same transformation
2194 * that is applied to painter.
2195 */
2196 float m;
2197 m = zoomer().transformation().scale();
2198
2199 fill_brush.no_transformation();
2200 fill_brush.apply_translate(zoomer().transformation().translation());
2201 fill_brush.apply_shear(m, m);
2202 fill_brush.apply_shear(shear().x(), shear().y());
2203 fill_brush.apply_rotate(angle() * FASTUIDRAW_PI / 180.0f);
2204 fill_brush.apply_shear(shear2().x(), shear2().y());
2205 }
2206 else
2207 {
2208 fill_brush.no_transformation_matrix();
2209 }
2210
2211 if (repeat_window())
2212 {
2213 fill_brush.repeat_window(repeat_xy(), repeat_wh(),
2214 repeat_window_spread_type_x(),
2215 repeat_window_spread_type_y());
2216 }
2217 else
2218 {
2219 fill_brush.no_repeat_window();
2220 }
2221
2222 if (m_gradient_draw_mode == draw_linear_gradient)
2223 {
2224 fill_brush.linear_gradient(m_color_stops[m_active_color_stop].second,
2225 gradient_p0(), gradient_p1(), gradient_spread_type());
2226 }
2227 else if (m_gradient_draw_mode == draw_radial_gradient)
2228 {
2229 fill_brush.radial_gradient(m_color_stops[m_active_color_stop].second,
2230 gradient_p0(), gradient_r0(),
2231 gradient_p1(), gradient_r1(),
2232 gradient_spread_type());
2233 }
2234 else if (m_gradient_draw_mode == draw_sweep_gradient)
2235 {
2236 vec2 d;
2237 d = gradient_p1() - gradient_p0();
2238 fill_brush.sweep_gradient(m_color_stops[m_active_color_stop].second,
2239 gradient_p0(), t_atan2(d.y(), d.x()),
2240 Painter::y_increases_downwards,
2241 Painter::clockwise, sweep_repeat_factor(),
2242 gradient_spread_type());
2243 }
2244 else
2245 {
2246 fill_brush.no_gradient();
2247 }
2248
2249 if (!m_image || m_image_filter == no_image)
2250 {
2251 fill_brush.no_image();
2252 }
2253 else
2254 {
2255 enum PainterImageBrushShader::filter_t f;
2256 enum PainterImageBrushShader::mipmap_t mf;
2257
2258 switch(m_image_filter)
2259 {
2260 case image_nearest_filter:
2261 f = PainterImageBrushShader::filter_nearest;
2262 break;
2263 case image_linear_filter:
2264 f = PainterImageBrushShader::filter_linear;
2265 break;
2266 case image_cubic_filter:
2267 f = PainterImageBrushShader::filter_cubic;
2268 break;
2269 default:
2270 FASTUIDRAWassert(!"Incorrect value for m_image_filter!");
2271 f = PainterImageBrushShader::filter_nearest;
2272 }
2273 mf = (m_apply_mipmapping) ?
2274 PainterImageBrushShader::apply_mipmapping:
2275 PainterImageBrushShader::dont_apply_mipmapping;
2276 fill_brush.sub_image(m_image, m_image_offset, m_image_size, f, mf);
2277 }
2278
2279 if (current_fill_rule() < Painter::number_fill_rule)
2280 {
2281 fill_rule_function = CustomFillRuleFunction(static_cast<enum Painter::fill_rule_t>(current_fill_rule()));
2282 }
2283 else if (current_fill_rule() != current_end_fill_rule())
2284 {
2285 int value;
2286 c_array<const int> wnd;
2287
2288 wnd = path().tessellation().filled().root_subset().winding_numbers();
2289 value = wnd[current_fill_rule() - Painter::number_fill_rule];
2290 value_fill_rule = WindingValueFillRule(value);
2291 fill_rule = &value_fill_rule;
2292 }
2293
2294 PainterData D;
2295 if (drawing_wire_frame)
2296 {
2297 D = PainterData(m_draw_line_pen);
2298 }
2299 else
2300 {
2301 D = PainterData(&fill_brush);
2302 }
2303
2304 if (m_fill_by_mode == fill_by_clipping)
2305 {
2306 Rect R;
2307
2308 path().approximate_bounding_box(&R);
2309 m_painter->save();
2310 m_painter->clip_in_path(path(), *fill_rule);
2311 m_painter->fill_rect(D, R);
2312 m_painter->restore();
2313 }
2314 else if (m_fill_by_mode == fill_by_filled_path)
2315 {
2316 m_painter->fill_path(D, path(), *fill_rule, m_aa_fill_mode);
2317 }
2318 else if(current_fill_rule() < Painter::number_fill_rule)
2319 {
2320 m_painter->fill_path(D, path().shader_filled_path(),
2321 static_cast<enum Painter::fill_rule_t>(current_fill_rule()));
2322 }
2323 }
2324
2325 if (m_draw_path_pts)
2326 {
2327 float inv_scale, r;
2328
2329 inv_scale = 1.0f / zoomer().transformation().scale();
2330 r = 15.0f * inv_scale;
2331 if (!m_blue_pen.packed())
2332 {
2333 FASTUIDRAWassert(!m_red_pen.packed());
2334 FASTUIDRAWassert(!m_green_pen.packed());
2335 m_blue_pen = m_painter->packed_value_pool().create_packed_brush(PainterBrush().color(0.0, 0.0, 1.0, 1.0));
2336 m_red_pen = m_painter->packed_value_pool().create_packed_brush(PainterBrush().color(1.0, 0.0, 0.0, 1.0));
2337 m_green_pen = m_painter->packed_value_pool().create_packed_brush(PainterBrush().color(0.0, 1.0, 0.0, 1.0));
2338 }
2339
2340 for (const vec2 &pt : m_paths[m_selected_path]->m_pts)
2341 {
2342 fill_centered_rect(pt, r, PainterData(m_blue_pen));
2343 }
2344
2345 for (const vec2 &pt : m_paths[m_selected_path]->m_ctl_pts)
2346 {
2347 fill_centered_rect(pt, r, PainterData(m_red_pen));
2348 }
2349
2350 for (const vec2 &pt : m_paths[m_selected_path]->m_arc_center_pts)
2351 {
2352 fill_centered_rect(pt, r, PainterData(m_green_pen));
2353 }
2354 }
2355
2356 if (!m_stroke_pen.packed())
2357 {
2358 PainterBrush br;
2359 br.color(m_stroke_red.value(), m_stroke_green.value(), m_stroke_blue.value(), m_stroke_alpha.value());
2360 m_stroke_pen = m_painter->packed_value_pool().create_packed_brush(br);
2361 }
2362
2363 if (m_stroke_width > 0.0f)
2364 {
2365 PainterData::brush_value *stroke_pen;
2366 stroke_pen = (!drawing_wire_frame) ? &m_stroke_pen : &m_draw_line_pen;
2367
2368 if (m_draw_fill == draw_fill_path_occludes_stroking)
2369 {
2370 m_painter->save();
2371 m_painter->clip_out_path(path(), *fill_rule);
2372 }
2373
2374 if (is_dashed_stroking() && !m_use_path_effect)
2375 {
2376 ExampleItemData<PainterDashedStrokeParams> st;
2377
2378 st.m_stroke_params.miter_limit(m_miter_limit);
2379 st.m_stroke_params.width(m_stroke_width);
2380
2381 unsigned int D(dash_pattern());
2382 c_array<const PainterDashedStrokeParams::DashPatternElement> dash_ptr(&m_dash_patterns[D][0],
2383 m_dash_patterns[D].size());
2384 st.m_stroke_params.dash_pattern(dash_ptr);
2385 if (m_stroke_width_in_pixels)
2386 {
2387 st.m_stroke_params.stroking_units(PainterStrokeParams::pixel_stroking_units);
2388 }
2389
2390 if (m_use_shader_effect)
2391 {
2392 fill_way_effect(st.m_wavy_effect);
2393 m_painter->stroke_dashed_path(m_dashed_stroke_shader,
2394 PainterData(*stroke_pen, &st),
2395 path(),
2396 StrokingStyle()
2397 .join_style(m_join_style)
2398 .cap_style(m_cap_style),
2399 m_aa_stroke_mode, m_stroking_mode);
2400 }
2401 else
2402 {
2403 m_painter->stroke_dashed_path(PainterData(*stroke_pen, &st.m_stroke_params),
2404 path(),
2405 StrokingStyle()
2406 .join_style(m_join_style)
2407 .cap_style(m_cap_style),
2408 m_aa_stroke_mode, m_stroking_mode);
2409 }
2410
2411 }
2412 else
2413 {
2414 PathDashEffect *effect(nullptr);
2415 ExampleItemData<PainterStrokeParams> st;
2416
2417 if (m_use_path_effect)
2418 {
2419 effect = &m_current_effect;
2420 }
2421
2422 st.m_stroke_params.miter_limit(m_miter_limit);
2423 st.m_stroke_params.width(m_stroke_width);
2424 if (m_stroke_width_in_pixels)
2425 {
2426 st.m_stroke_params.stroking_units(PainterStrokeParams::pixel_stroking_units);
2427 }
2428
2429 if (m_use_shader_effect)
2430 {
2431 fill_way_effect(st.m_wavy_effect);
2432 m_painter->stroke_path(m_stroke_shader,
2433 PainterData(*stroke_pen, &st),
2434 path(),
2435 StrokingStyle()
2436 .join_style(m_join_style)
2437 .cap_style(m_cap_style),
2438 m_aa_stroke_mode, m_stroking_mode,
2439 effect);
2440 }
2441 else
2442 {
2443 m_painter->stroke_path(PainterData(*stroke_pen, &st.m_stroke_params),
2444 path(),
2445 StrokingStyle()
2446 .join_style(m_join_style)
2447 .cap_style(m_cap_style),
2448 m_aa_stroke_mode, m_stroking_mode,
2449 effect);
2450 }
2451 }
2452
2453 if (m_draw_fill == draw_fill_path_occludes_stroking)
2454 {
2455 m_painter->restore();
2456 }
2457 }
2458
2459 if (m_draw_fill != dont_draw_fill_path && m_gradient_draw_mode != draw_no_gradient && !drawing_wire_frame)
2460 {
2461 float r0, r1;
2462 vec2 p0, p1;
2463
2464 r0 = 15.0f;
2465 r1 = 30.0f;
2466
2467 p0 = gradient_p0();
2468 p1 = gradient_p1();
2469
2470 if (matrix_brush())
2471 {
2472 /* p0, p1 are suppose to be in screen coordinates,
2473 * avoid the drama of unapplying coordinates and just
2474 * change coordinate system temporarily to pixel
2475 * coordinates
2476 */
2477 m_painter->save();
2478 m_painter->transformation(m_pixel_matrix);
2479 }
2480 else
2481 {
2482 float inv_scale;
2483
2484 inv_scale = 1.0f / zoomer().transformation().scale();
2485 r0 *= inv_scale;
2486 r1 *= inv_scale;
2487 }
2488
2489 if (!m_black_pen.packed())
2490 {
2491 FASTUIDRAWassert(!m_white_pen.packed());
2492 m_white_pen = m_painter->packed_value_pool().create_packed_brush(PainterBrush().color(1.0, 1.0, 1.0, 1.0));
2493 m_black_pen = m_painter->packed_value_pool().create_packed_brush(PainterBrush().color(0.0, 0.0, 0.0, 1.0));
2494 }
2495
2496 fill_centered_rect(p0, r1, PainterData(m_black_pen));
2497 fill_centered_rect(p0, r0, PainterData(m_white_pen));
2498
2499 fill_centered_rect(p1, r1, PainterData(m_white_pen));
2500 fill_centered_rect(p1, r0, PainterData(m_black_pen));
2501
2502 if (matrix_brush())
2503 {
2504 m_painter->restore();
2505 }
2506 }
2507 m_painter->restore();
2508}
2509
2510void
2511painter_stroke_test::
2512draw_frame(void)
2513{
2514 ivec2 wh(dimensions());
2515 float us;
2516 PainterSurface::Viewport vwp;
2517
2518 us = static_cast<float>(m_fps_timer.restart_us());
2519
2520 update_cts_params();
2521 vwp.m_dimensions = wh;
2522
2523 /* we must set the viewport of the surface
2524 OUTSIDE of Painter::begin()/Painter::end().
2525 */
2526 m_surface->viewport(vwp);
2527 m_painter->begin(m_surface, Painter::y_increases_downwards);
2528
2529 m_painter->curve_flatness(m_curve_flatness);
2530 m_painter->save();
2531
2532 /* draw grid using painter. */
2533 if (m_draw_grid && m_stroke_width_in_pixels && m_stroke_width > 0.0f)
2534 {
2535 if (m_grid_path_dirty && m_stroke_width > 0.0f)
2536 {
2537 Path grid_path;
2538 for(float x = 0, endx = wh.x(); x < endx; x += m_stroke_width)
2539 {
2540 grid_path << Path::contour_start(x, 0.0f)
2541 << vec2(x, wh.y());
2542 }
2543 for(float y = 0, endy = wh.y(); y < endy; y += m_stroke_width)
2544 {
2545 grid_path << Path::contour_start(0.0f, y)
2546 << vec2(wh.x(), y);
2547 }
2548 m_grid_path_dirty = false;
2549 m_grid_path.swap(grid_path);
2550 }
2551
2552 PainterStrokeParams st;
2553 st.miter_limit(-1.0f);
2554 st.width(2.0f);
2555
2556 PainterBrush stroke_pen;
2557 stroke_pen.color(1.0f, 1.0f, 1.0f, 1.0f);
2558
2559 m_painter->stroke_path(PainterData(&stroke_pen, &st),
2560 m_grid_path,
2561 StrokingStyle()
2562 .cap_style(Painter::flat_caps)
2563 .join_style(Painter::no_joins),
2564 false);
2565 }
2566
2567 m_pixel_matrix = m_painter->transformation();
2568
2569 /* apply zoomer() */
2570 m_painter->concat(zoomer().transformation().matrix3());
2571
2572 /* apply shear */
2573 m_painter->shear(shear().x(), shear().y());
2574
2575 /* apply rotation */
2576 m_painter->rotate(angle() * FASTUIDRAW_PI / 180.0f);
2577
2578 /* apply shear2 */
2579 m_painter->shear(shear2().x(), shear2().y());
2580
2581 /* draw the scene */
2582 draw_scene(false);
2583 #ifndef FASTUIDRAW_GL_USE_GLES
2584 {
2585 if (m_wire_frame)
2586 {
2587 m_painter->queue_action(FASTUIDRAWnew EnableWireFrameAction(true));
2588 draw_scene(true);
2589 m_painter->queue_action(FASTUIDRAWnew EnableWireFrameAction(false));
2590 }
2591 }
2592 #endif
2593
2594 m_painter->restore();
2595
2596 if (m_draw_stats)
2597 {
2598 std::ostringstream ostr;
2599 ivec2 mouse_position;
2600
2601 SDL_GetMouseState(&mouse_position.x(), &mouse_position.y());
2602 ostr << "\nFPS = ";
2603 if (us > 0.0f)
2604 {
2605 ostr << 1000.0f * 1000.0f / us;
2606 }
2607 else
2608 {
2609 ostr << "NAN";
2610 }
2611
2612 ostr << "\nms = " << us / 1000.0f
2613 << "\nDrawing Path: " << m_paths[m_selected_path]->m_label;
2614
2615 if (m_stroke_width > 0.0f)
2616 {
2617 ostr << "\n\t[a]AA-Stroking mode:" << on_off(m_aa_stroke_mode)
2618 << "\n\t[v]Stroke by: " << m_stroking_mode
2619 << "\n\tStroke Width: " << m_stroke_width;
2620 if (m_stroke_width_in_pixels)
2621 {
2622 ostr << "([p]in pixels)";
2623 }
2624 else
2625 {
2626 ostr << "([p]in item units)";
2627 }
2628 if (is_dashed_stroking())
2629 {
2630 ostr << "([d]dashed)";
2631 }
2632 else
2633 {
2634 ostr << "([d]non-dashed)";
2635 }
2636
2637 ostr << "\n\t[c]CapStyle: " << Painter::label(m_cap_style)
2638 << "\n\t[j]JoinStyle: " << Painter::label(m_join_style);
2639 }
2640
2641 if (m_draw_fill != dont_draw_fill_path)
2642 {
2643 bool print_fill_stats(true);
2644
2645 if (m_fill_by_mode == fill_by_shader_filled_path)
2646 {
2647 if (current_fill_rule() >= Painter::number_fill_rule)
2648 {
2649 print_fill_stats = false;
2650 ostr << "\n\nUnable to fill by " << m_fill_by_mode_labels[m_fill_by_mode]
2651 << "\nbecause ShaderFilledPath\nonly supports the standard fill modes\n";
2652 }
2653 }
2654
2655 if (print_fill_stats)
2656 {
2657 if (m_fill_by_mode == fill_by_filled_path)
2658 {
2659 ostr << "\n\t[u]AA-Filling mode: " << on_off(m_aa_fill_mode);
2660 }
2661
2662 ostr << "\n\t[f]Fill Mode: " << m_draw_fill_labels[m_draw_fill]
2663 << "(via " << m_fill_by_mode_labels[m_fill_by_mode] << ")"
2664 << "\n\t[r]Fill Rule: ";
2665 if (current_fill_rule() < Painter::number_fill_rule)
2666 {
2667 ostr << Painter::label(static_cast<enum Painter::fill_rule_t>(current_fill_rule()));
2668 }
2669 else if (current_fill_rule() == current_end_fill_rule())
2670 {
2671 ostr << "Custom (All Windings Filled)";
2672 }
2673 else
2674 {
2675 c_array<const int> wnd;
2676 int value;
2677 wnd = path().tessellation().filled().root_subset().winding_numbers();
2678 value = wnd[current_fill_rule() - Painter::number_fill_rule];
2679 ostr << "Custom (Winding == " << value << ")";
2680 }
2681 }
2682 }
2683 c_array<const unsigned int> stats(painter_stats());
2684 for (unsigned int i = 0; i < stats.size(); ++i)
2685 {
2686 enum Painter::query_stats_t st;
2687
2688 st = static_cast<enum Painter::query_stats_t>(i);
2689 ostr << "\n" << Painter::label(st) << ": " << stats[i];
2690 }
2691 ostr << "\nMouse position:"
2692 << item_coordinates(mouse_position)
2693 << "\ncurve_flatness: " << m_curve_flatness
2694 << "\nView:\n\tzoom = " << zoomer().transformation().scale()
2695 << "\n\ttranslation = " << zoomer().transformation().translation()
2696 << "\n";
2697
2698 PainterBrush brush;
2699 brush.color(0.0f, 1.0f, 1.0f, 1.0f);
2700 draw_text(ostr.str(), 32.0f, m_font.get(), PainterData(&brush));
2701 }
2702
2703 c_array<const PainterSurface* const> surfaces;
2704
2705 surfaces = m_painter->end();
2706 fastuidraw_glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
2707 fastuidraw_glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
2708
2709 m_show_surface = t_min(m_show_surface, (int)surfaces.size());
2710 if (m_show_surface <= 0 || m_show_surface > surfaces.size())
2711 {
2712 m_surface->blit_surface(GL_NEAREST);
2713 }
2714 else
2715 {
2716 const gl::PainterSurfaceGL *S;
2717 PainterSurface::Viewport src, dest;
2718
2719 src = m_surface->viewport();
2720 S = dynamic_cast<const gl::PainterSurfaceGL*>(surfaces[m_show_surface - 1]);
2721
2722 dest.m_origin = src.m_origin;
2723 dest.m_dimensions = ivec2(src.m_dimensions.x(), src.m_dimensions.y() / 2);
2724 m_surface->blit_surface(src, dest, GL_LINEAR);
2725
2726 dest.m_origin.y() += dest.m_dimensions.y();
2727 S->blit_surface(src, dest, GL_LINEAR);
2728 }
2729
2730 if (m_last_shown_surface != m_show_surface)
2731 {
2732 if (m_show_surface > 0)
2733 {
2734 std::cout << "Show offscreen surface: " << m_show_surface - 1 << "\n";
2735 }
2736 else
2737 {
2738 std::cout << "Don't show offscreen surface\n";
2739 }
2740 m_last_shown_surface = m_show_surface;
2741 }
2742}
2743
2744void
2745painter_stroke_test::
2746derived_init(int w, int h)
2747{
2748 //put into unit of per ms.
2749 m_window_change_rate.value() /= 1000.0f;
2750 m_change_stroke_width_rate.value() /= 1000.0f;
2751 m_change_miter_limit_rate.value() /= 1000.0f;
2752
2753 // generate font
2754 reference_counted_ptr<FreeTypeFace::GeneratorFile> gen;
2755 gen = FASTUIDRAWnew FreeTypeFace::GeneratorFile(m_font_file.value().c_str(), 0);
2756 m_font = FASTUIDRAWnew FontFreeType(gen, m_ft_lib);
2757 if (gen->check_creation() != routine_success)
2758 {
2759 std::cout << "\n-----------------------------------------------------"
2760 << "\nWarning: unable to create font from file \""
2761 << m_font_file.value() << "\"\n"
2762 << "-----------------------------------------------------\n";
2763 }
2764
2765 construct_paths(w, h);
2766 per_path_processing();
2767 construct_color_stops();
2768 construct_dash_patterns();
2769
2770 if (!m_image_file.value().empty())
2771 {
2772 ImageLoader image_data(m_image_file.value());
2773 if (image_data.non_empty())
2774 {
2775 if (m_use_atlas.value())
2776 {
2777 m_image = m_painter->image_atlas().create(image_data.width(),
2778 image_data.height(),
2779 image_data,
2780 Image::on_atlas);
2781 }
2782 else
2783 {
2784 m_image = m_painter->image_atlas().create_non_atlas(image_data.width(),
2785 image_data.height(),
2786 image_data);
2787 }
2788 }
2789 }
2790
2791 if (m_image)
2792 {
2793 if (m_sub_image_x.value() < 0 || m_sub_image_y.value() < 0
2794 || m_sub_image_w.value() < 0 || m_sub_image_h.value() < 0)
2795 {
2796 m_image_offset = uvec2(0, 0);
2797 m_image_size = uvec2(m_image->dimensions());
2798 }
2799 else
2800 {
2801 m_image_offset = uvec2(m_sub_image_x.value(), m_sub_image_y.value());
2802 m_image_size = uvec2(m_sub_image_w.value(), m_sub_image_h.value());
2803 }
2804 }
2805
2806 m_curve_flatness = m_painter->curve_flatness();
2807 m_draw_timer.restart();
2808 m_fps_timer.restart();
2809
2810 CustomShaderGenerator<glsl::PainterItemShaderGLSL> item_shaders;
2811 CustomShaderGenerator<glsl::PainterItemCoverageShaderGLSL> cvg_shaders;
2812 m_stroke_shader = generate_stroke_shader(m_backend->default_shaders().stroke_shader(),
2813 item_shaders, cvg_shaders);
2814 m_dashed_stroke_shader = generate_dashed_stroke_shader(m_backend->default_shaders().dashed_stroke_shader(),
2815 item_shaders, cvg_shaders);
2816 m_backend->register_shader(m_stroke_shader);
2817 m_backend->register_shader(m_dashed_stroke_shader);
2818}
2819
2820int
2821main(int argc, char **argv)
2822{
2823 painter_stroke_test P;
2824 return P.main(argc, argv);
2825}
2826