Merge with trunk r40782
[blender.git] / source / blender / imbuf / intern / radiance_hdr.c
1 /*
2  * radiance_hdr.c
3  *
4  * $Id$
5  *
6  * ***** BEGIN GPL LICENSE BLOCK *****
7  *
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.
12  *
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.
17  *
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  * The Original Code is Copyright
23  * All rights reserved.
24  *
25  * The Original Code is: all of this file.
26  *
27  * Contributor(s): none yet.
28  *
29  * ***** END GPL LICENSE BLOCK *****
30 */
31
32 /** \file blender/imbuf/intern/radiance_hdr.c
33  *  \ingroup imbuf
34  */
35
36 /* ----------------------------------------------------------------------
37   Radiance High Dynamic Range image file IO
38   For description and code for reading/writing of radiance hdr files 
39         by Greg Ward, refer to:
40   http://radsite.lbl.gov/radiance/refer/Notes/picture_format.html
41 ----------------------------------------------------------------------
42 */
43
44 #ifdef WIN32
45 #  include <io.h>
46 #endif
47
48 #include "MEM_guardedalloc.h"
49
50 #include "BLI_blenlib.h"
51
52 #include "imbuf.h"
53
54 #include "IMB_imbuf_types.h"
55 #include "IMB_imbuf.h"
56
57 #include "IMB_allocimbuf.h"
58 #include "IMB_filetype.h"
59
60 /* needed constants */
61 #define MINELEN 8
62 #define MAXELEN 0x7fff
63 #define MINRUN  4       /* minimum run length */
64 #define RED 0
65 #define GRN 1
66 #define BLU 2
67 #define EXP 3
68 #define COLXS 128
69 #define STR_MAX 540
70 typedef unsigned char RGBE[4];
71 typedef float fCOLOR[3];
72 /* copy source -> dest */
73 #define copy_rgbe(c1, c2) (c2[RED]=c1[RED], c2[GRN]=c1[GRN], c2[BLU]=c1[BLU], c2[EXP]=c1[EXP])
74 #define copy_fcol(f1, f2) (f2[RED]=f1[RED], f2[GRN]=f1[GRN], f2[BLU]=f1[BLU])
75
76 /* read routines */
77 static unsigned char* oldreadcolrs(RGBE *scan, unsigned char *mem, int xmax)
78 {
79         int i, rshift = 0, len = xmax;
80         while (len > 0) {
81                 scan[0][RED] = *mem++;
82                 scan[0][GRN] = *mem++;
83                 scan[0][BLU] = *mem++;
84                 scan[0][EXP] = *mem++;
85                 if (scan[0][RED] == 1 && scan[0][GRN] == 1 && scan[0][BLU] == 1) {
86                         for (i=scan[0][EXP]<<rshift;i>0;i--) {
87                                 copy_rgbe(scan[-1], scan[0]);
88                                 scan++;
89                                 len--;
90                         }
91                         rshift += 8;
92                 }
93                 else {
94                         scan++;
95                         len--;
96                         rshift = 0;
97                 }
98         }
99         return mem;
100 }
101
102 static unsigned char* freadcolrs(RGBE *scan, unsigned char* mem, int xmax)
103 {
104         int i, j, code, val;
105
106         if ((xmax < MINELEN) | (xmax > MAXELEN)) return oldreadcolrs(scan, mem, xmax);
107
108         i = *mem++;
109         if (i != 2) return oldreadcolrs(scan, mem-1, xmax);
110
111         scan[0][GRN] = *mem++;
112         scan[0][BLU] = *mem++;
113
114         i = *mem++;
115         if (((scan[0][BLU] << 8) | i) != xmax) return NULL;
116
117         for (i=0;i<4;i++)
118                 for (j=0;j<xmax;) {
119                         code = *mem++;
120                         if (code > 128) {
121                                 code &= 127;
122                                 val = *mem++;
123                                 while (code--)
124                                         scan[j++][i] = (unsigned char)val;
125                         }
126                         else
127                                 while (code--)
128                                         scan[j++][i] = *mem++;
129                 }
130         return mem;
131 }
132
133 /* helper functions */
134
135 /* rgbe -> float color */
136 static void RGBE2FLOAT(RGBE rgbe, fCOLOR fcol)
137 {
138         if (rgbe[EXP]==0) {
139                 fcol[RED] = fcol[GRN] = fcol[BLU] = 0;
140         }
141         else {
142                 float f = ldexp(1.0, rgbe[EXP]-(COLXS+8));
143                 fcol[RED] = f*(rgbe[RED] + 0.5f);
144                 fcol[GRN] = f*(rgbe[GRN] + 0.5f);
145                 fcol[BLU] = f*(rgbe[BLU] + 0.5f);
146         }
147 }
148
149 /* float color -> rgbe */
150 static void FLOAT2RGBE(fCOLOR fcol, RGBE rgbe)
151 {
152         int e;
153         float d = (fcol[RED]>fcol[GRN]) ? fcol[RED] : fcol[GRN];
154         if (fcol[BLU]>d) d = fcol[BLU];
155         if (d <= 1e-32f)
156                 rgbe[RED] = rgbe[GRN] = rgbe[BLU] = rgbe[EXP] = 0;
157         else {
158                 d = frexp(d, &e) * 256.f / d;
159                 rgbe[RED] = (unsigned char)(fcol[RED] * d);
160                 rgbe[GRN] = (unsigned char)(fcol[GRN] * d);
161                 rgbe[BLU] = (unsigned char)(fcol[BLU] * d);
162                 rgbe[EXP] = (unsigned char)(e + COLXS);
163         }
164 }
165
166 /* ImBuf read */
167
168 int imb_is_a_hdr(unsigned char *buf)
169 {
170         // For recognition, Blender only loads first 32 bytes, so use #?RADIANCE id instead
171         // update: actually, the 'RADIANCE' part is just an optional program name, the magic word is really only the '#?' part
172         //if (strstr((char*)buf, "#?RADIANCE")) return 1;
173         if (strstr((char*)buf, "#?")) return 1;
174         // if (strstr((char*)buf, "32-bit_rle_rgbe")) return 1;
175         return 0;
176 }
177
178 struct ImBuf *imb_loadhdr(unsigned char *mem, size_t size, int flags)
179 {
180         struct ImBuf* ibuf;
181         RGBE* sline;
182         fCOLOR fcol;
183         float* rect_float;
184         int found=0;
185         int width=0, height=0;
186         int x, y;
187         unsigned char* ptr;
188         char oriY[80], oriX[80];
189
190         if (imb_is_a_hdr((void*)mem))
191         {
192                 /* find empty line, next line is resolution info */
193                 for (x=1;x<size;x++) {
194                         if ((mem[x-1]=='\n') && (mem[x]=='\n')) {
195                                 found = 1;
196                                 break;
197                         }
198                 }
199                 if (found && (x<(size + 2))) {
200                         if (sscanf((char *)&mem[x+1], "%79s %d %79s %d", (char*)&oriY, &height, 
201                                 (char*)&oriX, &width) != 4) return NULL;
202
203                         /* find end of this line, data right behind it */
204                         ptr = (unsigned char *)strchr((char*)&mem[x+1], '\n');
205                         ptr++;
206
207                         if (flags & IB_test) ibuf = IMB_allocImBuf(width, height, 32, 0);
208                         else ibuf = IMB_allocImBuf(width, height, 32, (flags & IB_rect)|IB_rectfloat);
209
210                         if (ibuf==NULL) return NULL;
211                         ibuf->ftype = RADHDR;
212                         ibuf->profile = IB_PROFILE_LINEAR_RGB;
213
214                         if (flags & IB_test) return ibuf;
215
216                         /* read in and decode the actual data */
217                         sline = (RGBE*)MEM_mallocN(sizeof(RGBE)*width, "radhdr_read_tmpscan");
218                         rect_float = (float *)ibuf->rect_float;
219                         
220                         for (y=0;y<height;y++) {
221                                 ptr = freadcolrs(sline, ptr, width);
222                                 if (ptr==NULL) {
223                                         printf("HDR decode error\n");
224                                         MEM_freeN(sline);
225                                         return ibuf;
226                                 }
227                                 for (x=0;x<width;x++) {
228                                         /* convert to ldr */
229                                         RGBE2FLOAT(sline[x], fcol);
230                                         *rect_float++ = fcol[RED];
231                                         *rect_float++ = fcol[GRN];
232                                         *rect_float++ = fcol[BLU];
233                                         *rect_float++ = 1.0f;
234                                 }
235                         }
236                         MEM_freeN(sline);
237                         if (oriY[0]=='-') IMB_flipy(ibuf);
238                         
239                         if (flags & IB_rect) {
240                                 IMB_rect_from_float(ibuf);
241                         }
242                         
243                         return ibuf;
244                 }
245                 //else printf("Data not found!\n");
246         }
247         //else printf("Not a valid radiance HDR file!\n");
248
249         return NULL;
250 }
251
252 /* ImBuf write */
253 static int fwritecolrs(FILE* file, int width, int channels, unsigned char* ibufscan, float* fpscan)
254 {
255         int x, i, j, beg, c2, cnt=0;
256         fCOLOR fcol;
257         RGBE rgbe, *rgbe_scan;
258
259         if ((ibufscan==NULL) && (fpscan==NULL)) return 0;
260
261         rgbe_scan = (RGBE*)MEM_mallocN(sizeof(RGBE)*width, "radhdr_write_tmpscan");
262
263         /* convert scanline */
264                 j= 0;
265         for (i=0;i<width;i++) {
266                 if (fpscan) {
267                         fcol[RED] = fpscan[j];
268                         fcol[GRN] = (channels >= 2)? fpscan[j+1]: fpscan[j];
269                         fcol[BLU] = (channels >= 3)? fpscan[j+2]: fpscan[j];
270                 } else {
271                         fcol[RED] = (float)ibufscan[j] / 255.f;
272                         fcol[GRN] = (float)((channels >= 2)? ibufscan[j+1]: ibufscan[j]) / 255.f;
273                         fcol[BLU] = (float)((channels >= 3)? ibufscan[j+2]: ibufscan[j]) / 255.f;
274                 }
275                 FLOAT2RGBE(fcol, rgbe);
276                 copy_rgbe(rgbe, rgbe_scan[i]);
277                 j+=channels;
278         }
279
280         if ((width < MINELEN) | (width > MAXELEN)) {    /* OOBs, write out flat */
281                 x=fwrite((char *)rgbe_scan, sizeof(RGBE), width, file) - width;
282                 MEM_freeN(rgbe_scan);
283                 return x;
284         }
285         /* put magic header */
286         putc(2, file);
287         putc(2, file);
288         putc((unsigned char)(width >> 8), file);
289         putc((unsigned char)(width & 255), file);
290         /* put components separately */
291         for (i=0;i<4;i++) {
292                 for (j=0;j<width;j+=cnt) {      /* find next run */
293                         for (beg=j;beg<width;beg+=cnt) {
294                                 for (cnt=1;(cnt<127) && ((beg+cnt)<width) && (rgbe_scan[beg+cnt][i] == rgbe_scan[beg][i]); cnt++);
295                                 if (cnt>=MINRUN) break;   /* long enough */
296                         }
297                         if (((beg-j)>1) && ((beg-j) < MINRUN)) {
298                                 c2 = j+1;
299                                 while (rgbe_scan[c2++][i] == rgbe_scan[j][i])
300                                         if (c2 == beg) {        /* short run */
301                                                 putc((unsigned char)(128+beg-j), file);
302                                                 putc((unsigned char)(rgbe_scan[j][i]), file);
303                                                 j = beg;
304                                                 break;
305                                         }
306                         }
307                         while (j < beg) {     /* write out non-run */
308                                 if ((c2 = beg-j) > 128) c2 = 128;
309                                 putc((unsigned char)(c2), file);
310                                 while (c2--) putc(rgbe_scan[j++][i], file);
311                         }
312                         if (cnt >= MINRUN) {      /* write out run */
313                                 putc((unsigned char)(128+cnt), file);
314                                 putc(rgbe_scan[beg][i], file);
315                         }
316                         else cnt = 0;
317                 }
318         }
319         MEM_freeN(rgbe_scan);
320         return(ferror(file) ? -1 : 0);
321 }
322
323 static void writeHeader(FILE *file, int width, int height)
324 {
325         fprintf(file, "#?RADIANCE");
326         fputc(10, file);
327         fprintf(file, "# %s", "Created with Blender");
328         fputc(10, file);
329         fprintf(file, "EXPOSURE=%25.13f", 1.0);
330         fputc(10, file);
331         fprintf(file, "FORMAT=32-bit_rle_rgbe");
332         fputc(10, file);
333         fputc(10, file);
334         fprintf(file, "-Y %d +X %d", height, width);
335         fputc(10, file);
336 }
337
338 int imb_savehdr(struct ImBuf *ibuf, const char *name, int flags)
339 {
340         FILE* file = fopen(name, "wb");
341         float *fp= NULL;
342         int y, width=ibuf->x, height=ibuf->y;
343         unsigned char *cp= NULL;
344         
345         (void)flags; /* unused */
346         
347         if (file==NULL) return 0;
348
349         writeHeader(file, width, height);
350
351         if(ibuf->rect)
352                 cp= (unsigned char *)ibuf->rect + ibuf->channels*(height-1)*width;
353         if(ibuf->rect_float)
354                 fp= ibuf->rect_float + ibuf->channels*(height-1)*width;
355         
356         for (y=height-1;y>=0;y--) {
357                 if (fwritecolrs(file, width, ibuf->channels, cp, fp) < 0) {
358                         fclose(file);
359                         printf("HDR write error\n");
360                         return 0;
361                 }
362                 if(cp) cp-= ibuf->channels*width;
363                 if(fp) fp-= ibuf->channels*width;
364         }
365
366         fclose(file);
367         return 1;
368 }