Add year 2009 to copyright info everywhere.
[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 HISTOGRAM_SIZE 256
27
28 struct _Histogram {
29         gulong histmap[HISTOGRAM_SIZE*4];
30         gint histogram_chan;
31         gint histogram_logmode;
32 };
33
34
35 Histogram *histogram_new(void)
36 {
37         Histogram *histogram;
38
39         histogram = g_new0(Histogram, 1);
40         histogram->histogram_chan = options->histogram.last_channel_mode;
41         histogram->histogram_logmode = options->histogram.last_log_mode;
42
43         return histogram;
44 }
45
46 void histogram_free(Histogram *histogram)
47 {
48         g_free(histogram);
49 }
50
51
52 gint histogram_set_channel(Histogram *histogram, gint chan)
53 {
54         if (!histogram) return 0;
55         options->histogram.last_channel_mode = histogram->histogram_chan = chan;
56         return chan;
57 }
58
59 gint histogram_get_channel(Histogram *histogram)
60 {
61         if (!histogram) return 0;
62         return histogram->histogram_chan;
63 }
64
65 gint histogram_set_mode(Histogram *histogram, gint mode)
66 {
67         if (!histogram) return 0;
68         options->histogram.last_log_mode = histogram->histogram_logmode = mode;
69         return mode;
70 }
71
72 gint histogram_get_mode(Histogram *histogram)
73 {
74         if (!histogram) return 0;
75         return histogram->histogram_logmode;
76 }
77
78 const gchar *histogram_label(Histogram *histogram)
79 {
80         const gchar *t1 = "";
81         
82         if (!histogram) return NULL;
83
84         if (histogram->histogram_logmode)
85                 switch (histogram->histogram_chan)
86                         {
87                         case HCHAN_R:   t1 = _("logarithmical histogram on red"); break;
88                         case HCHAN_G:   t1 = _("logarithmical histogram on green"); break;
89                         case HCHAN_B:   t1 = _("logarithmical histogram on blue"); break;
90                         case HCHAN_VAL: t1 = _("logarithmical histogram on value"); break;
91                         case HCHAN_RGB: t1 = _("logarithmical histogram on RGB"); break;
92                         case HCHAN_MAX: t1 = _("logarithmical histogram on max value"); break;
93                         }
94         else
95                 switch (histogram->histogram_chan)
96                         {
97                         case HCHAN_R:   t1 = _("linear histogram on red"); break;
98                         case HCHAN_G:   t1 = _("linear histogram on green"); break;
99                         case HCHAN_B:   t1 = _("linear histogram on blue"); break;
100                         case HCHAN_VAL: t1 = _("linear histogram on value"); break;
101                         case HCHAN_RGB: t1 = _("linear histogram on RGB"); break;
102                         case HCHAN_MAX: t1 = _("linear histogram on max value"); break;
103                         }
104         return t1;
105 }
106
107 gulong histogram_read(Histogram *histogram, GdkPixbuf *imgpixbuf)
108 {
109         gint w, h, i, j, srs, has_alpha, step;
110         guchar *s_pix;
111
112         if (!histogram) return 0;
113
114         w = gdk_pixbuf_get_width(imgpixbuf);
115         h = gdk_pixbuf_get_height(imgpixbuf);
116         srs = gdk_pixbuf_get_rowstride(imgpixbuf);
117         s_pix = gdk_pixbuf_get_pixels(imgpixbuf);
118         has_alpha = gdk_pixbuf_get_has_alpha(imgpixbuf);
119
120         memset(histogram->histmap, 0, sizeof(histogram->histmap));
121
122         /* code duplication is here to speed up the calculation */
123         step = 3 + !!(has_alpha);
124         if (histogram->histogram_chan == HCHAN_MAX)
125                 {
126                 for (i = 0; i < h; i++)
127                         {
128                         guchar *sp = s_pix + (i * srs); /* 8bit */
129                         for (j = 0; j < w; j++)
130                                 {
131                                 guchar t = sp[0];
132                                 if (sp[1]>t) t = sp[1];
133                                 if (sp[2]>t) t = sp[2];
134
135                                 histogram->histmap[sp[0] + 0 * HISTOGRAM_SIZE]++;
136                                 histogram->histmap[sp[1] + 1 * HISTOGRAM_SIZE]++;
137                                 histogram->histmap[sp[2] + 2 * HISTOGRAM_SIZE]++;
138                                 histogram->histmap[t + 3 * HISTOGRAM_SIZE]++;
139                                 sp += step;
140                                 }
141                         }
142                 }
143         else
144                 {
145                 for (i = 0; i < h; i++)
146                         {
147                         guchar *sp = s_pix + (i * srs); /* 8bit */
148                         for (j = 0; j < w; j++)
149                                 {
150                                 histogram->histmap[sp[0] + 0 * HISTOGRAM_SIZE]++;
151                                 histogram->histmap[sp[1] + 1 * HISTOGRAM_SIZE]++;
152                                 histogram->histmap[sp[2] + 2 * HISTOGRAM_SIZE]++;
153                                 histogram->histmap[3 * HISTOGRAM_SIZE + (sp[0]+sp[1]+sp[2])/3]++;
154                                 sp += step;
155                                 }
156                         }
157                 }
158
159         return w*h;
160 }
161
162 gint histogram_draw(Histogram *histogram, GdkPixbuf *pixbuf, gint x, gint y, gint width, gint height)
163 {
164         /* FIXME: use the coordinates correctly */
165         gint i;
166         gulong max = 0;
167         gdouble logmax;
168
169         if (!histogram) return 0;
170
171         for (i = 0; i < 1024; i++) {
172                 gint flag = 0;
173
174                 switch (histogram->histogram_chan)
175                         {
176                         case HCHAN_RGB: if ((i%4) != 3) flag = 1; break;
177                         case HCHAN_R:   if ((i%4) == 0) flag = 1; break;
178                         case HCHAN_G:   if ((i%4) == 1) flag = 1; break;
179                         case HCHAN_B:   if ((i%4) == 2) flag = 1; break;
180                         case HCHAN_VAL: if ((i%4) == 3) flag = 1; break;
181                         case HCHAN_MAX: if ((i%4) == 3) flag = 1; break;
182                         }
183                 if (flag && histogram->histmap[i] > max) max = histogram->histmap[i];
184         }
185
186         logmax = log(max);
187         for (i = 0; i < width; i++)
188                 {
189                 gint j;
190                 glong v[4] = {0, 0, 0, 0};
191                 gint rplus = 0;
192                 gint gplus = 0;
193                 gint bplus = 0;
194                 gint ii = i * HISTOGRAM_SIZE / width;
195                 gint combine  = (HISTOGRAM_SIZE - 1) / width + 1;
196
197                 for (j = 0; j < combine; j++)
198                         {
199                         v[0] += histogram->histmap[ii + j + 0 * HISTOGRAM_SIZE]; // r
200                         v[1] += histogram->histmap[ii + j + 1 * HISTOGRAM_SIZE]; // g
201                         v[2] += histogram->histmap[ii + j + 2 * HISTOGRAM_SIZE]; // b
202                         v[3] += histogram->histmap[ii + j + 3 * HISTOGRAM_SIZE]; // value, max
203                         }
204
205                 for (j = 0; j < 4; j++)
206                         {
207                         gint max2 = 0;
208                         gint k;
209                 
210                         for (k = 1; k < 4; k++)
211                                 if (v[k] > v[max2]) max2 = k;
212                         
213                         if (histogram->histogram_chan >= HCHAN_RGB
214                             || max2 == histogram->histogram_chan)
215                                 {
216                                 gulong pt;
217                                 gint r = rplus;
218                                 gint g = gplus;
219                                 gint b = bplus;
220
221                                 switch (max2)
222                                         {
223                                         case HCHAN_R: rplus = r = 255; break;
224                                         case HCHAN_G: gplus = g = 255; break;
225                                         case HCHAN_B: bplus = b = 255; break;
226                                         }
227
228                                 switch (histogram->histogram_chan)
229                                         {
230                                         case HCHAN_RGB:
231                                                 if (r == 255 && g == 255 && b == 255)
232                                                         {
233                                                         r = 0; b = 0; g = 0;
234                                                         }
235                                                 break;
236                                         case HCHAN_R:          b = 0; g = 0; break;
237                                         case HCHAN_G:   r = 0; b = 0;        break;
238                                         case HCHAN_B:   r = 0;        g = 0; break;
239                                         case HCHAN_MAX:
240                                         case HCHAN_VAL: r = 0; b = 0; g = 0; break;
241                                         }
242                                 
243                                 if (v[max2] == 0)
244                                         pt = 0;
245                                 else if (histogram->histogram_logmode)
246                                         pt = ((float)log(v[max2])) / logmax * (height - 1);
247                                 else
248                                         pt = ((float)v[max2])/ max * (height - 1);
249
250                                 pixbuf_draw_line(pixbuf,
251                                         x, y, width, height,
252                                         x + i, y + height, x + i, y + height - pt,
253                                         r, g, b, 255);
254                                 }
255                         v[max2] = -1;
256                         }
257                 }
258
259         return TRUE;
260 }
261 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */