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 ----------------------------------------------------------------------
43 #include "BLI_blenlib.h"
46 #include "imbuf_patch.h"
48 #include "IMB_imbuf_types.h"
49 #include "IMB_imbuf.h"
51 #include "IMB_allocimbuf.h"
53 #include "IMB_radiance_hdr.h"
55 /* needed constants */
57 #define MAXELEN 0x7fff
58 #define MINRUN 4 /* minimum run length */
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])
72 static unsigned char* oldreadcolrs(RGBE
*scan
, unsigned char *mem
, int xmax
)
74 int i
, rshift
= 0, len
= xmax
;
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]);
97 static unsigned char* freadcolrs(RGBE
*scan
, unsigned char* mem
, int xmax
)
101 if ((xmax
< MINELEN
) | (xmax
> MAXELEN
)) return oldreadcolrs(scan
, mem
, xmax
);
104 if (i
!= 2) return oldreadcolrs(scan
, mem
-1, xmax
);
106 scan
[0][GRN
] = *mem
++;
107 scan
[0][BLU
] = *mem
++;
110 if (((scan
[0][BLU
] << 8) | i
) != xmax
) return NULL
;
119 scan
[j
++][i
] = (unsigned char)val
;
123 scan
[j
++][i
] = *mem
++;
128 /* helper functions */
130 /* rgbe -> float color */
131 static void RGBE2FLOAT(RGBE rgbe
, fCOLOR fcol
)
134 fcol
[RED
] = fcol
[GRN
] = fcol
[BLU
] = 0;
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
)
148 float d
= (fcol
[RED
]>fcol
[GRN
]) ? fcol
[RED
] : fcol
[GRN
];
149 if (fcol
[BLU
]>d
) d
= fcol
[BLU
];
151 rgbe
[RED
] = rgbe
[GRN
] = rgbe
[BLU
] = rgbe
[EXP
] = 0;
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
);
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;
173 struct ImBuf
*imb_loadhdr(unsigned char *mem
, int size
, int flags
)
180 int width
=0, height
=0;
184 char oriY
[80], oriX
[80];
187 if (imb_is_a_hdr((void*)mem
))
189 /* find empty line, next line is resolution info */
190 for (x
=1;x
<size
;x
++) {
191 if ((mem
[x
-1]=='\n') && (mem
[x
]=='\n')) {
197 BLI_strncpy(buff
, (char *)&mem
[x
+1], sizeof(buff
));
198 if (sscanf(buff
, "%s %d %s %d", (char*)&oriY
, &height
,
199 (char*)&oriX
, &width
) != 4) return NULL
;
201 /* find end of this line, data right behind it */
202 ptr
= (unsigned char *)strchr((char*)&mem
[x
+1], '\n');
205 if (flags
& IB_test
) ibuf
= IMB_allocImBuf(width
, height
, 32, 0, 0);
206 else ibuf
= IMB_allocImBuf(width
, height
, 32, IB_rect
|IB_rectfloat
, 0);
208 if (ibuf
==NULL
) return NULL
;
209 ibuf
->ftype
= RADHDR
;
210 ibuf
->xorig
= ibuf
->yorig
= 0;
212 if (flags
& IB_test
) return ibuf
;
214 /* read in and decode the actual data */
215 sline
= (RGBE
*)MEM_mallocN(sizeof(RGBE
)*width
, "radhdr_read_tmpscan");
216 rect
= (unsigned char*)ibuf
->rect
;
217 rect_float
= (float *)ibuf
->rect_float
;
219 for (y
=0;y
<height
;y
++) {
220 ptr
= freadcolrs(sline
, ptr
, width
);
222 printf("HDR decode error\n");
226 for (x
=0;x
<width
;x
++) {
228 RGBE2FLOAT(sline
[x
], fcol
);
229 *rect_float
++ = fcol
[RED
];
230 *rect_float
++ = fcol
[GRN
];
231 *rect_float
++ = fcol
[BLU
];
232 *rect_float
++ = 1.0f
;
233 /* Also old oldstyle for the rest of blender which is not using floats yet */
234 // e: changed to simpler tonemapping, previous code was rather slow (is this actually still relevant at all?)
235 fcol
[RED
] = fcol
[RED
]/(1.f
+ fcol
[RED
]);
236 fcol
[GRN
] = fcol
[GRN
]/(1.f
+ fcol
[GRN
]);
237 fcol
[BLU
] = fcol
[BLU
]/(1.f
+ fcol
[BLU
]);
238 *rect
++ = (unsigned char)((fcol
[RED
] < 0.f
) ? 0 : ((fcol
[RED
] > 1.f
) ? 255 : (255.f
*fcol
[RED
])));
239 *rect
++ = (unsigned char)((fcol
[GRN
] < 0.f
) ? 0 : ((fcol
[GRN
] > 1.f
) ? 255 : (255.f
*fcol
[GRN
])));
240 *rect
++ = (unsigned char)((fcol
[BLU
] < 0.f
) ? 0 : ((fcol
[BLU
] > 1.f
) ? 255 : (255.f
*fcol
[BLU
])));
245 if (oriY
[0]=='-') IMB_flipy(ibuf
);
248 //else printf("Data not found!\n");
250 //else printf("Not a valid radiance HDR file!\n");
256 static int fwritecolrs(FILE* file
, int width
, unsigned char* ibufscan
, float* fpscan
)
258 int x
, i
, j
, beg
, c2
, cnt
=0;
260 RGBE rgbe
, *rgbe_scan
;
262 if ((ibufscan
==NULL
) && (fpscan
==NULL
)) return 0;
264 rgbe_scan
= (RGBE
*)MEM_mallocN(sizeof(RGBE
)*width
, "radhdr_write_tmpscan");
266 /* convert scanline */
268 for (i
=0;i
<width
;i
++) {
270 fcol
[RED
] = fpscan
[j
];
271 fcol
[GRN
] = fpscan
[j
+1];
272 fcol
[BLU
] = fpscan
[j
+2];
274 fcol
[RED
] = (float)ibufscan
[j
] / 255.f
;
275 fcol
[GRN
] = (float)ibufscan
[j
+1] / 255.f
;
276 fcol
[BLU
] = (float)ibufscan
[j
+2] /255.f
;
278 FLOAT2RGBE(fcol
, rgbe
);
279 copy_rgbe(rgbe
, rgbe_scan
[i
]);
283 if ((width
< MINELEN
) | (width
> MAXELEN
)) { /* OOBs, write out flat */
284 x
=fwrite((char *)rgbe_scan
, sizeof(RGBE
), width
, file
) - width
;
285 MEM_freeN(rgbe_scan
);
288 /* put magic header */
291 putc((unsigned char)(width
>> 8), file
);
292 putc((unsigned char)(width
& 255), file
);
293 /* put components seperately */
295 for (j
=0;j
<width
;j
+=cnt
) { /* find next run */
296 for (beg
=j
;beg
<width
;beg
+=cnt
) {
297 for (cnt
=1;(cnt
<127) && ((beg
+cnt
)<width
) && (rgbe_scan
[beg
+cnt
][i
] == rgbe_scan
[beg
][i
]); cnt
++);
298 if (cnt
>=MINRUN
) break; /* long enough */
300 if (((beg
-j
)>1) && ((beg
-j
) < MINRUN
)) {
302 while (rgbe_scan
[c2
++][i
] == rgbe_scan
[j
][i
])
303 if (c2
== beg
) { /* short run */
304 putc((unsigned char)(128+beg
-j
), file
);
305 putc((unsigned char)(rgbe_scan
[j
][i
]), file
);
310 while (j
< beg
) { /* write out non-run */
311 if ((c2
= beg
-j
) > 128) c2
= 128;
312 putc((unsigned char)(c2
), file
);
313 while (c2
--) putc(rgbe_scan
[j
++][i
], file
);
315 if (cnt
>= MINRUN
) { /* write out run */
316 putc((unsigned char)(128+cnt
), file
);
317 putc(rgbe_scan
[beg
][i
], file
);
322 MEM_freeN(rgbe_scan
);
323 return(ferror(file
) ? -1 : 0);
326 static void writeHeader(FILE *file
, int width
, int height
)
328 fprintf(file
, "#?RADIANCE");
330 fprintf(file
, "# %s", "Created with Blender");
332 fprintf(file
, "EXPOSURE=%25.13f", 1.0);
334 fprintf(file
, "FORMAT=32-bit_rle_rgbe");
337 fprintf(file
, "-Y %d +X %d", height
, width
);
341 short imb_savehdr(struct ImBuf
*ibuf
, char *name
, int flags
)
343 FILE* file
= fopen(name
, "wb");
345 int y
, width
=ibuf
->x
, height
=ibuf
->y
;
346 unsigned char *cp
= NULL
;
348 if (file
==NULL
) return 0;
350 writeHeader(file
, width
, height
);
353 cp
= (unsigned char *)(ibuf
->rect
+ (height
-1)*width
);
355 fp
= ibuf
->rect_float
+ 4*(height
-1)*width
;
357 for (y
=height
-1;y
>=0;y
--) {
358 if (fwritecolrs(file
, width
, cp
, fp
) < 0) {
360 printf("HDR write error\n");