My last commit for the sscanf buffer overflow did not really fix the
[plumiferos.git] / source / blender / imbuf / intern / radiance_hdr.c
blobdf6211ff390e29f943954d3a8e93394066722be8
1 /*
2 * radiance_hdr.c
4 * $Id:
6 * ***** BEGIN GPL LICENSE BLOCK *****
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 * The Original Code is Copyright
23 * All rights reserved.
25 * The Original Code is: all of this file.
27 * Contributor(s): none yet.
29 * ***** END GPL LICENSE BLOCK *****
32 /* ----------------------------------------------------------------------
33 Radiance High Dynamic Range image file IO
34 For description and code for reading/writing of radiance hdr files
35 by Greg Ward, refer to:
36 http://radsite.lbl.gov/radiance/refer/Notes/picture_format.html
37 ----------------------------------------------------------------------
40 #ifdef WIN32
41 #include <io.h>
42 #endif
43 #include "BLI_blenlib.h"
45 #include "imbuf.h"
46 #include "imbuf_patch.h"
48 #include "IMB_imbuf_types.h"
49 #include "IMB_imbuf.h"
51 #include "IMB_allocimbuf.h"
52 #include "IMB_cmap.h"
53 #include "IMB_radiance_hdr.h"
55 /* needed constants */
56 #define MINELEN 8
57 #define MAXELEN 0x7fff
58 #define MINRUN 4 /* minimum run length */
59 #define RED 0
60 #define GRN 1
61 #define BLU 2
62 #define EXP 3
63 #define COLXS 128
64 #define STR_MAX 540
65 typedef unsigned char RGBE[4];
66 typedef float fCOLOR[3];
67 /* copy source -> dest */
68 #define copy_rgbe(c1, c2) (c2[RED]=c1[RED], c2[GRN]=c1[GRN], c2[BLU]=c1[BLU], c2[EXP]=c1[EXP])
69 #define copy_fcol(f1, f2) (f2[RED]=f1[RED], f2[GRN]=f1[GRN], f2[BLU]=f1[BLU])
71 /* read routines */
72 static unsigned char* oldreadcolrs(RGBE *scan, unsigned char *mem, int xmax)
74 int i, rshift = 0, len = xmax;
75 while (len > 0) {
76 scan[0][RED] = *mem++;
77 scan[0][GRN] = *mem++;
78 scan[0][BLU] = *mem++;
79 scan[0][EXP] = *mem++;
80 if (scan[0][RED] == 1 && scan[0][GRN] == 1 && scan[0][BLU] == 1) {
81 for (i=scan[0][EXP]<<rshift;i>0;i--) {
82 copy_rgbe(scan[-1], scan[0]);
83 scan++;
84 len--;
86 rshift += 8;
88 else {
89 scan++;
90 len--;
91 rshift = 0;
94 return mem;
97 static unsigned char* freadcolrs(RGBE *scan, unsigned char* mem, int xmax)
99 int i, j, code, val;
101 if ((xmax < MINELEN) | (xmax > MAXELEN)) return oldreadcolrs(scan, mem, xmax);
103 i = *mem++;
104 if (i != 2) return oldreadcolrs(scan, mem-1, xmax);
106 scan[0][GRN] = *mem++;
107 scan[0][BLU] = *mem++;
109 i = *mem++;
110 if (((scan[0][BLU] << 8) | i) != xmax) return NULL;
112 for (i=0;i<4;i++)
113 for (j=0;j<xmax;) {
114 code = *mem++;
115 if (code > 128) {
116 code &= 127;
117 val = *mem++;
118 while (code--)
119 scan[j++][i] = (unsigned char)val;
121 else
122 while (code--)
123 scan[j++][i] = *mem++;
125 return mem;
128 /* helper functions */
130 /* rgbe -> float color */
131 static void RGBE2FLOAT(RGBE rgbe, fCOLOR fcol)
133 if (rgbe[EXP]==0) {
134 fcol[RED] = fcol[GRN] = fcol[BLU] = 0;
136 else {
137 float f = ldexp(1.0, rgbe[EXP]-(COLXS+8));
138 fcol[RED] = f*(rgbe[RED] + 0.5f);
139 fcol[GRN] = f*(rgbe[GRN] + 0.5f);
140 fcol[BLU] = f*(rgbe[BLU] + 0.5f);
144 /* float color -> rgbe */
145 static void FLOAT2RGBE(fCOLOR fcol, RGBE rgbe)
147 int e;
148 float d = (fcol[RED]>fcol[GRN]) ? fcol[RED] : fcol[GRN];
149 if (fcol[BLU]>d) d = fcol[BLU];
150 if (d <= 1e-32f)
151 rgbe[RED] = rgbe[GRN] = rgbe[BLU] = rgbe[EXP] = 0;
152 else {
153 d = frexp(d, &e) * 256.f / d;
154 rgbe[RED] = (unsigned char)(fcol[RED] * d);
155 rgbe[GRN] = (unsigned char)(fcol[GRN] * d);
156 rgbe[BLU] = (unsigned char)(fcol[BLU] * d);
157 rgbe[EXP] = (unsigned char)(e + COLXS);
161 /* ImBuf read */
163 int imb_is_a_hdr(void *buf)
165 // For recognition, Blender only loads first 32 bytes, so use #?RADIANCE id instead
166 // update: actually, the 'RADIANCE' part is just an optional program name, the magic word is really only the '#?' part
167 //if (strstr((char*)buf, "#?RADIANCE")) return 1;
168 if (strstr((char*)buf, "#?")) return 1;
169 // if (strstr((char*)buf, "32-bit_rle_rgbe")) return 1;
170 return 0;
173 struct ImBuf *imb_loadhdr(unsigned char *mem, int size, int flags)
175 struct ImBuf* ibuf;
176 RGBE* sline;
177 fCOLOR fcol;
178 float* rect_float;
179 int found=0;
180 int width=0, height=0;
181 int x, y;
182 unsigned char* ptr;
183 unsigned char* rect;
184 char oriY[80], oriX[80];
186 if (imb_is_a_hdr((void*)mem))
188 /* find empty line, next line is resolution info */
189 for (x=1;x<size;x++) {
190 if ((mem[x-1]=='\n') && (mem[x]=='\n')) {
191 found = 1;
192 break;
195 if (found) {
196 if (sscanf((char *)&mem[x+1], "%80s %d %80s %d", (char*)&oriY, &height,
197 (char*)&oriX, &width) != 4) return NULL;
199 /* find end of this line, data right behind it */
200 ptr = (unsigned char *)strchr((char*)&mem[x+1], '\n');
201 ptr++;
203 if (flags & IB_test) ibuf = IMB_allocImBuf(width, height, 32, 0, 0);
204 else ibuf = IMB_allocImBuf(width, height, 32, IB_rect|IB_rectfloat, 0);
206 if (ibuf==NULL) return NULL;
207 ibuf->ftype = RADHDR;
208 ibuf->xorig = ibuf->yorig = 0;
210 if (flags & IB_test) return ibuf;
212 /* read in and decode the actual data */
213 sline = (RGBE*)MEM_mallocN(sizeof(RGBE)*width, "radhdr_read_tmpscan");
214 rect = (unsigned char*)ibuf->rect;
215 rect_float = (float *)ibuf->rect_float;
217 for (y=0;y<height;y++) {
218 ptr = freadcolrs(sline, ptr, width);
219 if (ptr==NULL) {
220 printf("HDR decode error\n");
221 MEM_freeN(sline);
222 return ibuf;
224 for (x=0;x<width;x++) {
225 /* convert to ldr */
226 RGBE2FLOAT(sline[x], fcol);
227 *rect_float++ = fcol[RED];
228 *rect_float++ = fcol[GRN];
229 *rect_float++ = fcol[BLU];
230 *rect_float++ = 1.0f;
231 /* Also old oldstyle for the rest of blender which is not using floats yet */
232 // e: changed to simpler tonemapping, previous code was rather slow (is this actually still relevant at all?)
233 fcol[RED] = fcol[RED]/(1.f + fcol[RED]);
234 fcol[GRN] = fcol[GRN]/(1.f + fcol[GRN]);
235 fcol[BLU] = fcol[BLU]/(1.f + fcol[BLU]);
236 *rect++ = (unsigned char)((fcol[RED] < 0.f) ? 0 : ((fcol[RED] > 1.f) ? 255 : (255.f*fcol[RED])));
237 *rect++ = (unsigned char)((fcol[GRN] < 0.f) ? 0 : ((fcol[GRN] > 1.f) ? 255 : (255.f*fcol[GRN])));
238 *rect++ = (unsigned char)((fcol[BLU] < 0.f) ? 0 : ((fcol[BLU] > 1.f) ? 255 : (255.f*fcol[BLU])));
239 *rect++ = 255;
242 MEM_freeN(sline);
243 if (oriY[0]=='-') IMB_flipy(ibuf);
244 return ibuf;
246 //else printf("Data not found!\n");
248 //else printf("Not a valid radiance HDR file!\n");
250 return NULL;
253 /* ImBuf write */
254 static int fwritecolrs(FILE* file, int width, unsigned char* ibufscan, float* fpscan)
256 int x, i, j, beg, c2, cnt=0;
257 fCOLOR fcol;
258 RGBE rgbe, *rgbe_scan;
260 if ((ibufscan==NULL) && (fpscan==NULL)) return 0;
262 rgbe_scan = (RGBE*)MEM_mallocN(sizeof(RGBE)*width, "radhdr_write_tmpscan");
264 /* convert scanline */
265 j= 0;
266 for (i=0;i<width;i++) {
267 if (fpscan) {
268 fcol[RED] = fpscan[j];
269 fcol[GRN] = fpscan[j+1];
270 fcol[BLU] = fpscan[j+2];
271 } else {
272 fcol[RED] = (float)ibufscan[j] / 255.f;
273 fcol[GRN] = (float)ibufscan[j+1] / 255.f;
274 fcol[BLU] = (float)ibufscan[j+2] /255.f;
276 FLOAT2RGBE(fcol, rgbe);
277 copy_rgbe(rgbe, rgbe_scan[i]);
278 j+=4;
281 if ((width < MINELEN) | (width > MAXELEN)) { /* OOBs, write out flat */
282 x=fwrite((char *)rgbe_scan, sizeof(RGBE), width, file) - width;
283 MEM_freeN(rgbe_scan);
284 return x;
286 /* put magic header */
287 putc(2, file);
288 putc(2, file);
289 putc((unsigned char)(width >> 8), file);
290 putc((unsigned char)(width & 255), file);
291 /* put components seperately */
292 for (i=0;i<4;i++) {
293 for (j=0;j<width;j+=cnt) { /* find next run */
294 for (beg=j;beg<width;beg+=cnt) {
295 for (cnt=1;(cnt<127) && ((beg+cnt)<width) && (rgbe_scan[beg+cnt][i] == rgbe_scan[beg][i]); cnt++);
296 if (cnt>=MINRUN) break; /* long enough */
298 if (((beg-j)>1) && ((beg-j) < MINRUN)) {
299 c2 = j+1;
300 while (rgbe_scan[c2++][i] == rgbe_scan[j][i])
301 if (c2 == beg) { /* short run */
302 putc((unsigned char)(128+beg-j), file);
303 putc((unsigned char)(rgbe_scan[j][i]), file);
304 j = beg;
305 break;
308 while (j < beg) { /* write out non-run */
309 if ((c2 = beg-j) > 128) c2 = 128;
310 putc((unsigned char)(c2), file);
311 while (c2--) putc(rgbe_scan[j++][i], file);
313 if (cnt >= MINRUN) { /* write out run */
314 putc((unsigned char)(128+cnt), file);
315 putc(rgbe_scan[beg][i], file);
317 else cnt = 0;
320 MEM_freeN(rgbe_scan);
321 return(ferror(file) ? -1 : 0);
324 static void writeHeader(FILE *file, int width, int height)
326 fprintf(file, "#?RADIANCE");
327 fputc(10, file);
328 fprintf(file, "# %s", "Created with Blender");
329 fputc(10, file);
330 fprintf(file, "EXPOSURE=%25.13f", 1.0);
331 fputc(10, file);
332 fprintf(file, "FORMAT=32-bit_rle_rgbe");
333 fputc(10, file);
334 fputc(10, file);
335 fprintf(file, "-Y %d +X %d", height, width);
336 fputc(10, file);
339 short imb_savehdr(struct ImBuf *ibuf, char *name, int flags)
341 FILE* file = fopen(name, "wb");
342 float *fp= NULL;
343 int y, width=ibuf->x, height=ibuf->y;
344 unsigned char *cp= NULL;
346 if (file==NULL) return 0;
348 writeHeader(file, width, height);
350 if(ibuf->rect)
351 cp= (unsigned char *)(ibuf->rect + (height-1)*width);
352 if(ibuf->rect_float)
353 fp= ibuf->rect_float + 4*(height-1)*width;
355 for (y=height-1;y>=0;y--) {
356 if (fwritecolrs(file, width, cp, fp) < 0) {
357 fclose(file);
358 printf("HDR write error\n");
359 return 0;
361 if(cp) cp-= 4*width;
362 if(fp) fp-= 4*width;
365 fclose(file);
366 return 1;