258a3771a272a14d911f16cf0fd01d3d611ee58f
[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 _c, preview_asis = 0;
183
184         if (w < 0)
185                 w = window_width;
186         if (h < 0)
187                 h = window_width;
188
189         guchar *data;
190         unsigned char compose[w*3];
191         int i, j, rs;
192         unsigned char c;
193
194         if (!img_pixbuf)
195                 return;
196         data = gdk_pixbuf_get_pixels(img_pixbuf);
197         rs = gdk_pixbuf_get_rowstride(img_pixbuf);
198
199         /* this should not happen */
200         if (window_width < img_width*img_scale_x/16)
201                 window_width = img_width*img_scale_x/16;
202         if (window_height < img_height*img_scale_y/16)
203                 window_height = img_height*img_scale_y/16;
204
205         /* we need to calculate an offset, since the drawing area is now larger */
206         x_offset = (window_width - img_width*img_scale_x/16) / 2;
207         y_offset = (window_height - img_height*img_scale_y/16) / 2;
208
209         /* clip to window size */
210         if (x < 0)
211                 x = 0;
212         if (x >= window_width)
213                 x = window_width - 1;
214         if (y < 0)
215                 y = 0;
216         if (y >= window_height)
217                 y = window_height - 1;
218         if (x + w > window_width)
219                 w = window_width - x;
220         if (w <= 0)
221                 return;
222         if (y + h > window_height)
223                 h = window_height - y;
224         if (h <= 0)
225                 return;
226
227         /* draw top border */
228         if (y < y_offset) {
229                 int temp = (h+y >= y_offset) ? y_offset-y : h; /* height */
230                 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);
231                 h -= temp;
232                 y += temp;
233                 if (h <= 0)
234                         return;
235         }
236
237         /* draw bottom border */
238         if (y+h >= y_offset+img_height*img_scale_y/16) {
239                 int temp = (y < y_offset+img_height*img_scale_y/16) ? y_offset+img_height*img_scale_y/16 : y; /* y start */
240                 gdk_draw_rectangle(draw, img_drawing_area->style->bg_gc[GTK_WIDGET_STATE(img_drawing_area)], TRUE, x, temp, w, h - (temp-y));
241                 h = temp-y;
242                 if (h <= 0)
243                         return;
244         }
245
246         /* draw left border */
247         if (x < x_offset) {
248                 int temp = (w+x >= x_offset) ? x_offset-x : w; /* width */
249                 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);
250                 w -= temp;
251                 x += temp;
252                 if (w <= 0)
253                         return;
254         }
255
256         /* draw right border */
257         if (x+w >= x_offset+img_width*img_scale_x/16) {
258                 int temp = (x < x_offset+img_width*img_scale_x/16) ? x_offset+img_width*img_scale_x/16 : x; /* x start **/
259                 gdk_draw_rectangle(draw, img_drawing_area->style->bg_gc[GTK_WIDGET_STATE(img_drawing_area)], TRUE, temp, y, w - (temp-x), h);
260                 w = temp-x;
261                 if (w <= 0)
262                         return;
263         }
264
265         x -= x_offset;
266         y -= y_offset;
267
268         if (img_grey_buffer) {
269                 if (preview) {
270                         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);
271                         /* check for white color (no change) */
272                         if (mark_palette[mark_selected].r == 255 && mark_palette[mark_selected].g == 255 && mark_palette[mark_selected].r == 255)
273                                 preview_asis = 1;
274                 }
275                 /* compose image (segment) line by line */
276                 for (i = 0; i < h; i++) {
277                         for (j = 0; j < w; j++) {
278                                 if (preview && !rendered && !flowview) {
279                                         /* apply selected color from palette to all pixles */
280                                         _r = img_grey_buffer[((j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y))*3] / 65535.0F;
281                                         _g = img_grey_buffer[((j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y))*3+1] / 65535.0F;
282                                         _b = img_grey_buffer[((j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y))*3+2] / 65535.0F;
283                                         rgb2yuv_pixle(_r, _g, _b, &_y, &_u, &_v);
284                                         _y = (_y - 0.5) * mark_palette[mark_selected].contrast + 0.5;
285                                         _y += mark_palette[mark_selected].bright;
286                                         if (_y < 0)
287                                                 _y = 0;
288                                         if (_y > 1)
289                                                 _y = 1;
290                                         if (preview_asis)
291                                                 yuv2rgb_pixle(_y, _u, _v, &_r, &_g, &_b);
292                                         else
293                                                 yuv2rgb_pixle(_y, u_palette, v_palette, &_r, &_g, &_b);
294                                         _c = _r * 255.0F;
295                                         if (_c < 0)
296                                                 _c = 0;
297                                         else if (_c > 255)
298                                                 _c = 255;
299                                         compose[j*3] = _c;
300                                                 _c = _g * 255.0F;
301                                         if (_c < 0)
302                                                 _c = 0;
303                                         else if (_c > 255)
304                                                 _c = 255;
305                                         compose[j*3+1] = _c;
306                                                 _c = _b * 255.0F;
307                                         if (_c < 0)
308                                                 _c = 0;
309                                         else if (_c > 255)
310                                                 _c = 255;
311                                         compose[j*3+2] = _c;
312
313                                 } else {
314                                         compose[j*3] = img_grey_buffer[((j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y))*3] >> 8;
315                                         compose[j*3+1] = img_grey_buffer[((j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y))*3+1] >> 8;
316                                         compose[j*3+2] = img_grey_buffer[((j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y))*3+2] >> 8;
317                                 }
318                                 c = img_mark_buffer[(j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y)];
319                                 if (c > 0 && !flowview && !(rendered && preview)) {
320                                         if (highlight) {
321                                                 if (c-1 == mark_selected) {
322                                                         compose[j*3] = 255;
323                                                         compose[j*3+1] = 0;
324                                                         compose[j*3+2] = 0;
325                                                 } else {
326                                                         compose[j*3] = 0;
327                                                         compose[j*3+1] = 0;
328                                                         compose[j*3+2] = 128;
329                                                 }
330                                         } else {
331                                                 compose[j*3] = mark_palette[c-1].r;
332                                                 compose[j*3+1] = mark_palette[c-1].g;
333                                                 compose[j*3+2] = mark_palette[c-1].b;
334                                         }
335                                 }
336                         }
337                         memcpy(data + rs*(i+y) + x*3, compose, w*3);
338                 }
339         } else {
340                 for (i = 0; i < h; i++)
341                         memset(data + rs*(i+y) + x*3, 0, w*3);
342         }
343
344         gdk_draw_pixbuf(draw, NULL, img_pixbuf, x, y, x+x_offset, y+y_offset, w, h, GDK_RGB_DITHER_NONE, 0, 0);
345
346 }
347
348 /* plot brush on mark buffer */
349 void paint_brush(int x, int y, int size, int paint)
350 {
351         GdkDrawable *draw = gtk_widget_get_window(img_drawing_area);
352         int window_width, window_height;
353         gdk_drawable_get_size (draw, &window_width, &window_height);
354
355         /* we need to calculate an offset, since the drawing area is now larger */
356         x -= (window_width - img_width*img_scale_x/16) / 2;
357         y -= (window_height - img_height*img_scale_y/16) / 2;
358
359         x = x*16/img_scale_x;
360         y = y*16/img_scale_y;
361
362         if (!img_mark_buffer || flowview)
363                 return;
364
365         int x1 = x - size + 1;
366         int x2 = x + size - 1;
367         int y1 = y - size + 1;
368         int y2 = y + size - 1;
369         int i, j;
370
371         if (x1 >= img_width)
372                 return;
373         if (x1 < 0)
374                 x1 = 0;
375         if (x2 >= img_width)
376                 x2 = img_width - 1;
377         if (x2 < 0)
378                 return;
379         if (y1 >= img_height)
380                 return;
381         if (y1 < 0)
382                 y1 = 0;
383         if (y2 >= img_height)
384                 y2 = img_height - 1;
385         if (y2 < 0)
386                 return;
387
388         /* only paint color, if paint is set */
389         if (paint)
390                 paint = mark_selected + 1;
391
392         for (i = y1; i <= y2; i++) {
393                 for (j = x1; j <= x2; j++) {
394                         img_mark_buffer[j+img_width*i] = paint;
395                 }
396         }
397 }
398
399 void erase_mark(int index)
400 {
401         int i;
402
403         if (!img_mark_buffer)
404                 return;
405
406         /* no index, erase all */
407         if (!index) {
408                 memset(img_mark_buffer, 0, img_width * img_height);
409                 return;
410         }
411
412         /* only erase indexed color */
413         for (i = 0; i < img_width * img_height; i++) {
414                 if (img_mark_buffer[i] == index)
415                         img_mark_buffer[i] = 0;
416         }
417 }
418
419 void move_mark(int x, int y)
420 {
421         int i, j;
422         unsigned char *src;
423
424         if (!img_mark_buffer || undo_current == 0)
425                 return;
426
427         src = img_mark_buffer_undo[undo_current-1];
428         if (!src)
429                 return;
430
431         if (y < 0 || (y == 0 && x < 0)) {
432                 /* move up in memory */
433                 for (j = 0; j < img_height; j++) {
434                         for (i = 0; i < img_width; i++) {
435                                 if (i < x || i >= img_width+x || j < y || j >= img_height+y)
436                                         img_mark_buffer[j*img_width+i] = 0;
437                                 else
438                                         img_mark_buffer[j*img_width+i] = src[(j-y)*img_width+(i-x)];
439                         }
440                 }
441         } else {
442                 /* move down in memory */
443                 for (j = img_height-1; j >= 0; j--) {
444                         for (i = img_width-1; i >= 0; i--) {
445                                 if (i < x || i >= img_width+x || j < y || j >= img_height+y)
446                                         img_mark_buffer[j*img_width+i] = 0;
447                                 else
448                                         img_mark_buffer[j*img_width+i] = src[(j-y)*img_width+(i-x)];
449                         }
450                 }
451         }
452 }
453
454 void copy_mark_to_undo(void)
455 {
456         anything_modified = 1;
457
458         if (!img_mark_buffer)
459                 return;
460
461         if (undo_current < UNDO_MAX) {
462                 /* allocate new undobuffer and append */
463                 if (!img_mark_buffer_undo[undo_current])
464                         img_mark_buffer_undo[undo_current] = malloc(img_width*img_height);
465                 if (!img_mark_buffer_undo[undo_current])
466                         return;
467                 undo_current++;
468         } else {
469                 /* shift and use oldest undobuffer */
470                 unsigned char *temp = img_mark_buffer_undo[0];
471                 int i;
472                 for (i = 0; i < UNDO_MAX - 1; i++)
473                         img_mark_buffer_undo[i] = img_mark_buffer_undo[i+1];
474                 img_mark_buffer_undo[UNDO_MAX - 1] = temp;
475         }
476         undo_num = undo_current;
477 //printf("storing undo #%d\n", undo_current-1);
478         memcpy(img_mark_buffer_undo[undo_current-1], img_mark_buffer, img_width*img_height);
479         button_down_x_undo[undo_current-1] = button_down_x;
480         button_down_y_undo[undo_current-1] = button_down_y;
481 }
482
483 void copy_undo_to_mark(int redo)
484 {
485         if (!img_mark_buffer)
486                 return;
487
488 //printf("num is %d\n", undo_num);
489         if (!redo) {
490                 /* nothing to undo */
491                 if (undo_current == 0)
492                         return;
493
494                 /* if we are at the end of the history, we need to add the current image to undo history */
495                 if (undo_current == undo_num) {
496                         copy_mark_to_undo();
497                         undo_current--;
498                 }
499                 undo_current--;
500 //printf("restore undo #%d\n", undo_current);
501         } else {
502                 /* nothing to redo */
503                 if (undo_current >= undo_num-1)
504                         return;
505                 undo_current++;
506 //printf("restore undo #%d\n", undo_current);
507         }
508         memcpy(img_mark_buffer, img_mark_buffer_undo[undo_current], img_width*img_height);
509         button_down_x = button_down_x_undo[undo_current];
510         button_down_y = button_down_y_undo[undo_current];
511 }
512
513 void copy_color(int index)
514 {
515         int i;
516
517         if (img_mark_buffer_copy)
518                 free(img_mark_buffer_copy);
519         img_mark_buffer_copy = malloc(img_width*img_height);
520         if (!img_mark_buffer_copy)
521                 return;
522
523         memcpy(img_mark_buffer_copy, img_mark_buffer, img_width*img_height);
524         copy_width = img_width;
525         copy_height = img_height;
526
527         /* erase indexed color except given index */
528         for (i = 0; i < img_width * img_height; i++) {
529                 if (index && img_mark_buffer_copy[i] != index)
530                         img_mark_buffer_copy[i] = 0;
531         }
532 }
533
534 void paste_color(void)
535 {
536         int i;
537
538         if (!img_mark_buffer_copy)
539                 return;
540
541         if (copy_width != img_width || copy_height != img_height) {
542                 printerror("Image in copy buffer has different dimenstions.");
543                 return;
544         }
545
546         /* paste indexed pixles */
547         for (i = 0; i < img_width * img_height; i++) {
548                 if (img_mark_buffer_copy[i])
549                         img_mark_buffer[i] = img_mark_buffer_copy[i];
550         }
551 }
552