1 | /*
|
---|
2 |
|
---|
3 | gbmgamma.c - Gamma correct General Bitmap
|
---|
4 |
|
---|
5 | */
|
---|
6 |
|
---|
7 | /*...sincludes:0:*/
|
---|
8 | #include <stdio.h>
|
---|
9 | #include <ctype.h>
|
---|
10 | #include <math.h>
|
---|
11 | #include <string.h>
|
---|
12 | #include <stddef.h>
|
---|
13 | #include <stdlib.h>
|
---|
14 | #include <stdarg.h>
|
---|
15 | #if defined(AIX) || defined(LINUX) || defined(MAC)
|
---|
16 | #include <unistd.h>
|
---|
17 | #else
|
---|
18 | #include <io.h>
|
---|
19 | #endif
|
---|
20 | #include <fcntl.h>
|
---|
21 | #ifdef MAC
|
---|
22 | #include <types.h>
|
---|
23 | #include <stat.h>
|
---|
24 | #else
|
---|
25 | #include <sys/types.h>
|
---|
26 | #include <sys/stat.h>
|
---|
27 | #endif
|
---|
28 | #ifndef O_BINARY
|
---|
29 | #define O_BINARY 0
|
---|
30 | #endif
|
---|
31 | #include "gbm.h"
|
---|
32 |
|
---|
33 | /*...vgbm\46\h:0:*/
|
---|
34 | /*...e*/
|
---|
35 |
|
---|
36 | static char progname[] = "gbmgamma";
|
---|
37 |
|
---|
38 | /*...sfatal:0:*/
|
---|
39 | static void fatal(const char *fmt, ...)
|
---|
40 | {
|
---|
41 | va_list vars;
|
---|
42 | char s[256+1];
|
---|
43 |
|
---|
44 | va_start(vars, fmt);
|
---|
45 | vsprintf(s, fmt, vars);
|
---|
46 | va_end(vars);
|
---|
47 | fprintf(stderr, "%s: %s\n", progname, s);
|
---|
48 | exit(1);
|
---|
49 | }
|
---|
50 | /*...e*/
|
---|
51 | /*...susage:0:*/
|
---|
52 | static void usage(void)
|
---|
53 | {
|
---|
54 | int ft, n_ft;
|
---|
55 |
|
---|
56 | fprintf(stderr, "usage: %s [-m map] [-g gamma] [-s shelf] fn1.ext{,opt} [--] [fn2.ext{,opt}]\n", progname);
|
---|
57 | fprintf(stderr, "flags: -m map mapping in the form ?_to_? (default: i_to_l), where ? is\n");
|
---|
58 | fprintf(stderr, " i physical intensitys (eg: raytracer output)\n");
|
---|
59 | fprintf(stderr, " p gamma corrected for a specific monitor\n");
|
---|
60 | fprintf(stderr, " l L* cyclometric linear perceived intensitys (as in PM)\n");
|
---|
61 | fprintf(stderr, " -g gamma set monitor gamma (default 2.1)\n");
|
---|
62 | fprintf(stderr, " -s shelf set monitor shelf (default 0.0)\n");
|
---|
63 | fprintf(stderr, " gamma and shelf only used for mapping to or from p\n");
|
---|
64 | fprintf(stderr, " for 8514 monitor gamma=2.3,shelf=0.136\n");
|
---|
65 | fprintf(stderr, " for 8515 monitor gamma=2.1,shelf=0.0\n");
|
---|
66 | fprintf(stderr, " fn1.ext{,opt} input filename (with any format specific options)\n");
|
---|
67 | fprintf(stderr, " fn2.ext{,opt} optional output filename (or will use fn1 if not present)\n");
|
---|
68 | fprintf(stderr, " ext's are used to deduce desired bitmap file formats\n");
|
---|
69 |
|
---|
70 | gbm_init();
|
---|
71 | gbm_query_n_filetypes(&n_ft);
|
---|
72 | for ( ft = 0; ft < n_ft; ft++ )
|
---|
73 | {
|
---|
74 | GBMFT gbmft;
|
---|
75 |
|
---|
76 | gbm_query_filetype(ft, &gbmft);
|
---|
77 | fprintf(stderr, " %s when ext in [%s]\n",
|
---|
78 | gbmft.short_name, gbmft.extensions);
|
---|
79 | }
|
---|
80 | gbm_deinit();
|
---|
81 |
|
---|
82 | fprintf(stderr, " opt's bitmap format specific options\n");
|
---|
83 |
|
---|
84 | exit(1);
|
---|
85 | }
|
---|
86 | /*...e*/
|
---|
87 | /*...sget_opt_double:0:*/
|
---|
88 | static double get_opt_double(const char *s, const char *name)
|
---|
89 | {
|
---|
90 | double v;
|
---|
91 |
|
---|
92 | if ( s == NULL )
|
---|
93 | fatal("missing %s argument", name);
|
---|
94 | sscanf(s, "%lf", &v);
|
---|
95 |
|
---|
96 | return v;
|
---|
97 | }
|
---|
98 | /*...e*/
|
---|
99 | /*...ssame:0:*/
|
---|
100 | static BOOLEAN same(const char *s1, const char *s2, int n)
|
---|
101 | {
|
---|
102 | for ( ; n--; s1++, s2++ )
|
---|
103 | if ( tolower(*s1) != tolower(*s2) )
|
---|
104 | return FALSE;
|
---|
105 | return TRUE;
|
---|
106 | }
|
---|
107 | /*...e*/
|
---|
108 | /*...smain:0:*/
|
---|
109 | /*...smapinfos:0:*/
|
---|
110 | #define CVT_NONE 0
|
---|
111 | #define CVT_I_TO_P 1
|
---|
112 | #define CVT_P_TO_I 2
|
---|
113 | #define CVT_I_TO_L 3
|
---|
114 | #define CVT_L_TO_I 4
|
---|
115 | #define CVT_P_TO_L 5
|
---|
116 | #define CVT_L_TO_P 6
|
---|
117 |
|
---|
118 | typedef struct { char *name; int m; } MAPINFO;
|
---|
119 |
|
---|
120 | static MAPINFO mapinfos[] =
|
---|
121 | {
|
---|
122 | "none", CVT_NONE,
|
---|
123 | "i_to_p", CVT_I_TO_P,
|
---|
124 | "p_to_i", CVT_P_TO_I,
|
---|
125 | "i_to_l", CVT_I_TO_L,
|
---|
126 | "l_to_i", CVT_L_TO_I,
|
---|
127 | "p_to_l", CVT_P_TO_L,
|
---|
128 | "l_to_p", CVT_L_TO_P,
|
---|
129 | };
|
---|
130 |
|
---|
131 | #define N_MAPINFOS (sizeof(mapinfos)/sizeof(mapinfos[0]))
|
---|
132 | /*...e*/
|
---|
133 |
|
---|
134 | /*...smap_compute:0:*/
|
---|
135 | /*...slstar_from_i:0:*/
|
---|
136 | static double lstar_from_i(double y)
|
---|
137 | {
|
---|
138 | y = pow(1.16 * y, 1.0/3.0) - 0.16;
|
---|
139 |
|
---|
140 | if ( y < 0.0 ) y = 0.0; else if ( y > 1.0 ) y = 1.0;
|
---|
141 |
|
---|
142 | return y;
|
---|
143 | }
|
---|
144 | /*...e*/
|
---|
145 | /*...si_from_lstar:0:*/
|
---|
146 | static double i_from_lstar(double y)
|
---|
147 | {
|
---|
148 | y = pow(y + 0.16, 3.0) / 1.16;
|
---|
149 |
|
---|
150 | if ( y < 0.0 ) y = 0.0; else if ( y > 1.0 ) y = 1.0;
|
---|
151 |
|
---|
152 | return y;
|
---|
153 | }
|
---|
154 | /*...e*/
|
---|
155 | /*...spal_from_i:0:*/
|
---|
156 | static double pal_from_i(double y, double gam, double shelf)
|
---|
157 | {
|
---|
158 | y = pow(y,1.0 / gam) * (1.0 - shelf) + shelf;
|
---|
159 |
|
---|
160 | if ( y < 0.0 ) y = 0.0; else if ( y > 1.0 ) y = 1.0;
|
---|
161 |
|
---|
162 | return y;
|
---|
163 | }
|
---|
164 | /*...e*/
|
---|
165 | /*...si_from_pal:0:*/
|
---|
166 | static double i_from_pal(double y, double gam, double shelf)
|
---|
167 | {
|
---|
168 | if ( y >= shelf )
|
---|
169 | y = pow((y - shelf) / (1.0 - shelf), gam);
|
---|
170 | else
|
---|
171 | y = 0.0;
|
---|
172 |
|
---|
173 | if ( y < 0.0 ) y = 0.0; else if ( y > 1.0 ) y = 1.0;
|
---|
174 |
|
---|
175 | return y;
|
---|
176 | }
|
---|
177 | /*...e*/
|
---|
178 |
|
---|
179 | static void map_compute(int m, byte remap[], double gam, double shelf)
|
---|
180 | {
|
---|
181 | int i;
|
---|
182 |
|
---|
183 | for ( i = 0; i < 0x100; i++ )
|
---|
184 | {
|
---|
185 | double y = (double) i / 255.0;
|
---|
186 |
|
---|
187 | switch ( m )
|
---|
188 | {
|
---|
189 | case CVT_I_TO_P: y = pal_from_i(y, gam, shelf); break;
|
---|
190 | case CVT_P_TO_I: y = i_from_pal(y, gam, shelf); break;
|
---|
191 | case CVT_I_TO_L: y = lstar_from_i(y); break;
|
---|
192 | case CVT_L_TO_I: y = i_from_lstar(y); break;
|
---|
193 | case CVT_P_TO_L: y = lstar_from_i(i_from_pal(y, gam, shelf)); break;
|
---|
194 | case CVT_L_TO_P: y = pal_from_i(i_from_lstar(y), gam, shelf); break;
|
---|
195 | }
|
---|
196 |
|
---|
197 | remap[i] = (byte) (y * 255.0);
|
---|
198 | }
|
---|
199 | }
|
---|
200 | /*...e*/
|
---|
201 | /*...smap_data:0:*/
|
---|
202 | static void map_data(byte *data, int w, int h, const byte remap[])
|
---|
203 | {
|
---|
204 | int stride = ((w * 3 + 3) & ~3);
|
---|
205 | int x, y;
|
---|
206 |
|
---|
207 | for ( y = 0; y < h; y++, data += stride )
|
---|
208 | for ( x = 0; x < w * 3; x++ )
|
---|
209 | data[x] = remap[data[x]];
|
---|
210 | }
|
---|
211 | /*...e*/
|
---|
212 | /*...smap_palette:0:*/
|
---|
213 | static void map_palette(GBMRGB *gbmrgb, int npals, const byte remap[])
|
---|
214 | {
|
---|
215 | for ( ; npals--; gbmrgb++ )
|
---|
216 | {
|
---|
217 | gbmrgb->b = remap[gbmrgb->b];
|
---|
218 | gbmrgb->g = remap[gbmrgb->g];
|
---|
219 | gbmrgb->r = remap[gbmrgb->r];
|
---|
220 | }
|
---|
221 | }
|
---|
222 | /*...e*/
|
---|
223 |
|
---|
224 | int main(int argc, char *argv[])
|
---|
225 | {
|
---|
226 | char fn_src[500+1], fn_dst[500+1], *opt_src, *opt_dst;
|
---|
227 | int fd, ft_src, ft_dst, i, stride, bytes, flag, m;
|
---|
228 | GBM_ERR rc;
|
---|
229 | GBMFT gbmft;
|
---|
230 | GBM gbm;
|
---|
231 | GBMRGB gbmrgb[0x100];
|
---|
232 | byte *data;
|
---|
233 | char *map = "none";
|
---|
234 | byte remap[0x100];
|
---|
235 | double gam = 2.1, shelf = 0.0;
|
---|
236 |
|
---|
237 | /*...scommand line arguments:8:*/
|
---|
238 | for ( i = 1; i < argc; i++ )
|
---|
239 | {
|
---|
240 | if ( argv[i][0] != '-' )
|
---|
241 | break;
|
---|
242 | else if ( argv[i][1] == '-' )
|
---|
243 | { ++i; break; }
|
---|
244 | switch ( argv[i][1] )
|
---|
245 | {
|
---|
246 | case 'm':
|
---|
247 | if ( ++i == argc )
|
---|
248 | fatal("expected map argument");
|
---|
249 | map = argv[i];
|
---|
250 | break;
|
---|
251 | case 'g':
|
---|
252 | if ( ++i == argc ) usage();
|
---|
253 | gam = get_opt_double(argv[i], "gam");
|
---|
254 | if ( gam < 0.1 || gam > 10.0 )
|
---|
255 | fatal("only gammas in the range 0.1 to 10.0 are sensible");
|
---|
256 | break;
|
---|
257 | case 's':
|
---|
258 | if ( ++i == argc ) usage();
|
---|
259 | shelf = get_opt_double(argv[i], "shelf");
|
---|
260 | break;
|
---|
261 | default:
|
---|
262 | usage();
|
---|
263 | break;
|
---|
264 | }
|
---|
265 | }
|
---|
266 | /*...e*/
|
---|
267 |
|
---|
268 | /*...sdeduce mapping and bits per pixel etc\46\:8:*/
|
---|
269 | {
|
---|
270 | int j;
|
---|
271 |
|
---|
272 | for ( j = 0; j < N_MAPINFOS; j++ )
|
---|
273 | if ( same(map, mapinfos[j].name, (int) strlen(map) + 1) )
|
---|
274 | break;
|
---|
275 | if ( j == N_MAPINFOS )
|
---|
276 | fatal("unrecognised mapping %s", map);
|
---|
277 | m = mapinfos[j].m;
|
---|
278 | }
|
---|
279 | /*...e*/
|
---|
280 |
|
---|
281 | if ( i == argc )
|
---|
282 | usage();
|
---|
283 | strcpy(fn_src, argv[i++]);
|
---|
284 | strcpy(fn_dst, ( i == argc ) ? fn_src : argv[i++]);
|
---|
285 | if ( i < argc )
|
---|
286 | usage();
|
---|
287 |
|
---|
288 | if ( (opt_src = strchr(fn_src, ',')) != NULL )
|
---|
289 | *opt_src++ = '\0';
|
---|
290 | else
|
---|
291 | opt_src = "";
|
---|
292 |
|
---|
293 | if ( (opt_dst = strchr(fn_dst, ',')) != NULL )
|
---|
294 | *opt_dst++ = '\0';
|
---|
295 | else
|
---|
296 | opt_dst = "";
|
---|
297 |
|
---|
298 | gbm_init();
|
---|
299 |
|
---|
300 | if ( gbm_guess_filetype(fn_src, &ft_src) != GBM_ERR_OK )
|
---|
301 | fatal("can't guess bitmap file format for %s", fn_src);
|
---|
302 |
|
---|
303 | if ( gbm_guess_filetype(fn_dst, &ft_dst) != GBM_ERR_OK )
|
---|
304 | fatal("can't guess bitmap file format for %s", fn_dst);
|
---|
305 |
|
---|
306 | if ( (fd = gbm_io_open(fn_src, O_RDONLY|O_BINARY)) == -1 )
|
---|
307 | fatal("can't open %s", fn_src);
|
---|
308 |
|
---|
309 | if ( (rc = gbm_read_header(fn_src, fd, ft_src, &gbm, opt_src)) != GBM_ERR_OK )
|
---|
310 | {
|
---|
311 | gbm_io_close(fd);
|
---|
312 | fatal("can't read header of %s: %s", fn_src, gbm_err(rc));
|
---|
313 | }
|
---|
314 |
|
---|
315 | gbm_query_filetype(ft_dst, &gbmft);
|
---|
316 | switch ( gbm.bpp )
|
---|
317 | {
|
---|
318 | case 24: flag = GBM_FT_W24; break;
|
---|
319 | case 8: flag = GBM_FT_W8; break;
|
---|
320 | case 4: flag = GBM_FT_W4; break;
|
---|
321 | case 1: flag = GBM_FT_W1; break;
|
---|
322 | }
|
---|
323 | if ( (gbmft.flags & flag) == 0 )
|
---|
324 | {
|
---|
325 | gbm_io_close(fd);
|
---|
326 | fatal("output bitmap format %s does not support writing %d bpp data",
|
---|
327 | gbmft.short_name, gbm.bpp);
|
---|
328 | }
|
---|
329 |
|
---|
330 | if ( (rc = gbm_read_palette(fd, ft_src, &gbm, gbmrgb)) != GBM_ERR_OK )
|
---|
331 | {
|
---|
332 | gbm_io_close(fd);
|
---|
333 | fatal("can't read palette of %s: %s", fn_src, gbm_err(rc));
|
---|
334 | }
|
---|
335 |
|
---|
336 | stride = ( ((gbm.w * gbm.bpp + 31)/32) * 4 );
|
---|
337 | bytes = stride * gbm.h;
|
---|
338 | if ( (data = malloc((size_t) bytes)) == NULL )
|
---|
339 | {
|
---|
340 | gbm_io_close(fd);
|
---|
341 | fatal("out of memory allocating %d bytes for bitmap", bytes);
|
---|
342 | }
|
---|
343 |
|
---|
344 | if ( (rc = gbm_read_data(fd, ft_src, &gbm, data)) != GBM_ERR_OK )
|
---|
345 | {
|
---|
346 | gbm_io_close(fd);
|
---|
347 | fatal("can't read bitmap data of %s: %s", fn_src, gbm_err(rc));
|
---|
348 | }
|
---|
349 |
|
---|
350 | gbm_io_close(fd);
|
---|
351 |
|
---|
352 | map_compute(m, remap, gam, shelf);
|
---|
353 |
|
---|
354 | if ( gbm.bpp == 24 )
|
---|
355 | map_data(data, gbm.w, gbm.h, remap);
|
---|
356 | else
|
---|
357 | map_palette(gbmrgb, 1 << gbm.bpp, remap);
|
---|
358 |
|
---|
359 | if ( (fd = gbm_io_create(fn_dst, O_WRONLY|O_BINARY)) == -1 )
|
---|
360 | fatal("can't create %s", fn_dst);
|
---|
361 |
|
---|
362 | if ( (rc = gbm_write(fn_dst, fd, ft_dst, &gbm, gbmrgb, data, opt_dst)) != GBM_ERR_OK )
|
---|
363 | {
|
---|
364 | gbm_io_close(fd);
|
---|
365 | remove(fn_dst);
|
---|
366 | fatal("can't write %s: %s", fn_dst, gbm_err(rc));
|
---|
367 | }
|
---|
368 |
|
---|
369 | gbm_io_close(fd);
|
---|
370 |
|
---|
371 | free(data);
|
---|
372 |
|
---|
373 | gbm_deinit();
|
---|
374 |
|
---|
375 | return 0;
|
---|
376 | }
|
---|
377 | /*...e*/
|
---|