Remove histogram_ prefix from struct _Histogram fields names and rename them more...
[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
18 #include <math.h>
19
20 /*
21  *----------------------------------------------------------------------------
22  * image histogram
23  *----------------------------------------------------------------------------
24  */
25
26 #define HISTMAP_SIZE 256
27 typedef enum {
28         HISTMAP_CHANNEL_R = 0,
29         HISTMAP_CHANNEL_G,
30         HISTMAP_CHANNEL_B,
31         HISTMAP_CHANNEL_AVG,
32         HISTMAP_CHANNEL_MAX,
33         HISTMAP_CHANNELS
34 } HistMapChannels;
35
36 struct _HistMap {
37         gulong histmap[HISTMAP_SIZE * HISTMAP_CHANNELS];
38         gulong area;
39 };
40
41 struct _Histogram {
42         gint channel_mode;
43         gint log_mode;
44         guint vgrid; /* number of vertical divisions, 0 for none */
45         guint hgrid; /* number of horizontal divisions, 0 for none */
46
47 };
48
49 Histogram *histogram_new(void)
50 {
51         Histogram *histogram;
52
53         histogram = g_new0(Histogram, 1);
54         histogram->channel_mode = options->histogram.last_channel_mode;
55         histogram->log_mode = options->histogram.last_log_mode;
56         histogram->vgrid = 5;
57         histogram->hgrid = 3;
58
59         return histogram;
60 }
61
62 void histogram_free(Histogram *histogram)
63 {
64         g_free(histogram);
65 }
66
67
68 gint histogram_set_channel(Histogram *histogram, gint chan)
69 {
70         if (!histogram) return 0;
71         options->histogram.last_channel_mode = histogram->channel_mode = chan;
72         return chan;
73 }
74
75 gint histogram_get_channel(Histogram *histogram)
76 {
77         if (!histogram) return 0;
78         return histogram->channel_mode;
79 }
80
81 gint histogram_set_mode(Histogram *histogram, gint mode)
82 {
83         if (!histogram) return 0;
84         options->histogram.last_log_mode = histogram->log_mode = mode;
85         return mode;
86 }
87
88 gint histogram_get_mode(Histogram *histogram)
89 {
90         if (!histogram) return 0;
91         return histogram->log_mode;
92 }
93
94 const gchar *histogram_label(Histogram *histogram)
95 {
96         const gchar *t1 = "";
97         
98         if (!histogram) return NULL;
99
100         if (histogram->log_mode)
101                 switch (histogram->channel_mode)
102                         {
103                         case HCHAN_R:   t1 = _("logarithmical histogram on red"); break;
104                         case HCHAN_G:   t1 = _("logarithmical histogram on green"); break;
105                         case HCHAN_B:   t1 = _("logarithmical histogram on blue"); break;
106                         case HCHAN_VAL: t1 = _("logarithmical histogram on value"); break;
107                         case HCHAN_RGB: t1 = _("logarithmical histogram on RGB"); break;
108                         case HCHAN_MAX: t1 = _("logarithmical histogram on max value"); break;
109                         }
110         else
111                 switch (histogram->channel_mode)
112                         {
113                         case HCHAN_R:   t1 = _("linear histogram on red"); break;
114                         case HCHAN_G:   t1 = _("linear histogram on green"); break;
115                         case HCHAN_B:   t1 = _("linear histogram on blue"); break;
116                         case HCHAN_VAL: t1 = _("linear histogram on value"); break;
117                         case HCHAN_RGB: t1 = _("linear histogram on RGB"); break;
118                         case HCHAN_MAX: t1 = _("linear histogram on max value"); break;
119                         }
120         return t1;
121 }
122
123 static HistMap *histmap_read(GdkPixbuf *imgpixbuf)
124 {
125         gint w, h, i, j, srs, has_alpha, step;
126         guchar *s_pix;
127
128         HistMap *histmap;
129         
130         w = gdk_pixbuf_get_width(imgpixbuf);
131         h = gdk_pixbuf_get_height(imgpixbuf);
132         srs = gdk_pixbuf_get_rowstride(imgpixbuf);
133         s_pix = gdk_pixbuf_get_pixels(imgpixbuf);
134         has_alpha = gdk_pixbuf_get_has_alpha(imgpixbuf);
135
136         histmap = g_new0(HistMap, 1);
137
138         step = 3 + !!(has_alpha);
139         for (i = 0; i < h; i++)
140                 {
141                 guchar *sp = s_pix + (i * srs); /* 8bit */
142                 for (j = 0; j < w; j++)
143                         {
144                         guint avg = (sp[0] + sp[1] + sp[2]) / 3;
145                         guint max = sp[0];
146                         if (sp[1] > max) max = sp[1];
147                         if (sp[2] > max) max = sp[2];
148
149                         histmap->histmap[sp[0] * HISTMAP_CHANNELS + HISTMAP_CHANNEL_R]++;
150                         histmap->histmap[sp[1] * HISTMAP_CHANNELS + HISTMAP_CHANNEL_G]++;
151                         histmap->histmap[sp[2] * HISTMAP_CHANNELS + HISTMAP_CHANNEL_B]++;
152                         histmap->histmap[avg   * HISTMAP_CHANNELS + HISTMAP_CHANNEL_AVG]++;
153                         histmap->histmap[max   * HISTMAP_CHANNELS + HISTMAP_CHANNEL_MAX]++;
154                         sp += step;
155                         }
156                 }
157         histmap->area = w * h;
158         return histmap;
159 }
160
161 const HistMap *histmap_get(FileData *fd)
162 {
163         if (fd->histmap) return fd->histmap;
164         
165         if (fd->pixbuf) 
166                 {
167                 fd->histmap = histmap_read(fd->pixbuf);
168                 return fd->histmap;
169                 }
170         return NULL;
171 }
172
173 static void histogram_vgrid(Histogram *histogram, GdkPixbuf *pixbuf, gint x, gint y, gint width, gint height)
174 {
175         static gint c = 160;
176         static gint alpha = 250;
177         guint i;
178         float add;
179         
180         if (histogram->vgrid == 0) return;
181
182         add = width / (float)histogram->vgrid;
183
184         for (i = 1; i < histogram->vgrid; i++)
185                 {
186                 gint xpos = x + (int)(i * add + 0.5);
187
188                 pixbuf_draw_line(pixbuf, x, y, width, height, xpos, y, xpos, y + height, c, c, c, alpha);
189                 }
190 }
191
192 static void histogram_hgrid(Histogram *histogram, GdkPixbuf *pixbuf, gint x, gint y, gint width, gint height)
193 {
194         static gint c = 160;
195         static gint alpha = 250;
196         guint i;
197         float add;
198         
199         if (histogram->hgrid == 0) return;
200
201         add = height / (float)histogram->hgrid;
202
203         for (i = 1; i < histogram->hgrid; i++)
204                 {
205                 gint ypos = y + (int)(i * add + 0.5);
206         
207                 pixbuf_draw_line(pixbuf, x, y, width, height, x, ypos, x + width, ypos, c, c, c, alpha);
208                 }
209 }
210
211 gint histogram_draw(Histogram *histogram, const HistMap *histmap, GdkPixbuf *pixbuf, gint x, gint y, gint width, gint height)
212 {
213         /* FIXME: use the coordinates correctly */
214         gint i;
215         gulong max = 0;
216         gdouble logmax;
217
218         if (!histogram || !histmap) return 0;
219         
220         /* Draw the grid */
221         histogram_vgrid(histogram, pixbuf, x, y, width, height);
222         histogram_hgrid(histogram, pixbuf, x, y, width, height);
223
224         for (i = 0; i < 1024; i++) {
225 #if 0
226                 /* this is probably broken for MAX or VAL mode */
227                 gint flag = 0;
228
229                 switch (histogram->channel_mode)
230                         {
231                         case HCHAN_RGB: if ((i % HISTMAP_CHANNELS) < 3) flag = 1; break;
232                         case HCHAN_R:   if ((i % HISTMAP_CHANNELS) == HISTMAP_CHANNEL_R) flag = 1; break;
233                         case HCHAN_G:   if ((i % HISTMAP_CHANNELS) == HISTMAP_CHANNEL_G) flag = 1; break;
234                         case HCHAN_B:   if ((i % HISTMAP_CHANNELS) == HISTMAP_CHANNEL_B) flag = 1; break;
235                         case HCHAN_VAL: if ((i % HISTMAP_CHANNELS) == HISTMAP_CHANNEL_AVG) flag = 1; break;
236                         case HCHAN_MAX: if ((i % HISTMAP_CHANNELS) == HISTMAP_CHANNEL_MAX) flag = 1; break;
237                         }
238                 if (flag && histmap->histmap[i] > max) max = histmap->histmap[i];
239 #else
240                 if (histmap->histmap[i] > max) max = histmap->histmap[i];
241 #endif
242         }
243
244         logmax = log(max);
245         for (i = 0; i < width; i++)
246                 {
247                 gint j;
248                 glong v[4] = {0, 0, 0, 0};
249                 gint rplus = 0;
250                 gint gplus = 0;
251                 gint bplus = 0;
252                 gint ii = i * HISTMAP_SIZE / width;
253                 gint combine  = (HISTMAP_SIZE - 1) / width + 1;
254
255                 for (j = 0; j < combine; j++)
256                         {
257                         v[0] += histmap->histmap[(ii + j) * HISTMAP_CHANNELS + HISTMAP_CHANNEL_R]; // r
258                         v[1] += histmap->histmap[(ii + j) * HISTMAP_CHANNELS + HISTMAP_CHANNEL_G]; // g
259                         v[2] += histmap->histmap[(ii + j) * HISTMAP_CHANNELS + HISTMAP_CHANNEL_B]; // b
260                         v[3] += histmap->histmap[(ii + j) * HISTMAP_CHANNELS + 
261                                 ((histogram->channel_mode == HCHAN_VAL) ? HISTMAP_CHANNEL_AVG : HISTMAP_CHANNEL_MAX)]; // value, max
262                         }
263                         
264                 for (j = 0; j < 4; j++)
265                         {
266                         v[j] /= combine;
267                         }
268
269                 for (j = 0; j < 4; j++)
270                         {
271                         gint max2 = 0;
272                         gint k;
273                 
274                         for (k = 1; k < 4; k++)
275                                 if (v[k] > v[max2]) max2 = k;
276                         
277                         if (histogram->channel_mode >= HCHAN_RGB
278                             || max2 == histogram->channel_mode)
279                                 {
280                                 gulong pt;
281                                 gint r = rplus;
282                                 gint g = gplus;
283                                 gint b = bplus;
284
285                                 switch (max2)
286                                         {
287                                         case HCHAN_R: rplus = r = 255; break;
288                                         case HCHAN_G: gplus = g = 255; break;
289                                         case HCHAN_B: bplus = b = 255; break;
290                                         }
291
292                                 switch (histogram->channel_mode)
293                                         {
294                                         case HCHAN_RGB:
295                                                 if (r == 255 && g == 255 && b == 255)
296                                                         {
297                                                         r = 0; b = 0; g = 0;
298                                                         }
299                                                 break;
300                                         case HCHAN_R:          b = 0; g = 0; break;
301                                         case HCHAN_G:   r = 0; b = 0;        break;
302                                         case HCHAN_B:   r = 0;        g = 0; break;
303                                         case HCHAN_MAX:
304                                         case HCHAN_VAL: r = 0; b = 0; g = 0; break;
305                                         }
306                                 
307                                 if (v[max2] == 0)
308                                         pt = 0;
309                                 else if (histogram->log_mode)
310                                         pt = ((float)log(v[max2])) / logmax * (height - 1);
311                                 else
312                                         pt = ((float)v[max2])/ max * (height - 1);
313
314                                 pixbuf_draw_line(pixbuf,
315                                         x, y, width, height,
316                                         x + i, y + height, x + i, y + height - pt,
317                                         r, g, b, 255);
318                                 }
319                         v[max2] = -1;
320                         }
321                 }
322
323         return TRUE;
324 }
325
326 void histogram_notify_cb(FileData *fd, NotifyType type, gpointer data)
327 {
328         if (type != NOTIFY_TYPE_INTERNAL && fd->histmap)
329                 {
330                 g_free(fd->histmap);
331                 fd->histmap = NULL;
332                 }
333 }
334
335 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */