1 // Copyright (c) 2015 Sergio Gonzalez. All rights reserved.
2 // License: https://github.com/serge-rgb/milton#license
3 
4 #include "milton.h"
5 
6 #include "common.h"
7 #include "color.h"
8 #include "canvas.h"
9 #include "gui.h"
10 #include "renderer.h"
11 #include "localization.h"
12 #include "persist.h"
13 #include "platform.h"
14 #include "vector.h"
15 #include "bindings.h"
16 
17 // Defined below.
18 static void milton_validate(Milton* milton);
19 
20 void
milton_set_background_color(Milton * milton,v3f background_color)21 milton_set_background_color(Milton* milton, v3f background_color)
22 {
23     milton->view->background_color = background_color;
24     gpu_update_background(milton->renderer, milton->view->background_color);
25 }
26 
27 static void
init_view(CanvasView * view,v3f background_color,i32 width,i32 height)28 init_view(CanvasView* view, v3f background_color, i32 width, i32 height)
29 {
30     milton_log("Setting default view\n");
31 
32     auto size = view->screen_size;
33 
34     *view = CanvasView{};
35 
36     view->size = sizeof(CanvasView);
37     view->background_color  = background_color;
38     view->screen_size       = size;
39     view->zoom_center       = size / 2;
40     view->scale             = MILTON_DEFAULT_SCALE;
41     view->angle             = 0.0f;
42     view->screen_size       = { width, height };
43 }
44 
45 int
milton_get_brush_enum(Milton const * milton)46 milton_get_brush_enum(Milton const* milton)
47 {
48     int brush_enum = BrushEnum_NOBRUSH;
49     switch ( milton->current_mode ) {
50         case MiltonMode::PEN: {
51             brush_enum = BrushEnum_PEN;
52         } break;
53         case MiltonMode::ERASER: {
54             brush_enum = BrushEnum_ERASER;
55         } break;
56         case MiltonMode::PRIMITIVE: {
57             brush_enum = BrushEnum_PRIMITIVE;
58         } break;
59         case MiltonMode::DRAG_BRUSH_SIZE: {
60             brush_enum = milton->drag_brush->brush_idx;
61         } break;
62         case MiltonMode::EXPORTING:
63         case MiltonMode::EYEDROPPER:
64         case MiltonMode::HISTORY:
65         default: {
66         } break;
67     }
68     return brush_enum;
69 }
70 
71 static void
milton_update_brushes(Milton * milton)72 milton_update_brushes(Milton* milton)
73 {
74     for ( int i = 0; i < BrushEnum_COUNT; ++i ) {
75         Brush* brush = &milton->brushes[i];
76         i32 size = milton->brush_sizes[i];
77 
78         brush->radius = size * milton->view->scale;
79         mlt_assert(brush->radius < FLT_MAX);
80         if ( i == BrushEnum_PEN ) {
81             // Alpha is set by the UI
82             brush->color = to_premultiplied(gui_get_picker_rgb(milton->gui), brush->alpha);
83         }
84         else if ( i == BrushEnum_ERASER ) {
85             // Nothing
86         }
87         else if ( i == BrushEnum_PRIMITIVE ) {
88             brush->color = to_premultiplied(gui_get_picker_rgb(milton->gui), brush->alpha);
89         }
90     }
91 }
92 
93 
94 // Eyedropper
95 void
eyedropper_init(Milton * milton)96 eyedropper_init(Milton* milton)
97 {
98     size_t bpp = 4;
99     i32 w = milton->view->screen_size.w;
100     i32 h = milton->view->screen_size.h;
101 
102     Eyedropper* e = milton->eyedropper;
103     if (!e->buffer)
104     {
105         e->buffer = (u8*)mlt_calloc(w*h*bpp, 1, "Bitmap");
106         gpu_render_to_buffer(milton, e->buffer, 1, 0, 0, w, h, 1.0f);
107     }
108 }
109 
110 void
eyedropper_input(Eyedropper * e,MiltonGui * gui,i32 w,i32 h,v2i point)111 eyedropper_input(Eyedropper* e, MiltonGui* gui, i32 w, i32 h, v2i point)
112 {
113 
114     u32* pixels = (u32*)e->buffer;
115     if ( point.y > 0 && point.y <= h && point.x > 0 && point.x <= w ) {
116         v4f color = color_u32_to_v4f(pixels[point.y * w + point.x]);
117         gui_picker_from_rgb(&gui->picker, color.rgb);
118     }
119 }
120 
121 void
eyedropper_deinit(Eyedropper * e)122 eyedropper_deinit(Eyedropper* e)
123 {
124     if ( e->buffer ) {
125         mlt_free(e->buffer, "Bitmap");
126         e->buffer = NULL;
127     }
128 }
129 
130 static Brush
milton_get_brush(Milton const * milton)131 milton_get_brush(Milton const* milton)
132 {
133     int brush_enum = milton_get_brush_enum(milton);
134 
135     Brush brush = milton->brushes[brush_enum];
136 
137     return brush;
138 }
139 
140 static i32*
pointer_to_brush_size(Milton * milton)141 pointer_to_brush_size(Milton* milton)
142 {
143     int brush_enum = milton_get_brush_enum(milton);
144     i32* ptr = &milton->brush_sizes[brush_enum];
145     return ptr;
146 }
147 
148 static b32
is_user_drawing(Milton const * milton)149 is_user_drawing(Milton const* milton)
150 {
151     b32 result = milton->working_stroke.num_points > 0;
152     return result;
153 }
154 
155 static b32
mode_is_for_drawing(MiltonMode mode)156 mode_is_for_drawing(MiltonMode mode)
157 {
158     b32 result = mode == MiltonMode::PEN ||
159             mode == MiltonMode::ERASER ||
160             mode == MiltonMode::PRIMITIVE ||
161             mode == MiltonMode::DRAG_BRUSH_SIZE;
162     return result;
163 }
164 
165 static size_t
get_gui_visibility_index(Milton * milton)166 get_gui_visibility_index(Milton* milton)
167 {
168     size_t idx = Milton::GuiVisibleCategory_OTHER;
169     if (current_mode_is_for_drawing(milton)) {
170         idx = Milton::GuiVisibleCategory_DRAWING;
171     }
172     else if (milton->current_mode == MiltonMode::EXPORTING) {
173         idx = Milton::GuiVisibleCategory_EXPORTING;
174     }
175     return idx;
176 }
177 
178 void
milton_toggle_gui_visibility(Milton * milton)179 milton_toggle_gui_visibility(Milton* milton)
180 {
181     size_t idx = get_gui_visibility_index(milton);
182     milton->mode_gui_visibility[idx] = !milton->mode_gui_visibility[idx];
183 }
184 
185 void
milton_set_gui_visibility(Milton * milton,b32 visible)186 milton_set_gui_visibility(Milton* milton, b32 visible)
187 {
188     size_t idx = get_gui_visibility_index(milton);
189     milton->mode_gui_visibility[idx] = visible;
190 }
191 
192 b32
current_mode_is_for_drawing(Milton const * milton)193 current_mode_is_for_drawing(Milton const* milton)
194 {
195     return mode_is_for_drawing(milton->current_mode);
196 }
197 
198 static void
clear_stroke_redo(Milton * milton)199 clear_stroke_redo(Milton* milton)
200 {
201     while ( milton->canvas->stroke_graveyard.count > 0 ) {
202         Stroke s = pop(&milton->canvas->stroke_graveyard);
203     }
204     for ( i64 i = 0; i < milton->canvas->redo_stack.count; ++i ) {
205         HistoryElement h = milton->canvas->redo_stack.data[i];
206         if ( h.type == HistoryElement_STROKE_ADD ) {
207             for ( i64 j = i; j < milton->canvas->redo_stack.count-1; ++j ) {
208                 milton->canvas->redo_stack.data[j] = milton->canvas->redo_stack.data[j+1];
209             }
210             pop(&milton->canvas->redo_stack);
211         }
212     }
213 }
214 
215 static void
milton_primitive_input(Milton * milton,MiltonInput const * input,b32 end_stroke)216 milton_primitive_input(Milton* milton, MiltonInput const* input, b32 end_stroke)
217 {
218     if ( end_stroke && milton->primitive_fsm == Primitive_DRAWING) {
219         milton->primitive_fsm = Primitive_WAITING;
220     }
221     else if (input->input_count > 0) {
222         v2l point = raster_to_canvas(milton->view, input->points[input->input_count - 1]);
223         Stroke* ws = &milton->working_stroke;
224         if ( milton->primitive_fsm == Primitive_WAITING ) {
225             milton->primitive_fsm             = Primitive_DRAWING;
226             ws->points[0]  = ws->points[1] = point;
227             ws->pressures[0] = ws->pressures[1] = 1.0f;
228             milton->working_stroke.num_points = 2;
229             ws->brush                         = milton_get_brush(milton);
230             ws->layer_id                      = milton->view->working_layer_id;
231         }
232         else if ( milton->primitive_fsm == Primitive_DRAWING ) {
233             milton->working_stroke.points[1] = point;
234         }
235     }
236 }
237 
238 void
stroke_append_point(Stroke * stroke,v2l canvas_point,f32 pressure)239 stroke_append_point(Stroke* stroke, v2l canvas_point, f32 pressure)
240 {
241     b32 not_the_first =  stroke->num_points >= 1;
242 
243     // A point passes inspection if:
244     // Add to current stroke.
245     if ( stroke->num_points < STROKE_MAX_POINTS ) {
246         int index = stroke->num_points++;
247         stroke->points[index] = canvas_point;
248         stroke->pressures[index] = pressure;
249     }
250 }
251 
252 static v2l
smooth_filter(SmoothFilter * filter,v2l input)253 smooth_filter(SmoothFilter* filter, v2l input)
254 {
255     v2f point = v2l_to_v2f(input - filter->center);
256 
257     if (filter->first)
258     {
259         filter->prediction = point;
260         filter->first = false;
261     }
262     else
263     {
264         f32 alpha = 0.5;
265 
266         filter->prediction = alpha * point + (1 - alpha) * filter->prediction;
267     }
268     v2l result = v2f_to_v2l(filter->prediction) + filter->center;
269     return result;
270 }
271 
272 static void
clear_smooth_filter(SmoothFilter * filter,v2l center)273 clear_smooth_filter(SmoothFilter* filter, v2l center)
274 {
275     filter->first = true;
276     filter->center = center;
277 }
278 
279 static u64 peek_out_duration_ms(Milton* milton);  // forward decl
280 
281 static float
peek_out_interpolation(Milton * milton)282 peek_out_interpolation(Milton* milton)
283 {
284     float interp = 0.0f;
285 
286     WallTime time = platform_get_walltime();
287     u64 ms = difference_in_ms(milton->peek_out->begin_anim_time, time);
288 
289     if (ms < peek_out_duration_ms(milton)) {
290         interp = ms / (float)peek_out_duration_ms(milton);
291     }
292     else {
293         interp = 1.0f;
294     }
295 
296     if (milton->peek_out->peek_out_ended) {
297         interp = 1 - interp;
298     }
299     return interp;
300 }
301 
302 i64
milton_render_scale_with_interpolation(Milton * milton,float interp)303 milton_render_scale_with_interpolation(Milton* milton, float interp)
304 {
305     if ( milton->current_mode == MiltonMode::PEEK_OUT ) {
306         return lerp(milton->peek_out->low_scale, milton->peek_out->high_scale, interp);
307     }
308     else {
309         return milton->view->scale;
310     }
311 }
312 
313 i64
milton_render_scale(Milton * milton)314 milton_render_scale(Milton* milton)
315 {
316     float interp = 1.0f;
317     if (milton->current_mode == MiltonMode::PEEK_OUT) {
318         interp = peek_out_interpolation(milton);
319     }
320     return milton_render_scale_with_interpolation(milton, interp);
321 }
322 
323 static void
milton_stroke_input(Milton * milton,MiltonInput const * input)324 milton_stroke_input(Milton* milton, MiltonInput const* input)
325 {
326     if ( input->input_count == 0 ) {
327         return;
328     }
329 
330     if ( milton->view->scale != milton_render_scale(milton) ) {
331         return;  // We can't draw while peeking.
332     }
333 
334     Stroke* ws = &milton->working_stroke;
335 
336     if ((milton->flags & MiltonStateFlags_BRUSH_SMOOTHING) && ws->num_points == 0) {
337         clear_smooth_filter(milton->smooth_filter, input->points[0]);
338     }
339 
340     //milton_log("Stroke input with %d packets\n", input->input_count);
341     ws->brush    = milton_get_brush(milton);
342     ws->layer_id = milton->view->working_layer_id;
343 
344     for ( int input_i = 0; input_i < input->input_count; ++input_i ) {
345 
346         v2l in_point = input->points[input_i];
347         if (milton->flags & MiltonStateFlags_BRUSH_SMOOTHING) {
348             in_point = smooth_filter(milton->smooth_filter, in_point);
349         }
350 
351         v2l canvas_point = raster_to_canvas(milton->view, in_point);
352 
353         f32 pressure = NO_PRESSURE_INFO;
354 
355         if ( input->pressures[input_i] != NO_PRESSURE_INFO ) {
356             f32 pressure_min = 0.01f;
357             pressure = pressure_min + input->pressures[input_i] * (1.0f - pressure_min);
358         } else {
359             pressure = 1.0f;
360         }
361 
362         stroke_append_point(ws, canvas_point, pressure);
363     }
364 }
365 
366 void
milton_set_zoom_at_point_with_scale(Milton * milton,v2i new_zoom_center,i64 scale)367 milton_set_zoom_at_point_with_scale(Milton* milton, v2i new_zoom_center, i64 scale)
368 {
369     milton->view->pan_center = raster_to_canvas_with_scale(milton->view, v2i_to_v2l(new_zoom_center), scale);
370     milton->view->zoom_center = new_zoom_center;
371     gpu_update_canvas(milton->renderer, milton->canvas, milton->view);
372 }
373 
374 void
milton_set_zoom_at_point(Milton * milton,v2i new_zoom_center)375 milton_set_zoom_at_point(Milton* milton, v2i new_zoom_center)
376 {
377     milton_set_zoom_at_point_with_scale(milton, new_zoom_center, milton_render_scale(milton));
378 }
379 
380 void
milton_set_zoom_at_screen_center(Milton * milton)381 milton_set_zoom_at_screen_center(Milton* milton)
382 {
383     milton_set_zoom_at_point(milton, milton->view->screen_size / 2);
384 }
385 
386 void
milton_set_canvas_file_(Milton * milton,PATH_CHAR * fname,b32 is_default)387 milton_set_canvas_file_(Milton* milton, PATH_CHAR* fname, b32 is_default)
388 {
389     milton_log("Set milton file: %s\n", fname);
390     if ( is_default ) {
391         milton->flags |= MiltonStateFlags_DEFAULT_CANVAS;
392     } else {
393         milton->flags &= ~MiltonStateFlags_DEFAULT_CANVAS;
394     }
395 
396     u64 len = PATH_STRLEN(fname);
397     if ( len > MAX_PATH ) {
398         milton_log("milton_set_canvas_file: fname was too long %lu\n", len);
399         fname = TO_PATH_STR("MiltonPersist.mlt");
400     }
401     milton->persist->mlt_file_path = fname;
402 
403     if ( !is_default ) {
404         milton_set_last_canvas_fname(fname);
405     } else {
406         milton_unset_last_canvas_fname();
407     }
408 }
409 
410 void
milton_set_canvas_file(Milton * milton,PATH_CHAR * fname)411 milton_set_canvas_file(Milton* milton, PATH_CHAR* fname)
412 {
413     milton_set_canvas_file_(milton, fname, false);
414 }
415 
416 // Helper function
417 void
milton_set_default_canvas_file(Milton * milton)418 milton_set_default_canvas_file(Milton* milton)
419 {
420     PATH_CHAR* f = (PATH_CHAR*)mlt_calloc(MAX_PATH, sizeof(PATH_CHAR), "Strings");
421 
422     PATH_STRNCPY(f, TO_PATH_STR("MiltonPersist.mlt"), MAX_PATH);
423     platform_fname_at_config(f, MAX_PATH);
424     milton_set_canvas_file_(milton, f, true);
425     milton->flags |= MiltonStateFlags_DEFAULT_CANVAS;
426 }
427 
428 static i32
milton_get_brush_radius_for_enum(Milton const * milton,int brush_enum)429 milton_get_brush_radius_for_enum(Milton const* milton, int brush_enum)
430 {
431     i32 brush_size = milton->brush_sizes[brush_enum];
432     if ( brush_size <= 0 ) {
433         brush_size = 1;
434     }
435     return brush_size;
436 }
437 
438 i32
milton_get_brush_radius(Milton const * milton)439 milton_get_brush_radius(Milton const* milton)
440 {
441     i32 radius = milton_get_brush_radius_for_enum(milton, milton_get_brush_enum(milton));
442     return radius;
443 }
444 
445 void
milton_set_brush_size_for_enum(Milton * milton,i32 size,int brush_idx)446 milton_set_brush_size_for_enum(Milton* milton, i32 size, int brush_idx)
447 {
448     if ( current_mode_is_for_drawing(milton) ) {
449         (*pointer_to_brush_size(milton)) = size;
450         milton_update_brushes(milton);
451     }
452 }
453 
454 void
milton_set_brush_size(Milton * milton,i32 size)455 milton_set_brush_size(Milton* milton, i32 size)
456 {
457     milton_set_brush_size_for_enum(milton, size, milton_get_brush_enum(milton));
458 }
459 
460 
461 // For keyboard shortcut.
462 void
milton_increase_brush_size(Milton * milton)463 milton_increase_brush_size(Milton* milton)
464 {
465     if ( current_mode_is_for_drawing(milton) ) {
466         i32 brush_size = milton_get_brush_radius(milton);
467         if ( brush_size < MILTON_MAX_BRUSH_SIZE && brush_size > 0 ) {
468             milton_set_brush_size(milton, brush_size + 1);
469         }
470         milton_update_brushes(milton);
471     }
472 }
473 
474 // For keyboard shortcut.
475 void
milton_decrease_brush_size(Milton * milton)476 milton_decrease_brush_size(Milton* milton)
477 {
478     if ( current_mode_is_for_drawing(milton) ) {
479         i32 brush_size = milton_get_brush_radius(milton);
480         if ( brush_size > 1 ) {
481             milton_set_brush_size(milton, brush_size - 1);
482         }
483         milton_update_brushes(milton);
484     }
485 }
486 
487 void
milton_set_brush_alpha(Milton * milton,float alpha)488 milton_set_brush_alpha(Milton* milton, float alpha)
489 {
490     int brush_enum = milton_get_brush_enum(milton);
491 
492     milton->brushes[brush_enum].alpha = alpha;
493     milton->brushes[brush_enum].pressure_opacity_min = min(alpha, milton->brushes[brush_enum].pressure_opacity_min);
494     milton_update_brushes(milton);
495 }
496 
497 float
milton_get_brush_alpha(Milton const * milton)498 milton_get_brush_alpha(Milton const* milton)
499 {
500     int brush_enum = milton_get_brush_enum(milton);
501     const float alpha = milton->brushes[brush_enum].alpha;
502     return alpha;
503 }
504 
505 void
settings_init(MiltonSettings * s)506 settings_init(MiltonSettings* s)
507 {
508     s->background_color = v3f{1,1,1};
509     s->peek_out_increment = DEFAULT_PEEK_OUT_INCREMENT_LOG;
510 }
511 
512 int milton_save_thread(void* state_);  // forward
513 
514 void
reset_working_stroke(Milton * milton)515 reset_working_stroke(Milton* milton)
516 {
517     milton->working_stroke.num_points = 0;
518     gpu_reset_stroke(milton->renderer, milton->working_stroke.render_handle);
519     milton->working_stroke.bounding_rect = rect_without_size();
520 }
521 
522 
523 void
milton_init(Milton * milton,i32 width,i32 height,f32 ui_scale,PATH_CHAR * file_to_open,MiltonInitFlags init_flags)524 milton_init(Milton* milton, i32 width, i32 height, f32 ui_scale, PATH_CHAR* file_to_open, MiltonInitFlags init_flags)
525 {
526     b32 init_graphics = !(init_flags & MiltonInit_FOR_TEST);
527     b32 read_from_disk = !(init_flags & MiltonInit_FOR_TEST);
528 
529     init_localization();
530 
531     milton->canvas = arena_bootstrap(CanvasState, arena, 1024*1024);
532     milton->working_stroke.points    = arena_alloc_array(&milton->root_arena, STROKE_MAX_POINTS, v2l);
533     milton->working_stroke.pressures = arena_alloc_array(&milton->root_arena, STROKE_MAX_POINTS, f32);
534 #if STROKE_DEBUG_VIZ
535     milton->working_stroke.debug_flags = arena_alloc_array(&milton->root_arena, STROKE_MAX_POINTS, int);
536 #endif
537 
538     reset_working_stroke(milton);
539 
540     milton->current_mode = MiltonMode::PEN;
541 
542     milton->renderer = gpu_allocate_render_backend(&milton->root_arena);
543 
544     milton->smooth_filter = arena_alloc_elem(&milton->root_arena, SmoothFilter);
545 
546     if (init_graphics) { milton->gl = arena_alloc_elem(&milton->root_arena, MiltonGLState); }
547     milton->gui = arena_alloc_elem(&milton->root_arena, MiltonGui);
548     milton->settings = arena_alloc_elem(&milton->root_arena, MiltonSettings);
549     milton->eyedropper = arena_alloc_elem(&milton->root_arena, Eyedropper);
550     milton->persist = arena_alloc_elem(&milton->root_arena, MiltonPersist);
551     milton->drag_brush = arena_alloc_elem(&milton->root_arena, MiltonDragBrush);
552     milton->transform = arena_alloc_elem(&milton->root_arena, TransformMode);
553 
554     milton->persist->target_MB_per_sec = 0.2f;
555 
556     gui_init(&milton->root_arena, milton->gui, ui_scale);
557     settings_init(milton->settings);
558 
559     milton->peek_out = arena_alloc_elem(&milton->root_arena, PeekOut);
560 
561     b32 loaded_settings = false;
562     if (read_from_disk) {
563         loaded_settings = milton_settings_load(milton->settings);
564     }
565 
566     if (!loaded_settings) {
567         set_default_bindings(&milton->settings->bindings);
568     }
569 
570     milton->view = arena_alloc_elem(&milton->root_arena, CanvasView);
571 
572     init_view(milton->view, milton->settings->background_color, width, height);
573     if (init_graphics) { gpu_init(milton->renderer, milton->view, &milton->gui->picker); }
574 
575     if (init_graphics) { gpu_update_background(milton->renderer, milton->view->background_color); }
576 
577     { // Get/Set Milton Canvas (.mlt) file
578         if ( file_to_open == NULL ) {
579             PATH_CHAR* last_fname = milton_get_last_canvas_fname();
580 
581             if ( last_fname != NULL ) {
582                 milton_set_canvas_file(milton, last_fname);
583             } else {
584                 milton_set_default_canvas_file(milton);
585             }
586         }
587         else {
588             milton_set_canvas_file(milton, file_to_open);
589         }
590     }
591 
592     // Set default brush.
593     {
594         for ( int i = 0; i < BrushEnum_COUNT; ++i ) {
595 
596             milton->brushes[i].alpha = 1.0f;
597             milton->brushes[i].hardness = k_max_hardness;
598 
599             switch ( i ) {
600             case BrushEnum_PEN: {
601                 milton->brush_sizes[i] = 30;
602             } break;
603             case BrushEnum_ERASER: {
604                 milton->brush_sizes[i] = 40;
605             } break;
606             case BrushEnum_PRIMITIVE: {
607                 milton->brush_sizes[i] = 10;
608             } break;
609             case BrushEnum_NOBRUSH: { {
610                 milton->brush_sizes[i] = 1;
611             } } break;
612             default: {
613                 INVALID_CODE_PATH;
614             } break;
615             }
616             mlt_assert(milton->brush_sizes[i] > 0 && milton->brush_sizes[i] <= MILTON_MAX_BRUSH_SIZE);
617         }
618     }
619 
620     milton->persist->last_save_time = {};
621     // Note: This will fill out uninitialized data like default layers.
622     if (read_from_disk) { milton_load(milton); }
623 
624     milton_validate(milton);
625 
626     // Enable brush smoothing by default
627     if ( !milton_brush_smoothing_enabled(milton) ) {
628         milton_toggle_brush_smoothing(milton);
629     }
630 
631     // Default mode GUI visibility
632     milton->mode_gui_visibility[Milton::GuiVisibleCategory_DRAWING] = true;
633 
634 #if MILTON_ENABLE_PROFILING
635     milton->viz_window_visible = false;  // hidden by default
636 #endif
637 
638     milton->flags |= MiltonStateFlags_RUNNING;
639 
640 #if MILTON_ENABLE_PROFILING
641     profiler_init();
642 #endif
643 
644     #if MILTON_SAVE_ASYNC
645         milton->save_mutex = SDL_CreateMutex();
646         milton->save_cond = SDL_CreateCond();
647         milton->save_thread = SDL_CreateThread(milton_save_thread, "Save thread", (void*)milton);
648     #endif
649 }
650 
651 void
upload_gui(Milton * milton)652 upload_gui(Milton* milton)
653 {
654     if (milton->gl)
655     {
656         gpu_update_canvas(milton->renderer, milton->canvas, milton->view);
657         gpu_resize(milton->renderer, milton->view);
658         gpu_update_picker(milton->renderer, &milton->gui->picker);
659     }
660 }
661 
662 // Returns false if the pan_delta moves the pan vector outside of the canvas.
663 void
milton_resize_and_pan(Milton * milton,v2l pan_delta,v2i new_screen_size)664 milton_resize_and_pan(Milton* milton, v2l pan_delta, v2i new_screen_size)
665 {
666     if ( milton->max_width <= new_screen_size.w ) {
667         milton->max_width = new_screen_size.w + 256;
668     }
669     if ( milton->max_height <= new_screen_size.h ) {
670         milton->max_height = new_screen_size.h + 256;
671     }
672 
673     if ( new_screen_size.w < milton->max_width && new_screen_size.h < milton->max_height ) {
674         milton->view->screen_size = new_screen_size;
675 
676         f32 x = pan_delta.x;
677         f32 y = pan_delta.y;
678 
679         f32 cos_angle = cosf(milton->view->angle);
680         f32 sin_angle = sinf(milton->view->angle);
681 
682         v2f deltaf = v2f{x * cos_angle - y * sin_angle, y * cos_angle + x * sin_angle };
683         // Add delta to pan vector
684         v2l pan_center = milton->view->pan_center - (v2f_to_v2l(deltaf) * milton->view->scale);
685 
686         milton->view->pan_center = pan_center;
687 
688         upload_gui(milton);
689     } else {
690         milton_die_gracefully("Fatal error. Screen size is more than Milton can handle.");
691     }
692 }
693 
694 void
milton_reset_canvas(Milton * milton)695 milton_reset_canvas(Milton* milton)
696 {
697     CanvasState* canvas = milton->canvas;
698 
699     gpu_free_strokes(milton->renderer, milton->canvas);
700     milton->persist->mlt_binary_version = MILTON_MINOR_VERSION;
701     milton->persist->last_save_time = {};
702 
703     // Clear history
704     release(&canvas->history);
705     release(&canvas->redo_stack);
706     release(&canvas->stroke_graveyard);
707 
708     size_t size = canvas->arena.min_block_size;
709     arena_free(&canvas->arena);  // Note: This destroys the canvas
710     milton->canvas = arena_bootstrap(CanvasState, arena, size);
711 
712     mlt_assert(milton->canvas->history.count == 0);
713 }
714 
715 void
milton_reset_canvas_and_set_default(Milton * milton)716 milton_reset_canvas_and_set_default(Milton* milton)
717 {
718     milton_reset_canvas(milton);
719 
720     // New Root
721     milton_new_layer(milton);
722 
723     // New View
724     init_view(milton->view,
725         milton->settings->background_color,
726         milton->view->screen_size.x,
727         milton->view->screen_size.y);
728     milton->view->background_color = milton->settings->background_color;
729     gpu_update_background(milton->renderer, milton->view->background_color);
730 
731     // Reset color buttons
732     for ( ColorButton* b = milton->gui->picker.color_buttons; b!=NULL; b=b->next ) {
733         b->rgba = {};
734     }
735 
736     // gui init
737     {
738         MiltonGui* gui = milton->gui;
739         gui->picker.data.hsv = { 0.0f, 1.0f, 0.7f };
740         gui->visible = true;
741 
742         picker_init(&gui->picker);
743 
744         gui->preview_pos      = v2i{-1, -1};
745         gui->preview_pos_prev = v2i{-1, -1};
746 
747         exporter_init(&gui->exporter);
748     }
749 
750     milton_update_brushes(milton);
751 
752     milton_set_default_canvas_file(milton);
753     upload_gui(milton);
754 }
755 
756 static void
push_mode(Milton * milton,MiltonMode mode)757 push_mode(Milton* milton, MiltonMode mode)
758 {
759     if (milton->n_mode_stack < MODE_STACK_MAX) {
760         milton->mode_stack[ milton->n_mode_stack++ ] = mode;
761     }
762 }
763 
764 static MiltonMode
pop_mode(Milton * milton)765 pop_mode(Milton* milton)
766 {
767     MiltonMode result = MiltonMode::PEN;
768     if (milton->n_mode_stack) {
769         result = milton->mode_stack[ --milton->n_mode_stack ];
770     }
771     return result;
772 }
773 
774 MiltonMode
milton_leave_mode(Milton * milton)775 milton_leave_mode(Milton* milton)
776 {
777     if (milton->current_mode == MiltonMode::EYEDROPPER) {
778         eyedropper_deinit(milton->eyedropper);
779     }
780     MiltonMode leaving = milton->current_mode;
781     milton->current_mode = pop_mode(milton);
782     return leaving;
783 }
784 
785 void
milton_enter_mode(Milton * milton,MiltonMode mode)786 milton_enter_mode(Milton* milton, MiltonMode mode)
787 {
788     if ( mode != milton->current_mode ) {
789         if (mode == MiltonMode::EYEDROPPER) {
790             eyedropper_init(milton);
791         }
792         push_mode(milton, milton->current_mode);
793         milton->current_mode = mode;
794 
795         if (is_user_drawing(milton)) {
796             milton->flags |= MiltonStateFlags_FINISH_CURRENT_STROKE;
797         }
798     }
799 }
800 
801 void
milton_try_quit(Milton * milton)802 milton_try_quit(Milton* milton)
803 {
804     milton->flags &= ~MiltonStateFlags_RUNNING;
805 }
806 
807 void
milton_save_postlude(Milton * milton)808 milton_save_postlude(Milton* milton)
809 {
810     MiltonPersist* p = milton->persist;
811     p->last_save_time = platform_get_walltime();
812     p->last_save_stroke_count = layer::count_strokes(milton->canvas->root_layer);
813 
814     milton->flags &= ~MiltonStateFlags_LAST_SAVE_FAILED;
815 }
816 
817 #if MILTON_SAVE_ASYNC
818 void
trigger_async_save(Milton * milton)819 trigger_async_save(Milton* milton)
820 {
821     SDL_LockMutex(milton->save_mutex);
822     {
823         milton->save_flag = SaveEnum_SAVE_REQUESTED;
824     }
825     SDL_UnlockMutex(milton->save_mutex);
826 }
827 
828 void
milton_kill_save_thread(Milton * milton)829 milton_kill_save_thread(Milton* milton)
830 {
831     SDL_LockMutex(milton->save_mutex);
832     milton->save_flag = SaveEnum_KILL;
833     SDL_UnlockMutex(milton->save_mutex);
834 
835     // Do a save tick.
836     SDL_LockMutex(milton->save_mutex);
837     SDL_CondSignal(milton->save_cond);
838     SDL_UnlockMutex(milton->save_mutex);
839 
840     SDL_WaitThread(milton->save_thread, NULL);
841 }
842 
843 int
milton_save_thread(void * state_)844 milton_save_thread(void* state_)
845 {
846     Milton* milton = (Milton*)state_;
847     MiltonPersist* p = milton->persist;
848 
849     b32 running = true;
850     float time_to_wait_s = 0.0f;
851     u64 wait_begin_us = perf_counter();
852 
853     while ( running ) {
854         bool do_save = false;
855         SDL_LockMutex(milton->save_mutex);
856 
857         SDL_CondWait(milton->save_cond, milton->save_mutex); // Wait for a frame tick.
858 
859         if ( milton->save_flag == SaveEnum_KILL ) {
860             running = false;
861         }
862         else {
863             float time_waited_s = perf_count_to_sec(perf_counter() - wait_begin_us);
864             if (time_waited_s <= time_to_wait_s) {
865                 time_to_wait_s -= time_waited_s;
866             }
867             else {
868                 if ( milton->save_flag == SaveEnum_SAVE_REQUESTED ) {
869                     do_save = true;
870                     milton->save_flag = SaveEnum_WAITING;
871                 }
872             }
873             wait_begin_us = perf_counter();
874         }
875         SDL_UnlockMutex(milton->save_mutex);
876 
877         if ( do_save ) {
878             // Wait. Either one frame, or the time to stay below bandwidth.
879             u64 begin_us = perf_counter();
880             u64 bytes_written = milton_save(milton);
881             u64 duration_us = perf_counter() - begin_us;
882 
883             // Sleep, if necessary.
884             float duration_s = duration_us / 1000000.0f;
885 
886             float MB_written = bytes_written / (1024.0f * 1024.0f);
887             float MB_per_sec = MB_written / duration_s;
888 
889             if (MB_per_sec > p->target_MB_per_sec) {
890                 time_to_wait_s = MB_written / p->target_MB_per_sec - duration_s;
891                 wait_begin_us = perf_counter();
892             }
893         }
894     }
895     return 0;
896 }
897 #endif
898 
899 void
milton_new_layer(Milton * milton)900 milton_new_layer(Milton* milton)
901 {
902     CanvasState* canvas = milton->canvas;
903     i32 id = canvas->layer_guid++;
904     milton_log("Increased guid to %d\n", canvas->layer_guid);
905     milton_new_layer_with_id(milton, id);
906 }
907 
908 void
milton_new_layer_with_id(Milton * milton,i32 new_id)909 milton_new_layer_with_id(Milton* milton, i32 new_id)
910 {
911     CanvasState* canvas = milton->canvas;
912     Layer* layer = arena_alloc_elem(&canvas->arena, Layer);
913     {
914         layer->id = new_id;
915         layer->flags = LayerFlags_VISIBLE;
916         layer->strokes.arena = &canvas->arena;
917         layer->alpha = 1.0f;
918         strokelist_init_bucket(&layer->strokes.root);
919     }
920     snprintf(layer->name, MAX_LAYER_NAME_LEN, "Layer %d", layer->id);
921 
922     if ( canvas->root_layer != NULL ) {
923         Layer* top = layer::get_topmost(canvas->root_layer);
924         top->next = layer;
925         layer->prev = top;
926         milton_set_working_layer(milton, top->next);
927     } else {
928         canvas->root_layer = layer;
929         milton_set_working_layer(milton, layer);
930     }
931 }
932 
933 void
milton_set_working_layer(Milton * milton,Layer * layer)934 milton_set_working_layer(Milton* milton, Layer* layer)
935 {
936     milton->canvas->working_layer = layer;
937     milton->view->working_layer_id = layer->id;
938 }
939 
940 void
milton_delete_working_layer(Milton * milton)941 milton_delete_working_layer(Milton* milton)
942 {
943     Layer* layer = milton->canvas->working_layer;
944     if ( layer->next || layer->prev ) {
945         if (layer->next) layer->next->prev = layer->prev;
946         if (layer->prev) layer->prev->next = layer->next;
947 
948         Layer* wl = NULL;
949         if (layer->next) wl = layer->next;
950         else wl = layer->prev;
951         milton_set_working_layer(milton, wl);
952     }
953     if ( layer == milton->canvas->root_layer ) {
954         milton->canvas->root_layer = milton->canvas->working_layer;
955     }
956 }
957 
958 b32
milton_brush_smoothing_enabled(Milton * milton)959 milton_brush_smoothing_enabled(Milton* milton)
960 {
961     b32 enabled = (milton->flags & MiltonStateFlags_BRUSH_SMOOTHING);
962     return enabled;
963 }
964 
965 void
milton_toggle_brush_smoothing(Milton * milton)966 milton_toggle_brush_smoothing(Milton* milton)
967 {
968     if ( milton_brush_smoothing_enabled(milton) ) {
969         milton->flags &= ~MiltonStateFlags_BRUSH_SMOOTHING;
970     } else {
971         milton->flags |= MiltonStateFlags_BRUSH_SMOOTHING;
972     }
973 }
974 
975 static void
milton_validate(Milton * milton)976 milton_validate(Milton* milton)
977 {
978     // Make sure that the history reflects the strokes that exist
979     i64 num_layers=0;
980     for ( Layer* l = milton->canvas->root_layer; l != NULL; l = l->next ) {
981         ++num_layers;
982     }
983     i32* layer_ids = (i32*)mlt_calloc((size_t)num_layers, sizeof(i32), "Validate");
984     i64 i = 0;
985     for ( Layer* l = milton->canvas->root_layer; l != NULL; l = l->next ) {
986         layer_ids[i] = l->id;
987         ++i;
988     }
989 
990     i64 history_count = 0;
991     for ( i64 hi = 0; hi < milton->canvas->history.count; ++hi ) {
992         i32 id = milton->canvas->history.data[hi].layer_id;
993         for ( i64 li = 0; li < num_layers; ++li ) {
994             if ( id == layer_ids[li] ) {
995                 ++history_count;
996             }
997         }
998     }
999 
1000     i64 stroke_count = layer::count_strokes(milton->canvas->root_layer);
1001     if ( history_count != stroke_count ) {
1002         milton_log("WARNING: Recreating history. File says History: %d(max %d) Actual strokes: %d\n",
1003                    history_count, milton->canvas->history.count,
1004                    stroke_count);
1005         reset(&milton->canvas->history);
1006         i32 id = 0;
1007         for ( Layer *l = milton->canvas->root_layer;
1008               l != NULL;
1009               l = l->next ) {
1010             for ( i64 si = 0; si < l->strokes.count; ++si ) {
1011                 Stroke* s = get(&l->strokes, si);
1012                 HistoryElement he = { HistoryElement_STROKE_ADD, s->layer_id };
1013                 push(&milton->canvas->history, he);
1014             }
1015         }
1016     }
1017 
1018     mlt_free(layer_ids, "Validate");
1019 }
1020 
1021 
1022 // Copy points from in_stroke to out_stroke, but do interpolation to smooth it out.
1023 static void
copy_stroke(Arena * arena,CanvasView * view,Stroke * in_stroke,Stroke * out_stroke)1024 copy_stroke(Arena* arena, CanvasView* view, Stroke* in_stroke, Stroke* out_stroke)
1025 {
1026     i32 num_points = in_stroke->num_points;
1027     // Shallow copy
1028     *out_stroke = *in_stroke;
1029 
1030     // Deep copy
1031     out_stroke->points    = arena_alloc_array(arena, num_points, v2l);
1032     out_stroke->pressures = arena_alloc_array(arena, num_points, f32);
1033 
1034     memcpy(out_stroke->points, in_stroke->points, num_points * sizeof(v2l));
1035     memcpy(out_stroke->pressures, in_stroke->pressures, num_points * sizeof(f32));
1036 
1037 #if STROKE_DEBUG_VIZ
1038     out_stroke->debug_flags = arena_alloc_array(arena, num_points * sizeof(int), int);
1039     memcpy(out_stroke->debug_flags, in_stroke->debug_flags, num_points*sizeof(int));
1040 #endif
1041 
1042     out_stroke->render_handle = 0;
1043 }
1044 
1045 static i64
peek_out_target_scale(Milton * milton)1046 peek_out_target_scale(Milton* milton)
1047 {
1048     double log_scale = log2(1 + milton->view->scale) / log2(SCALE_FACTOR);
1049     i64 target = min(pow(SCALE_FACTOR, log_scale + milton->settings->peek_out_increment), VIEW_SCALE_LIMIT);
1050     return target;
1051 }
1052 
1053 static u64
peek_out_duration_ms(Milton * milton)1054 peek_out_duration_ms(Milton* milton)
1055 {
1056     u64 duration = milton->settings->peek_out_increment * PEEK_OUT_SPEED;
1057     return duration;
1058 }
1059 
1060 void
peek_out_trigger_start(Milton * milton,int peek_out_flags)1061 peek_out_trigger_start(Milton* milton, int peek_out_flags)
1062 {
1063     milton_set_zoom_at_screen_center(milton);
1064     milton->peek_out->begin_pan = milton->view->pan_center;
1065 
1066     milton->peek_out->low_scale = milton_render_scale(milton);
1067     milton->peek_out->high_scale = peek_out_target_scale(milton);
1068     milton->peek_out->peek_out_ended = false;
1069     milton->peek_out->begin_anim_time = platform_get_walltime();
1070     milton->peek_out->flags = peek_out_flags;
1071 
1072     if (milton->current_mode != MiltonMode::PEEK_OUT) {
1073         milton_enter_mode(milton, MiltonMode::PEEK_OUT);
1074     }
1075 }
1076 
1077 void
peek_out_trigger_stop(Milton * milton)1078 peek_out_trigger_stop(Milton* milton)
1079 {
1080     if (milton->current_mode == MiltonMode::PEEK_OUT && !milton->peek_out->peek_out_ended) {
1081         milton_set_zoom_at_point(milton, milton->platform->pointer);
1082 
1083         i64 scale = milton_render_scale(milton);
1084         milton->peek_out->high_scale = scale;
1085         milton->peek_out->low_scale = milton->view->scale;
1086         milton->peek_out->begin_anim_time = platform_get_walltime();
1087         milton->peek_out->peek_out_ended = true;
1088         milton->peek_out->end_pan = raster_to_canvas_with_scale(milton->view, v2i_to_v2l(milton->platform->pointer), scale);
1089     }
1090 }
1091 
1092 static void
peek_out_tick(Milton * milton,MiltonInput const * input)1093 peek_out_tick(Milton* milton, MiltonInput const* input)
1094 {
1095     PeekOut* peek = milton->peek_out;
1096 
1097     if (milton->current_mode == MiltonMode::PEEK_OUT) {
1098 
1099         float interp = peek_out_interpolation(milton);
1100 
1101         if ((peek->flags & PeekOut_CLICK_TO_EXIT) && input->input_count > 0)
1102         {
1103             peek_out_trigger_stop(milton);
1104         }
1105 
1106         if ( milton->peek_out->peek_out_ended ) {
1107             i64 panx = lerp(peek->end_pan.x, peek->begin_pan.x, interp);
1108             i64 pany = lerp(peek->end_pan.y, peek->begin_pan.y, interp);
1109 
1110             v2l pan = { panx, pany };
1111 
1112             milton->view->pan_center = pan;
1113             milton->view->zoom_center = milton->view->screen_size / 2;
1114             gpu_update_canvas(milton->renderer, milton->canvas, milton->view);
1115 
1116             if ( difference_in_ms(peek->begin_anim_time, platform_get_walltime()) > peek_out_duration_ms(milton) ) {
1117                 milton_leave_mode(milton);
1118             }
1119         }
1120         gpu_update_scale(milton->renderer, milton_render_scale(milton));
1121 
1122         {
1123             i32 width = 100;
1124             i32 height = 50;
1125             i32 line_width = 2;
1126 
1127             // TODO: Interpolate pan vector
1128             f32 scale = (f32)milton_render_scale_with_interpolation(milton, interp);
1129 
1130             f32 cx = 2 * milton->platform->pointer.x / (f32)milton->view->screen_size.w - 1;
1131             f32 cy = (2 * milton->platform->pointer.y / (f32)milton->view->screen_size.h - 1)*-1;
1132             f32 left = cx - milton->view->scale / scale;
1133             f32 right = cx + milton->view->scale / scale;
1134             f32 top = cy - milton->view->scale / scale;
1135             f32 bottom = cy + milton->view->scale / scale;
1136 
1137             imm_rect(milton->renderer, left, right, top, bottom, line_width);
1138         }
1139     }
1140 }
1141 
1142 void
drag_brush_size_start(Milton * milton,v2i pointer)1143 drag_brush_size_start(Milton* milton, v2i pointer)
1144 {
1145     if ( milton->current_mode != MiltonMode::DRAG_BRUSH_SIZE &&
1146          current_mode_is_for_drawing(milton) ) {
1147         milton->drag_brush->brush_idx = milton_get_brush_enum(milton);
1148         i32 size = milton_get_brush_radius(milton);
1149         milton->drag_brush->start_point = platform_cursor_get_position(milton->platform);
1150         milton->drag_brush->start_size = size;
1151         milton_enter_mode(milton, MiltonMode::DRAG_BRUSH_SIZE);
1152     }
1153 }
1154 
1155 void
drag_brush_size_stop(Milton * milton)1156 drag_brush_size_stop(Milton* milton)
1157 {
1158     if (milton->current_mode == MiltonMode::DRAG_BRUSH_SIZE) {
1159         v2i point = milton->drag_brush->start_point;
1160         platform_cursor_set_position(milton->platform, point);
1161         milton_leave_mode(milton);
1162     }
1163 }
1164 
1165 static void
drag_brush_size_tick(Milton * milton,MiltonInput const * input)1166 drag_brush_size_tick(Milton* milton, MiltonInput const* input)
1167 {
1168     MiltonDragBrush* drag = milton->drag_brush;
1169     f32 drag_factor = 0.5f;
1170     i64 mouse_x = platform_cursor_get_position(milton->platform).x;
1171 
1172     f32 new_size = drag->start_size + drag_factor * (mouse_x - drag->start_point.x);
1173     if ( new_size < 1 )
1174         new_size = 1;
1175     if ( new_size > 300 )
1176         new_size = 300;
1177     milton_set_brush_size_for_enum(milton, static_cast<i32>(new_size), drag->brush_idx);
1178     milton_update_brushes(milton);
1179 }
1180 
1181 void
transform_start(Milton * milton,v2i pointer)1182 transform_start(Milton* milton, v2i pointer)
1183 {
1184     if (milton->current_mode != MiltonMode::TRANSFORM) {
1185         milton_enter_mode(milton, MiltonMode::TRANSFORM);
1186         milton_set_zoom_at_screen_center(milton);
1187     }
1188 }
1189 
1190 void
transform_stop(Milton * milton)1191 transform_stop(Milton* milton)
1192 {
1193     if (milton->current_mode == MiltonMode::TRANSFORM) {
1194         milton_leave_mode(milton);
1195     }
1196 }
1197 
1198 static void
transform_tick(Milton * milton,MiltonInput const * input)1199 transform_tick(Milton* milton, MiltonInput const* input)
1200 {
1201     TransformMode* t = milton->transform;
1202 
1203     if (input->input_count > 0) {
1204         v2f point = v2l_to_v2f(input->points[ input->input_count - 1 ]);
1205         if (t->fsm == TransformModeFSM::START) {
1206             t->fsm = TransformModeFSM::ROTATING;
1207             t->last_point = point;
1208         }
1209         else if (t->fsm == TransformModeFSM::ROTATING) {
1210             // Rotate!
1211             const v2f center = v2i_to_v2f(milton->view->screen_size / 2);
1212             const v2f a = point - center;
1213             const v2f b = t->last_point - center;
1214             const f32 lena = magnitude(a);
1215             const f32 lenb = magnitude(b);
1216             if (lena > 0.0f && lenb > 0.0f) {
1217                 const f32 cos_angle = clamp(DOT(b, a) / (lena * lenb), 0.0f, 1.0f);
1218                 const f32 sign = orientation(center, t->last_point, point) > 0 ? -1.0f : 1.0f;
1219                 milton->view->angle += sign * acosf(cos_angle);
1220                 t->last_point = point;
1221                 gpu_update_canvas(milton->renderer, milton->canvas, milton->view);
1222             }
1223         }
1224     }
1225     if (input->flags & MiltonInputFlags_CLICKUP) {
1226         if (t->fsm == TransformModeFSM::ROTATING) {
1227             t->fsm = TransformModeFSM::START;
1228         }
1229     }
1230 }
1231 
1232 void
milton_update_and_render(Milton * milton,MiltonInput const * input)1233 milton_update_and_render(Milton* milton, MiltonInput const* input)
1234 {
1235     imm_begin_frame(milton->renderer);
1236 
1237     PROFILE_GRAPH_BEGIN(update);
1238 
1239     b32 end_stroke = (input->flags & MiltonInputFlags_END_STROKE) || (milton->flags & MiltonStateFlags_FINISH_CURRENT_STROKE);
1240 
1241     milton->flags &= ~MiltonStateFlags_FINISH_CURRENT_STROKE;
1242 
1243     milton->render_settings.do_full_redraw = false;
1244 
1245     b32 brush_outline_should_draw = false;
1246     int render_flags = RenderBackendFlags_NONE;
1247 
1248     b32 draw_custom_rectangle = false;  // Custom rectangle used for new strokes, undo/redo.
1249 
1250     b32 should_save =
1251             ((input->flags & MiltonInputFlags_OPEN_FILE)) ||
1252             ((input->flags & MiltonInputFlags_SAVE_FILE)) ||
1253             ((input->flags & MiltonInputFlags_END_STROKE)) ||
1254             ((input->flags & MiltonInputFlags_UNDO)) ||
1255             ((input->flags & MiltonInputFlags_REDO));
1256 
1257     if ( input->flags & MiltonInputFlags_OPEN_FILE ) {
1258         milton_load(milton);
1259         upload_gui(milton);
1260         milton->render_settings.do_full_redraw = true;
1261     }
1262 
1263     i32 now = (i32)SDL_GetTicks();
1264 
1265     // Set GUI visibility
1266     {
1267         size_t idx = get_gui_visibility_index(milton);
1268 
1269         milton->gui->visible = milton->mode_gui_visibility[idx];
1270     }
1271 
1272     if ( input->flags & MiltonInputFlags_FULL_REFRESH ) {
1273         milton->render_settings.do_full_redraw = true;
1274     }
1275 
1276     if ( input->scale ) {
1277         milton->render_settings.do_full_redraw = true;
1278 
1279         f32 scale_factor = SCALE_FACTOR;
1280         i32 view_scale_limit = VIEW_SCALE_LIMIT;
1281 
1282         i32 min_scale = MINIMUM_SCALE;
1283 
1284         // Update the current brush if it's canvas-relative
1285         if (milton->working_stroke.flags & StrokeFlag_RELATIVE_TO_CANVAS) {
1286 
1287             i32* psize = pointer_to_brush_size(milton);
1288 
1289             if ( input->scale > 0 && milton->view->scale >= min_scale ) {
1290                 (*psize) = (i32)((*psize) * scale_factor) + 1;
1291             }
1292             else if ( input->scale < 0 && (*psize) < view_scale_limit ) {
1293                 (*psize) = (i32)(ceilf((*psize) / scale_factor));
1294             }
1295         }
1296 
1297         if ( input->scale > 0 && milton->view->scale >= min_scale ) {
1298             milton->view->scale = (i32)(ceilf(milton->view->scale / scale_factor));
1299         }
1300         else if ( input->scale < 0 && milton->view->scale < view_scale_limit ) {
1301             milton->view->scale = (i32)(milton->view->scale * scale_factor) + 1;
1302         }
1303 
1304         milton_update_brushes(milton);
1305         gpu_update_scale(milton->renderer, milton->view->scale);
1306     }
1307     else if ( (input->flags & MiltonInputFlags_PANNING) ) {
1308         // If we are *not* zooming and we are panning, we can copy most of the
1309         // framebuffer
1310         if ( !(input->pan_delta == v2l{}) ) {
1311             milton->render_settings.do_full_redraw = true;
1312         }
1313     }
1314 
1315     if ( input->mode_to_set != milton->current_mode
1316          && mode_is_for_drawing(input->mode_to_set)) {
1317         end_stroke = true;
1318     }
1319 
1320     { // Undo / Redo
1321         if ( (input->flags & MiltonInputFlags_UNDO) ) {
1322             // Grab undo elements. They might be from deleted layers, so discard dead results.
1323             while ( milton->canvas->history.count > 0 ) {
1324                 HistoryElement h = pop(&milton->canvas->history);
1325                 Layer* l = layer::get_by_id(milton->canvas->root_layer, h.layer_id);
1326                 // found a thing to undo.
1327                 if ( l ) {
1328                     if ( l->strokes.count > 0 ) {
1329                         Stroke* stroke_ptr = peek(&l->strokes);
1330                         Stroke stroke = pop(&l->strokes);
1331                         push(&milton->canvas->stroke_graveyard, stroke);
1332                         push(&milton->canvas->redo_stack, h);
1333 
1334                         milton->render_settings.do_full_redraw = true;
1335                     }
1336                     break;
1337                 }
1338             }
1339         }
1340         else if ( (input->flags & MiltonInputFlags_REDO) ) {
1341             if ( milton->canvas->redo_stack.count > 0 ) {
1342                 HistoryElement h = pop(&milton->canvas->redo_stack);
1343                 switch ( h.type ) {
1344                 case HistoryElement_STROKE_ADD: {
1345                     Layer* l = layer::get_by_id(milton->canvas->root_layer, h.layer_id);
1346                     if ( l && count(&milton->canvas->stroke_graveyard) > 0 ) {
1347                         Stroke stroke = pop(&milton->canvas->stroke_graveyard);
1348                         if ( stroke.layer_id == h.layer_id ) {
1349                             push(&l->strokes, stroke);
1350                             push(&milton->canvas->history, h);
1351 
1352                             milton->render_settings.do_full_redraw = true;
1353 
1354                             break;
1355                         }
1356 
1357                         stroke = pop(&milton->canvas->stroke_graveyard);  // Keep popping in case the graveyard has info from deleted layers
1358                     }
1359 
1360                 } break;
1361                 /* case HistoryElement_LAYER_DELETE: { */
1362                 /* } break; */
1363                 }
1364             }
1365         }
1366     }
1367 
1368     // If the current mode is Pen or Eraser, we show the hover. It can be unset under various conditions later.
1369     if ( current_mode_is_for_drawing(milton) ) {
1370         brush_outline_should_draw = true;
1371     }
1372 
1373 
1374     if ( gui_point_hovers(milton->gui, milton->platform->pointer) ) {
1375         brush_outline_should_draw = false;
1376     }
1377 
1378     if ( (input->flags & MiltonInputFlags_IMGUI_GRABBED_INPUT) ) {
1379         // Start drawing the preview if we just grabbed a slider.
1380         brush_outline_should_draw = false;
1381 
1382         if ( (milton->gui->flags & MiltonGuiFlags_SHOWING_PREVIEW) ) {
1383             auto preview_pos = milton->gui->preview_pos;
1384             mlt_assert(preview_pos.x >= 0);
1385             mlt_assert(preview_pos.y >= 0);
1386             v4f color = {};
1387             color.rgb = milton->view->background_color;
1388             color.a = 1;
1389             if ( milton->current_mode == MiltonMode::PEN ) {
1390                 color = to_premultiplied(hsv_to_rgb(milton->gui->picker.data.hsv),
1391                                          milton_get_brush_alpha(milton));
1392             }
1393             gpu_update_brush_outline(milton->renderer,
1394                                      preview_pos.x, preview_pos.y,
1395                                      milton_get_brush_radius(milton), BrushOutline_FILL, color);
1396         }
1397     } else {
1398         gui_imgui_set_ungrabbed(milton->gui);
1399     }
1400 
1401     if ( milton->gui->visible ) {
1402         render_flags |= RenderBackendFlags_GUI_VISIBLE;
1403     } else {
1404         render_flags &= ~RenderBackendFlags_GUI_VISIBLE;
1405     }
1406 
1407     // Mode tick
1408     if (milton->current_mode == MiltonMode::ERASER) {
1409         milton->working_stroke.flags |= StrokeFlag_ERASER;
1410     }
1411     else {
1412         milton->working_stroke.flags &= ~StrokeFlag_ERASER;
1413     }
1414     if ( current_mode_is_for_drawing(milton) &&
1415         (input->input_count > 0 || end_stroke) ) {
1416         if ( !is_user_drawing(milton)
1417              && gui_consume_input(milton->gui, input) ) {
1418             milton_update_brushes(milton);
1419             gpu_update_picker(milton->renderer, &milton->gui->picker);
1420         }
1421         else if ( !milton->gui->owns_user_input
1422                   && (milton->canvas->working_layer->flags & LayerFlags_VISIBLE) ) {
1423             if ( milton->current_mode == MiltonMode::PRIMITIVE ) {
1424                 // Input for primitive.
1425                 milton_primitive_input(milton, input, end_stroke);
1426             }
1427             else if ( milton->current_mode != MiltonMode::DRAG_BRUSH_SIZE )  {  // Input for eraser and pen
1428                 Stroke* ws = &milton->working_stroke;
1429                 auto prev_num_points = ws->num_points;
1430                 milton_stroke_input(milton, input);
1431                 if ( prev_num_points == 0 && ws->num_points > 0 ) {
1432                     // New stroke. Clear screen without blur.
1433                     milton->render_settings.do_full_redraw = true;
1434                 }
1435             }
1436         }
1437     }
1438 
1439     if ( milton->current_mode == MiltonMode::EXPORTING ) {
1440         Exporter* exporter = &milton->gui->exporter;
1441         b32 changed = exporter_input(exporter, input);
1442 
1443         {
1444             i32 x = min(exporter->pivot.x, exporter->needle.x);
1445             i32 y = min(exporter->pivot.y, exporter->needle.y);
1446             i32 w = MLT_ABS(exporter->pivot.x - exporter->needle.x);
1447             i32 h = MLT_ABS(exporter->pivot.y - exporter->needle.y);
1448 
1449             float left = 2*((float)    x / (milton->view->screen_size.w))-1;
1450             float right = 2*((GLfloat)(x+w) / (milton->view->screen_size.w))-1;
1451             float top = -(2*((GLfloat)y     / (milton->view->screen_size.h))-1);
1452             float bottom = -(2*((GLfloat)(y+h) / (milton->view->screen_size.h))-1);
1453 
1454             imm_rect(milton->renderer, left, right, top, bottom, 2.0);
1455         }
1456 
1457         milton->gui->flags &= ~(MiltonGuiFlags_SHOWING_PREVIEW);
1458     }
1459     else if ( milton->current_mode == MiltonMode::EYEDROPPER ) {
1460         v2i point = milton->platform->pointer;
1461 
1462         eyedropper_input(milton->eyedropper, milton->gui,
1463                          milton->view->screen_size.w,
1464                          milton->view->screen_size.h,
1465                          point);
1466         gpu_update_picker(milton->renderer, &milton->gui->picker);
1467         if( input->flags & MiltonInputFlags_CLICKUP ) {
1468             milton_update_brushes(milton);
1469             milton_leave_mode(milton);
1470         }
1471         render_flags |= RenderBackendFlags_GUI_VISIBLE;
1472     }
1473     else if (milton->current_mode == MiltonMode::PEEK_OUT) {
1474         peek_out_tick(milton, input);
1475     }
1476     else if (milton->current_mode == MiltonMode::DRAG_BRUSH_SIZE) {
1477         drag_brush_size_tick(milton, input);
1478     }
1479     else if (milton->current_mode == MiltonMode::TRANSFORM) {
1480         transform_tick(milton, input);
1481     }
1482 
1483     // ---- End stroke
1484     if ( end_stroke ) {
1485         if ( milton->gui->owns_user_input ) {
1486             gui_deactivate(milton->gui);
1487             brush_outline_should_draw = false;
1488         } else {
1489             if ( milton->working_stroke.num_points > 0 ) {
1490                 // We used the selected color to draw something. Push.
1491                 if (  (milton->current_mode == MiltonMode::PEN ||
1492                        milton->current_mode == MiltonMode::PRIMITIVE)
1493                      && gui_mark_color_used(milton->gui) ) {
1494                     // Tell the renderer to update the picker
1495                     gpu_update_picker(milton->renderer, &milton->gui->picker);
1496                 }
1497                 // Copy current stroke.
1498                 Stroke new_stroke = {};
1499                 CanvasState* canvas = milton->canvas;
1500                 copy_stroke(&canvas->arena, milton->view, &milton->working_stroke, &new_stroke);
1501                 {
1502                     new_stroke.layer_id = milton->view->working_layer_id;
1503                     new_stroke.bounding_rect = bounding_box_for_stroke(&new_stroke);
1504 
1505                     new_stroke.id = milton->canvas->stroke_id_count++;
1506 
1507                     Rect bounds = new_stroke.bounding_rect;
1508                     bounds.top_left = canvas_to_raster(milton->view, bounds.top_left);
1509                     bounds.bot_right = canvas_to_raster(milton->view, bounds.bot_right);
1510                 }
1511 
1512                 mlt_assert(new_stroke.num_points > 0);
1513                 mlt_assert(new_stroke.num_points <= STROKE_MAX_POINTS);
1514                 auto* stroke = layer::layer_push_stroke(milton->canvas->working_layer, new_stroke);
1515 
1516                 // Invalidate working stroke render element
1517 
1518                 HistoryElement h = { HistoryElement_STROKE_ADD, milton->canvas->working_layer->id };
1519                 push(&milton->canvas->history, h);
1520 
1521                 reset_working_stroke(milton);
1522 
1523                 clear_stroke_redo(milton);
1524 
1525                 // Make sure we show blurred layers when finishing a stroke.
1526                 milton->render_settings.do_full_redraw = true;
1527             }
1528         }
1529     }
1530     else if ( is_user_drawing(milton) ) {
1531         Rect previous_bounds = milton->working_stroke.bounding_rect;
1532         Rect new_bounds = bounding_box_for_stroke(&milton->working_stroke);
1533 
1534         new_bounds.left = min(new_bounds.left, previous_bounds.left);
1535         new_bounds.top = min(new_bounds.top, previous_bounds.top);
1536         new_bounds.right = max(new_bounds.right, previous_bounds.right);
1537         new_bounds.bottom = max(new_bounds.bottom, previous_bounds.bottom);
1538 
1539         milton->working_stroke.bounding_rect  = new_bounds;
1540     }
1541 
1542     MiltonMode current_mode = milton->current_mode;
1543     if ( input->mode_to_set < MiltonMode::MODE_COUNT ) {
1544         if ( current_mode == input->mode_to_set ) {
1545             // Modes we can toggle
1546             MiltonMode toggleable_modes[] = {
1547                 MiltonMode::EYEDROPPER,
1548                 MiltonMode::PRIMITIVE,
1549             };
1550 
1551             for ( size_t i = 0; i < array_count(toggleable_modes); ++i ) {
1552                 MiltonMode toggle = toggleable_modes[i];
1553                 if ( current_mode == toggle ) {
1554                     if ( milton->n_mode_stack > 0 && milton->mode_stack[milton->n_mode_stack - 1] != toggle ) {
1555                         milton_leave_mode(milton);
1556                     }
1557                     else {
1558                         // This is not supposed to happen but if we get here we won't crash and burn.
1559                         milton_enter_mode(milton, MiltonMode::PEN);
1560                         milton_log("Warning: Unexpected code path: Toggling modes. Toggleable mode was set twice. Switching to pen.\n");
1561                     }
1562                 }
1563             }
1564         }
1565         // Change the current mode if it's different from the mode to set
1566         else {
1567             if ( mode_is_for_drawing(input->mode_to_set) ) {
1568                 milton_update_brushes(milton);
1569 
1570                 // There is no way of leaving a drawing mode to some previous
1571                 // mode. Therefore, When entering a drawing mode we clear the
1572                 // mode stack.
1573                 while ( milton->n_mode_stack ) {
1574                     milton_leave_mode(milton);
1575                 }
1576             }
1577             milton_enter_mode(milton, input->mode_to_set);
1578         }
1579     }
1580 
1581     // Disable hover if panning.
1582     if ( input->flags & MiltonInputFlags_PANNING ) {
1583         brush_outline_should_draw = false;
1584     }
1585 
1586     if ( !(milton->gui->flags & MiltonGuiFlags_SHOWING_PREVIEW) ) {
1587         float radius = -1;
1588         if (brush_outline_should_draw && current_mode_is_for_drawing(milton)) {
1589             radius = (float)milton_get_brush_radius(milton);
1590         }
1591 
1592         v2i brush_point = milton->platform->pointer;
1593         if ( milton->current_mode == MiltonMode::DRAG_BRUSH_SIZE ) {
1594             brush_point = milton->drag_brush->start_point;
1595         }
1596 
1597         gpu_update_brush_outline(milton->renderer,
1598                                 brush_point.x, brush_point.y,
1599                                 radius);
1600     }
1601 
1602     PROFILE_GRAPH_END(update);
1603 
1604     if ( !(milton->flags & MiltonStateFlags_RUNNING) ) {
1605         // Someone tried to kill milton from outside the update. Make sure we save.
1606         should_save = true;
1607         // Don't want to leave the system with the cursor hidden.
1608         platform_cursor_show();
1609     }
1610 
1611     if ( should_save ) {
1612         if ( !(milton->flags & MiltonStateFlags_RUNNING) ) {
1613             // Always save synchronously when exiting.
1614             milton_save(milton);
1615         } else {
1616 #if MILTON_SAVE_ASYNC
1617             trigger_async_save(milton);
1618 #else
1619             milton_save(milton);
1620 #endif
1621         }
1622         // We're about to close and the last save failed and the drawing changed.
1623         if (    !(milton->flags & MiltonStateFlags_RUNNING)
1624              && (milton->flags & MiltonStateFlags_LAST_SAVE_FAILED)
1625              && (milton->flags & MiltonStateFlags_MOVE_FILE_FAILED)
1626              && milton->persist->last_save_stroke_count != layer::count_strokes(milton->canvas->root_layer) ) {
1627             // TODO: Stop using MoveFileEx?
1628             //  Why does MoveFileEx fail? Ask someone who knows this stuff.
1629             // Wait a moment and try again. If this fails, prompt to save somewhere else.
1630             SDL_Delay(3000);
1631             milton_save(milton);
1632 
1633             if (    (milton->flags & MiltonStateFlags_LAST_SAVE_FAILED)
1634                  && (milton->flags & MiltonStateFlags_MOVE_FILE_FAILED) ) {
1635                 char msg[1024];
1636                 WallTime lst = milton->persist->last_save_time;
1637                 snprintf(msg, 1024, "Milton failed to save this canvas. The last successful save was at %.2d:%.2d:%.2d. Try saving to another file?",
1638                          lst.hours, lst.minutes, lst.seconds);
1639                 b32 another = platform_dialog_yesno(msg, "Try another file?");
1640                 if ( another ) {
1641                     // NOTE(possible refactor): There is similar code. Guipp.cpp save_milton_canvas
1642                     PATH_CHAR* name = platform_save_dialog(FileKind_MILTON_CANVAS);
1643                     if ( name ) {
1644                         milton_log("Saving to %s\n", name);
1645                         milton_set_canvas_file(milton, name);
1646                         milton_save(milton);
1647                         if ( (milton->flags & MiltonStateFlags_LAST_SAVE_FAILED) ) {
1648                             platform_dialog("Still can't save. Please contact us for help. miltonpaint.com", "Info");
1649                         } else {
1650                             platform_dialog("Success.", "Info");
1651                         }
1652                         b32 del = platform_delete_file_at_config(TO_PATH_STR("MiltonPersist.mlt"), DeleteErrorTolerance_OK_NOT_EXIST);
1653                         if ( del == false ) {
1654                             platform_dialog("Could not delete default canvas."
1655                                             " Contents will be still there when you create a new canvas.", "Info");
1656                         }
1657                     }
1658                 }
1659             }
1660         }
1661 
1662 
1663         // About to quit.
1664         if ( !(milton->flags & MiltonStateFlags_RUNNING) ) {
1665             milton_kill_save_thread(milton);
1666             // Release resources
1667             milton_reset_canvas(milton);
1668             gpu_release_data(milton->renderer);
1669 
1670             debug_memory_dump_allocations();
1671         }
1672     }
1673 
1674 #if MILTON_SAVE_ASYNC
1675     SDL_LockMutex(milton->save_mutex);
1676     SDL_CondSignal(milton->save_cond);
1677     SDL_UnlockMutex(milton->save_mutex);
1678 #endif
1679 
1680     // Update render resources after loading
1681     if (milton->flags & MiltonStateFlags_JUST_SAVED) {
1682         milton_set_background_color(milton, milton->view->background_color);
1683         gpu_update_picker(milton->renderer, &milton->gui->picker);
1684         milton->flags &= ~MiltonStateFlags_JUST_SAVED;
1685     }
1686 
1687     i32 view_x = 0;
1688     i32 view_y = 0;
1689     i32 view_width = 0;
1690     i32 view_height = 0;
1691 
1692     gpu_reset_render_flags(milton->renderer, render_flags);
1693 
1694     ClipFlags clip_flags = ClipFlags_JUST_CLIP;
1695 
1696 #if REDRAW_EVERY_FRAME
1697     milton->render_settings.do_full_redraw = true;
1698 #endif
1699 
1700     b32 has_working_stroke = milton->working_stroke.num_points > 0;
1701 
1702     if (has_working_stroke) {
1703         b32 has_blur = false;
1704 
1705         Layer* layer = milton->canvas->root_layer;
1706         while (layer) {
1707             if (layer->flags & LayerFlags_VISIBLE) {
1708                 LayerEffect* e = layer->effects;
1709                 while (e) {
1710                     if (e->enabled && e->type == LayerEffectType_BLUR) {
1711                         has_blur = true;
1712                         break;
1713                     }
1714                     e = e->next;
1715                 }
1716             }
1717             if (has_blur) { break; }
1718             layer = layer->next;
1719         }
1720 
1721         if (has_blur) {
1722             milton->render_settings.do_full_redraw = true;
1723         }
1724     }
1725 
1726     static u64 scale_of_last_full_redraw = 0;
1727     static f32 angle_of_last_full_redraw = 0.0f;
1728 
1729     if (scale_of_last_full_redraw != milton_render_scale(milton) ||
1730         angle_of_last_full_redraw != milton->view->angle ) {
1731         // We want to draw everything when we're scaling up and down.
1732         milton->render_settings.do_full_redraw = true;
1733     }
1734 
1735     // Note: We flip the rectangles. GL is bottom-left by default.
1736     if ( milton->render_settings.do_full_redraw ) {
1737         view_width = milton->view->screen_size.w;
1738         view_height = milton->view->screen_size.h;
1739         // Only update GPU data if we are redrawing the full screen. This means
1740         // that the size of the screen will be used to determine if each stroke
1741         // should be freed from GPU memory.
1742         clip_flags = ClipFlags_UPDATE_GPU_DATA;
1743         scale_of_last_full_redraw = milton_render_scale(milton);
1744         angle_of_last_full_redraw = milton->view->angle;
1745     }
1746     else if (has_working_stroke) {
1747         Rect bounds  = canvas_to_raster_bounding_rect(milton->view, milton->working_stroke.bounding_rect);
1748 
1749         view_x           = bounds.left;
1750         view_y           = bounds.top;
1751 
1752         view_width  = bounds.right - bounds.left;
1753         view_height = bounds.bottom - bounds.top;
1754     }
1755 
1756     PROFILE_GRAPH_BEGIN(clipping);
1757 
1758     i64 render_scale = milton_render_scale(milton);
1759 
1760     gpu_clip_strokes_and_update(&milton->root_arena, milton->renderer, milton->view, render_scale,
1761                                 milton->canvas->root_layer, &milton->working_stroke,
1762                                 view_x, view_y, view_width, view_height, clip_flags);
1763     PROFILE_GRAPH_END(clipping);
1764 
1765     gpu_render(milton->renderer, view_x, view_y, view_width, view_height);
1766 
1767     ARENA_VALIDATE(&milton->root_arena);
1768 }
1769