Blender  V2.93
radiance_hdr.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright
17  * All rights reserved.
18  */
19 
24 /* ----------------------------------------------------------------------
25  * Radiance High Dynamic Range image file IO
26  * For description and code for reading/writing of radiance hdr files
27  * by Greg Ward, refer to:
28  * http://radsite.lbl.gov/radiance/refer/Notes/picture_format.html
29  * ----------------------------------------------------------------------
30  */
31 
32 #include "MEM_guardedalloc.h"
33 
34 #include "BLI_fileops.h"
35 #include "BLI_utildefines.h"
36 
37 #include "imbuf.h"
38 
39 #include "IMB_imbuf.h"
40 #include "IMB_imbuf_types.h"
41 
42 #include "IMB_allocimbuf.h"
43 #include "IMB_filetype.h"
44 
45 #include "IMB_colormanagement.h"
47 
48 /* needed constants */
49 #define MINELEN 8
50 #define MAXELEN 0x7fff
51 #define MINRUN 4 /* minimum run length */
52 #define RED 0
53 #define GRN 1
54 #define BLU 2
55 #define EXP 3
56 #define COLXS 128
57 typedef unsigned char RGBE[4];
58 typedef float fCOLOR[3];
59 
60 /* copy source -> dest */
61 #define COPY_RGBE(c1, c2) \
62  (c2[RED] = c1[RED], c2[GRN] = c1[GRN], c2[BLU] = c1[BLU], c2[EXP] = c1[EXP])
63 
64 /* read routines */
65 static const unsigned char *oldreadcolrs(RGBE *scan,
66  const unsigned char *mem,
67  int xmax,
68  const unsigned char *mem_eof)
69 {
70  size_t i, rshift = 0, len = xmax;
71  while (len > 0) {
72  if (UNLIKELY(mem_eof - mem < 4)) {
73  return NULL;
74  }
75  scan[0][RED] = *mem++;
76  scan[0][GRN] = *mem++;
77  scan[0][BLU] = *mem++;
78  scan[0][EXP] = *mem++;
79  if (scan[0][RED] == 1 && scan[0][GRN] == 1 && scan[0][BLU] == 1) {
80  for (i = scan[0][EXP] << rshift; i > 0 && len > 0; i--) {
81  COPY_RGBE(scan[-1], scan[0]);
82  scan++;
83  len--;
84  }
85  rshift += 8;
86  }
87  else {
88  scan++;
89  len--;
90  rshift = 0;
91  }
92  }
93  return mem;
94 }
95 
96 static const unsigned char *freadcolrs(RGBE *scan,
97  const unsigned char *mem,
98  int xmax,
99  const unsigned char *mem_eof)
100 {
101  if (UNLIKELY(mem_eof - mem < 4)) {
102  return NULL;
103  }
104 
105  if (UNLIKELY((xmax < MINELEN) | (xmax > MAXELEN))) {
106  return oldreadcolrs(scan, mem, xmax, mem_eof);
107  }
108 
109  int val = *mem++;
110  if (val != 2) {
111  return oldreadcolrs(scan, mem - 1, xmax, mem_eof);
112  }
113 
114  scan[0][GRN] = *mem++;
115  scan[0][BLU] = *mem++;
116 
117  val = *mem++;
118 
119  if (scan[0][GRN] != 2 || scan[0][BLU] & 128) {
120  scan[0][RED] = 2;
121  scan[0][EXP] = val;
122  return oldreadcolrs(scan + 1, mem, xmax - 1, mem_eof);
123  }
124 
125  if (UNLIKELY(((scan[0][BLU] << 8) | val) != xmax)) {
126  return NULL;
127  }
128 
129  for (size_t i = 0; i < 4; i++) {
130  if (UNLIKELY(mem_eof - mem < 2)) {
131  return NULL;
132  }
133  for (size_t j = 0; j < xmax;) {
134  int code = *mem++;
135  if (code > 128) {
136  code &= 127;
137  if (UNLIKELY(code + j > xmax)) {
138  return NULL;
139  }
140  val = *mem++;
141  while (code--) {
142  scan[j++][i] = (unsigned char)val;
143  }
144  }
145  else {
146  if (UNLIKELY(mem_eof - mem < code)) {
147  return NULL;
148  }
149  if (UNLIKELY(code + j > xmax)) {
150  return NULL;
151  }
152  while (code--) {
153  scan[j++][i] = *mem++;
154  }
155  }
156  }
157  }
158 
159  return mem;
160 }
161 
162 /* helper functions */
163 
164 /* rgbe -> float color */
165 static void RGBE2FLOAT(RGBE rgbe, fCOLOR fcol)
166 {
167  if (rgbe[EXP] == 0) {
168  fcol[RED] = fcol[GRN] = fcol[BLU] = 0;
169  }
170  else {
171  float f = ldexp(1.0, rgbe[EXP] - (COLXS + 8));
172  fcol[RED] = f * (rgbe[RED] + 0.5f);
173  fcol[GRN] = f * (rgbe[GRN] + 0.5f);
174  fcol[BLU] = f * (rgbe[BLU] + 0.5f);
175  }
176 }
177 
178 /* float color -> rgbe */
179 static void FLOAT2RGBE(const fCOLOR fcol, RGBE rgbe)
180 {
181  int e;
182  float d = (fcol[RED] > fcol[GRN]) ? fcol[RED] : fcol[GRN];
183  if (fcol[BLU] > d) {
184  d = fcol[BLU];
185  }
186  if (d <= 1e-32f) {
187  rgbe[RED] = rgbe[GRN] = rgbe[BLU] = rgbe[EXP] = 0;
188  }
189  else {
190  d = (float)frexp(d, &e) * 256.0f / d;
191  rgbe[RED] = (unsigned char)(fcol[RED] * d);
192  rgbe[GRN] = (unsigned char)(fcol[GRN] * d);
193  rgbe[BLU] = (unsigned char)(fcol[BLU] * d);
194  rgbe[EXP] = (unsigned char)(e + COLXS);
195  }
196 }
197 
198 /* ImBuf read */
199 
200 bool imb_is_a_hdr(const unsigned char *buf, const size_t size)
201 {
202  /* NOTE: `#?RADIANCE` is used by other programs such as `ImageMagik`,
203  * Although there are some files in the wild that only use `#?` (from looking online).
204  * If this is ever a problem we could check for the longer header since this is part of the spec.
205  *
206  * We could check `32-bit_rle_rgbe` or `32-bit_rle_xyze` too since this is part of the format.
207  * Currently this isn't needed.
208  *
209  * See: http://paulbourke.net/dataformats/pic/
210  */
211  const unsigned char magic[2] = {'#', '?'};
212  if (size < sizeof(magic)) {
213  return false;
214  }
215  return memcmp(buf, magic, sizeof(magic)) == 0;
216 }
217 
218 struct ImBuf *imb_loadhdr(const unsigned char *mem,
219  size_t size,
220  int flags,
221  char colorspace[IM_MAX_SPACE])
222 {
223  struct ImBuf *ibuf;
224  RGBE *sline;
225  fCOLOR fcol;
226  float *rect_float;
227  int found = 0;
228  int width = 0, height = 0;
229  const unsigned char *ptr, *mem_eof = mem + size;
230  char oriY[3], oriX[3];
231 
232  if (!imb_is_a_hdr(mem, size)) {
233  return NULL;
234  }
235 
237 
238  /* find empty line, next line is resolution info */
239  size_t x;
240  for (x = 1; x < size; x++) {
241  if ((mem[x - 1] == '\n') && (mem[x] == '\n')) {
242  found = 1;
243  break;
244  }
245  }
246 
247  if ((found && (x < (size - 1))) == 0) {
248  /* Data not found! */
249  return NULL;
250  }
251 
252  x++;
253 
254  /* sscanf requires a null-terminated buffer argument */
255  char buf[32] = {0};
256  memcpy(buf, &mem[x], MIN2(sizeof(buf) - 1, size - x));
257 
258  if (sscanf(buf,
259  "%2s %d %2s %d",
260  (char *)&oriY,
261  &height,
262  (char *)&oriX,
263  &width) != 4) {
264  return NULL;
265  }
266 
267  if (width < 1 || height < 1) {
268  return NULL;
269  }
270 
271  /* Checking that width x height does not extend past mem_eof is not easily possible
272  * since the format uses RLE compression. Can cause excessive memory allocation to occur. */
273 
274  /* find end of this line, data right behind it */
275  ptr = (const unsigned char *)strchr((const char *)&mem[x], '\n');
276  if (ptr == NULL || ptr >= mem_eof) {
277  return NULL;
278  }
279  ptr++;
280 
281  if (flags & IB_test) {
282  ibuf = IMB_allocImBuf(width, height, 32, 0);
283  }
284  else {
285  ibuf = IMB_allocImBuf(width, height, 32, (flags & IB_rect) | IB_rectfloat);
286  }
287 
288  if (UNLIKELY(ibuf == NULL)) {
289  return NULL;
290  }
291 
292  ibuf->ftype = IMB_FTYPE_RADHDR;
293 
294  if (flags & IB_alphamode_detect) {
295  ibuf->flags |= IB_alphamode_premul;
296  }
297 
298  if (flags & IB_test) {
299  return ibuf;
300  }
301 
302  /* read in and decode the actual data */
303  sline = (RGBE *)MEM_mallocN(sizeof(*sline) * width, __func__);
304  rect_float = ibuf->rect_float;
305 
306  for (size_t y = 0; y < height; y++) {
307  ptr = freadcolrs(sline, ptr, width, mem_eof);
308  if (ptr == NULL) {
309  printf("WARNING! HDR decode error, image may be just truncated, or completely wrong...\n");
310  break;
311  }
312  for (x = 0; x < width; x++) {
313  /* convert to ldr */
314  RGBE2FLOAT(sline[x], fcol);
315  *rect_float++ = fcol[RED];
316  *rect_float++ = fcol[GRN];
317  *rect_float++ = fcol[BLU];
318  *rect_float++ = 1.0f;
319  }
320  }
321  MEM_freeN(sline);
322  if (oriY[0] == '-') {
323  IMB_flipy(ibuf);
324  }
325 
326  if (flags & IB_rect) {
327  IMB_rect_from_float(ibuf);
328  }
329 
330  return ibuf;
331 }
332 
333 /* ImBuf write */
334 static int fwritecolrs(
335  FILE *file, int width, int channels, const unsigned char *ibufscan, const float *fpscan)
336 {
337  int beg, c2, cnt = 0;
338  fCOLOR fcol;
339  RGBE rgbe, *rgbe_scan;
340 
341  if (UNLIKELY((ibufscan == NULL) && (fpscan == NULL))) {
342  return 0;
343  }
344 
345  rgbe_scan = (RGBE *)MEM_mallocN(sizeof(RGBE) * width, "radhdr_write_tmpscan");
346 
347  /* convert scanline */
348  for (size_t i = 0, j = 0; i < width; i++) {
349  if (fpscan) {
350  fcol[RED] = fpscan[j];
351  fcol[GRN] = (channels >= 2) ? fpscan[j + 1] : fpscan[j];
352  fcol[BLU] = (channels >= 3) ? fpscan[j + 2] : fpscan[j];
353  }
354  else {
355  fcol[RED] = (float)ibufscan[j] / 255.0f;
356  fcol[GRN] = (float)((channels >= 2) ? ibufscan[j + 1] : ibufscan[j]) / 255.0f;
357  fcol[BLU] = (float)((channels >= 3) ? ibufscan[j + 2] : ibufscan[j]) / 255.0f;
358  }
359  FLOAT2RGBE(fcol, rgbe);
360  COPY_RGBE(rgbe, rgbe_scan[i]);
361  j += channels;
362  }
363 
364  if ((width < MINELEN) | (width > MAXELEN)) { /* OOBs, write out flat */
365  int x = fwrite((char *)rgbe_scan, sizeof(RGBE), width, file) - width;
366  MEM_freeN(rgbe_scan);
367  return x;
368  }
369  /* put magic header */
370  putc(2, file);
371  putc(2, file);
372  putc((unsigned char)(width >> 8), file);
373  putc((unsigned char)(width & 255), file);
374  /* put components separately */
375  for (size_t i = 0; i < 4; i++) {
376  for (size_t j = 0; j < width; j += cnt) { /* find next run */
377  for (beg = j; beg < width; beg += cnt) {
378  for (cnt = 1; (cnt < 127) && ((beg + cnt) < width) &&
379  (rgbe_scan[beg + cnt][i] == rgbe_scan[beg][i]);
380  cnt++) {
381  /* pass */
382  }
383  if (cnt >= MINRUN) {
384  break; /* long enough */
385  }
386  }
387  if (((beg - j) > 1) && ((beg - j) < MINRUN)) {
388  c2 = j + 1;
389  while (rgbe_scan[c2++][i] == rgbe_scan[j][i]) {
390  if (c2 == beg) { /* short run */
391  putc((unsigned char)(128 + beg - j), file);
392  putc((unsigned char)(rgbe_scan[j][i]), file);
393  j = beg;
394  break;
395  }
396  }
397  }
398  while (j < beg) { /* write out non-run */
399  if ((c2 = beg - j) > 128) {
400  c2 = 128;
401  }
402  putc((unsigned char)(c2), file);
403  while (c2--) {
404  putc(rgbe_scan[j++][i], file);
405  }
406  }
407  if (cnt >= MINRUN) { /* write out run */
408  putc((unsigned char)(128 + cnt), file);
409  putc(rgbe_scan[beg][i], file);
410  }
411  else {
412  cnt = 0;
413  }
414  }
415  }
416  MEM_freeN(rgbe_scan);
417  return (ferror(file) ? -1 : 0);
418 }
419 
420 static void writeHeader(FILE *file, int width, int height)
421 {
422  fprintf(file, "#?RADIANCE");
423  fputc(10, file);
424  fprintf(file, "# %s", "Created with Blender");
425  fputc(10, file);
426  fprintf(file, "EXPOSURE=%25.13f", 1.0);
427  fputc(10, file);
428  fprintf(file, "FORMAT=32-bit_rle_rgbe");
429  fputc(10, file);
430  fputc(10, file);
431  fprintf(file, "-Y %d +X %d", height, width);
432  fputc(10, file);
433 }
434 
435 bool imb_savehdr(struct ImBuf *ibuf, const char *filepath, int flags)
436 {
437  FILE *file = BLI_fopen(filepath, "wb");
438  float *fp = NULL;
439  size_t width = ibuf->x, height = ibuf->y;
440  unsigned char *cp = NULL;
441 
442  (void)flags; /* unused */
443 
444  if (file == NULL) {
445  return 0;
446  }
447 
449 
450  if (ibuf->rect) {
451  cp = (unsigned char *)ibuf->rect + ibuf->channels * (height - 1) * width;
452  }
453  if (ibuf->rect_float) {
454  fp = ibuf->rect_float + ibuf->channels * (height - 1) * width;
455  }
456 
457  for (size_t y = 0; y < height; y++) {
458  if (fwritecolrs(file, width, ibuf->channels, cp, fp) < 0) {
459  fclose(file);
460  printf("HDR write error\n");
461  return 0;
462  }
463  if (cp) {
464  cp -= ibuf->channels * width;
465  }
466  if (fp) {
467  fp -= ibuf->channels * width;
468  }
469  }
470 
471  fclose(file);
472  return 1;
473 }
typedef float(TangentPoint)[2]
File and directory operations.
FILE * BLI_fopen(const char *filename, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: fileops.c:1003
#define UNLIKELY(x)
#define MIN2(a, b)
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei width
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei height
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
Header file for allocimbuf.c.
@ COLOR_ROLE_DEFAULT_FLOAT
struct ImBuf * IMB_allocImBuf(unsigned int x, unsigned int y, unsigned char planes, unsigned int flags)
Definition: allocimbuf.c:478
void IMB_rect_from_float(struct ImBuf *ibuf)
Definition: divers.c:720
#define IM_MAX_SPACE
Definition: IMB_imbuf.h:65
void IMB_flipy(struct ImBuf *ibuf)
Definition: rotate.c:33
Contains defines and structs used throughout the imbuf module.
@ IB_alphamode_premul
@ IB_rectfloat
@ IB_alphamode_detect
@ IB_test
@ IB_rect
Read Guarded memory(de)allocation.
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
void colorspace_set_default_role(char *colorspace, int size, int role)
FILE * file
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:47
static const unsigned char * oldreadcolrs(RGBE *scan, const unsigned char *mem, int xmax, const unsigned char *mem_eof)
Definition: radiance_hdr.c:65
#define COLXS
Definition: radiance_hdr.c:56
static const unsigned char * freadcolrs(RGBE *scan, const unsigned char *mem, int xmax, const unsigned char *mem_eof)
Definition: radiance_hdr.c:96
static void writeHeader(FILE *file, int width, int height)
Definition: radiance_hdr.c:420
struct ImBuf * imb_loadhdr(const unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
Definition: radiance_hdr.c:218
static void FLOAT2RGBE(const fCOLOR fcol, RGBE rgbe)
Definition: radiance_hdr.c:179
bool imb_is_a_hdr(const unsigned char *buf, const size_t size)
Definition: radiance_hdr.c:200
#define MINELEN
Definition: radiance_hdr.c:49
#define MINRUN
Definition: radiance_hdr.c:51
static int fwritecolrs(FILE *file, int width, int channels, const unsigned char *ibufscan, const float *fpscan)
Definition: radiance_hdr.c:334
unsigned char RGBE[4]
Definition: radiance_hdr.c:57
bool imb_savehdr(struct ImBuf *ibuf, const char *filepath, int flags)
Definition: radiance_hdr.c:435
#define EXP
Definition: radiance_hdr.c:55
#define RED
Definition: radiance_hdr.c:52
#define MAXELEN
Definition: radiance_hdr.c:50
#define COPY_RGBE(c1, c2)
Definition: radiance_hdr.c:61
float fCOLOR[3]
Definition: radiance_hdr.c:58
#define BLU
Definition: radiance_hdr.c:54
#define GRN
Definition: radiance_hdr.c:53
static void RGBE2FLOAT(RGBE rgbe, fCOLOR fcol)
Definition: radiance_hdr.c:165
int channels
enum eImbFileType ftype
unsigned int * rect
float * rect_float
static int magic(const Tex *tex, const float texvec[3], TexResult *texres)
uint len
PointerRNA * ptr
Definition: wm_files.c:3157