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