f4ea4e74b08fd4d4d8ff2131bc00ca195eb86b26
[blender.git] / source / blender / imbuf / intern / radiance_hdr.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): none yet.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/imbuf/intern/radiance_hdr.c
29  *  \ingroup imbuf
30  */
31
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  * ----------------------------------------------------------------------
38  */
39
40 #ifdef WIN32
41 #  include <io.h>
42 #endif
43
44 #include "MEM_guardedalloc.h"
45
46 #include "BLI_utildefines.h"
47 #include "BLI_fileops.h"
48
49 #include "imbuf.h"
50
51 #include "IMB_imbuf_types.h"
52 #include "IMB_imbuf.h"
53
54 #include "IMB_allocimbuf.h"
55 #include "IMB_filetype.h"
56
57 #include "IMB_colormanagement.h"
58 #include "IMB_colormanagement_intern.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 typedef unsigned char RGBE[4];
70 typedef float fCOLOR[3];
71
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
75 /* read routines */
76 static unsigned char *oldreadcolrs(RGBE *scan, unsigned char *mem, int xmax)
77 {
78         int i, rshift = 0, len = xmax;
79         while (len > 0) {
80                 scan[0][RED] = *mem++;
81                 scan[0][GRN] = *mem++;
82                 scan[0][BLU] = *mem++;
83                 scan[0][EXP] = *mem++;
84                 if (scan[0][RED] == 1 && scan[0][GRN] == 1 && scan[0][BLU] == 1) {
85                         for (i = scan[0][EXP] << rshift; i > 0; i--) {
86                                 COPY_RGBE(scan[-1], scan[0]);
87                                 scan++;
88                                 len--;
89                         }
90                         rshift += 8;
91                 }
92                 else {
93                         scan++;
94                         len--;
95                         rshift = 0;
96                 }
97         }
98         return mem;
99 }
100
101 static unsigned char *freadcolrs(RGBE *scan, unsigned char *mem, int xmax)
102 {
103         int i, j, code, val;
104
105         if ((xmax < MINELEN) | (xmax > MAXELEN)) return oldreadcolrs(scan, mem, xmax);
106
107         i = *mem++;
108         if (i != 2) return oldreadcolrs(scan, mem - 1, xmax);
109
110         scan[0][GRN] = *mem++;
111         scan[0][BLU] = *mem++;
112
113         i = *mem++;
114         if (((scan[0][BLU] << 8) | i) != xmax) return NULL;
115
116         for (i = 0; i < 4; i++)
117                 for (j = 0; j < xmax; ) {
118                         code = *mem++;
119                         if (code > 128) {
120                                 code &= 127;
121                                 val = *mem++;
122                                 while (code--)
123                                         scan[j++][i] = (unsigned char)val;
124                         }
125                         else
126                                 while (code--)
127                                         scan[j++][i] = *mem++;
128                 }
129         return mem;
130 }
131
132 /* helper functions */
133
134 /* rgbe -> float color */
135 static void RGBE2FLOAT(RGBE rgbe, fCOLOR fcol)
136 {
137         if (rgbe[EXP] == 0) {
138                 fcol[RED] = fcol[GRN] = fcol[BLU] = 0;
139         }
140         else {
141                 float f = ldexp(1.0, rgbe[EXP] - (COLXS + 8));
142                 fcol[RED] = f * (rgbe[RED] + 0.5f);
143                 fcol[GRN] = f * (rgbe[GRN] + 0.5f);
144                 fcol[BLU] = f * (rgbe[BLU] + 0.5f);
145         }
146 }
147
148 /* float color -> rgbe */
149 static void FLOAT2RGBE(fCOLOR fcol, RGBE rgbe)
150 {
151         int e;
152         float d = (fcol[RED] > fcol[GRN]) ? fcol[RED] : fcol[GRN];
153         if (fcol[BLU] > d) d = fcol[BLU];
154         if (d <= 1e-32f)
155                 rgbe[RED] = rgbe[GRN] = rgbe[BLU] = rgbe[EXP] = 0;
156         else {
157                 d = (float)frexp(d, &e) * 256.0f / d;
158                 rgbe[RED] = (unsigned char)(fcol[RED] * d);
159                 rgbe[GRN] = (unsigned char)(fcol[GRN] * d);
160                 rgbe[BLU] = (unsigned char)(fcol[BLU] * d);
161                 rgbe[EXP] = (unsigned char)(e + COLXS);
162         }
163 }
164
165 /* ImBuf read */
166
167 int imb_is_a_hdr(unsigned char *buf)
168 {
169         /* For recognition, Blender only loads first 32 bytes, so use #?RADIANCE id instead */
170         /* update: actually, the 'RADIANCE' part is just an optional program name, the magic word is really only the '#?' part */
171         //if (strstr((char *)buf, "#?RADIANCE")) return 1;
172         if (strstr((char *)buf, "#?")) return 1;
173         // if (strstr((char *)buf, "32-bit_rle_rgbe")) return 1;
174         return 0;
175 }
176
177 struct ImBuf *imb_loadhdr(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
178 {
179         struct ImBuf *ibuf;
180         RGBE *sline;
181         fCOLOR fcol;
182         float *rect_float;
183         int found = 0;
184         int width = 0, height = 0;
185         int x, y;
186         unsigned char *ptr;
187         char oriY[80], oriX[80];
188
189         if (imb_is_a_hdr((void *)mem)) {
190                 colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_FLOAT);
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)
202                         {
203                                 return NULL;
204                         }
205
206                         /* find end of this line, data right behind it */
207                         ptr = (unsigned char *)strchr((char *)&mem[x + 1], '\n');
208                         ptr++;
209
210                         if (flags & IB_test) ibuf = IMB_allocImBuf(width, height, 32, 0);
211                         else ibuf = IMB_allocImBuf(width, height, 32, (flags & IB_rect) | IB_rectfloat);
212
213                         if (ibuf == NULL) return NULL;
214                         ibuf->ftype = RADHDR;
215
216                         if (flags & IB_alphamode_detect)
217                                 ibuf->flags |= IB_alphamode_premul;
218
219                         if (flags & IB_test) return ibuf;
220
221                         /* read in and decode the actual data */
222                         sline = (RGBE *)MEM_mallocN(sizeof(RGBE) * width, "radhdr_read_tmpscan");
223                         rect_float = ibuf->rect_float;
224                         
225                         for (y = 0; y < height; y++) {
226                                 ptr = freadcolrs(sline, ptr, width);
227                                 if (ptr == NULL) {
228                                         printf("HDR decode error\n");
229                                         MEM_freeN(sline);
230                                         return ibuf;
231                                 }
232                                 for (x = 0; x < width; x++) {
233                                         /* convert to ldr */
234                                         RGBE2FLOAT(sline[x], fcol);
235                                         *rect_float++ = fcol[RED];
236                                         *rect_float++ = fcol[GRN];
237                                         *rect_float++ = fcol[BLU];
238                                         *rect_float++ = 1.0f;
239                                 }
240                         }
241                         MEM_freeN(sline);
242                         if (oriY[0] == '-') IMB_flipy(ibuf);
243                         
244                         if (flags & IB_rect) {
245                                 IMB_rect_from_float(ibuf);
246                         }
247                         
248                         return ibuf;
249                 }
250                 //else printf("Data not found!\n");
251         }
252         //else printf("Not a valid radiance HDR file!\n");
253
254         return NULL;
255 }
256
257 /* ImBuf write */
258 static int fwritecolrs(FILE *file, int width, int channels, unsigned char *ibufscan, float *fpscan)
259 {
260         int x, i, j, beg, c2, cnt = 0;
261         fCOLOR fcol;
262         RGBE rgbe, *rgbe_scan;
263
264         if ((ibufscan == NULL) && (fpscan == NULL)) return 0;
265
266         rgbe_scan = (RGBE *)MEM_mallocN(sizeof(RGBE) * width, "radhdr_write_tmpscan");
267
268         /* convert scanline */
269         j = 0;
270         for (i = 0; i < width; i++) {
271                 if (fpscan) {
272                         fcol[RED] = fpscan[j];
273                         fcol[GRN] = (channels >= 2) ? fpscan[j + 1] : fpscan[j];
274                         fcol[BLU] = (channels >= 3) ? fpscan[j + 2] : fpscan[j];
275                 }
276                 else {
277                         fcol[RED] = (float)ibufscan[j] / 255.f;
278                         fcol[GRN] = (float)((channels >= 2) ? ibufscan[j + 1] : ibufscan[j]) / 255.f;
279                         fcol[BLU] = (float)((channels >= 3) ? ibufscan[j + 2] : ibufscan[j]) / 255.f;
280                 }
281                 FLOAT2RGBE(fcol, rgbe);
282                 COPY_RGBE(rgbe, rgbe_scan[i]);
283                 j += channels;
284         }
285
286         if ((width < MINELEN) | (width > MAXELEN)) {    /* OOBs, write out flat */
287                 x = fwrite((char *)rgbe_scan, sizeof(RGBE), width, file) - width;
288                 MEM_freeN(rgbe_scan);
289                 return x;
290         }
291         /* put magic header */
292         putc(2, file);
293         putc(2, file);
294         putc((unsigned char)(width >> 8), file);
295         putc((unsigned char)(width & 255), file);
296         /* put components separately */
297         for (i = 0; i < 4; i++) {
298                 for (j = 0; j < width; j += cnt) {  /* find next run */
299                         for (beg = j; beg < width; beg += cnt) {
300                                 for (cnt = 1; (cnt < 127) && ((beg + cnt) < width) && (rgbe_scan[beg + cnt][i] == rgbe_scan[beg][i]); cnt++) ;
301                                 if (cnt >= MINRUN) break;  /* long enough */
302                         }
303                         if (((beg - j) > 1) && ((beg - j) < MINRUN)) {
304                                 c2 = j + 1;
305                                 while (rgbe_scan[c2++][i] == rgbe_scan[j][i])
306                                         if (c2 == beg) {        /* short run */
307                                                 putc((unsigned char)(128 + beg - j), file);
308                                                 putc((unsigned char)(rgbe_scan[j][i]), file);
309                                                 j = beg;
310                                                 break;
311                                         }
312                         }
313                         while (j < beg) {     /* write out non-run */
314                                 if ((c2 = beg - j) > 128) c2 = 128;
315                                 putc((unsigned char)(c2), file);
316                                 while (c2--) putc(rgbe_scan[j++][i], file);
317                         }
318                         if (cnt >= MINRUN) {      /* write out run */
319                                 putc((unsigned char)(128 + cnt), file);
320                                 putc(rgbe_scan[beg][i], file);
321                         }
322                         else cnt = 0;
323                 }
324         }
325         MEM_freeN(rgbe_scan);
326         return(ferror(file) ? -1 : 0);
327 }
328
329 static void writeHeader(FILE *file, int width, int height)
330 {
331         fprintf(file, "#?RADIANCE");
332         fputc(10, file);
333         fprintf(file, "# %s", "Created with Blender");
334         fputc(10, file);
335         fprintf(file, "EXPOSURE=%25.13f", 1.0);
336         fputc(10, file);
337         fprintf(file, "FORMAT=32-bit_rle_rgbe");
338         fputc(10, file);
339         fputc(10, file);
340         fprintf(file, "-Y %d +X %d", height, width);
341         fputc(10, file);
342 }
343
344 int imb_savehdr(struct ImBuf *ibuf, const char *name, int flags)
345 {
346         FILE *file = BLI_fopen(name, "wb");
347         float *fp = NULL;
348         int y, width = ibuf->x, height = ibuf->y;
349         unsigned char *cp = NULL;
350         
351         (void)flags; /* unused */
352         
353         if (file == NULL) return 0;
354
355         writeHeader(file, width, height);
356
357         if (ibuf->rect)
358                 cp = (unsigned char *)ibuf->rect + ibuf->channels * (height - 1) * width;
359         if (ibuf->rect_float)
360                 fp = ibuf->rect_float + ibuf->channels * (height - 1) * width;
361         
362         for (y = height - 1; y >= 0; y--) {
363                 if (fwritecolrs(file, width, ibuf->channels, cp, fp) < 0) {
364                         fclose(file);
365                         printf("HDR write error\n");
366                         return 0;
367                 }
368                 if (cp) cp -= ibuf->channels * width;
369                 if (fp) fp -= ibuf->channels * width;
370         }
371
372         fclose(file);
373         return 1;
374 }