ba06625a489b5afe14d0e9f6070e94e29ba44f32
[colorize.git] / gui / timeline.c
1 #include <gtk/gtk.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <dirent.h> 
5 #include <stdio.h> 
6 #include "../src/mark.h"
7 #include "timeline.h"
8 #include "image.h"
9 #include "main.h"
10 #include "palette.h"
11 #include "../src/settings.h"
12 #include "../src/dir_seperator.h"
13 #ifdef WITH_OPENCV
14 #include "../src/opticalflow.h"
15 #endif
16
17 /* functions to load directory */
18
19 typedef struct dir_entry {
20         struct dir_entry        *next;
21         char name[0];
22 } dir_t;
23
24 static int cmpstringp(const void *p1, const void *p2)
25 {
26         dir_t *entry1 = *(dir_t **)p1;
27         dir_t *entry2 = *(dir_t **)p2;
28
29         return strcmp(entry1->name, entry2->name);
30 }
31
32 static dir_t *fetch_dir(const char *path) /* NOTE: if path is not empty, it must end with DIR_SEPERATOR */
33 {
34         int rc;
35         DIR *dir;
36         struct dirent dirent, *result;
37         dir_t *head = NULL, *entry, **entryp = &head;
38         int count = 0;
39         dir_t **list;
40         int i;
41
42         /* read linked list */
43         dir = opendir(path);
44         if (!dir)
45                 return NULL;
46         while (1) {
47                 rc = readdir_r(dir, &dirent, &result);
48                 if (rc < 0)
49                         break;
50                 if (result == NULL)
51                         break;
52                 if (result->d_type != DT_REG && result->d_type != DT_LNK)
53                         continue;
54                 entry = calloc(1, sizeof(dir_t) + strlen(path) + strlen(result->d_name) + 1);
55                 if (!entry) {
56                         fprintf(stderr, "no memory");
57                         return NULL;
58                 }
59                 *entryp = entry;
60                 entryp = &entry->next;
61                 strcpy(entry->name, path);
62                 strcat(entry->name, result->d_name);
63                 count++;
64         }
65         closedir(dir);
66
67         /* sort linked list */
68         list = calloc(count, sizeof(*list));
69         if (!list) {
70                 fprintf(stderr, "no memory");
71                 return NULL;
72         }
73         count = 0;
74         for (entry = head; entry; entry = entry->next) {
75                 list[count] = entry;
76                 count++;
77         }
78         qsort(list, count, sizeof(*list), cmpstringp);
79         entryp = &head;
80         for (i = 0; i < count; i++) {
81                 /* relink */
82                 entry = list[i];
83                 entry->next = NULL;
84                 *entryp = entry;
85                 entryp = &entry->next;
86         }
87         free(list);
88
89         return head;
90 }
91
92 static void free_dir(dir_t *dir)
93 {
94         dir_t *entry, *next;
95
96         entry = dir;
97         while (entry) {
98                 next = entry->next;
99                 free(entry);
100                 entry = next;
101         }
102 }
103
104
105 struct frame_list *frame_list = NULL;
106
107 /* currently loaded image */
108 GdkPixbuf *timeline_pixbuf = NULL;
109 int timeline_selected = 0;
110 int timeline_frames = 0;
111 int timeline_scale = 10;
112 int timeline_width, timeline_height = 30;
113
114 static void draw_timeline_frame(int frame)
115 {
116         int i, j, rs;
117         int r, g, b;
118         guchar *data;
119
120         if (!timeline_pixbuf)
121                 return;
122
123         if (frame == timeline_selected) {
124                 r = 150;
125                 g = 150;
126                 b = 150;
127                 if (frame_list && frame_list[frame].marked) {
128                         r = 100;
129                         g = 100;
130                         b = 200;
131                 }
132         } else {
133                 r = 100;
134                 g = 100;
135                 b = 100;
136                 if (frame_list && frame_list[frame].marked) {
137                         r = 0;
138                         g = 0;
139                         b = 100;
140                 }
141         }
142
143         data = gdk_pixbuf_get_pixels(timeline_pixbuf);
144         rs = gdk_pixbuf_get_rowstride(timeline_pixbuf);
145
146         for (i = 0; i < timeline_height; i++) {
147                 data[rs*i + frame*timeline_scale*3 + 0] = 0;
148                 data[rs*i + frame*timeline_scale*3 + 1] = 0;
149                 data[rs*i + frame*timeline_scale*3 + 2] = 0;
150                 for (j = 1; j < timeline_scale; j++) {
151                         data[rs*i + (frame*timeline_scale+j)*3 + 0] = r;
152                         data[rs*i + (frame*timeline_scale+j)*3 + 1] = g;
153                         data[rs*i + (frame*timeline_scale+j)*3 + 2] = b;
154                 }
155         }
156         if (frame_list && frame_list[frame].keyframe) {
157                 r = 255;
158                 g = 0;
159                 b = 0;
160                 for (i = 0; i < timeline_scale/2; i++) {
161                         for (j = i + 1; j < timeline_scale - i; j++) {
162                                 data[rs*i + (frame*timeline_scale+j)*3 + 0] = r;
163                                 data[rs*i + (frame*timeline_scale+j)*3 + 1] = g;
164                                 data[rs*i + (frame*timeline_scale+j)*3 + 2] = b;
165
166                                 data[rs*(timeline_height-1-i) + (frame*timeline_scale+j)*3 + 0] = r;
167                                 data[rs*(timeline_height-1-i) + (frame*timeline_scale+j)*3 + 1] = g;
168                                 data[rs*(timeline_height-1-i) + (frame*timeline_scale+j)*3 + 2] = b;
169                         }
170                 }
171         }
172 }
173
174 /* get path of given file name */
175 static char *get_path(const char *filename)
176 {
177         static char path[256];
178
179         /* copy name */
180         strcpy(path, filename);
181         /* remove until DIR_SEPERATOR */
182         while (path[0]) {
183                 if (path[strlen(path) - 1] == DIR_SEPERATOR)
184                         break;
185                 path[strlen(path) - 1] = '\0';
186         }
187
188         return path;
189 }
190
191 /* load directory and create pixbuf */
192 void create_timeline(const char *filename)
193 {
194         int i;
195         const char *p, *q;
196         char temp[256], suffix[256];
197         FILE *fp;
198
199         if (filename) {
200                 /* get suffix */
201                 p = filename;
202                 q = NULL;
203                 while ((q = strchr(p, '.')))
204                         p = q + 1;
205                 if (p == filename) {
206                         printerror("File '%s' does not have a 'prefix.suffix' format\n", filename);
207                         return;
208                 }
209                 strcpy(suffix, p - 1);
210
211 #ifndef WITH_MAGICK
212                 if ((strlen(filename) < 5 || !!strcmp(suffix, ".ppm"))) {
213                         printerror("File '%s' does not have '.ppm' suffix\n", filename);
214                         return;
215                 }
216 #endif
217         }
218
219         destroy_timeline();
220         timeline_frames = 0;
221
222         if (filename) {
223                 p = filename + strlen(filename) - strlen(suffix);
224                 while (p != filename) {
225                         p--;
226                         if (*p < '0' || *p > '9') {
227                                 p++;
228                                 break;
229                         }
230                 }
231                 if (*p < '0' || *p > '9') {
232 single_file:
233 //                      printf("single file\n");
234                         frame_list = malloc(sizeof(struct frame_list));
235                         memset(frame_list, 0, sizeof(struct frame_list));
236                         strcpy(frame_list[0].filename, filename);
237                         timeline_frames = 1;
238                 } else {
239                         /* create list of files that have same base name, same number of digits and suffix */
240                         int suffix_offset = strlen(filename) - strlen(suffix);
241                         int digits_offset = p - filename;
242                         int digits = filename + strlen(filename) - strlen(suffix) - p;
243                         const char *path = get_path(filename);
244                         dir_t *dir_head = fetch_dir(path), *dir, **dirp;
245                         int count;
246                         if (!dir_head)
247                                 goto single_file;
248                         dir = dir_head;
249                         dirp = &dir_head;
250                         while (dir) {
251                                 /* remove files that do not have equal prefix */
252                                 if (!!strncmp(filename, dir->name, p - filename)) {
253 free_file:
254                                         /* remove entry */
255                                         *dirp = dir->next;
256                                         free(dir);
257                                         dir = *dirp;
258                                         continue;
259                                 }
260                                 /* remove files that have no digits where expected */
261                                 for (i = 0; i < digits; i++) {
262                                         if (dir->name[digits_offset+i] < '0' || dir->name[digits_offset+i] > '9')
263                                                 break;
264                                 }
265                                 if (i < digits) {
266                                         goto free_file;
267                                 }
268                                 /* remove files that have different suffix */
269                                 if (!!strcmp(suffix, dir->name + suffix_offset)) {
270                                         goto free_file;
271                                 }
272                                 dirp = &(dir->next);
273                                 dir = dir->next;
274                         }
275                         count = 0;
276                         dir = dir_head;
277                         while (dir) {
278                                 count++;
279                                 dir = dir->next;
280                         }
281                         if (count < 2) {
282                                 free_dir(dir_head);
283                                 goto single_file;
284                         }
285                         frame_list = malloc(sizeof(struct frame_list) * count);
286                         memset(frame_list, 0, sizeof(struct frame_list) * count);
287                         dir = dir_head;
288                         for (i = 0; i < count; i++) {
289                                 strcpy(frame_list[i].filename, dir->name);
290                                 dir = dir->next;
291                         }
292                         timeline_frames = count;
293                         free_dir(dir_head);
294                 }
295                 /* get marked frames */
296                 for (i = 0; i < timeline_frames; i++) {
297                         sprintf(temp, "%s_marked", frame_list[i].filename);
298                         frame_list[i].marked = 0;
299                         fp = fopen(temp, "r");
300                         if (!fp)
301                                 continue;
302                         fclose(fp);
303                         frame_list[i].marked = 1;
304                 }
305                 /* get keyframes */
306                 load_sequence();
307         }
308
309         if (timeline_frames == 0)
310                 timeline_frames = 1;
311
312         if (timeline_frames > 1)
313                 gtk_widget_show(timeline_scroll);
314
315         timeline_width = timeline_frames * timeline_scale;
316
317         timeline_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, timeline_width, timeline_height);
318
319         for (i = 0; i < timeline_frames; i++)
320                 draw_timeline_frame(i);
321
322         gtk_widget_set_size_request(timeline_drawing_area, timeline_width, timeline_height);
323
324 #ifdef WITH_OPENCV
325         /* disable optical flow and set default */
326         flow_enable = 0;
327         flow_default();
328 #endif
329
330         if (frame_list) {
331                 int i;
332                 load_settings(frame_list[0].filename);
333                 load_palette(frame_list[0].filename);
334                 for (i = 0; i < 255; i++)
335                         update_color(i);
336         }
337 }
338
339 /* free image */
340 void destroy_timeline(void)
341 {
342         gtk_widget_hide(timeline_scroll);
343
344         if (!timeline_pixbuf)
345                 return;
346
347         g_object_unref(timeline_pixbuf);
348         timeline_pixbuf = NULL;
349
350         free(frame_list);
351         frame_list = NULL;
352
353         timeline_selected = 0;
354 }
355
356 /* draw (area) of pixbuf */
357 void draw_timeline(int x, int y, int w, int h)
358 {
359         GdkDrawable *draw = gtk_widget_get_window(timeline_drawing_area);
360
361         if (!timeline_pixbuf)
362                 return;
363
364         if (x < 0)
365                 x = 0;
366         if (x >= timeline_width)
367                 return;
368         if (y < 0)
369                 y = 0;
370         if (y >= timeline_height)
371                 return;
372         if (x + w > timeline_width)
373                 w = timeline_width - x;
374         if (y + h > timeline_height)
375                 h = timeline_height - y;
376
377         //printf("%d %d %d %d\n", x, y, w, h);
378
379         if (!timeline_pixbuf)
380                 return;
381
382         gdk_draw_pixbuf(draw, NULL, timeline_pixbuf, x, y, x, y, w, h, GDK_RGB_DITHER_NONE, 0, 0);
383 }
384
385 int timeline_select_and_save(int old_frame, int new_frame)
386 {
387
388         /* no frame loaded, no list */
389         if (!frame_list|| !img_pixbuf)
390                 return 0;
391
392         /* save all stuff */
393         if (anything_modified) {
394                 if (img_mark_buffer) {
395                         if (save_marked(img_mark_buffer, img_width, img_height, frame_list[old_frame].filename) > 0)
396                                 frame_list[old_frame].marked = 1;
397                         else
398                                 frame_list[old_frame].marked = 0;
399                 }
400
401                 if (frame_list) {
402                         save_palette(frame_list[0].filename);
403                         if (timeline_frames > 1)
404                                 save_settings(frame_list[0].filename);
405                         save_sequence();
406                 }
407         }
408
409         timeline_selected = new_frame;
410
411         create_image(frame_list[new_frame].filename, 0);
412 #ifdef WITH_OPENCV
413         if (flowview && flow_enable && img_grey_buffer)
414                 create_flow_view(
415                         (new_frame > 0) ? frame_list[new_frame - 1].filename : NULL,
416                         (new_frame < timeline_frames - 1) ? frame_list[new_frame + 1].filename : NULL,
417                         img_grey_buffer,
418                         img_width, img_height,
419                         flow_window,
420                         flow_view_vector,
421                         flow_view_uv);
422 #endif
423
424         if (timeline_frames > 1) {
425                 GdkDrawable *draw = gtk_widget_get_window(timeline_drawing_area);
426                 draw_timeline_frame(old_frame);
427                 draw_timeline_frame(new_frame);
428                 gdk_draw_pixbuf(draw, NULL, timeline_pixbuf, 0, 0, 0, 0, timeline_width, timeline_height, GDK_RGB_DITHER_NONE, 0, 0);
429         }
430
431         /* adjust position of timeline scroll */
432         int w, x;
433         GtkAdjustment *adjustment;
434         double pos;
435         x = new_frame * timeline_scale;
436         w = timeline_scroll->allocation.width - timeline_scale - 4;
437         adjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(timeline_scroll));
438         pos = gtk_adjustment_get_value(adjustment);
439         if (x < pos) {
440                 gtk_adjustment_set_value(adjustment, x);
441                 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(timeline_scroll), adjustment);
442         }
443         if (x > pos + w) {
444                 gtk_adjustment_set_value(adjustment, pos + x + - (pos + w));
445                 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(timeline_scroll), adjustment);
446         }
447
448         return 1;
449 }
450
451 /* while pointing at timeline, show current frame that we point to */
452 int timeline_point(int x, int y)
453 {
454         int frame = x / timeline_scale;
455
456         if (x < 0 || y < 0 || frame < 0 || frame >= timeline_frames) {
457                 timeline_show_name(timeline_selected);
458                 return 0;
459         }
460         
461         timeline_show_name(frame);
462         return 0;
463 }
464
465 void timeline_show_name(int frame)
466 {
467         char text[256];
468         const char *p;
469
470         if (!frame_list)
471                 return;
472
473         if (frame != timeline_selected) {
474                 sprintf(text, "Click to select frame %d", frame);
475         } else {
476                 p = frame_list[frame].filename;
477                 while (strchr(p, DIR_SEPERATOR))
478                         p = strchr(p, DIR_SEPERATOR) + 1;
479                 if (timeline_frames > 1)
480                         sprintf(text, "Frame %d: %s", frame, p);
481                 else
482                         strcpy(text, p);
483                 if (img_scale_x != 16)
484                         sprintf(strchr(text, '\0'), " (%d%%)", img_scale_x*100/16);
485         }
486         gtk_label_set_text(img_label, text);
487 }
488
489 int timeline_clicked(int x, int y)
490 {
491         int old_frame = timeline_selected;
492         int new_frame = x / timeline_scale;
493
494         if (new_frame < 0)
495                 return 0;
496         if (new_frame >= timeline_frames)
497                 return 0;
498
499         if (old_frame == new_frame)
500                 return 0;
501
502         return timeline_select_and_save(old_frame, new_frame);
503
504 }
505
506 /* get filename of first grey image directory */
507 static char *get_sequence_name(const char *filename)
508 {
509         static char name[256];
510
511         /* copy name */
512         strcpy(name, get_path(filename));
513         /* add palette name */
514         strcat(name, "sequence");
515
516         return name;
517 }
518
519 int save_sequence(void)
520 {
521         char *name, *p;
522         int i;
523         FILE *fp;
524
525         if (!frame_list)
526                 return 0;
527
528         if (timeline_frames <= 1)
529                 return 0;
530
531         name = get_sequence_name(frame_list[0].filename);
532 //      printf("save sequence '%s'\n", name);
533         fp = fopen(name, "w");
534         if (!fp) {
535                 printf("failed to save sequence '%s'\n", name);
536                 return -1;
537         }
538         for (i = 0; i < timeline_frames; i++) {
539                 p = frame_list[i].filename;
540                 while (p && strchr(p, DIR_SEPERATOR))
541                         p = strchr(p, DIR_SEPERATOR) + 1;
542                 fprintf(fp, "\"%s\"%s\n", p, (frame_list[i].keyframe) ? " keyframe" : "");
543         }
544         fclose(fp);
545
546         return 1;
547 }
548
549 int load_sequence(void)
550 {
551         char *name;
552         char buffer[256];
553         FILE *fp;
554         int i;
555         char *rc, *p;
556
557         if (!frame_list)
558                 return 0;
559
560         if (timeline_frames <= 1)
561                 return 0;
562
563         name = get_sequence_name(frame_list[0].filename);
564 //      printf("load sequence '%s'\n", name);
565         fp = fopen(name, "r");
566         if (!fp) {
567                 printf("no sequence file '%s' created yet\n", name);
568                 return -1;
569         }
570         for (i = 0; i < timeline_frames; i++) {
571                 rc = fgets(buffer, sizeof(buffer), fp);
572                 if (!rc)
573                         break;
574                 p = strchr(buffer + 1, '\"');
575                 if (!p)
576                         break;
577                 p++;
578                 if (strstr(p, "keyframe"))
579                         frame_list[i].keyframe = 1;
580                 else
581                         frame_list[i].keyframe = 0;
582         }
583         fclose(fp);
584
585         return 0;
586 }
587
588 void toggle_keyframe(void)
589 {
590         if (!frame_list|| !img_pixbuf)
591                 return;
592
593         anything_modified = 1;
594         
595         GdkDrawable *draw = gtk_widget_get_window(timeline_drawing_area);
596
597         frame_list[timeline_selected].keyframe = 1 - frame_list[timeline_selected].keyframe;
598         draw_timeline_frame(timeline_selected);
599         gdk_draw_pixbuf(draw, NULL, timeline_pixbuf, 0, 0, 0, 0, timeline_width, timeline_height, GDK_RGB_DITHER_NONE, 0, 0);
600 }
601