improved debug messages
[geeqie.git] / src / histogram.c
1 /*
2  * Geeqie
3  * Copyright (C) 2008 - 2009 The Geeqie Team
4  *
5  * Author: Vladimir Nadvornik
6  * based on a patch by Uwe Ohse
7  *
8  * This software is released under the GNU General Public License (GNU GPL).
9  * Please read the included file COPYING for more information.
10  * This software comes with no warranty of any kind, use at your own risk!
11  */
12
13 #include "main.h"
14 #include "histogram.h"
15
16 #include "pixbuf_util.h"
17 #include "filedata.h"
18
19 #include <math.h>
20
21 /*
22  *----------------------------------------------------------------------------
23  * image histogram
24  *----------------------------------------------------------------------------
25  */
26
27 #define HISTMAP_SIZE 256
28
29 struct _HistMap {
30         gulong r[HISTMAP_SIZE];
31         gulong g[HISTMAP_SIZE];
32         gulong b[HISTMAP_SIZE];
33         gulong max[HISTMAP_SIZE];
34         
35         gint idle_id;
36         GdkPixbuf *pixbuf;
37         gint y;
38 };
39
40
41 Histogram *histogram_new(void)
42 {
43         Histogram *histogram;
44
45         histogram = g_new0(Histogram, 1);
46         histogram->histogram_channel = HCHAN_RGB;
47         histogram->histogram_mode = 0;
48
49         /* grid */
50         histogram->vgrid = 5;
51         histogram->hgrid = 3;
52         histogram->grid_color.R = 160;
53         histogram->grid_color.G = 160;
54         histogram->grid_color.B = 160;
55         histogram->grid_color.A = 250;
56
57         return histogram;
58 }
59
60 void histogram_free(Histogram *histogram)
61 {
62         g_free(histogram);
63 }
64
65
66 gint histogram_set_channel(Histogram *histogram, gint chan)
67 {
68         if (!histogram) return 0;
69         histogram->histogram_channel = chan;
70         return chan;
71 }
72
73 gint histogram_get_channel(Histogram *histogram)
74 {
75         if (!histogram) return 0;
76         return histogram->histogram_channel;
77 }
78
79 gint histogram_set_mode(Histogram *histogram, gint mode)
80 {
81         if (!histogram) return 0;
82         histogram->histogram_mode = mode;
83         return mode;
84 }
85
86 gint histogram_get_mode(Histogram *histogram)
87 {
88         if (!histogram) return 0;
89         return histogram->histogram_mode;
90 }
91
92 gint histogram_toggle_channel(Histogram *histogram)
93 {
94         if (!histogram) return 0;
95         return histogram_set_channel(histogram, (histogram_get_channel(histogram)+1)%HCHAN_COUNT);
96 }
97
98 gint histogram_toggle_mode(Histogram *histogram)
99 {
100         if (!histogram) return 0;
101         return histogram_set_mode(histogram, !histogram_get_mode(histogram));
102 }
103
104 const gchar *histogram_label(Histogram *histogram)
105 {
106         const gchar *t1 = "";
107         
108         if (!histogram) return NULL;
109
110         if (histogram->histogram_mode)
111                 switch (histogram->histogram_channel)
112                         {
113                         case HCHAN_R:   t1 = _("Log Histogram on Red"); break;
114                         case HCHAN_G:   t1 = _("Log Histogram on Green"); break;
115                         case HCHAN_B:   t1 = _("Log Histogram on Blue"); break;
116                         case HCHAN_RGB: t1 = _("Log Histogram on RGB"); break;
117                         case HCHAN_MAX: t1 = _("Log Histogram on value"); break;
118                         }
119         else
120                 switch (histogram->histogram_channel)
121                         {
122                         case HCHAN_R:   t1 = _("Linear Histogram on Red"); break;
123                         case HCHAN_G:   t1 = _("Linear Histogram on Green"); break;
124                         case HCHAN_B:   t1 = _("Linear Histogram on Blue"); break;
125                         case HCHAN_RGB: t1 = _("Linear Histogram on RGB"); break;
126                         case HCHAN_MAX: t1 = _("Linear Histogram on value"); break;
127                         }
128         return t1;
129 }
130
131 static HistMap *histmap_new(void)
132 {
133         HistMap *histmap = g_new0(HistMap, 1);
134         histmap->idle_id = -1;
135         return histmap;
136 }
137
138 void histmap_free(HistMap *histmap)
139 {
140         if (!histmap) return;
141         if (histmap->idle_id != -1) g_source_remove(histmap->idle_id);
142         if (histmap->pixbuf) g_object_unref(histmap->pixbuf);
143         g_free(histmap);
144 }
145
146 static gboolean histmap_read(HistMap *histmap, gboolean whole)
147 {
148         gint w, h, i, j, srs, has_alpha, step, end_line;
149         guchar *s_pix;
150         GdkPixbuf *imgpixbuf = histmap->pixbuf;
151         
152         w = gdk_pixbuf_get_width(imgpixbuf);
153         h = gdk_pixbuf_get_height(imgpixbuf);
154         srs = gdk_pixbuf_get_rowstride(imgpixbuf);
155         s_pix = gdk_pixbuf_get_pixels(imgpixbuf);
156         has_alpha = gdk_pixbuf_get_has_alpha(imgpixbuf);
157         
158         if (whole)
159                 {
160                 end_line = h;
161                 }
162         else
163                 {
164                 gint lines = 1 + 16384 / w;
165                 end_line = histmap->y + lines;
166                 if (end_line > h) end_line = h;
167                 }
168
169         step = 3 + !!(has_alpha);
170         for (i = histmap->y; i < end_line; i++)
171                 {
172                 guchar *sp = s_pix + (i * srs); /* 8bit */
173                 for (j = 0; j < w; j++)
174                         {
175                         guint max = sp[0];
176                         if (sp[1] > max) max = sp[1];
177                         if (sp[2] > max) max = sp[2];
178                 
179                         histmap->r[sp[0]]++;
180                         histmap->g[sp[1]]++;
181                         histmap->b[sp[2]]++;
182                         histmap->max[max]++;
183
184                         sp += step;
185                         }
186                 }
187         histmap->y = end_line;
188         return end_line >= h;   
189 }
190
191 const HistMap *histmap_get(FileData *fd)
192 {
193         if (fd->histmap && fd->histmap->idle_id == -1) return fd->histmap; /* histmap exists and is finished */
194         
195         return NULL;
196 }
197
198 static gboolean histmap_idle_cb(gpointer data)
199 {
200         FileData *fd = data;
201         if (histmap_read(fd->histmap, FALSE))
202                 {
203                 /* finished */
204                 g_object_unref(fd->histmap->pixbuf); /*pixbuf is no longer needed */
205                 fd->histmap->pixbuf = NULL;
206                 fd->histmap->idle_id = -1;
207                 file_data_send_notification(fd, NOTIFY_HISTMAP);
208                 return FALSE;
209                 }
210         return TRUE;
211 }
212
213 gboolean histmap_start_idle(FileData *fd)
214 {
215         if (fd->histmap || !fd->pixbuf) return FALSE;
216
217         fd->histmap = histmap_new();
218         fd->histmap->pixbuf = fd->pixbuf;
219         g_object_ref(fd->histmap->pixbuf);
220
221         fd->histmap->idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, histmap_idle_cb, fd, NULL);
222         return TRUE;
223 }
224
225
226 static void histogram_vgrid(Histogram *histogram, GdkPixbuf *pixbuf, gint x, gint y, gint width, gint height)
227 {
228         guint i;
229         float add;
230         
231         if (histogram->vgrid == 0) return;
232
233         add = width / (float)histogram->vgrid;
234
235         for (i = 1; i < histogram->vgrid; i++)
236                 {
237                 gint xpos = x + (int)(i * add + 0.5);
238
239                 pixbuf_draw_line(pixbuf, x, y, width, height, xpos, y, xpos, y + height,
240                                  histogram->grid_color.R,
241                                  histogram->grid_color.G,
242                                  histogram->grid_color.B,
243                                  histogram->grid_color.A);
244                 }
245 }
246
247 static void histogram_hgrid(Histogram *histogram, GdkPixbuf *pixbuf, gint x, gint y, gint width, gint height)
248 {
249         guint i;
250         float add;
251         
252         if (histogram->hgrid == 0) return;
253
254         add = height / (float)histogram->hgrid;
255
256         for (i = 1; i < histogram->hgrid; i++)
257                 {
258                 gint ypos = y + (int)(i * add + 0.5);
259         
260                 pixbuf_draw_line(pixbuf, x, y, width, height, x, ypos, x + width, ypos,
261                                  histogram->grid_color.R,
262                                  histogram->grid_color.G,
263                                  histogram->grid_color.B,
264                                  histogram->grid_color.A);
265                 }
266 }
267
268 gboolean histogram_draw(Histogram *histogram, const HistMap *histmap, GdkPixbuf *pixbuf, gint x, gint y, gint width, gint height)
269 {
270         /* FIXME: use the coordinates correctly */
271         gint i;
272         gulong max = 0;
273         gdouble logmax;
274         gint combine = (HISTMAP_SIZE - 1) / width + 1;
275         gint ypos = y + height;
276         
277         if (!histogram || !histmap) return FALSE;
278         
279         /* Draw the grid */
280         histogram_vgrid(histogram, pixbuf, x, y, width, height);
281         histogram_hgrid(histogram, pixbuf, x, y, width, height);
282
283         /* exclude overexposed and underexposed */
284         for (i = 1; i < HISTMAP_SIZE - 1; i++)
285                 {
286                 if (histmap->r[i] > max) max = histmap->r[i];
287                 if (histmap->g[i] > max) max = histmap->g[i];
288                 if (histmap->b[i] > max) max = histmap->b[i];
289                 if (histmap->max[i] > max) max = histmap->max[i];
290                 }
291
292         if (max > 0)
293                 logmax = log(max);
294         else
295                 logmax = 1.0;
296
297         for (i = 0; i < width; i++)
298                 {
299                 gint j;
300                 glong v[4] = {0, 0, 0, 0};
301                 gint rplus = 0;
302                 gint gplus = 0;
303                 gint bplus = 0;
304                 gint ii = i * HISTMAP_SIZE / width;
305                 gint xpos = x + i;
306                 gint num_chan;
307
308                 for (j = 0; j < combine; j++)
309                         {
310                         guint p = ii + j;
311                         v[0] += histmap->r[p];
312                         v[1] += histmap->g[p];
313                         v[2] += histmap->b[p];
314                         v[3] += histmap->max[p];
315                         }
316         
317                 for (j = 0; combine > 1 && j < 4; j++)
318                         v[j] /= combine;
319                 
320                 num_chan = (histogram->histogram_channel == HCHAN_RGB) ? 3 : 1;
321                 for (j = 0; j < num_chan; j++)
322                         {
323                         gint chanmax;
324                         if (histogram->histogram_channel == HCHAN_RGB) 
325                                 {
326                                 chanmax = HCHAN_R;
327                                 if (v[HCHAN_G] > v[HCHAN_R]) chanmax = HCHAN_G;
328                                 if (v[HCHAN_B] > v[chanmax]) chanmax = HCHAN_B;
329                                 }
330                         else
331                                 {
332                                 chanmax = histogram->histogram_channel;
333                                 }
334                         
335                                 {
336                                 gulong pt;
337                                 gint r = rplus;
338                                 gint g = gplus;
339                                 gint b = bplus;
340
341                                 switch (chanmax)
342                                         {
343                                         case HCHAN_R: rplus = r = 255; break;
344                                         case HCHAN_G: gplus = g = 255; break;
345                                         case HCHAN_B: bplus = b = 255; break;
346                                         }
347
348                                 switch (histogram->histogram_channel)
349                                         {
350                                         case HCHAN_RGB:
351                                                 if (r == 255 && g == 255 && b == 255)
352                                                         {
353                                                         r = 0;  b = 0;  g = 0;
354                                                         }
355                                                 break;
356                                         case HCHAN_R:           b = 0;  g = 0;  break;
357                                         case HCHAN_G:   r = 0;  b = 0;          break;
358                                         case HCHAN_B:   r = 0;          g = 0;  break;
359                                         case HCHAN_MAX: r = 0;  b = 0;  g = 0;  break;
360                                         }
361                                 
362                                 if (v[chanmax] == 0)
363                                         pt = 0;
364                                 else if (histogram->histogram_mode)
365                                         pt = ((gdouble)log(v[chanmax])) / logmax * (height - 1);
366                                 else
367                                         pt = ((gdouble)v[chanmax]) / max * (height - 1);
368
369                                 pixbuf_draw_line(pixbuf,
370                                         x, y, width, height,
371                                         xpos, ypos, xpos, ypos - pt,
372                                         r, g, b, 255);
373                                 }
374
375                         v[chanmax] = -1;
376                         }
377                 }
378
379         return TRUE;
380 }
381
382 void histogram_notify_cb(FileData *fd, NotifyType type, gpointer data)
383 {
384         if ((type & (NOTIFY_CHANGE | NOTIFY_REREAD)) && fd->histmap)
385                 {
386                 DEBUG_1("Notify histogram: %s %04x", fd->path, type);
387                 histmap_free(fd->histmap);
388                 fd->histmap = NULL;
389                 }
390 }
391
392 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */