rev |
line source |
nuclear@14
|
1 /* This file contains code to read and write four byte rgbe file format
|
nuclear@14
|
2 * developed by Greg Ward. It handles the conversions between rgbe and
|
nuclear@14
|
3 * pixels consisting of floats. The data is assumed to be an array of floats.
|
nuclear@14
|
4 * By default there are three floats per pixel in the order red, green, blue.
|
nuclear@14
|
5 * (RGBE_DATA_??? values control this.)
|
nuclear@14
|
6 *
|
nuclear@14
|
7 * written by Bruce Walter (bjw@graphics.cornell.edu) 5/26/95
|
nuclear@14
|
8 * based on code written by Greg Ward
|
nuclear@14
|
9 * minor modifications by John Tsiombikas (nuclear@member.fsf.org) apr.9 2007
|
nuclear@14
|
10 */
|
nuclear@14
|
11
|
nuclear@14
|
12 #include <stdio.h>
|
nuclear@14
|
13 #include <stdlib.h>
|
nuclear@14
|
14 #include <string.h>
|
nuclear@14
|
15 #include <math.h>
|
nuclear@14
|
16 #include <ctype.h>
|
nuclear@14
|
17 #include <errno.h>
|
nuclear@14
|
18 #include "imago2.h"
|
nuclear@14
|
19 #include "ftype_module.h"
|
nuclear@14
|
20
|
nuclear@14
|
21
|
nuclear@14
|
22 typedef struct {
|
nuclear@14
|
23 int valid; /* indicate which fields are valid */
|
nuclear@14
|
24 char programtype[16]; /* listed at beginning of file to identify it
|
nuclear@14
|
25 * after "#?". defaults to "RGBE" */
|
nuclear@14
|
26 float gamma; /* image has already been gamma corrected with
|
nuclear@14
|
27 * given gamma. defaults to 1.0 (no correction) */
|
nuclear@14
|
28 float exposure; /* a value of 1.0 in an image corresponds to
|
nuclear@14
|
29 * <exposure> watts/steradian/m^2.
|
nuclear@14
|
30 * defaults to 1.0 */
|
nuclear@14
|
31 } rgbe_header_info;
|
nuclear@14
|
32
|
nuclear@14
|
33
|
nuclear@14
|
34 static int check(struct img_io *io);
|
nuclear@14
|
35 static int read(struct img_pixmap *img, struct img_io *io);
|
nuclear@14
|
36 static int write(struct img_pixmap *img, struct img_io *io);
|
nuclear@14
|
37
|
nuclear@14
|
38 static int rgbe_read_header(struct img_io *io, int *width, int *height, rgbe_header_info * info);
|
nuclear@14
|
39 static int rgbe_read_pixels_rle(struct img_io *io, float *data, int scanline_width, int num_scanlines);
|
nuclear@14
|
40
|
nuclear@14
|
41
|
nuclear@14
|
42 int img_register_rgbe(void)
|
nuclear@14
|
43 {
|
nuclear@14
|
44 static struct ftype_module mod = {".rgbe", check, read, write};
|
nuclear@14
|
45 return img_register_module(&mod);
|
nuclear@14
|
46 }
|
nuclear@14
|
47
|
nuclear@14
|
48
|
nuclear@14
|
49 static int check(struct img_io *io)
|
nuclear@14
|
50 {
|
nuclear@14
|
51 int xsz, ysz, res;
|
nuclear@14
|
52 long pos = io->seek(0, SEEK_CUR, io->uptr);
|
nuclear@14
|
53
|
nuclear@14
|
54 rgbe_header_info hdr;
|
nuclear@14
|
55 res = rgbe_read_header(io, &xsz, &ysz, &hdr);
|
nuclear@14
|
56
|
nuclear@14
|
57 io->seek(pos, SEEK_SET, io->uptr);
|
nuclear@14
|
58 return res;
|
nuclear@14
|
59 }
|
nuclear@14
|
60
|
nuclear@14
|
61 static int read(struct img_pixmap *img, struct img_io *io)
|
nuclear@14
|
62 {
|
nuclear@14
|
63 int xsz, ysz;
|
nuclear@14
|
64 rgbe_header_info hdr;
|
nuclear@14
|
65
|
nuclear@14
|
66 if(rgbe_read_header(io, &xsz, &ysz, &hdr) == -1) {
|
nuclear@14
|
67 return -1;
|
nuclear@14
|
68 }
|
nuclear@14
|
69
|
nuclear@14
|
70 if(img_set_pixels(img, xsz, ysz, IMG_FMT_RGBF, 0) == -1) {
|
nuclear@14
|
71 return -1;
|
nuclear@14
|
72 }
|
nuclear@14
|
73 if(rgbe_read_pixels_rle(io, img->pixels, xsz, ysz) == -1) {
|
nuclear@14
|
74 return -1;
|
nuclear@14
|
75 }
|
nuclear@14
|
76 return 0;
|
nuclear@14
|
77 }
|
nuclear@14
|
78
|
nuclear@14
|
79 static int write(struct img_pixmap *img, struct img_io *io)
|
nuclear@14
|
80 {
|
nuclear@14
|
81 return -1; /* TODO */
|
nuclear@14
|
82 }
|
nuclear@14
|
83
|
nuclear@14
|
84
|
nuclear@14
|
85 static int iofgetc(struct img_io *io)
|
nuclear@14
|
86 {
|
nuclear@14
|
87 char c;
|
nuclear@14
|
88 return io->read(&c, 1, io->uptr) < 1 ? -1 : c;
|
nuclear@14
|
89 }
|
nuclear@14
|
90
|
nuclear@14
|
91 static char *iofgets(char *buf, int size, struct img_io *io)
|
nuclear@14
|
92 {
|
nuclear@14
|
93 int c;
|
nuclear@14
|
94 char *ptr = buf;
|
nuclear@14
|
95
|
nuclear@14
|
96 while(--size > 0 && (c = iofgetc(io)) != -1) {
|
nuclear@14
|
97 *ptr++ = c;
|
nuclear@14
|
98 if(c == '\n') break;
|
nuclear@14
|
99 }
|
nuclear@14
|
100 *ptr = 0;
|
nuclear@14
|
101
|
nuclear@14
|
102 return ptr == buf ? 0 : buf;
|
nuclear@14
|
103 }
|
nuclear@14
|
104
|
nuclear@14
|
105
|
nuclear@14
|
106 /* flags indicating which fields in an rgbe_header_info are valid */
|
nuclear@14
|
107 #define RGBE_VALID_PROGRAMTYPE 0x01
|
nuclear@14
|
108 #define RGBE_VALID_GAMMA 0x02
|
nuclear@14
|
109 #define RGBE_VALID_EXPOSURE 0x04
|
nuclear@14
|
110
|
nuclear@14
|
111 /* return codes for rgbe routines */
|
nuclear@14
|
112 #define RGBE_RETURN_SUCCESS 0
|
nuclear@14
|
113 #define RGBE_RETURN_FAILURE -1
|
nuclear@14
|
114
|
nuclear@14
|
115
|
nuclear@14
|
116 #if defined(__cplusplus) || defined(GNUC) || __STDC_VERSION >= 199901L
|
nuclear@14
|
117 #define INLINE inline
|
nuclear@14
|
118 #else
|
nuclear@14
|
119 #define INLINE
|
nuclear@14
|
120 #endif
|
nuclear@14
|
121
|
nuclear@14
|
122 /* offsets to red, green, and blue components in a data (float) pixel */
|
nuclear@14
|
123 #define RGBE_DATA_RED 0
|
nuclear@14
|
124 #define RGBE_DATA_GREEN 1
|
nuclear@14
|
125 #define RGBE_DATA_BLUE 2
|
nuclear@14
|
126
|
nuclear@14
|
127 /* number of floats per pixel */
|
nuclear@14
|
128 #define RGBE_DATA_SIZE 3
|
nuclear@14
|
129
|
nuclear@14
|
130 enum rgbe_error_codes {
|
nuclear@14
|
131 rgbe_read_error,
|
nuclear@14
|
132 rgbe_write_error,
|
nuclear@14
|
133 rgbe_format_error,
|
nuclear@14
|
134 rgbe_memory_error
|
nuclear@14
|
135 };
|
nuclear@14
|
136
|
nuclear@14
|
137
|
nuclear@14
|
138 /* default error routine. change this to change error handling */
|
nuclear@14
|
139 static int rgbe_error(int rgbe_error_code, char *msg)
|
nuclear@14
|
140 {
|
nuclear@14
|
141 switch (rgbe_error_code) {
|
nuclear@14
|
142 case rgbe_read_error:
|
nuclear@14
|
143 fprintf(stderr, "RGBE read error: %s\n", strerror(errno));
|
nuclear@14
|
144 break;
|
nuclear@14
|
145
|
nuclear@14
|
146 case rgbe_write_error:
|
nuclear@14
|
147 fprintf(stderr, "RGBE write error: %s\n", strerror(errno));
|
nuclear@14
|
148 break;
|
nuclear@14
|
149
|
nuclear@14
|
150 case rgbe_format_error:
|
nuclear@14
|
151 fprintf(stderr, "RGBE bad file format: %s\n", msg);
|
nuclear@14
|
152 break;
|
nuclear@14
|
153
|
nuclear@14
|
154 default:
|
nuclear@14
|
155 case rgbe_memory_error:
|
nuclear@14
|
156 fprintf(stderr, "RGBE error: %s\n", msg);
|
nuclear@14
|
157 }
|
nuclear@14
|
158 return RGBE_RETURN_FAILURE;
|
nuclear@14
|
159 }
|
nuclear@14
|
160
|
nuclear@14
|
161 /* standard conversion from float pixels to rgbe pixels */
|
nuclear@14
|
162 /*static INLINE void float2rgbe(unsigned char rgbe[4], float red, float green, float blue)
|
nuclear@14
|
163 {
|
nuclear@14
|
164 float v;
|
nuclear@14
|
165 int e;
|
nuclear@14
|
166
|
nuclear@14
|
167 v = red;
|
nuclear@14
|
168 if(green > v)
|
nuclear@14
|
169 v = green;
|
nuclear@14
|
170 if(blue > v)
|
nuclear@14
|
171 v = blue;
|
nuclear@14
|
172 if(v < 1e-32) {
|
nuclear@14
|
173 rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
|
nuclear@14
|
174 } else {
|
nuclear@14
|
175 v = frexp(v, &e) * 256.0 / v;
|
nuclear@14
|
176 rgbe[0] = (unsigned char)(red * v);
|
nuclear@14
|
177 rgbe[1] = (unsigned char)(green * v);
|
nuclear@14
|
178 rgbe[2] = (unsigned char)(blue * v);
|
nuclear@14
|
179 rgbe[3] = (unsigned char)(e + 128);
|
nuclear@14
|
180 }
|
nuclear@14
|
181 }*/
|
nuclear@14
|
182
|
nuclear@14
|
183 /* standard conversion from rgbe to float pixels */
|
nuclear@14
|
184 /* note: Ward uses ldexp(col+0.5,exp-(128+8)). However we wanted pixels */
|
nuclear@14
|
185 /* in the range [0,1] to map back into the range [0,1]. */
|
nuclear@14
|
186 static INLINE void rgbe2float(float *red, float *green, float *blue, unsigned char rgbe[4])
|
nuclear@14
|
187 {
|
nuclear@14
|
188 float f;
|
nuclear@14
|
189
|
nuclear@14
|
190 if(rgbe[3]) { /*nonzero pixel */
|
nuclear@14
|
191 f = ldexp(1.0, rgbe[3] - (int)(128 + 8));
|
nuclear@14
|
192 *red = rgbe[0] * f;
|
nuclear@14
|
193 *green = rgbe[1] * f;
|
nuclear@14
|
194 *blue = rgbe[2] * f;
|
nuclear@14
|
195 } else
|
nuclear@14
|
196 *red = *green = *blue = 0.0;
|
nuclear@14
|
197 }
|
nuclear@14
|
198
|
nuclear@14
|
199 #if 0
|
nuclear@14
|
200 /* default minimal header. modify if you want more information in header */
|
nuclear@14
|
201 static int rgbe_write_header(FILE * fp, int width, int height, rgbe_header_info * info)
|
nuclear@14
|
202 {
|
nuclear@14
|
203 char *programtype = "RGBE";
|
nuclear@14
|
204
|
nuclear@14
|
205 if(info && (info->valid & RGBE_VALID_PROGRAMTYPE))
|
nuclear@14
|
206 programtype = info->programtype;
|
nuclear@14
|
207 if(fprintf(fp, "#?%s\n", programtype) < 0)
|
nuclear@14
|
208 return rgbe_error(rgbe_write_error, NULL);
|
nuclear@14
|
209 /* The #? is to identify file type, the programtype is optional. */
|
nuclear@14
|
210 if(info && (info->valid & RGBE_VALID_GAMMA)) {
|
nuclear@14
|
211 if(fprintf(fp, "GAMMA=%g\n", info->gamma) < 0)
|
nuclear@14
|
212 return rgbe_error(rgbe_write_error, NULL);
|
nuclear@14
|
213 }
|
nuclear@14
|
214 if(info && (info->valid & RGBE_VALID_EXPOSURE)) {
|
nuclear@14
|
215 if(fprintf(fp, "EXPOSURE=%g\n", info->exposure) < 0)
|
nuclear@14
|
216 return rgbe_error(rgbe_write_error, NULL);
|
nuclear@14
|
217 }
|
nuclear@14
|
218 if(fprintf(fp, "FORMAT=32-bit_rle_rgbe\n\n") < 0)
|
nuclear@14
|
219 return rgbe_error(rgbe_write_error, NULL);
|
nuclear@14
|
220 if(fprintf(fp, "-Y %d +X %d\n", height, width) < 0)
|
nuclear@14
|
221 return rgbe_error(rgbe_write_error, NULL);
|
nuclear@14
|
222 return RGBE_RETURN_SUCCESS;
|
nuclear@14
|
223 }
|
nuclear@14
|
224 #endif
|
nuclear@14
|
225
|
nuclear@14
|
226 /* minimal header reading. modify if you want to parse more information */
|
nuclear@14
|
227 static int rgbe_read_header(struct img_io *io, int *width, int *height, rgbe_header_info * info)
|
nuclear@14
|
228 {
|
nuclear@14
|
229 char buf[128];
|
nuclear@14
|
230 float tempf;
|
nuclear@14
|
231 int i;
|
nuclear@14
|
232
|
nuclear@14
|
233 if(info) {
|
nuclear@14
|
234 info->valid = 0;
|
nuclear@14
|
235 info->programtype[0] = 0;
|
nuclear@14
|
236 info->gamma = info->exposure = 1.0;
|
nuclear@14
|
237 }
|
nuclear@14
|
238 if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == NULL)
|
nuclear@14
|
239 return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_read_error, NULL);*/
|
nuclear@14
|
240 if((buf[0] != '#') || (buf[1] != '?')) {
|
nuclear@14
|
241 /* if you want to require the magic token then uncomment the next line */
|
nuclear@14
|
242 /*return rgbe_error(rgbe_format_error,"bad initial token"); */
|
nuclear@14
|
243 } else if(info) {
|
nuclear@14
|
244 info->valid |= RGBE_VALID_PROGRAMTYPE;
|
nuclear@14
|
245 for(i = 0; i < sizeof(info->programtype) - 1; i++) {
|
nuclear@14
|
246 if((buf[i + 2] == 0) || isspace(buf[i + 2]))
|
nuclear@14
|
247 break;
|
nuclear@14
|
248 info->programtype[i] = buf[i + 2];
|
nuclear@14
|
249 }
|
nuclear@14
|
250 info->programtype[i] = 0;
|
nuclear@14
|
251 if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0)
|
nuclear@14
|
252 return rgbe_error(rgbe_read_error, NULL);
|
nuclear@14
|
253 }
|
nuclear@14
|
254 for(;;) {
|
nuclear@14
|
255 if((buf[0] == 0) || (buf[0] == '\n'))
|
nuclear@14
|
256 return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_format_error, "no FORMAT specifier found");*/
|
nuclear@14
|
257 else if(strcmp(buf, "FORMAT=32-bit_rle_rgbe\n") == 0)
|
nuclear@14
|
258 break; /* format found so break out of loop */
|
nuclear@14
|
259 else if(info && (sscanf(buf, "GAMMA=%g", &tempf) == 1)) {
|
nuclear@14
|
260 info->gamma = tempf;
|
nuclear@14
|
261 info->valid |= RGBE_VALID_GAMMA;
|
nuclear@14
|
262 } else if(info && (sscanf(buf, "EXPOSURE=%g", &tempf) == 1)) {
|
nuclear@14
|
263 info->exposure = tempf;
|
nuclear@14
|
264 info->valid |= RGBE_VALID_EXPOSURE;
|
nuclear@14
|
265 }
|
nuclear@14
|
266 if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0)
|
nuclear@14
|
267 return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_read_error, NULL);*/
|
nuclear@14
|
268 }
|
nuclear@14
|
269 if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0)
|
nuclear@14
|
270 return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_read_error, NULL);*/
|
nuclear@14
|
271 if(strcmp(buf, "\n") != 0)
|
nuclear@14
|
272 return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_format_error, "missing blank line after FORMAT specifier");*/
|
nuclear@14
|
273 if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0)
|
nuclear@14
|
274 return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_read_error, NULL);*/
|
nuclear@14
|
275 if(sscanf(buf, "-Y %d +X %d", height, width) < 2)
|
nuclear@14
|
276 return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_format_error, "missing image size specifier");*/
|
nuclear@14
|
277 return RGBE_RETURN_SUCCESS;
|
nuclear@14
|
278 }
|
nuclear@14
|
279
|
nuclear@14
|
280 #if 0
|
nuclear@14
|
281 /* simple write routine that does not use run length encoding */
|
nuclear@14
|
282
|
nuclear@14
|
283 /* These routines can be made faster by allocating a larger buffer and
|
nuclear@14
|
284 fread-ing and fwrite-ing the data in larger chunks */
|
nuclear@14
|
285 static int rgbe_write_pixels(FILE * fp, float *data, int numpixels)
|
nuclear@14
|
286 {
|
nuclear@14
|
287 unsigned char rgbe[4];
|
nuclear@14
|
288
|
nuclear@14
|
289 while(numpixels-- > 0) {
|
nuclear@14
|
290 float2rgbe(rgbe, data[RGBE_DATA_RED], data[RGBE_DATA_GREEN], data[RGBE_DATA_BLUE]);
|
nuclear@14
|
291 data += RGBE_DATA_SIZE;
|
nuclear@14
|
292 if(fwrite(rgbe, sizeof(rgbe), 1, fp) < 1)
|
nuclear@14
|
293 return rgbe_error(rgbe_write_error, NULL);
|
nuclear@14
|
294 }
|
nuclear@14
|
295 return RGBE_RETURN_SUCCESS;
|
nuclear@14
|
296 }
|
nuclear@14
|
297 #endif
|
nuclear@14
|
298
|
nuclear@14
|
299 /* simple read routine. will not correctly handle run length encoding */
|
nuclear@14
|
300 static int rgbe_read_pixels(struct img_io *io, float *data, int numpixels)
|
nuclear@14
|
301 {
|
nuclear@14
|
302 unsigned char rgbe[4];
|
nuclear@14
|
303
|
nuclear@14
|
304 while(numpixels-- > 0) {
|
nuclear@14
|
305 if(io->read(rgbe, sizeof(rgbe), io->uptr) < 1)
|
nuclear@14
|
306 return rgbe_error(rgbe_read_error, NULL);
|
nuclear@14
|
307 rgbe2float(&data[RGBE_DATA_RED], &data[RGBE_DATA_GREEN], &data[RGBE_DATA_BLUE], rgbe);
|
nuclear@14
|
308 data += RGBE_DATA_SIZE;
|
nuclear@14
|
309 }
|
nuclear@14
|
310 return RGBE_RETURN_SUCCESS;
|
nuclear@14
|
311 }
|
nuclear@14
|
312
|
nuclear@14
|
313 #if 0
|
nuclear@14
|
314 /* The code below is only needed for the run-length encoded files. */
|
nuclear@14
|
315
|
nuclear@14
|
316 /* Run length encoding adds considerable complexity but does */
|
nuclear@14
|
317
|
nuclear@14
|
318 /* save some space. For each scanline, each channel (r,g,b,e) is */
|
nuclear@14
|
319
|
nuclear@14
|
320 /* encoded separately for better compression. */
|
nuclear@14
|
321
|
nuclear@14
|
322 static int rgbe_write_bytes_rle(struct img_io *io, unsigned char *data, int numbytes)
|
nuclear@14
|
323 {
|
nuclear@14
|
324 #define MINRUNLENGTH 4
|
nuclear@14
|
325 int cur, beg_run, run_count, old_run_count, nonrun_count;
|
nuclear@14
|
326 unsigned char buf[2];
|
nuclear@14
|
327
|
nuclear@14
|
328 cur = 0;
|
nuclear@14
|
329 while(cur < numbytes) {
|
nuclear@14
|
330 beg_run = cur;
|
nuclear@14
|
331 /* find next run of length at least 4 if one exists */
|
nuclear@14
|
332 run_count = old_run_count = 0;
|
nuclear@14
|
333 while((run_count < MINRUNLENGTH) && (beg_run < numbytes)) {
|
nuclear@14
|
334 beg_run += run_count;
|
nuclear@14
|
335 old_run_count = run_count;
|
nuclear@14
|
336 run_count = 1;
|
nuclear@14
|
337 while((beg_run + run_count < numbytes) && (run_count < 127)
|
nuclear@14
|
338 && (data[beg_run] == data[beg_run + run_count]))
|
nuclear@14
|
339 run_count++;
|
nuclear@14
|
340 }
|
nuclear@14
|
341 /* if data before next big run is a short run then write it as such */
|
nuclear@14
|
342 if((old_run_count > 1) && (old_run_count == beg_run - cur)) {
|
nuclear@14
|
343 buf[0] = 128 + old_run_count; /*write short run */
|
nuclear@14
|
344 buf[1] = data[cur];
|
nuclear@14
|
345 if(fwrite(buf, sizeof(buf[0]) * 2, 1, fp) < 1)
|
nuclear@14
|
346 return rgbe_error(rgbe_write_error, NULL);
|
nuclear@14
|
347 cur = beg_run;
|
nuclear@14
|
348 }
|
nuclear@14
|
349 /* write out bytes until we reach the start of the next run */
|
nuclear@14
|
350 while(cur < beg_run) {
|
nuclear@14
|
351 nonrun_count = beg_run - cur;
|
nuclear@14
|
352 if(nonrun_count > 128)
|
nuclear@14
|
353 nonrun_count = 128;
|
nuclear@14
|
354 buf[0] = nonrun_count;
|
nuclear@14
|
355 if(fwrite(buf, sizeof(buf[0]), 1, fp) < 1)
|
nuclear@14
|
356 return rgbe_error(rgbe_write_error, NULL);
|
nuclear@14
|
357 if(fwrite(&data[cur], sizeof(data[0]) * nonrun_count, 1, fp) < 1)
|
nuclear@14
|
358 return rgbe_error(rgbe_write_error, NULL);
|
nuclear@14
|
359 cur += nonrun_count;
|
nuclear@14
|
360 }
|
nuclear@14
|
361 /* write out next run if one was found */
|
nuclear@14
|
362 if(run_count >= MINRUNLENGTH) {
|
nuclear@14
|
363 buf[0] = 128 + run_count;
|
nuclear@14
|
364 buf[1] = data[beg_run];
|
nuclear@14
|
365 if(fwrite(buf, sizeof(buf[0]) * 2, 1, fp) < 1)
|
nuclear@14
|
366 return rgbe_error(rgbe_write_error, NULL);
|
nuclear@14
|
367 cur += run_count;
|
nuclear@14
|
368 }
|
nuclear@14
|
369 }
|
nuclear@14
|
370 return RGBE_RETURN_SUCCESS;
|
nuclear@14
|
371 #undef MINRUNLENGTH
|
nuclear@14
|
372 }
|
nuclear@14
|
373
|
nuclear@14
|
374 static int rgbe_write_pixels_rle(struct img_io *io, float *data, int scanline_width, int num_scanlines)
|
nuclear@14
|
375 {
|
nuclear@14
|
376 unsigned char rgbe[4];
|
nuclear@14
|
377 unsigned char *buffer;
|
nuclear@14
|
378 int i, err;
|
nuclear@14
|
379
|
nuclear@14
|
380 if((scanline_width < 8) || (scanline_width > 0x7fff))
|
nuclear@14
|
381 /* run length encoding is not allowed so write flat */
|
nuclear@14
|
382 return rgbe_write_pixels(io, data, scanline_width * num_scanlines);
|
nuclear@14
|
383 buffer = (unsigned char *)malloc(sizeof(unsigned char) * 4 * scanline_width);
|
nuclear@14
|
384 if(buffer == NULL)
|
nuclear@14
|
385 /* no buffer space so write flat */
|
nuclear@14
|
386 return rgbe_write_pixels(fp, data, scanline_width * num_scanlines);
|
nuclear@14
|
387 while(num_scanlines-- > 0) {
|
nuclear@14
|
388 rgbe[0] = 2;
|
nuclear@14
|
389 rgbe[1] = 2;
|
nuclear@14
|
390 rgbe[2] = scanline_width >> 8;
|
nuclear@14
|
391 rgbe[3] = scanline_width & 0xFF;
|
nuclear@14
|
392 if(fwrite(rgbe, sizeof(rgbe), 1, fp) < 1) {
|
nuclear@14
|
393 free(buffer);
|
nuclear@14
|
394 return rgbe_error(rgbe_write_error, NULL);
|
nuclear@14
|
395 }
|
nuclear@14
|
396 for(i = 0; i < scanline_width; i++) {
|
nuclear@14
|
397 float2rgbe(rgbe, data[RGBE_DATA_RED], data[RGBE_DATA_GREEN], data[RGBE_DATA_BLUE]);
|
nuclear@14
|
398 buffer[i] = rgbe[0];
|
nuclear@14
|
399 buffer[i + scanline_width] = rgbe[1];
|
nuclear@14
|
400 buffer[i + 2 * scanline_width] = rgbe[2];
|
nuclear@14
|
401 buffer[i + 3 * scanline_width] = rgbe[3];
|
nuclear@14
|
402 data += RGBE_DATA_SIZE;
|
nuclear@14
|
403 }
|
nuclear@14
|
404 /* write out each of the four channels separately run length encoded */
|
nuclear@14
|
405 /* first red, then green, then blue, then exponent */
|
nuclear@14
|
406 for(i = 0; i < 4; i++) {
|
nuclear@14
|
407 if((err = rgbe_write_bytes_rle(fp, &buffer[i * scanline_width],
|
nuclear@14
|
408 scanline_width)) != RGBE_RETURN_SUCCESS) {
|
nuclear@14
|
409 free(buffer);
|
nuclear@14
|
410 return err;
|
nuclear@14
|
411 }
|
nuclear@14
|
412 }
|
nuclear@14
|
413 }
|
nuclear@14
|
414 free(buffer);
|
nuclear@14
|
415 return RGBE_RETURN_SUCCESS;
|
nuclear@14
|
416 }
|
nuclear@14
|
417 #endif
|
nuclear@14
|
418
|
nuclear@14
|
419 static int rgbe_read_pixels_rle(struct img_io *io, float *data, int scanline_width, int num_scanlines)
|
nuclear@14
|
420 {
|
nuclear@14
|
421 unsigned char rgbe[4], *scanline_buffer, *ptr, *ptr_end;
|
nuclear@14
|
422 int i, count;
|
nuclear@14
|
423 unsigned char buf[2];
|
nuclear@14
|
424
|
nuclear@14
|
425 if((scanline_width < 8) || (scanline_width > 0x7fff))
|
nuclear@14
|
426 /* run length encoding is not allowed so read flat */
|
nuclear@14
|
427 return rgbe_read_pixels(io, data, scanline_width * num_scanlines);
|
nuclear@14
|
428 scanline_buffer = NULL;
|
nuclear@14
|
429 /* read in each successive scanline */
|
nuclear@14
|
430 while(num_scanlines > 0) {
|
nuclear@14
|
431 if(io->read(rgbe, sizeof(rgbe), io->uptr) < 1) {
|
nuclear@14
|
432 free(scanline_buffer);
|
nuclear@14
|
433 return rgbe_error(rgbe_read_error, NULL);
|
nuclear@14
|
434 }
|
nuclear@14
|
435 if((rgbe[0] != 2) || (rgbe[1] != 2) || (rgbe[2] & 0x80)) {
|
nuclear@14
|
436 /* this file is not run length encoded */
|
nuclear@14
|
437 rgbe2float(&data[0], &data[1], &data[2], rgbe);
|
nuclear@14
|
438 data += RGBE_DATA_SIZE;
|
nuclear@14
|
439 free(scanline_buffer);
|
nuclear@14
|
440 return rgbe_read_pixels(io, data, scanline_width * num_scanlines - 1);
|
nuclear@14
|
441 }
|
nuclear@14
|
442 if((((int)rgbe[2]) << 8 | rgbe[3]) != scanline_width) {
|
nuclear@14
|
443 free(scanline_buffer);
|
nuclear@14
|
444 return rgbe_error(rgbe_format_error, "wrong scanline width");
|
nuclear@14
|
445 }
|
nuclear@14
|
446 if(scanline_buffer == NULL)
|
nuclear@14
|
447 scanline_buffer = (unsigned char *)
|
nuclear@14
|
448 malloc(sizeof(unsigned char) * 4 * scanline_width);
|
nuclear@14
|
449 if(scanline_buffer == NULL)
|
nuclear@14
|
450 return rgbe_error(rgbe_memory_error, "unable to allocate buffer space");
|
nuclear@14
|
451
|
nuclear@14
|
452 ptr = &scanline_buffer[0];
|
nuclear@14
|
453 /* read each of the four channels for the scanline into the buffer */
|
nuclear@14
|
454 for(i = 0; i < 4; i++) {
|
nuclear@14
|
455 ptr_end = &scanline_buffer[(i + 1) * scanline_width];
|
nuclear@14
|
456 while(ptr < ptr_end) {
|
nuclear@14
|
457 if(io->read(buf, sizeof(buf[0]) * 2, io->uptr) < 1) {
|
nuclear@14
|
458 free(scanline_buffer);
|
nuclear@14
|
459 return rgbe_error(rgbe_read_error, NULL);
|
nuclear@14
|
460 }
|
nuclear@14
|
461 if(buf[0] > 128) {
|
nuclear@14
|
462 /* a run of the same value */
|
nuclear@14
|
463 count = buf[0] - 128;
|
nuclear@14
|
464 if((count == 0) || (count > ptr_end - ptr)) {
|
nuclear@14
|
465 free(scanline_buffer);
|
nuclear@14
|
466 return rgbe_error(rgbe_format_error, "bad scanline data");
|
nuclear@14
|
467 }
|
nuclear@14
|
468 while(count-- > 0)
|
nuclear@14
|
469 *ptr++ = buf[1];
|
nuclear@14
|
470 } else {
|
nuclear@14
|
471 /* a non-run */
|
nuclear@14
|
472 count = buf[0];
|
nuclear@14
|
473 if((count == 0) || (count > ptr_end - ptr)) {
|
nuclear@14
|
474 free(scanline_buffer);
|
nuclear@14
|
475 return rgbe_error(rgbe_format_error, "bad scanline data");
|
nuclear@14
|
476 }
|
nuclear@14
|
477 *ptr++ = buf[1];
|
nuclear@14
|
478 if(--count > 0) {
|
nuclear@14
|
479 if(io->read(ptr, sizeof(*ptr) * count, io->uptr) < 1) {
|
nuclear@14
|
480 free(scanline_buffer);
|
nuclear@14
|
481 return rgbe_error(rgbe_read_error, NULL);
|
nuclear@14
|
482 }
|
nuclear@14
|
483 ptr += count;
|
nuclear@14
|
484 }
|
nuclear@14
|
485 }
|
nuclear@14
|
486 }
|
nuclear@14
|
487 }
|
nuclear@14
|
488 /* now convert data from buffer into floats */
|
nuclear@14
|
489 for(i = 0; i < scanline_width; i++) {
|
nuclear@14
|
490 rgbe[0] = scanline_buffer[i];
|
nuclear@14
|
491 rgbe[1] = scanline_buffer[i + scanline_width];
|
nuclear@14
|
492 rgbe[2] = scanline_buffer[i + 2 * scanline_width];
|
nuclear@14
|
493 rgbe[3] = scanline_buffer[i + 3 * scanline_width];
|
nuclear@14
|
494 rgbe2float(&data[RGBE_DATA_RED], &data[RGBE_DATA_GREEN], &data[RGBE_DATA_BLUE], rgbe);
|
nuclear@14
|
495 data += RGBE_DATA_SIZE;
|
nuclear@14
|
496 }
|
nuclear@14
|
497 num_scanlines--;
|
nuclear@14
|
498 }
|
nuclear@14
|
499 free(scanline_buffer);
|
nuclear@14
|
500 return RGBE_RETURN_SUCCESS;
|
nuclear@14
|
501 }
|