Fix buffer overflows in TIFF, PNG, IRIS, DPX, HDR and AVI loading.
[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 #include "MEM_guardedalloc.h"
41
42 #include "BLI_fileops.h"
43 #include "BLI_utildefines.h"
44
45 #include "imbuf.h"
46
47 #include "IMB_imbuf_types.h"
48 #include "IMB_imbuf.h"
49
50 #include "IMB_allocimbuf.h"
51 #include "IMB_filetype.h"
52
53 #include "IMB_colormanagement.h"
54 #include "IMB_colormanagement_intern.h"
55
56 /* needed constants */
57 #define MINELEN 8
58 #define MAXELEN 0x7fff
59 #define MINRUN  4   /* minimum run length */
60 #define RED 0
61 #define GRN 1
62 #define BLU 2
63 #define EXP 3
64 #define COLXS 128
65 typedef unsigned char RGBE[4];
66 typedef float fCOLOR[3];
67
68 /* copy source -> dest */
69 #define COPY_RGBE(c1, c2) (c2[RED] = c1[RED], c2[GRN] = c1[GRN], c2[BLU] = c1[BLU], c2[EXP] = c1[EXP])
70
71 /* read routines */
72 static const unsigned char *oldreadcolrs(RGBE *scan, const unsigned char *mem, int xmax, const unsigned char *mem_eof)
73 {
74         size_t i, rshift = 0, len = xmax;
75         while (len > 0) {
76                 if (UNLIKELY(mem_eof - mem < 4)) {
77                         return NULL;
78                 }
79                 scan[0][RED] = *mem++;
80                 scan[0][GRN] = *mem++;
81                 scan[0][BLU] = *mem++;
82                 scan[0][EXP] = *mem++;
83                 if (scan[0][RED] == 1 && scan[0][GRN] == 1 && scan[0][BLU] == 1) {
84                         for (i = scan[0][EXP] << rshift; i > 0; i--) {
85                                 COPY_RGBE(scan[-1], scan[0]);
86                                 scan++;
87                                 len--;
88                         }
89                         rshift += 8;
90                 }
91                 else {
92                         scan++;
93                         len--;
94                         rshift = 0;
95                 }
96         }
97         return mem;
98 }
99
100 static const unsigned char *freadcolrs(RGBE *scan, const unsigned char *mem, int xmax, const unsigned char *mem_eof)
101 {
102         if (UNLIKELY(mem_eof - mem < 4)) {
103                 return NULL;
104         }
105
106         if (UNLIKELY((xmax < MINELEN) | (xmax > MAXELEN))) {
107                 return oldreadcolrs(scan, mem, xmax, mem_eof);
108         }
109
110         int val = *mem++;
111         if (val != 2) {
112                 return oldreadcolrs(scan, mem - 1, xmax, mem_eof);
113         }
114
115         scan[0][GRN] = *mem++;
116         scan[0][BLU] = *mem++;
117
118         val = *mem++;
119
120         if (scan[0][GRN] != 2 || scan[0][BLU] & 128) {
121                 scan[0][RED] = 2;
122                 scan[0][EXP] = val;
123                 return oldreadcolrs(scan + 1, mem, xmax - 1, mem_eof);
124         }
125
126         if (UNLIKELY(((scan[0][BLU] << 8) | val) != xmax)) {
127                 return NULL;
128         }
129
130         for (size_t i = 0; i < 4; i++) {
131                 if (UNLIKELY(mem_eof - mem < 2)) {
132                         return NULL;
133                 }
134                 for (size_t j = 0; j < xmax; ) {
135                         int code = *mem++;
136                         if (code > 128) {
137                                 code &= 127;
138                                 if (UNLIKELY(code + j > xmax)) {
139                                         return NULL;
140                                 }
141                                 int val = *mem++;
142                                 while (code--) {
143                                         scan[j++][i] = (unsigned char)val;
144                                 }
145                         }
146                         else {
147                                 if (UNLIKELY(mem_eof - mem < code)) {
148                                         return NULL;
149                                 }
150                                 if (UNLIKELY(code + j > xmax)) {
151                                         return NULL;
152                                 }
153                                 while (code--) {
154                                         scan[j++][i] = *mem++;
155                                 }
156                         }
157                 }
158         }
159
160         return mem;
161 }
162
163 /* helper functions */
164
165 /* rgbe -> float color */
166 static void RGBE2FLOAT(RGBE rgbe, fCOLOR fcol)
167 {
168         if (rgbe[EXP] == 0) {
169                 fcol[RED] = fcol[GRN] = fcol[BLU] = 0;
170         }
171         else {
172                 float f = ldexp(1.0, rgbe[EXP] - (COLXS + 8));
173                 fcol[RED] = f * (rgbe[RED] + 0.5f);
174                 fcol[GRN] = f * (rgbe[GRN] + 0.5f);
175                 fcol[BLU] = f * (rgbe[BLU] + 0.5f);
176         }
177 }
178
179 /* float color -> rgbe */
180 static void FLOAT2RGBE(fCOLOR fcol, RGBE rgbe)
181 {
182         int e;
183         float d = (fcol[RED] > fcol[GRN]) ? fcol[RED] : fcol[GRN];
184         if (fcol[BLU] > d) d = fcol[BLU];
185         if (d <= 1e-32f)
186                 rgbe[RED] = rgbe[GRN] = rgbe[BLU] = rgbe[EXP] = 0;
187         else {
188                 d = (float)frexp(d, &e) * 256.0f / d;
189                 rgbe[RED] = (unsigned char)(fcol[RED] * d);
190                 rgbe[GRN] = (unsigned char)(fcol[GRN] * d);
191                 rgbe[BLU] = (unsigned char)(fcol[BLU] * d);
192                 rgbe[EXP] = (unsigned char)(e + COLXS);
193         }
194 }
195
196 /* ImBuf read */
197
198 int imb_is_a_hdr(const unsigned char *buf)
199 {
200         /* For recognition, Blender only loads first 32 bytes, so use #?RADIANCE id instead */
201         /* update: actually, the 'RADIANCE' part is just an optional program name, the magic word is really only the '#?' part */
202         //if (strstr((char *)buf, "#?RADIANCE")) return 1;
203         if (strstr((char *)buf, "#?")) return 1;
204         // if (strstr((char *)buf, "32-bit_rle_rgbe")) return 1;
205         return 0;
206 }
207
208 struct ImBuf *imb_loadhdr(const unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
209 {
210         struct ImBuf *ibuf;
211         RGBE *sline;
212         fCOLOR fcol;
213         float *rect_float;
214         int found = 0;
215         int width = 0, height = 0;
216         const unsigned char *ptr, *mem_eof = mem + size;
217         char oriY[80], oriX[80];
218
219         if (imb_is_a_hdr((void *)mem)) {
220                 colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_FLOAT);
221
222                 /* find empty line, next line is resolution info */
223                 size_t x;
224                 for (x = 1; x < size; x++) {
225                         if ((mem[x - 1] == '\n') && (mem[x] == '\n')) {
226                                 found = 1;
227                                 break;
228                         }
229                 }
230                 if (found && (x < (size + 2))) {
231                         if (sscanf((char *)&mem[x + 1], "%79s %d %79s %d", (char *)&oriY, &height,
232                                    (char *)&oriX, &width) != 4)
233                         {
234                                 return NULL;
235                         }
236
237                         /* find end of this line, data right behind it */
238                         ptr = (unsigned char *)strchr((char *)&mem[x + 1], '\n');
239                         ptr++;
240
241                         if (flags & IB_test) ibuf = IMB_allocImBuf(width, height, 32, 0);
242                         else ibuf = IMB_allocImBuf(width, height, 32, (flags & IB_rect) | IB_rectfloat);
243
244                         if (UNLIKELY(ibuf == NULL)) {
245                                 return NULL;
246                         }
247                         ibuf->ftype = IMB_FTYPE_RADHDR;
248
249                         if (flags & IB_alphamode_detect)
250                                 ibuf->flags |= IB_alphamode_premul;
251
252                         if (flags & IB_test) {
253                                 return ibuf;
254                         }
255
256                         /* read in and decode the actual data */
257                         sline = (RGBE *)MEM_mallocN(sizeof(*sline) * width, __func__);
258                         rect_float = ibuf->rect_float;
259                         
260                         for (size_t y = 0; y < height; y++) {
261                                 ptr = freadcolrs(sline, ptr, width, mem_eof);
262                                 if (ptr == NULL) {
263                                         printf("WARNING! HDR decode error, image may be just truncated, or completely wrong...\n");
264                                         break;
265                                 }
266                                 for (size_t x = 0; x < width; x++) {
267                                         /* convert to ldr */
268                                         RGBE2FLOAT(sline[x], fcol);
269                                         *rect_float++ = fcol[RED];
270                                         *rect_float++ = fcol[GRN];
271                                         *rect_float++ = fcol[BLU];
272                                         *rect_float++ = 1.0f;
273                                 }
274                         }
275                         MEM_freeN(sline);
276                         if (oriY[0] == '-') IMB_flipy(ibuf);
277                         
278                         if (flags & IB_rect) {
279                                 IMB_rect_from_float(ibuf);
280                         }
281                         
282                         return ibuf;
283                 }
284                 //else printf("Data not found!\n");
285         }
286         //else printf("Not a valid radiance HDR file!\n");
287
288         return NULL;
289 }
290
291 /* ImBuf write */
292 static int fwritecolrs(FILE *file, int width, int channels, unsigned char *ibufscan, float *fpscan)
293 {
294         int beg, c2, cnt = 0;
295         fCOLOR fcol;
296         RGBE rgbe, *rgbe_scan;
297
298         if (UNLIKELY((ibufscan == NULL) && (fpscan == NULL))) {
299                 return 0;
300         }
301
302         rgbe_scan = (RGBE *)MEM_mallocN(sizeof(RGBE) * width, "radhdr_write_tmpscan");
303
304         /* convert scanline */
305         size_t j = 0;
306         for (size_t i = 0; i < width; i++) {
307                 if (fpscan) {
308                         fcol[RED] = fpscan[j];
309                         fcol[GRN] = (channels >= 2) ? fpscan[j + 1] : fpscan[j];
310                         fcol[BLU] = (channels >= 3) ? fpscan[j + 2] : fpscan[j];
311                 }
312                 else {
313                         fcol[RED] = (float)ibufscan[j] / 255.f;
314                         fcol[GRN] = (float)((channels >= 2) ? ibufscan[j + 1] : ibufscan[j]) / 255.f;
315                         fcol[BLU] = (float)((channels >= 3) ? ibufscan[j + 2] : ibufscan[j]) / 255.f;
316                 }
317                 FLOAT2RGBE(fcol, rgbe);
318                 COPY_RGBE(rgbe, rgbe_scan[i]);
319                 j += channels;
320         }
321
322         if ((width < MINELEN) | (width > MAXELEN)) {    /* OOBs, write out flat */
323                 int x = fwrite((char *)rgbe_scan, sizeof(RGBE), width, file) - width;
324                 MEM_freeN(rgbe_scan);
325                 return x;
326         }
327         /* put magic header */
328         putc(2, file);
329         putc(2, file);
330         putc((unsigned char)(width >> 8), file);
331         putc((unsigned char)(width & 255), file);
332         /* put components separately */
333         for (size_t i = 0; i < 4; i++) {
334                 for (size_t j = 0; j < width; j += cnt) {  /* find next run */
335                         for (beg = j; beg < width; beg += cnt) {
336                                 for (cnt = 1; (cnt < 127) && ((beg + cnt) < width) && (rgbe_scan[beg + cnt][i] == rgbe_scan[beg][i]); cnt++) ;
337                                 if (cnt >= MINRUN) break;  /* long enough */
338                         }
339                         if (((beg - j) > 1) && ((beg - j) < MINRUN)) {
340                                 c2 = j + 1;
341                                 while (rgbe_scan[c2++][i] == rgbe_scan[j][i]) {
342                                         if (c2 == beg) {        /* short run */
343                                                 putc((unsigned char)(128 + beg - j), file);
344                                                 putc((unsigned char)(rgbe_scan[j][i]), file);
345                                                 j = beg;
346                                                 break;
347                                         }
348                                 }
349                         }
350                         while (j < beg) {     /* write out non-run */
351                                 if ((c2 = beg - j) > 128) c2 = 128;
352                                 putc((unsigned char)(c2), file);
353                                 while (c2--) putc(rgbe_scan[j++][i], file);
354                         }
355                         if (cnt >= MINRUN) {      /* write out run */
356                                 putc((unsigned char)(128 + cnt), file);
357                                 putc(rgbe_scan[beg][i], file);
358                         }
359                         else {
360                                 cnt = 0;
361                         }
362                 }
363         }
364         MEM_freeN(rgbe_scan);
365         return(ferror(file) ? -1 : 0);
366 }
367
368 static void writeHeader(FILE *file, int width, int height)
369 {
370         fprintf(file, "#?RADIANCE");
371         fputc(10, file);
372         fprintf(file, "# %s", "Created with Blender");
373         fputc(10, file);
374         fprintf(file, "EXPOSURE=%25.13f", 1.0);
375         fputc(10, file);
376         fprintf(file, "FORMAT=32-bit_rle_rgbe");
377         fputc(10, file);
378         fputc(10, file);
379         fprintf(file, "-Y %d +X %d", height, width);
380         fputc(10, file);
381 }
382
383 int imb_savehdr(struct ImBuf *ibuf, const char *name, int flags)
384 {
385         FILE *file = BLI_fopen(name, "wb");
386         float *fp = NULL;
387         size_t width = ibuf->x, height = ibuf->y;
388         unsigned char *cp = NULL;
389         
390         (void)flags; /* unused */
391         
392         if (file == NULL) {
393                 return 0;
394         }
395
396         writeHeader(file, width, height);
397
398         if (ibuf->rect)
399                 cp = (unsigned char *)ibuf->rect + ibuf->channels * (height - 1) * width;
400         if (ibuf->rect_float)
401                 fp = ibuf->rect_float + ibuf->channels * (height - 1) * width;
402         
403         for (size_t y = height - 1; y >= 0; y--) {
404                 if (fwritecolrs(file, width, ibuf->channels, cp, fp) < 0) {
405                         fclose(file);
406                         printf("HDR write error\n");
407                         return 0;
408                 }
409                 if (cp) cp -= ibuf->channels * width;
410                 if (fp) fp -= ibuf->channels * width;
411         }
412
413         fclose(file);
414         return 1;
415 }