1 | #include <fstream> |
2 | #include <dirent.h> |
3 | |
4 | #include <fastuidraw/painter/painter.hpp> |
5 | #include <fastuidraw/text/glyph_cache.hpp> |
6 | #include <fastuidraw/text/font_freetype.hpp> |
7 | #include <fastuidraw/text/font_database.hpp> |
8 | #include <fastuidraw/gl_backend/gl_context_properties.hpp> |
9 | |
10 | #include "sdl_painter_demo.hpp" |
11 | #include "PainterWidget.hpp" |
12 | #include "simple_time.hpp" |
13 | #include "PanZoomTracker.hpp" |
14 | #include "text_helper.hpp" |
15 | #include "read_path.hpp" |
16 | #include "ImageLoader.hpp" |
17 | #include "colorstop_command_line.hpp" |
18 | #include "command_line_list.hpp" |
19 | #include "cycle_value.hpp" |
20 | #include "text_helper.hpp" |
21 | |
22 | #include "cell.hpp" |
23 | #include "table.hpp" |
24 | #include "random.hpp" |
25 | |
26 | using namespace fastuidraw; |
27 | |
28 | bool |
29 | compare_named_images(const named_image &lhs, |
30 | const named_image &rhs) |
31 | { |
32 | return lhs.second < rhs.second; |
33 | } |
34 | |
35 | class painter_cells:public sdl_painter_demo |
36 | { |
37 | public: |
38 | painter_cells(void); |
39 | |
40 | ~painter_cells(); |
41 | |
42 | protected: |
43 | void |
44 | derived_init(int w, int h); |
45 | |
46 | void |
47 | draw_frame(void); |
48 | |
49 | void |
50 | handle_event(const SDL_Event &ev); |
51 | |
52 | private: |
53 | typedef std::pair<enum Painter::blend_mode_t, std::string> named_blend_mode; |
54 | |
55 | static |
56 | void |
57 | generate_random_colors(int count, std::vector<vec4> &out_values, bool force_opaque); |
58 | |
59 | static |
60 | void |
61 | dump_file(const std::string &filename, std::vector<std::string> &dest); |
62 | |
63 | void |
64 | add_images(const std::string &filename, std::vector<named_image> &dest); |
65 | |
66 | void |
67 | add_single_image(const std::string &filename, std::vector<named_image> &dest); |
68 | |
69 | void |
70 | update_cts_params(void); |
71 | |
72 | command_line_argument_value<float> m_table_width, m_table_height; |
73 | command_line_argument_value<int> m_num_cells_x, m_num_cells_y; |
74 | command_line_argument_value<int> m_cell_group_size; |
75 | command_line_argument_value<std::string> m_font; |
76 | command_line_argument_value<float> m_pixel_size; |
77 | enumerated_command_line_argument_value<enum glyph_type> m_renderer; |
78 | command_line_argument_value<float> m_fps_pixel_size; |
79 | command_line_list<std::string> m_strings; |
80 | command_line_list<std::string> m_files; |
81 | command_line_list<std::string> m_images; |
82 | enumerated_command_line_argument_value<enum PainterImageBrushShader::filter_t> m_image_filter; |
83 | command_line_argument_value<bool> m_image_use_mipmaps; |
84 | command_line_argument_value<bool> m_use_atlas; |
85 | command_line_argument_value<bool> m_draw_image_name; |
86 | command_line_argument_value<int> m_num_background_colors; |
87 | command_line_argument_value<bool> m_background_colors_opaque; |
88 | command_line_argument_value<int> m_num_text_colors; |
89 | command_line_argument_value<bool> m_text_colors_opaque; |
90 | command_line_argument_value<int> m_num_rect_colors; |
91 | command_line_argument_value<bool> m_rect_colors_opaque; |
92 | command_line_argument_value<float> m_min_x_velocity, m_max_x_velocity; |
93 | command_line_argument_value<float> m_min_y_velocity, m_max_y_velocity; |
94 | command_line_argument_value<int> m_min_degree_per_second; |
95 | command_line_argument_value<int> m_max_degree_per_second; |
96 | command_line_argument_value<int> m_table_rotate_degrees_per_s; |
97 | command_line_argument_value<float> m_change_stroke_width_rate; |
98 | |
99 | command_line_argument_value<int> m_num_frames; |
100 | command_line_argument_value<int> m_skip_frames; |
101 | command_line_argument_value<bool> m_init_show_all_table; |
102 | command_line_argument_value<bool> m_init_table_rotating; |
103 | command_line_argument_value<bool> m_init_table_clipped; |
104 | command_line_argument_value<bool> m_init_cell_rotating; |
105 | command_line_argument_value<bool> m_init_draw_text; |
106 | command_line_argument_value<bool> m_init_draw_images; |
107 | command_line_argument_value<float> m_init_stroke_width; |
108 | command_line_argument_value<bool> m_init_anti_alias_stroking; |
109 | |
110 | CellSharedState m_cell_shared_state; |
111 | TableParams m_table_params; |
112 | PanZoomTrackerSDLEvent m_zoomer; |
113 | Table *m_table; |
114 | simple_time m_time, m_draw_timer; |
115 | PainterData::brush_value m_text_brush; |
116 | |
117 | unsigned int m_current_blend; |
118 | std::vector<named_blend_mode> m_blend_labels; |
119 | |
120 | int m_frame; |
121 | uint64_t m_benchmark_time_us; |
122 | simple_time m_benchmark_timer; |
123 | std::vector<uint64_t> m_frame_times; |
124 | |
125 | int m_show_surface, m_last_shown_surface; |
126 | }; |
127 | |
128 | painter_cells:: |
129 | painter_cells(void): |
130 | m_table_width(800, "table_width" , "Table Width" , *this), |
131 | m_table_height(600, "table_height" , "Table Height" , *this), |
132 | m_num_cells_x(10, "num_cells_x" , "Number of cells across" , *this), |
133 | m_num_cells_y(10, "num_cells_y" , "Number of cells down" , *this), |
134 | m_cell_group_size(1, "cell_group_size" , "width and height in number of cells for cell group size" , *this), |
135 | m_font(default_font(), "font" , "File from which to take font" , *this), |
136 | m_pixel_size(24.0f, "font_pixel_size" , "Render size for text rendering" , *this), |
137 | m_renderer(adaptive_rendering, |
138 | enumerated_string_type<enum glyph_type>() |
139 | .add_entry("distance_field" , distance_field_glyph, "Distance field rendering" ) |
140 | .add_entry("restricted_rays" , restricted_rays_glyph, "Restricted Rays rendering" ) |
141 | .add_entry("adaptive" , adaptive_rendering, "Adaptive rendering" ), |
142 | "glyph_render" , |
143 | "Specifies how to render glyphs" , |
144 | *this), |
145 | m_fps_pixel_size(24.0f, "fps_font_pixel_size" , "Render size for text rendering of fps" , *this), |
146 | m_strings("add_string" , "add a string to use by the cells" , *this), |
147 | m_files("add_string_file" , "add a string to use by a cell, taken from file" , *this), |
148 | m_images("add_image" , "Add an image to use by the cells" , *this), |
149 | m_image_filter(PainterImageBrushShader::filter_nearest, |
150 | enumerated_string_type<enum PainterImageBrushShader::filter_t>() |
151 | .add_entry("nearest" , PainterImageBrushShader::filter_nearest, "nearest filtering" ) |
152 | .add_entry("linear" , PainterImageBrushShader::filter_linear, "(bi)linear filtering" ) |
153 | .add_entry("cubic" , PainterImageBrushShader::filter_cubic, "(bi)cubic filtering" ), |
154 | "image_filter" , |
155 | "Specifies how to filter the images applied to the rects" , |
156 | *this), |
157 | m_image_use_mipmaps(false, "image_mipmap" , "If true, apply mipmapp filtering to images" , *this), |
158 | m_use_atlas(true, "use_atlas" , |
159 | "If false, each image is realized as a texture; if " |
160 | "GL_ARB_bindless_texture or GL_NV_bindless_texture " |
161 | "is supported, the Image objects are realized as bindless " |
162 | "texture, thus avoding draw breaks; if both of these " |
163 | "extensions is not present, then images are realized as " |
164 | "bound textures which means that a draw break will be present " |
165 | "whenever the image changes, harming performance." , |
166 | *this), |
167 | m_draw_image_name(false, "draw_image_name" , "If true draw the image name in each cell as part of the text" , *this), |
168 | m_num_background_colors(1, "num_background_colors" , "Number of distinct background colors in cells" , *this), |
169 | m_background_colors_opaque(false, "background_colors_opaque" , |
170 | "If true, all background colors for rects are forced to be opaque" , |
171 | *this), |
172 | m_num_text_colors(1, "num_text_colors" , "Number of distinct text colors in cells" , *this), |
173 | m_text_colors_opaque(true, "text_colors_opaque" , |
174 | "If true, all text colors are forced to be opaque" , |
175 | *this), |
176 | m_num_rect_colors(0, "num_rect_colors" , "Number of distinct colors which modulates the image, 0 means no modulation" , *this), |
177 | m_rect_colors_opaque(true, "rect_colors_opaque" , |
178 | "If true, all rect colors are forced to be opaque" , |
179 | *this), |
180 | m_min_x_velocity(-10.0f, "min_x_velocity" , "Minimum x-velocity for cell content in pixels/s" , *this), |
181 | m_max_x_velocity(+10.0f, "max_x_velocity" , "Maximum x-velocity for cell content in pixels/s" , *this), |
182 | m_min_y_velocity(-10.0f, "min_y_velocity" , "Minimum y-velocity for cell content in pixels/s" , *this), |
183 | m_max_y_velocity(+10.0f, "max_y_velocity" , "Maximum y-velocity for cell content in pixels/s" , *this), |
184 | m_min_degree_per_second(60, "min_degree_velocity" , "max rotation speed in degrees/second" , *this), |
185 | m_max_degree_per_second(60, "max_degree_velocity" , "max rotation speed in degrees/second" , *this), |
186 | m_table_rotate_degrees_per_s(20, "table_degree_velocity" , "rotation speed of table in degrees/second" , *this), |
187 | m_change_stroke_width_rate(10.0f, "change_stroke_width_rate" , |
188 | "rate of change in pixels/sec for changing stroke width " |
189 | "when changing stroke when key is down" , |
190 | *this), |
191 | m_num_frames(-1, "num_frames" , |
192 | "If positive, then run demo in benchmark mode terminating after the given number of frames" , |
193 | *this), |
194 | m_skip_frames(1, "num_skip_frames" , |
195 | "If num_frames > 0, then gives the number of frames to ignore in benchmarking" , |
196 | *this), |
197 | m_init_show_all_table(true, "init_show_all_table" , |
198 | "If true, initialize scroll and zoom to show entire table" , |
199 | *this), |
200 | m_init_table_rotating(false, "init_table_rotating" , |
201 | "If true, initialize table to be rotating" , |
202 | *this), |
203 | m_init_table_clipped(false, "init_table_clipped" , |
204 | "If true, initialize to enable clipping on the table" , |
205 | *this), |
206 | m_init_cell_rotating(false, "init_cell_rotating" , |
207 | "If true, intialize to have cells rotating" , |
208 | *this), |
209 | m_init_draw_text(true, "init_draw_text" , |
210 | "If true, intialize to draw text in cells" , |
211 | *this), |
212 | m_init_draw_images(true, "init_draw_image" , |
213 | "If true, intialize to draw image in cells" , |
214 | *this), |
215 | m_init_stroke_width(10.0f, "init_stroke_width" , |
216 | "Initial value for stroking width" , |
217 | *this), |
218 | m_init_anti_alias_stroking(true, "init_antialias_stroking" , |
219 | "Initial value for anti-aliasing for stroking" , |
220 | *this), |
221 | m_table(nullptr), |
222 | m_current_blend(0), |
223 | m_show_surface(0), |
224 | m_last_shown_surface(0) |
225 | { |
226 | std::cout << "Controls:\n" |
227 | << "\t[: decrease stroke width(hold left-shift for slower rate and right shift for faster)\n" |
228 | << "\t]: increase stroke width(hold left-shift for slower rate and right shift for faster)\n" |
229 | << "\ta: toggle anti-aliasing of stroking\n" |
230 | << "\tp: pause cell rotate\n" |
231 | << "\t0: set zoom factor to 1.0\n" |
232 | << "\tc: toggle clipping of table\n" |
233 | << "\tv: toggle table rotating\n" |
234 | << "\tr: toggle rotating individual cells\n" |
235 | << "\tt: toggle draw cell text\n" |
236 | << "\ti: toggle draw cell image\n" |
237 | << "\tb: cycle blend mode applied to image rect\n" |
238 | << "\tctrl-b: cycle blend mode applied to image rect\n" |
239 | << "\ty: toggle drawing widgets as transparent or opaque\n" |
240 | << "\tLeft Mouse Drag: pan\n" |
241 | << "\tHold Left Mouse, then drag up/down: zoom out/in\n" ; |
242 | } |
243 | |
244 | painter_cells:: |
245 | ~painter_cells() |
246 | { |
247 | if (m_table != nullptr) |
248 | { |
249 | FASTUIDRAWdelete(m_table); |
250 | } |
251 | } |
252 | |
253 | void |
254 | painter_cells:: |
255 | generate_random_colors(int count, std::vector<vec4> &out_values, bool force_opaque) |
256 | { |
257 | out_values.resize(count); |
258 | for(int i = 0; i < count; ++i) |
259 | { |
260 | out_values[i] = random_value(vec4(0.0f, 0.0f, 0.0f, 0.2f), |
261 | vec4(1.0f, 1.0f, 1.0f, 0.8f)); |
262 | if (force_opaque) |
263 | { |
264 | out_values[i].w() = 1.0f; |
265 | } |
266 | } |
267 | } |
268 | |
269 | void |
270 | painter_cells:: |
271 | dump_file(const std::string &filename, std::vector<std::string> &dest) |
272 | { |
273 | std::ifstream istr(filename.c_str()); |
274 | if (istr) |
275 | { |
276 | std::ostringstream str; |
277 | str << istr.rdbuf(); |
278 | dest.push_back(str.str()); |
279 | } |
280 | } |
281 | |
282 | void |
283 | painter_cells:: |
284 | add_images(const std::string &filename, std::vector<named_image> &dest) |
285 | { |
286 | DIR *dir; |
287 | struct dirent *entry; |
288 | |
289 | dir = opendir(filename.c_str()); |
290 | if (!dir) |
291 | { |
292 | add_single_image(filename, dest); |
293 | return; |
294 | } |
295 | |
296 | for(entry = readdir(dir); entry != nullptr; entry = readdir(dir)) |
297 | { |
298 | std::string file; |
299 | file = entry->d_name; |
300 | if (file != ".." && file != "." ) |
301 | { |
302 | add_images(filename + "/" + file, dest); |
303 | } |
304 | } |
305 | closedir(dir); |
306 | } |
307 | |
308 | void |
309 | painter_cells:: |
310 | add_single_image(const std::string &filename, std::vector<named_image> &dest) |
311 | { |
312 | ImageLoader image_data(filename); |
313 | if (image_data.non_empty()) |
314 | { |
315 | reference_counted_ptr<const Image> im; |
316 | |
317 | std::cout << "\tImage \"" << filename << "\" of size " |
318 | << image_data.dimensions() << " loaded" ; |
319 | |
320 | if (m_use_atlas.value()) |
321 | { |
322 | im = m_painter->image_atlas().create(image_data.width(), |
323 | image_data.height(), |
324 | image_data, |
325 | Image::on_atlas); |
326 | } |
327 | else |
328 | { |
329 | im = m_painter->image_atlas().create_non_atlas(image_data.width(), |
330 | image_data.height(), |
331 | image_data); |
332 | } |
333 | |
334 | switch (im->type()) |
335 | { |
336 | case Image::on_atlas: |
337 | std::cout << " on atlas with number_mipmap_levels = " |
338 | << im->number_mipmap_levels(); |
339 | break; |
340 | case Image::bindless_texture2d: |
341 | std::cout << " bindlessly" ; |
342 | break; |
343 | case Image::context_texture2d: |
344 | std::cout << " as bound texture (WARNING: large performance impact expected)" ; |
345 | break; |
346 | } |
347 | |
348 | std::cout << " @" << im.get() << ".\n" ; |
349 | dest.push_back(named_image(im, filename)); |
350 | } |
351 | } |
352 | |
353 | void |
354 | painter_cells:: |
355 | derived_init(int w, int h) |
356 | { |
357 | m_table_params.m_wh = vec2(m_table_width.value(), m_table_height.value()); |
358 | m_table_params.m_cell_count = ivec2(m_num_cells_x.value(), m_num_cells_y.value()); |
359 | m_table_params.m_line_color = vec4(1.0f, 1.0f, 1.0f, 1.0f); |
360 | m_table_params.m_cell_state = &m_cell_shared_state; |
361 | m_table_params.m_zoomer = &m_zoomer; |
362 | m_table_params.m_draw_image_name = m_draw_image_name.value(); |
363 | m_table_params.m_table_rotate_degrees_per_s = m_table_rotate_degrees_per_s.value(); |
364 | m_table_params.m_timer_based_animation = (m_num_frames.value() <= 0); |
365 | |
366 | reference_counted_ptr<FreeTypeFace::GeneratorBase> gen; |
367 | gen = FASTUIDRAWnew FreeTypeFace::GeneratorMemory(m_font.value().c_str(), 0); |
368 | m_table_params.m_font_database = m_font_database; |
369 | m_table_params.m_glyph_cache = &m_painter->glyph_cache(); |
370 | if (gen->check_creation() == routine_success) |
371 | { |
372 | m_table_params.m_font = FASTUIDRAWnew FontFreeType(gen, m_ft_lib); |
373 | } |
374 | else |
375 | { |
376 | std::cout << "\n-----------------------------------------------------" |
377 | << "\nWarning: unable to create font from file \"" |
378 | << m_font.value() << "\"\n" |
379 | << "-----------------------------------------------------\n" ; |
380 | } |
381 | |
382 | m_table_params.m_pixel_size = m_pixel_size.value(); |
383 | m_table_params.m_texts.reserve(m_strings.size() + m_files.size()); |
384 | for(const auto &S : m_strings) |
385 | { |
386 | m_table_params.m_texts.push_back(S); |
387 | } |
388 | |
389 | for(const auto &S : m_files) |
390 | { |
391 | dump_file(S, m_table_params.m_texts); |
392 | } |
393 | |
394 | for(const auto &S : m_images) |
395 | { |
396 | add_images(S, m_table_params.m_images); |
397 | } |
398 | std::cout << "Loaded " << m_table_params.m_images.size() << " images total\n" ; |
399 | std::sort(m_table_params.m_images.begin(), |
400 | m_table_params.m_images.end(), |
401 | compare_named_images); |
402 | m_table_params.m_image_filter = m_image_filter.value(); |
403 | m_table_params.m_image_mipmapping = m_image_use_mipmaps.value() ? |
404 | PainterImageBrushShader::apply_mipmapping: |
405 | PainterImageBrushShader::dont_apply_mipmapping; |
406 | |
407 | generate_random_colors(m_num_background_colors.value(), m_table_params.m_background_colors, |
408 | m_background_colors_opaque.value()); |
409 | generate_random_colors(m_num_text_colors.value(), m_table_params.m_text_colors, |
410 | m_text_colors_opaque.value()); |
411 | generate_random_colors(m_num_rect_colors.value(), m_table_params.m_rect_colors, |
412 | m_rect_colors_opaque.value()); |
413 | |
414 | if (m_table_params.m_images.empty() && m_table_params.m_rect_colors.empty()) |
415 | { |
416 | vec4 C(0.2f, 0.7f, 0.7f, 0.6f); |
417 | m_table_params.m_rect_colors.push_back(C); |
418 | } |
419 | |
420 | m_table_params.m_min_speed = vec2(m_min_x_velocity.value(), m_min_y_velocity.value()); |
421 | m_table_params.m_max_speed = vec2(m_max_x_velocity.value(), m_max_y_velocity.value()); |
422 | m_table_params.m_min_degrees_per_s = m_min_degree_per_second.value(); |
423 | m_table_params.m_max_degrees_per_s = m_max_degree_per_second.value(); |
424 | |
425 | if (m_cell_group_size.value() > 0) |
426 | { |
427 | m_table_params.m_max_cell_group_size = m_cell_group_size.value(); |
428 | } |
429 | else |
430 | { |
431 | m_table_params.m_max_cell_group_size = 2 * std::max(m_num_cells_x.value(), m_num_cells_y.value()); |
432 | } |
433 | |
434 | m_table = FASTUIDRAWnew Table(m_table_params); |
435 | m_table->m_clipped = m_init_table_clipped.value(); |
436 | m_table->m_rotating = m_init_table_rotating.value(); |
437 | m_cell_shared_state.m_draw_text = m_init_draw_text.value(); |
438 | m_cell_shared_state.m_draw_image = m_init_draw_images.value(); |
439 | m_cell_shared_state.m_rotating = m_init_cell_rotating.value(); |
440 | m_cell_shared_state.m_stroke_width = m_init_stroke_width.value(); |
441 | m_cell_shared_state.m_anti_alias_stroking = m_init_anti_alias_stroking.value(); |
442 | m_cell_shared_state.m_glyph_render = m_renderer.value(); |
443 | |
444 | /* init m_zoomer so that table contents fit into screen. |
445 | */ |
446 | vec2 twh; |
447 | ScaleTranslate<float> tr1, tr2; |
448 | |
449 | twh = m_table_params.m_wh / vec2(w, h); |
450 | tr1.translation(-0.5f * m_table_params.m_wh); |
451 | tr2.translation(0.5f * vec2(w, h)); |
452 | |
453 | if (m_init_show_all_table.value()) |
454 | { |
455 | ScaleTranslate<float> sc; |
456 | sc.scale(1.0f / std::max(twh.x(), twh.y())); |
457 | m_zoomer.transformation(tr2 * sc * tr1); |
458 | } |
459 | else |
460 | { |
461 | m_zoomer.transformation(tr2 * tr1); |
462 | } |
463 | |
464 | |
465 | if (m_table_params.m_font) |
466 | { |
467 | std::cout << "Font: " << m_table_params.m_font->properties() << "\n" ; |
468 | } |
469 | else |
470 | { |
471 | std::cout << "Font: nullptr\n" ; |
472 | } |
473 | |
474 | std::cout << "Window resolution = " << dimensions() << "\n" ; |
475 | |
476 | m_frame = -m_skip_frames.value(); |
477 | if (m_num_frames.value() > 0) |
478 | { |
479 | m_frame_times.reserve(m_num_frames.value()); |
480 | } |
481 | |
482 | #define ADD_COMPOSITE_MODE(X) \ |
483 | m_blend_labels.push_back(named_blend_mode(Painter::X, #X)) |
484 | |
485 | ADD_COMPOSITE_MODE(blend_porter_duff_src_over); |
486 | ADD_COMPOSITE_MODE(blend_porter_duff_clear); |
487 | ADD_COMPOSITE_MODE(blend_porter_duff_src); |
488 | ADD_COMPOSITE_MODE(blend_porter_duff_dst); |
489 | ADD_COMPOSITE_MODE(blend_porter_duff_dst_over); |
490 | ADD_COMPOSITE_MODE(blend_porter_duff_src_in); |
491 | ADD_COMPOSITE_MODE(blend_porter_duff_dst_in); |
492 | ADD_COMPOSITE_MODE(blend_porter_duff_src_out); |
493 | ADD_COMPOSITE_MODE(blend_porter_duff_dst_out); |
494 | ADD_COMPOSITE_MODE(blend_porter_duff_src_atop); |
495 | ADD_COMPOSITE_MODE(blend_porter_duff_dst_atop); |
496 | ADD_COMPOSITE_MODE(blend_porter_duff_xor); |
497 | |
498 | #define ADD_BLEND_MODE(X) do { \ |
499 | if (m_painter->default_shaders().blend_shaders().shader(Painter::X)) \ |
500 | { \ |
501 | m_blend_labels.push_back(named_blend_mode(Painter::X, #X)); \ |
502 | } \ |
503 | } while(0) |
504 | |
505 | ADD_BLEND_MODE(blend_w3c_multiply); |
506 | ADD_BLEND_MODE(blend_w3c_screen); |
507 | ADD_BLEND_MODE(blend_w3c_overlay); |
508 | ADD_BLEND_MODE(blend_w3c_darken); |
509 | ADD_BLEND_MODE(blend_w3c_lighten); |
510 | ADD_BLEND_MODE(blend_w3c_color_dodge); |
511 | ADD_BLEND_MODE(blend_w3c_color_burn); |
512 | ADD_BLEND_MODE(blend_w3c_hardlight); |
513 | ADD_BLEND_MODE(blend_w3c_softlight); |
514 | ADD_BLEND_MODE(blend_w3c_difference); |
515 | ADD_BLEND_MODE(blend_w3c_exclusion); |
516 | ADD_BLEND_MODE(blend_w3c_hue); |
517 | ADD_BLEND_MODE(blend_w3c_saturation); |
518 | ADD_BLEND_MODE(blend_w3c_color); |
519 | ADD_BLEND_MODE(blend_w3c_luminosity); |
520 | } |
521 | |
522 | void |
523 | painter_cells:: |
524 | update_cts_params(void) |
525 | { |
526 | const Uint8 *keyboard_state = SDL_GetKeyboardState(nullptr); |
527 | FASTUIDRAWassert(keyboard_state != nullptr); |
528 | |
529 | float speed = static_cast<float>(m_draw_timer.restart()) * 0.001f; |
530 | |
531 | if (keyboard_state[SDL_SCANCODE_LSHIFT]) |
532 | { |
533 | speed *= 0.1f; |
534 | } |
535 | if (keyboard_state[SDL_SCANCODE_RSHIFT]) |
536 | { |
537 | speed *= 10.0f; |
538 | } |
539 | |
540 | if (keyboard_state[SDL_SCANCODE_RIGHTBRACKET]) |
541 | { |
542 | m_cell_shared_state.m_stroke_width += m_change_stroke_width_rate.value() * speed / m_zoomer.transformation().scale(); |
543 | } |
544 | |
545 | if (keyboard_state[SDL_SCANCODE_LEFTBRACKET]) |
546 | { |
547 | m_cell_shared_state.m_stroke_width -= m_change_stroke_width_rate.value() * speed / m_zoomer.transformation().scale(); |
548 | m_cell_shared_state.m_stroke_width = std::max(m_cell_shared_state.m_stroke_width, 0.0f); |
549 | } |
550 | } |
551 | |
552 | void |
553 | painter_cells:: |
554 | draw_frame(void) |
555 | { |
556 | uint64_t ms, us; |
557 | ivec2 wh(dimensions()); |
558 | |
559 | us = m_time.restart_us(); |
560 | ms = us / 1000; |
561 | |
562 | if (m_frame == 0) |
563 | { |
564 | m_benchmark_timer.restart(); |
565 | } |
566 | else if (m_frame > 0) |
567 | { |
568 | m_frame_times.push_back(us); |
569 | } |
570 | |
571 | if (m_num_frames.value() > 0 && m_frame == m_num_frames.value()) |
572 | { |
573 | m_benchmark_time_us = m_benchmark_timer.elapsed_us(); |
574 | std::cout << "Frame times(in us):\n" ; |
575 | for(unsigned int i = 0, endi = m_frame_times.size(); i < endi; ++i) |
576 | { |
577 | std::cout << m_frame_times[i] << " us\n" ; |
578 | } |
579 | std::cout << "Did " << m_num_frames.value() << " frames in " |
580 | << m_benchmark_time_us << "us, average time = " |
581 | << static_cast<float>(m_benchmark_time_us) / static_cast<float>(m_frame) |
582 | << "us\n " << 1000.0f * 1000.0f * static_cast<float>(m_frame) / static_cast<float>(m_benchmark_time_us) |
583 | << " FPS\n" ; |
584 | end_demo(0); |
585 | return; |
586 | } |
587 | |
588 | update_cts_params(); |
589 | m_cell_shared_state.m_cells_drawn = 0; |
590 | |
591 | m_surface->clear_color(vec4(0.5f, 0.5f, 0.5f, 1.0f)); |
592 | m_painter->begin(m_surface, Painter::y_increases_downwards); |
593 | |
594 | m_painter->save(); |
595 | m_painter->translate(m_zoomer.transformation().translation()); |
596 | m_painter->scale(m_zoomer.transformation().scale()); |
597 | /* update m_bb_min and m_bb_max as the window corners in |
598 | coordinates; Table will do the right thing with these numbers |
599 | to change them |
600 | */ |
601 | m_table->m_bb_min = vec2(0.0f, 0.0f); |
602 | m_table->m_bb_max = vec2(wh); |
603 | |
604 | m_table->paint(m_painter); |
605 | m_painter->restore(); |
606 | |
607 | |
608 | if (m_table_params.m_timer_based_animation) |
609 | { |
610 | std::ostringstream ostr; |
611 | |
612 | ostr << "\nFPS = " ; |
613 | if (us > 0) |
614 | { |
615 | ostr << static_cast<int>(1000.0f * 1000.0f / static_cast<float>(us)); |
616 | } |
617 | else |
618 | { |
619 | ostr << "NAN" ; |
620 | } |
621 | |
622 | ostr << "\nms = " << ms |
623 | << "\nDrew " << m_cell_shared_state.m_cells_drawn << " cells" ; |
624 | |
625 | fastuidraw::c_array<const unsigned int> stats(painter_stats()); |
626 | for (unsigned int i = 0; i < stats.size(); ++i) |
627 | { |
628 | enum Painter::query_stats_t st; |
629 | |
630 | st = static_cast<enum Painter::query_stats_t>(i); |
631 | ostr << "\n" << Painter::label(st) << ": " << stats[i]; |
632 | } |
633 | ostr << "\ndraw_generics: " << m_painter->draw_data_added_count() << "\n" ; |
634 | |
635 | if (!m_text_brush.packed()) |
636 | { |
637 | PainterBrush brush; |
638 | brush.color(0.0f, 1.0f, 1.0f, 1.0f); |
639 | m_text_brush = m_painter->packed_value_pool().create_packed_brush(brush); |
640 | } |
641 | draw_text(ostr.str(), m_fps_pixel_size.value(), |
642 | m_table_params.m_font.get(), PainterData(m_text_brush)); |
643 | } |
644 | |
645 | c_array<const PainterSurface* const> surfaces; |
646 | |
647 | surfaces = m_painter->end(); |
648 | fastuidraw_glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); |
649 | fastuidraw_glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
650 | |
651 | m_show_surface = t_min(m_show_surface, (int)surfaces.size()); |
652 | if (m_show_surface <= 0 || m_show_surface > surfaces.size()) |
653 | { |
654 | m_surface->blit_surface(GL_NEAREST); |
655 | } |
656 | else |
657 | { |
658 | const gl::PainterSurfaceGL *S; |
659 | PainterSurface::Viewport src, dest; |
660 | |
661 | src = m_surface->viewport(); |
662 | S = dynamic_cast<const gl::PainterSurfaceGL*>(surfaces[m_show_surface - 1]); |
663 | |
664 | dest.m_origin = src.m_origin; |
665 | dest.m_dimensions = ivec2(src.m_dimensions.x(), src.m_dimensions.y() / 2); |
666 | m_surface->blit_surface(src, dest, GL_LINEAR); |
667 | |
668 | dest.m_origin.y() += dest.m_dimensions.y(); |
669 | S->blit_surface(src, dest, GL_LINEAR); |
670 | } |
671 | |
672 | if (m_last_shown_surface != m_show_surface) |
673 | { |
674 | if (m_show_surface > 0) |
675 | { |
676 | std::cout << "Show offscreen surface: " << m_show_surface - 1 << "\n" ; |
677 | } |
678 | else |
679 | { |
680 | std::cout << "Don't show offscreen surface\n" ; |
681 | } |
682 | m_last_shown_surface = m_show_surface; |
683 | } |
684 | ++m_frame; |
685 | } |
686 | |
687 | void |
688 | painter_cells:: |
689 | handle_event(const SDL_Event &ev) |
690 | { |
691 | m_zoomer.handle_event(ev); |
692 | |
693 | switch(ev.type) |
694 | { |
695 | case SDL_QUIT: |
696 | end_demo(0); |
697 | break; |
698 | |
699 | case SDL_WINDOWEVENT: |
700 | if (ev.window.event == SDL_WINDOWEVENT_RESIZED) |
701 | { |
702 | on_resize(ev.window.data1, ev.window.data2); |
703 | } |
704 | break; |
705 | |
706 | case SDL_KEYUP: |
707 | switch(ev.key.keysym.sym) |
708 | { |
709 | case SDLK_ESCAPE: |
710 | end_demo(0); |
711 | break; |
712 | case SDLK_a: |
713 | if (m_cell_shared_state.m_stroke_width > 0.0f) |
714 | { |
715 | m_cell_shared_state.m_anti_alias_stroking = !m_cell_shared_state.m_anti_alias_stroking; |
716 | std::cout << "Stroking anti-aliasing = " |
717 | << m_cell_shared_state.m_anti_alias_stroking |
718 | << "\n" ; |
719 | } |
720 | break; |
721 | case SDLK_v: |
722 | m_table->m_rotating = !m_table->m_rotating; |
723 | std::cout << "Table Rotating = " << m_table->m_rotating << "\n" ; |
724 | break; |
725 | case SDLK_c: |
726 | m_table->m_clipped = !m_table->m_clipped; |
727 | std::cout << "Table clipped = " << m_table->m_clipped << "\n" ; |
728 | break; |
729 | case SDLK_p: |
730 | m_cell_shared_state.m_pause = !m_cell_shared_state.m_pause; |
731 | std::cout << "Paused = " << m_cell_shared_state.m_pause << "\n" ; |
732 | break; |
733 | case SDLK_r: |
734 | m_cell_shared_state.m_rotating = !m_cell_shared_state.m_rotating; |
735 | std::cout << "Cell Rotating = " << m_cell_shared_state.m_rotating << "\n" ; |
736 | break; |
737 | case SDLK_t: |
738 | m_cell_shared_state.m_draw_text = !m_cell_shared_state.m_draw_text; |
739 | std::cout << "Draw Text = " << m_cell_shared_state.m_draw_text << "\n" ; |
740 | break; |
741 | case SDLK_i: |
742 | m_cell_shared_state.m_draw_image = !m_cell_shared_state.m_draw_image; |
743 | std::cout << "Draw Image = " << m_cell_shared_state.m_draw_image << "\n" ; |
744 | break; |
745 | case SDLK_b: |
746 | cycle_value(m_current_blend, ev.key.keysym.mod & (KMOD_SHIFT | KMOD_ALT | KMOD_CTRL), m_blend_labels.size()); |
747 | std::cout << "Rect Blend mode set to: " << m_blend_labels[m_current_blend].second << "\n" ; |
748 | m_cell_shared_state.m_rect_blend_mode = m_blend_labels[m_current_blend].first; |
749 | break; |
750 | case SDLK_y: |
751 | m_cell_shared_state.m_draw_transparent = !m_cell_shared_state.m_draw_transparent; |
752 | if (m_cell_shared_state.m_draw_transparent) |
753 | { |
754 | std::cout << "Draw cells transparently\n" ; |
755 | } |
756 | else |
757 | { |
758 | std::cout << "Draw cells opaquely\n" ; |
759 | } |
760 | break; |
761 | |
762 | case SDLK_o: |
763 | if (ev.key.keysym.mod & (KMOD_SHIFT | KMOD_ALT)) |
764 | { |
765 | if (m_show_surface > 0) |
766 | { |
767 | --m_show_surface; |
768 | } |
769 | } |
770 | else |
771 | { |
772 | ++m_show_surface; |
773 | } |
774 | break; |
775 | |
776 | case SDLK_0: |
777 | m_zoomer.transformation(ScaleTranslate<float>()); |
778 | break; |
779 | } |
780 | break; |
781 | } |
782 | } |
783 | |
784 | int |
785 | main(int argc, char **argv) |
786 | { |
787 | painter_cells P; |
788 | return P.main(argc, argv); |
789 | } |
790 | |