61d396d246c050e5a487b62f4f2398fb947d2cfd
[colorize.git] / gui / colorize.c
1 #include <gtk/gtk.h>
2 #include <gdk/gdkkeysyms.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <unistd.h>
6 #include <math.h>
7 #include "main.h"
8 #include "image.h"
9 #include "colorize.h"
10 #include "../src/mark.h"
11 #include "../src/ppm.h"
12 #include "../src/yuv.h"
13 #include "../src/dir_seperator.h"
14 #include "../lib/darray.h"
15 #include "../lib/colorize.h"
16 #include "palette.h"
17 #include "timeline.h"
18
19 #define min(x,y) ((x < y) ? x : y)
20
21 struct colorize_priv {
22         GtkWidget *window;
23         GdkPixbuf *pixbuf;
24         int width, height;
25         char filename[256];
26         char folder[256];
27 };
28
29 static void colorize_destroy(GtkWidget *widget, gpointer priv)
30 {
31         struct colorize_priv *cp = (struct colorize_priv *) priv;
32
33         gtk_widget_destroy(cp->window);
34         g_object_unref(cp->pixbuf);
35         free(cp);
36 }
37
38 static gboolean colorize_key_press(GtkWidget *widget, GdkEventKey *event, gpointer priv)
39 {
40         switch (event->keyval) {
41         case GDK_q:
42         case GDK_w:
43                 if (event->state & GDK_CONTROL_MASK) {
44                         gtk_widget_destroy(widget);
45                         return TRUE;
46                 }
47         default:
48                 ;//printf("%x\n", event->keyval);
49         }
50
51         return FALSE;
52 }
53
54 static void save_event(gpointer *priv)
55 {
56         struct colorize_priv *cp = (struct colorize_priv *) priv;
57         guchar *data;
58         int rs, i, j;
59         unsigned char *buffer;
60         GtkWidget *dialog;
61         char *filename = NULL;
62
63         dialog = gtk_file_chooser_dialog_new("Select file to save colorized image",
64                 GTK_WINDOW(main_window),
65                 GTK_FILE_CHOOSER_ACTION_SAVE,
66                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
67                 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
68                 NULL);
69
70         gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), cp->folder);
71         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), cp->filename);
72         if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
73                 filename = strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)));
74         }
75
76         gtk_widget_destroy(dialog);
77
78         if (!filename)
79                 return;
80
81         data = gdk_pixbuf_get_pixels(cp->pixbuf);
82         rs = gdk_pixbuf_get_rowstride(cp->pixbuf);
83         buffer = malloc(cp->width*cp->height*3);
84
85         for (i = 0; i < cp->height; i++) {
86                 for (j = 0; j < cp->width; j++) {
87                         buffer[(i*cp->width+j)*3+0] = data[i*rs+j*3+0];
88                         buffer[(i*cp->width+j)*3+1] = data[i*rs+j*3+1];
89                         buffer[(i*cp->width+j)*3+2] = data[i*rs+j*3+2];
90                 }
91         }
92
93         save_img(buffer, cp->width, cp->height, filename, 0);
94 }
95
96 static void dummy_event(gpointer *priv)
97 {
98 }
99
100 static void close_event(gpointer *priv)
101 {
102         struct colorize_priv *cp = (struct colorize_priv *) priv;
103
104         gtk_widget_destroy(cp->window);
105 }
106
107 void colorize_image(void)
108 {
109         darray_t *gI = NULL, *cI = NULL, *markIm = NULL, *ntscIm = NULL;
110         double *ptr, *ptr2, *ptr3;
111         int dims[4];
112         int width = img_width, height = img_height;
113         int features, change_bc;
114         int i, j;
115         int c;
116         int rc;
117         GdkPixbuf *pixbuf;
118         GtkWidget *image;
119         guchar *data;
120         GtkWidget *colorize_window;
121         GtkWidget *vbox;
122         GtkWidget *menu_bar;
123         GtkWidget *root_menu;
124         GtkWidget *menu;
125         GtkWidget *menu_item;
126         struct colorize_priv *cp;
127         int rs;
128         int zoom_field;
129         char *p, *q;
130
131         if (!img_pixbuf)
132                 return;
133
134         /* generate grey image array */
135         dims[0] = width; dims[1] = height; dims[2] = 3; dims[3] = 1;
136         gI = darrayCreate(4, dims);
137         if (!gI) {
138                 printerror("No memory: failed to create grey image array");
139                 goto error;
140         }
141         ptr = darrayGetPr(gI);
142         img2array(img_grey_buffer, img_width, img_height, ptr, width, height);
143
144         /* generade marked color image */
145         dims[0] = width; dims[1] = height; dims[2] = 3; dims[3] = 1;
146         cI = darrayCreate(4, dims);
147         if (!cI) {
148                 printerror("No memory: failed to create marked image array");
149                 goto error;
150         }
151         ptr = darrayGetPr(cI);
152         img2array(img_grey_buffer, img_width, img_height, ptr, width, height);
153         change_bc = 0;
154         for (i = 0; i < img_height; i++) {
155                 for (j = 0; j < img_width; j++) {
156                         /* do not apply mask on index 0 */
157                         c = img_mark_buffer[i*img_width+j];
158                         if (c == 0)
159                                 continue;
160                         /* check for any brightness/contrast change */
161                         if (mark_palette[c-1].bright != 0 || mark_palette[c-1].contrast != 1)
162                                 change_bc = 1;
163                         /* do not apply white pixles, this meas: keep original color */
164                         if (mark_palette[c-1].r == 255 && mark_palette[c-1].g == 255 && mark_palette[c-1].b == 255)
165                                 continue;
166                         ptr[i*width+j] = mark_palette[c-1].r / 255.0F;
167                         ptr[i*width+j + width*height] = mark_palette[c-1].g / 255.0F;
168                         ptr[i*width+j + width*height*2] = mark_palette[c-1].b / 255.0F;
169                 }
170         }
171
172         // convert grey image into YUV
173         ptr = darrayGetPr(gI);
174         rgb2yuv(ptr, ptr, width, height);
175
176         // convert marked image into YUV
177         ptr = darrayGetPr(cI);
178         rgb2yuv(ptr, ptr, width, height);
179
180         /* create color mask and ntsc arrays for the colorization process */
181         dims[0] = width; dims[1] = height; dims[2] = 1;
182         markIm = darrayCreate(3, dims);
183         if (!markIm) {
184                 printerror("No memory: failed to create color array");
185                 goto error;
186         }
187         features = (change_bc) ? 4 : 2;
188         dims[0] = width; dims[1] = height; dims[2] = features+1; dims[3] = 1;
189         ntscIm = darrayCreate(4, dims);
190         if (!ntscIm) {
191                 printerror("No memory: failed to create ntsc array");
192                 goto error;
193         }
194
195         /* apply mask to markIm */
196         ptr = darrayGetPr(markIm);
197         for (i = 0; i < img_height; i++) {
198                 for (j = 0; j < img_width; j++) {
199                         /* do not apply mask on index 0 */
200                         c = img_mark_buffer[i*img_width+j];
201                         ptr[i*width+j] = (c == 0) ? 0.0F : 1.0F;
202                 }
203         }
204
205         // generate NTSC image: use luminance from original image and chrominance from original or marked image
206         ptr = darrayGetPr(gI);
207         ptr2 = darrayGetPr(cI);
208         ptr3 = darrayGetPr(ntscIm);
209         memcpy(ptr3, ptr, width * height * sizeof(double));
210         for (i = 0; i < img_height; i++) {
211                 for (j = 0; j < img_width; j++) {
212                         c = img_mark_buffer[i*img_width+j];
213                         if (c == 0) {
214                                 ptr3[width * height + width * i + j] = ptr[width * height + width * i + j];
215                                 ptr3[width * height * 2 + width * i + j] = ptr[width * height * 2 + width * i + j];
216                         } else {
217                                 ptr3[width * height + width * i + j] = ptr2[width * height + width * i + j];
218                                 ptr3[width * height * 2 + width * i + j] = ptr2[width * height * 2 + width * i + j];
219                         }
220                 }
221         }
222         /* if we have a change, we modify brightness+contrast first */
223         if (change_bc) {
224                 /* apply brightness and contrast from makred pixles to grey image */
225                 for (i = 0; i < height; i++) {
226                         for (j = 0; j < width; j++) {
227                                 ptr3[i*width+j] = ptr[i*width+j];
228                                 if (j < img_width && i < img_height) {
229                                         /* use unchanged brightness and contrast on index 0 */
230                                         c = img_mark_buffer[i*img_width+j];
231                                 } else
232                                         c = 0;
233                                 if (c == 0) {
234                                         ptr3[i*width+j + width*height*3] = 0;
235                                         ptr3[i*width+j + width*height*4] = 0.1;
236                                 } else {
237                                         ptr3[i*width+j + width*height*3] = mark_palette[c-1].bright / 10.0;
238                                         ptr3[i*width+j + width*height*4] = mark_palette[c-1].contrast / 10.0;
239                                 }
240                         }
241                 }
242         }
243
244         /* destroy temporary gI and cI */
245         darrayDestroy(gI);
246         gI = NULL;
247         darrayDestroy(cI);
248         cI = NULL;
249
250         /* render u and v change */
251         rc = colorize(ntscIm, markIm, NULL, NULL, 5, 1, 0);
252         if (rc < 0) {
253                 printerror("No memory! Use smaller image or add more memory.");
254                 goto error;
255         }
256
257         /* if we have a change, we apply brightness+contrast from ntscIm */
258         if (change_bc) {
259                 ptr = darrayGetPr(ntscIm);
260                 ptr2 = darrayGetPr(ntscIm);
261                 for (i = 0; i < img_height; i++) {
262                         for (j = 0; j < img_width; j++) {
263                                 /* apply contrast */
264                                 ptr2[width * i + j] = (ptr2[width * i + j] - 0.5) * ptr[width * height * 4 + width * i + j] * 10.0 + 0.5;
265                                 /* apply brightness */
266                                 ptr2[width * i + j] += ptr[width * height * 3 + width * i + j] * 10.0;
267                                 if (ptr2[width * i + j] < 0)
268                                         ptr2[width * i + j] = 0;
269                                 if (ptr2[width * i + j] > 1)
270                                         ptr2[width * i + j] = 1;
271                         }
272                 }
273         }
274
275         if (img_scale_y == img_scale_x*2)
276                 zoom_field = 2;
277         else
278                 zoom_field = 1;
279
280         /* apply YUV to pixbuffer and display */
281         pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, img_width, img_height * zoom_field);
282         ptr = darrayGetPr(ntscIm);
283         yuv2rgb(ptr, ptr, width, height);
284         data = gdk_pixbuf_get_pixels(pixbuf);
285         rs = gdk_pixbuf_get_rowstride(pixbuf);
286         for (i = 0; i < img_height*zoom_field; i++) {
287                 for (j = 0; j < img_width; j++) {
288                         c = ptr[(i/zoom_field)*width+j] * 255.0F;
289                         if (c < 0)
290                                 c = 0;
291                         else if (c > 255)
292                                 c = 255;
293                         data[i*rs + 3*j + 0] = c;
294                         c = ptr[(i/zoom_field)*width+j + width*height] * 255.0F;
295                         if (c < 0)
296                                 c = 0;
297                         else if (c > 255)
298                                 c = 255;
299                         data[i*rs + 3*j + 1] = c;
300                         c = ptr[(i/zoom_field)*width+j + width*height*2] * 255.0F;
301                         if (c < 0)
302                                 c = 0;
303                         else if (c > 255)
304                                 c = 255;
305                         data[i*rs + 3*j + 2] = c;
306                 }
307         }
308
309         /* create image */
310         image = gtk_image_new_from_pixbuf(pixbuf);
311         gtk_widget_show(image);
312
313         darrayDestroy(markIm);
314         markIm = NULL;
315         darrayDestroy(ntscIm);
316         ntscIm = NULL;
317
318         darrayDone();
319
320         /* private structure */
321         cp = calloc(sizeof(struct colorize_priv), 1);
322
323         /* create window */
324         colorize_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
325         gtk_window_set_title(GTK_WINDOW(colorize_window), "Colorized");
326         g_signal_connect(colorize_window, "destroy", G_CALLBACK(colorize_destroy), cp);
327         g_signal_connect(colorize_window, "key_press_event", G_CALLBACK(colorize_key_press), cp);
328
329         cp->window = colorize_window;
330         cp->pixbuf = pixbuf;
331         cp->width = img_width*zoom_field;
332         cp->height = img_height*zoom_field;
333         p = frame_list[timeline_selected].filename;
334         while((q = strchr(p, DIR_SEPERATOR)))
335                 p = q + 1;
336         strcpy(cp->folder, frame_list[timeline_selected].filename);
337         cp->folder[p - frame_list[timeline_selected].filename] = '\0';
338         sprintf(cp->filename, "colorized_%s", p);
339
340         /* create menu*/
341         menu_bar = gtk_menu_bar_new();
342         gtk_widget_show(menu_bar);
343         menu = gtk_menu_new();
344         root_menu = gtk_menu_item_new_with_mnemonic("_File");
345         gtk_widget_show(root_menu);
346
347         menu_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_SAVE_AS, NULL);
348         gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
349         g_signal_connect_swapped(menu_item, "activate", G_CALLBACK(save_event), cp);
350         gtk_widget_show(menu_item);
351         menu_item = gtk_separator_menu_item_new();
352         gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
353         g_signal_connect_swapped(menu_item, "activate", G_CALLBACK(dummy_event), cp);
354         gtk_widget_show(menu_item);
355         menu_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_CLOSE, NULL);
356         gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
357         g_signal_connect_swapped(menu_item, "activate", G_CALLBACK(close_event), cp);
358         gtk_widget_show(menu_item);
359         gtk_menu_item_set_submenu(GTK_MENU_ITEM(root_menu), menu);
360         gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), root_menu);
361
362         /* create vbox (complete window) */
363         vbox = gtk_vbox_new(FALSE, 0);
364         gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 2);
365         gtk_box_pack_start(GTK_BOX(vbox), image, FALSE, FALSE, 2);
366         gtk_widget_show(vbox);
367
368         gtk_container_add(GTK_CONTAINER(colorize_window), vbox);
369         gtk_widget_show(colorize_window);
370
371 error:
372         darrayDestroy(gI);
373         darrayDestroy(cI);
374         darrayDestroy(markIm);
375         darrayDestroy(ntscIm);
376
377         darrayDone();
378 }