1590279202b6fb6ae73839db44c949d97bd70e51
[colorize.git] / gui / image.c
1 #include <gtk/gtk.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <math.h>
6 #include "main.h"
7 #include "../src/ppm.h"
8 #include "../src/mark.h"
9 #include "../src/yuv.h"
10 #include "image.h"
11 #include "palette.h"
12 #include "timeline.h"
13 #include "../src/dir_seperator.h"
14
15 /* currently loaded image */
16 #define UNDO_MAX 20
17 GdkPixbuf *img_pixbuf = NULL;
18 int img_width, img_height;
19 int copy_width, copy_height;
20 char img_name[256];
21 unsigned char *img_grey_buffer = NULL;
22 unsigned char *img_mark_buffer = NULL, *img_mark_buffer_undo[UNDO_MAX], *img_mark_buffer_copy;
23 int button_down_x_undo[UNDO_MAX], button_down_y_undo[UNDO_MAX];
24 int undo_current, undo_num;
25 int img_scale_x = 16, img_scale_y = 16;
26 int anything_modified;
27 extern int button_down_x, button_down_y;
28
29 /* load image and create pixbuf */
30 void create_image(const char *filename)
31 {
32         int rc;
33         static char imgfile[256];
34
35         destroy_image();
36
37         anything_modified = 0;
38
39         strcpy(img_name, filename);
40         if (rendered) {
41                 const char *p, *q;
42                 p = filename;
43                 while((q = strchr(p, DIR_SEPERATOR)))
44                 p = q + 1;
45                 strcpy(imgfile, filename);
46                 imgfile[p - filename] = '\0';
47                 strcat(imgfile, "colorized_");
48                 strcat(imgfile, p);
49         } else
50                 strcpy(imgfile, filename);
51         rc = load_img(-1, &img_grey_buffer, &img_width, &img_height, imgfile, 0);
52         if (rc) {
53                 img_grey_buffer = NULL;
54                 if (!rendered)
55                         printerror("Failed to load grey image '%s'", imgfile);
56                 else {
57                         img_width = 16;
58                         img_height = 16;
59                         goto no_mark;
60                 }
61                 return;
62         }
63         img_mark_buffer = malloc(img_width*img_height);
64         if (!img_mark_buffer) {
65                 free(img_grey_buffer);
66                 img_grey_buffer = NULL;
67                 return;
68         }
69         memset(img_mark_buffer, 0, img_width*img_height);
70         undo_current = undo_num = 0;
71         memset(img_mark_buffer_undo, 0, sizeof(img_mark_buffer_undo));
72         if (frame_list) {
73                 if (load_marked(img_mark_buffer, img_width, img_height, frame_list[timeline_selected].filename) == 0)
74                         frame_list[timeline_selected].marked = 1;
75                 else
76                         frame_list[timeline_selected].marked = 0;
77         }
78
79 no_mark:
80         /* create pixbuf */
81         create_or_reset_pixbuf(1);
82 }
83
84 void create_or_reset_pixbuf(int resize)
85 {
86         int w, h, dw = 0, dh = 0;
87         GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(main_window));
88         GdkRectangle max;
89
90         /* calculate the maximum windows size, so that the border will not exceed the screen size */
91         gdk_window_get_frame_extents(gtk_widget_get_window(main_window), &max);
92         max.width -= main_window->allocation.width;
93         max.height -= main_window->allocation.height;
94         if (max.width < 0)
95                 max.width = 0;
96         if (max.height < 0)
97                 max.height = 0;
98         max.width = gdk_screen_get_width(screen)-max.width;
99         max.height = gdk_screen_get_height(screen)-max.height;
100
101         if (resize && screen) {
102 try_smaller:
103                 w = img_scroll->allocation.width-20;
104                 h = img_scroll->allocation.height-20;
105                 if (w < img_width*img_scale_x/16) {
106                         w = main_window->allocation.width - w + img_width*img_scale_x/16;
107                         if (w > max.width) {
108                                 if (img_scale_x > 4) {
109                                         int aspect = img_scale_y / img_scale_x;
110                                         img_scale_x -= 4;
111                                         img_scale_y = img_scale_x * aspect;
112                                         goto try_smaller;
113                                 }
114                                 w = max.width;
115                         }
116                         dw = w - main_window->allocation.width;
117                 }
118                 if (h < img_height*img_scale_y/16) {
119                         h = main_window->allocation.height - h + img_height*img_scale_y/16;
120                         if (h > max.height) {
121                                 if (img_scale_x > 4) {
122                                         int aspect = img_scale_y / img_scale_x;
123                                         img_scale_x -= 4;
124                                         img_scale_y = img_scale_x * aspect;
125                                         goto try_smaller;
126                                 }
127                                 h = max.height;
128                         }
129                         dh = h - main_window->allocation.height;
130                 }
131 //              printf("%d %d\n", gdk_screen_get_width(screen), gdk_screen_get_height(screen));
132                 if (dw || dh)
133                         gtk_window_resize(GTK_WINDOW(main_window), main_window->allocation.width+dw, main_window->allocation.height+dh);
134         }
135
136         if (img_pixbuf) {
137                 g_object_unref(img_pixbuf);
138                 img_pixbuf = NULL;
139         }
140         img_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, img_width*img_scale_x/16, img_height*img_scale_y/16);
141
142         gtk_drawing_area_size(GTK_DRAWING_AREA(img_drawing_area), img_width*img_scale_x/16, img_height*img_scale_y/16);
143
144         /* label */
145         timeline_show_name(timeline_selected);
146 }
147
148 /* free image */
149 void destroy_image(void)
150 {
151         int i;
152
153         if (!img_pixbuf)
154                 return;
155
156         g_object_unref(img_pixbuf);
157         img_pixbuf = NULL;
158
159         free(img_grey_buffer);
160         img_grey_buffer = NULL;
161         free(img_mark_buffer);
162         img_mark_buffer = NULL;
163         for (i = 0; i < UNDO_MAX; i++) {
164                 if (img_mark_buffer_undo[i]) {
165                         free(img_mark_buffer_undo[i]);
166                         img_mark_buffer_undo[i] = NULL;
167                 }
168         }
169 }
170
171 /* draw (area) of pixbuf */
172 void draw_image(int x, int y, int w, int h)
173 {
174         GdkDrawable *draw = gtk_widget_get_window(img_drawing_area);
175         int window_width, window_height, x_offset, y_offset;
176         gdk_drawable_get_size (draw, &window_width, &window_height);
177         double _r, _g, _b, _y, _u, _v, u_palette, v_palette;
178         int _c, preview_asis = 0;
179
180         if (w < 0)
181                 w = window_width;
182         if (h < 0)
183                 h = window_width;
184
185         guchar *data;
186         unsigned char compose[w*3];
187         int i, j, rs;
188         unsigned char c;
189
190         if (!img_pixbuf)
191                 return;
192         data = gdk_pixbuf_get_pixels(img_pixbuf);
193         rs = gdk_pixbuf_get_rowstride(img_pixbuf);
194
195         /* this should not happen */
196         if (window_width < img_width*img_scale_x/16)
197                 window_width = img_width*img_scale_x/16;
198         if (window_height < img_height*img_scale_y/16)
199                 window_height = img_height*img_scale_y/16;
200
201         /* we need to calculate an offset, since the drawing area is now larger */
202         x_offset = (window_width - img_width*img_scale_x/16) / 2;
203         y_offset = (window_height - img_height*img_scale_y/16) / 2;
204
205         /* clip to window size */
206         if (x < 0)
207                 x = 0;
208         if (x >= window_width)
209                 x = window_width - 1;
210         if (y < 0)
211                 y = 0;
212         if (y >= window_height)
213                 y = window_height - 1;
214         if (x + w > window_width)
215                 w = window_width - x;
216         if (w <= 0)
217                 return;
218         if (y + h > window_height)
219                 h = window_height - y;
220         if (h <= 0)
221                 return;
222
223         /* draw top border */
224         if (y < y_offset) {
225                 int temp = (h+y >= y_offset) ? y_offset-y : h; /* height */
226                 gdk_draw_rectangle(draw, img_drawing_area->style->bg_gc[GTK_WIDGET_STATE(img_drawing_area)], TRUE, x, y, w, (h+y >= y_offset) ? y_offset-y : h);
227                 h -= temp;
228                 y += temp;
229                 if (h <= 0)
230                         return;
231         }
232
233         /* draw bottom border */
234         if (y+h >= y_offset+img_height*img_scale_y/16) {
235                 int temp = (y < y_offset+img_height*img_scale_y/16) ? y_offset+img_height*img_scale_y/16 : y; /* y start */
236                 gdk_draw_rectangle(draw, img_drawing_area->style->bg_gc[GTK_WIDGET_STATE(img_drawing_area)], TRUE, x, temp, w, h - (temp-y));
237                 h = temp-y;
238                 if (h <= 0)
239                         return;
240         }
241
242         /* draw left border */
243         if (x < x_offset) {
244                 int temp = (w+x >= x_offset) ? x_offset-x : w; /* width */
245                 gdk_draw_rectangle(draw, img_drawing_area->style->bg_gc[GTK_WIDGET_STATE(img_drawing_area)], TRUE, x, y, (w+x >= x_offset) ? x_offset-x : w, h);
246                 w -= temp;
247                 x += temp;
248                 if (w <= 0)
249                         return;
250         }
251
252         /* draw right border */
253         if (x+w >= x_offset+img_width*img_scale_x/16) {
254                 int temp = (x < x_offset+img_width*img_scale_x/16) ? x_offset+img_width*img_scale_x/16 : x; /* x start **/
255                 gdk_draw_rectangle(draw, img_drawing_area->style->bg_gc[GTK_WIDGET_STATE(img_drawing_area)], TRUE, temp, y, w - (temp-x), h);
256                 w = temp-x;
257                 if (w <= 0)
258                         return;
259         }
260
261         x -= x_offset;
262         y -= y_offset;
263
264         if (img_grey_buffer) {
265                 if (preview) {
266                         rgb2yuv_pixle(mark_palette[mark_selected].r / 255.0F, mark_palette[mark_selected].g / 255.0F, mark_palette[mark_selected].b / 255.0F, &_y, &u_palette, &v_palette);
267                         /* check for white color (no change) */
268                         if (mark_palette[mark_selected].r == 255 && mark_palette[mark_selected].g == 255 && mark_palette[mark_selected].r == 255)
269                                 preview_asis = 1;
270                 }
271                 /* compose image (segment) line by line */
272                 for (i = 0; i < h; i++) {
273                         for (j = 0; j < w; j++) {
274                                 if (preview && !rendered && !flowview) {
275                                         /* apply selected color from palette to all pixles */
276                                         _r = img_grey_buffer[((j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y))*3] / 255.0F;
277                                         _g = img_grey_buffer[((j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y))*3+1] / 255.0F;
278                                         _b = img_grey_buffer[((j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y))*3+2] / 255.0F;
279                                         rgb2yuv_pixle(_r, _g, _b, &_y, &_u, &_v);
280                                         _y = (_y - 0.5) * mark_palette[mark_selected].contrast + 0.5;
281                                         _y += mark_palette[mark_selected].bright;
282                                         if (_y < 0)
283                                                 _y = 0;
284                                         if (_y > 1)
285                                                 _y = 1;
286                                         if (preview_asis)
287                                                 yuv2rgb_pixle(_y, _u, _v, &_r, &_g, &_b);
288                                         else
289                                                 yuv2rgb_pixle(_y, u_palette, v_palette, &_r, &_g, &_b);
290                                         _c = _r * 255.0F;
291                                         if (_c < 0)
292                                                 _c = 0;
293                                         else if (_c > 255)
294                                                 _c = 255;
295                                         compose[j*3] = _c;
296                                                 _c = _g * 255.0F;
297                                         if (_c < 0)
298                                                 _c = 0;
299                                         else if (_c > 255)
300                                                 _c = 255;
301                                         compose[j*3+1] = _c;
302                                                 _c = _b * 255.0F;
303                                         if (_c < 0)
304                                                 _c = 0;
305                                         else if (_c > 255)
306                                                 _c = 255;
307                                         compose[j*3+2] = _c;
308
309                                 } else {
310                                         compose[j*3] = img_grey_buffer[((j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y))*3];
311                                         compose[j*3+1] = img_grey_buffer[((j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y))*3+1];
312                                         compose[j*3+2] = img_grey_buffer[((j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y))*3+2];
313                                 }
314                                 c = img_mark_buffer[(j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y)];
315                                 if (c > 0 && !flowview && !(rendered && preview)) {
316                                         if (highlight) {
317                                                 if (c-1 == mark_selected) {
318                                                         compose[j*3] = 255;
319                                                         compose[j*3+1] = 0;
320                                                         compose[j*3+2] = 0;
321                                                 } else {
322                                                         compose[j*3] = 0;
323                                                         compose[j*3+1] = 0;
324                                                         compose[j*3+2] = 128;
325                                                 }
326                                         } else {
327                                                 compose[j*3] = mark_palette[c-1].r;
328                                                 compose[j*3+1] = mark_palette[c-1].g;
329                                                 compose[j*3+2] = mark_palette[c-1].b;
330                                         }
331                                 }
332                         }
333                         memcpy(data + rs*(i+y) + x*3, compose, w*3);
334                 }
335         } else {
336                 for (i = 0; i < h; i++)
337                         memset(data + rs*(i+y) + x*3, 0, w*3);
338         }
339
340         gdk_draw_pixbuf(draw, NULL, img_pixbuf, x, y, x+x_offset, y+y_offset, w, h, GDK_RGB_DITHER_NONE, 0, 0);
341
342 }
343
344 /* plot brush on mark buffer */
345 void paint_brush(int x, int y, int size, int paint)
346 {
347         GdkDrawable *draw = gtk_widget_get_window(img_drawing_area);
348         int window_width, window_height;
349         gdk_drawable_get_size (draw, &window_width, &window_height);
350
351         /* we need to calculate an offset, since the drawing area is now larger */
352         x -= (window_width - img_width*img_scale_x/16) / 2;
353         y -= (window_height - img_height*img_scale_y/16) / 2;
354
355         x = x*16/img_scale_x;
356         y = y*16/img_scale_y;
357
358         if (!img_mark_buffer || flowview)
359                 return;
360
361         int x1 = x - size + 1;
362         int x2 = x + size - 1;
363         int y1 = y - size + 1;
364         int y2 = y + size - 1;
365         int i, j;
366
367         if (x1 >= img_width)
368                 return;
369         if (x1 < 0)
370                 x1 = 0;
371         if (x2 >= img_width)
372                 x2 = img_width - 1;
373         if (x2 < 0)
374                 return;
375         if (y1 >= img_height)
376                 return;
377         if (y1 < 0)
378                 y1 = 0;
379         if (y2 >= img_height)
380                 y2 = img_height - 1;
381         if (y2 < 0)
382                 return;
383
384         /* only paint color, if paint is set */
385         if (paint)
386                 paint = mark_selected + 1;
387
388         for (i = y1; i <= y2; i++) {
389                 for (j = x1; j <= x2; j++) {
390                         img_mark_buffer[j+img_width*i] = paint;
391                 }
392         }
393 }
394
395 void erase_mark(int index)
396 {
397         int i;
398
399         if (!img_mark_buffer)
400                 return;
401
402         /* no index, erase all */
403         if (!index) {
404                 memset(img_mark_buffer, 0, img_width * img_height);
405                 return;
406         }
407
408         /* only erase indexed color */
409         for (i = 0; i < img_width * img_height; i++) {
410                 if (img_mark_buffer[i] == index)
411                         img_mark_buffer[i] = 0;
412         }
413 }
414
415 void move_mark(int x, int y)
416 {
417         int i, j;
418         unsigned char *src;
419
420         if (!img_mark_buffer || undo_current == 0)
421                 return;
422
423         src = img_mark_buffer_undo[undo_current-1];
424         if (!src)
425                 return;
426
427         if (y < 0 || (y == 0 && x < 0)) {
428                 /* move up in memory */
429                 for (j = 0; j < img_height; j++) {
430                         for (i = 0; i < img_width; i++) {
431                                 if (i < x || i >= img_width+x || j < y || j >= img_height+y)
432                                         img_mark_buffer[j*img_width+i] = 0;
433                                 else
434                                         img_mark_buffer[j*img_width+i] = src[(j-y)*img_width+(i-x)];
435                         }
436                 }
437         } else {
438                 /* move down in memory */
439                 for (j = img_height-1; j >= 0; j--) {
440                         for (i = img_width-1; i >= 0; i--) {
441                                 if (i < x || i >= img_width+x || j < y || j >= img_height+y)
442                                         img_mark_buffer[j*img_width+i] = 0;
443                                 else
444                                         img_mark_buffer[j*img_width+i] = src[(j-y)*img_width+(i-x)];
445                         }
446                 }
447         }
448 }
449
450 void copy_mark_to_undo(void)
451 {
452         anything_modified = 1;
453
454         if (!img_mark_buffer)
455                 return;
456
457         if (undo_current < UNDO_MAX) {
458                 /* allocate new undobuffer and append */
459                 if (!img_mark_buffer_undo[undo_current])
460                         img_mark_buffer_undo[undo_current] = malloc(img_width*img_height);
461                 if (!img_mark_buffer_undo[undo_current])
462                         return;
463                 undo_current++;
464         } else {
465                 /* shift and use oldest undobuffer */
466                 unsigned char *temp = img_mark_buffer_undo[0];
467                 int i;
468                 for (i = 0; i < UNDO_MAX - 1; i++)
469                         img_mark_buffer_undo[i] = img_mark_buffer_undo[i+1];
470                 img_mark_buffer_undo[UNDO_MAX - 1] = temp;
471         }
472         undo_num = undo_current;
473 //printf("storing undo #%d\n", undo_current-1);
474         memcpy(img_mark_buffer_undo[undo_current-1], img_mark_buffer, img_width*img_height);
475         button_down_x_undo[undo_current-1] = button_down_x;
476         button_down_y_undo[undo_current-1] = button_down_y;
477 }
478
479 void copy_undo_to_mark(int redo)
480 {
481         if (!img_mark_buffer)
482                 return;
483
484 //printf("num is %d\n", undo_num);
485         if (!redo) {
486                 /* nothing to undo */
487                 if (undo_current == 0)
488                         return;
489
490                 /* if we are at the end of the history, we need to add the current image to undo history */
491                 if (undo_current == undo_num) {
492                         copy_mark_to_undo();
493                         undo_current--;
494                 }
495                 undo_current--;
496 //printf("restore undo #%d\n", undo_current);
497         } else {
498                 /* nothing to redo */
499                 if (undo_current >= undo_num-1)
500                         return;
501                 undo_current++;
502 //printf("restore undo #%d\n", undo_current);
503         }
504         memcpy(img_mark_buffer, img_mark_buffer_undo[undo_current], img_width*img_height);
505         button_down_x = button_down_x_undo[undo_current];
506         button_down_y = button_down_y_undo[undo_current];
507 }
508
509 void copy_color(int index)
510 {
511         int i;
512
513         if (img_mark_buffer_copy)
514                 free(img_mark_buffer_copy);
515         img_mark_buffer_copy = malloc(img_width*img_height);
516         if (!img_mark_buffer_copy)
517                 return;
518
519         memcpy(img_mark_buffer_copy, img_mark_buffer, img_width*img_height);
520         copy_width = img_width;
521         copy_height = img_height;
522
523         /* erase indexed color except given index */
524         for (i = 0; i < img_width * img_height; i++) {
525                 if (index && img_mark_buffer_copy[i] != index)
526                         img_mark_buffer_copy[i] = 0;
527         }
528 }
529
530 void paste_color(void)
531 {
532         int i;
533
534         if (!img_mark_buffer_copy)
535                 return;
536
537         if (copy_width != img_width || copy_height != img_height) {
538                 printerror("Image in copy buffer has different dimenstions.");
539                 return;
540         }
541
542         /* paste indexed pixles */
543         for (i = 0; i < img_width * img_height; i++) {
544                 if (img_mark_buffer_copy[i])
545                         img_mark_buffer[i] = img_mark_buffer_copy[i];
546         }
547 }
548