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