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