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 | |
26 | using namespace fastuidraw; |
27 | |
28 | c_string |
29 | on_off(bool v) |
30 | { |
31 | return v ? "ON" : "OFF" ; |
32 | } |
33 | |
34 | class WavyEffect |
35 | { |
36 | public: |
37 | float m_domain_coeff, m_phase; |
38 | vecN<float, 4> m_cos_coeffs; |
39 | vecN<float, 4> m_sin_coeffs; |
40 | }; |
41 | |
42 | template<typename T> |
43 | class ExampleItemData:public PainterItemShaderData |
44 | { |
45 | public: |
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 | |
78 | reference_counted_ptr<glsl::PainterItemShaderGLSL> |
79 | call_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 | |
93 | reference_counted_ptr<glsl::PainterItemCoverageShaderGLSL> |
94 | call_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 | |
107 | bool |
108 | uses_discard(const reference_counted_ptr<glsl::PainterItemShaderGLSL> &sh, |
109 | bool apply_wavy_effect) |
110 | { |
111 | return apply_wavy_effect || sh->uses_discard(); |
112 | } |
113 | |
114 | bool |
115 | uses_discard(const reference_counted_ptr<glsl::PainterItemCoverageShaderGLSL>&, |
116 | bool) |
117 | { |
118 | return false; |
119 | } |
120 | |
121 | template<typename T> |
122 | reference_counted_ptr<T> |
123 | create_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 | |
229 | template<typename T> |
230 | class CustomShaderGenerator:noncopyable |
231 | { |
232 | public: |
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 | |
255 | private: |
256 | shader_map m_shaders; |
257 | }; |
258 | |
259 | reference_counted_ptr<PainterItemShader> |
260 | generate_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 | |
315 | void |
316 | set_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 | |
332 | class CustomStrokingDataSelector:public StrokingDataSelectorBase |
333 | { |
334 | public: |
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 | |
376 | PainterStrokeShader |
377 | generate_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 | |
410 | void |
411 | set_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 | |
420 | PainterDashedStrokeShaderSet |
421 | generate_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 | |
434 | class DashPatternList:public command_line_argument |
435 | { |
436 | public: |
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 | |
473 | private: |
474 | std::string m_desc; |
475 | }; |
476 | |
477 | class character_code_range:public command_line_argument |
478 | { |
479 | public: |
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 | |
522 | private: |
523 | std::string m_desc; |
524 | command_line_list<uint32_t> *m_p; |
525 | }; |
526 | |
527 | class WindingValueFillRule:public CustomFillRuleBase |
528 | { |
529 | public: |
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 | |
540 | private: |
541 | int m_winding_number; |
542 | }; |
543 | |
544 | bool |
545 | everything_filled(int) |
546 | { |
547 | return true; |
548 | } |
549 | |
550 | #ifndef FASTUIDRAW_GL_USE_GLES |
551 | |
552 | class EnableWireFrameAction:public PainterDrawBreakAction |
553 | { |
554 | public: |
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 | |
576 | private: |
577 | bool m_lines; |
578 | }; |
579 | |
580 | #endif |
581 | |
582 | class PerPath:public reference_counted<PerPath>::non_concurrent |
583 | { |
584 | public: |
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 | |
622 | private: |
623 | void |
624 | common_init(int w, int h); |
625 | |
626 | Path m_path_v; |
627 | const Path *m_path_ptr; |
628 | }; |
629 | |
630 | class painter_stroke_test:public sdl_painter_demo |
631 | { |
632 | public: |
633 | painter_stroke_test(void); |
634 | |
635 | protected: |
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 | |
646 | private: |
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 |
974 | PerPath:: |
975 | PerPath(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 | |
995 | PerPath:: |
996 | PerPath(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 | |
1015 | void |
1016 | PerPath:: |
1017 | common_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 |
1072 | painter_stroke_test:: |
1073 | painter_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 | |
1254 | void |
1255 | painter_stroke_test:: |
1256 | update_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 | |
1500 | void |
1501 | painter_stroke_test:: |
1502 | fill_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 | |
1512 | vec2 |
1513 | painter_stroke_test:: |
1514 | brush_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 | |
1529 | vec2 |
1530 | painter_stroke_test:: |
1531 | item_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 | |
1570 | void |
1571 | painter_stroke_test:: |
1572 | handle_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 | |
1910 | void |
1911 | painter_stroke_test:: |
1912 | construct_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 | |
1988 | void |
1989 | painter_stroke_test:: |
1990 | per_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 | |
2043 | void |
2044 | painter_stroke_test:: |
2045 | construct_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 | |
2072 | void |
2073 | painter_stroke_test:: |
2074 | construct_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 | |
2100 | void |
2101 | painter_stroke_test:: |
2102 | fill_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 | |
2127 | void |
2128 | painter_stroke_test:: |
2129 | draw_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 | |
2510 | void |
2511 | painter_stroke_test:: |
2512 | draw_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 | |
2744 | void |
2745 | painter_stroke_test:: |
2746 | derived_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 | |
2820 | int |
2821 | main(int argc, char **argv) |
2822 | { |
2823 | painter_stroke_test P; |
2824 | return P.main(argc, argv); |
2825 | } |
2826 | |