Blender  V3.3
radiance_hdr.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
11 #include "MEM_guardedalloc.h"
12 
13 #include "BLI_fileops.h"
14 #include "BLI_utildefines.h"
15 
16 #include "imbuf.h"
17 
18 #include "IMB_imbuf.h"
19 #include "IMB_imbuf_types.h"
20 
21 #include "IMB_allocimbuf.h"
22 #include "IMB_filetype.h"
23 
24 #include "IMB_colormanagement.h"
26 
27 /* needed constants */
28 #define MINELEN 8
29 #define MAXELEN 0x7fff
30 #define MINRUN 4 /* minimum run length */
31 #define RED 0
32 #define GRN 1
33 #define BLU 2
34 #define EXP 3
35 #define COLXS 128
36 typedef unsigned char RGBE[4];
37 typedef float fCOLOR[3];
38 
39 /* copy source -> dest */
40 #define COPY_RGBE(c1, c2) \
41  (c2[RED] = c1[RED], c2[GRN] = c1[GRN], c2[BLU] = c1[BLU], c2[EXP] = c1[EXP])
42 
43 /* read routines */
44 static const unsigned char *oldreadcolrs(RGBE *scan,
45  const unsigned char *mem,
46  int xmax,
47  const unsigned char *mem_eof)
48 {
49  size_t i, rshift = 0, len = xmax;
50  while (len > 0) {
51  if (UNLIKELY(mem_eof - mem < 4)) {
52  return NULL;
53  }
54  scan[0][RED] = *mem++;
55  scan[0][GRN] = *mem++;
56  scan[0][BLU] = *mem++;
57  scan[0][EXP] = *mem++;
58  if (scan[0][RED] == 1 && scan[0][GRN] == 1 && scan[0][BLU] == 1) {
59  for (i = scan[0][EXP] << rshift; i > 0 && len > 0; i--) {
60  COPY_RGBE(scan[-1], scan[0]);
61  scan++;
62  len--;
63  }
64  rshift += 8;
65  }
66  else {
67  scan++;
68  len--;
69  rshift = 0;
70  }
71  }
72  return mem;
73 }
74 
75 static const unsigned char *freadcolrs(RGBE *scan,
76  const unsigned char *mem,
77  int xmax,
78  const unsigned char *mem_eof)
79 {
80  if (UNLIKELY(mem_eof - mem < 4)) {
81  return NULL;
82  }
83 
84  if (UNLIKELY((xmax < MINELEN) | (xmax > MAXELEN))) {
85  return oldreadcolrs(scan, mem, xmax, mem_eof);
86  }
87 
88  int val = *mem++;
89  if (val != 2) {
90  return oldreadcolrs(scan, mem - 1, xmax, mem_eof);
91  }
92 
93  scan[0][GRN] = *mem++;
94  scan[0][BLU] = *mem++;
95 
96  val = *mem++;
97 
98  if (scan[0][GRN] != 2 || scan[0][BLU] & 128) {
99  scan[0][RED] = 2;
100  scan[0][EXP] = val;
101  return oldreadcolrs(scan + 1, mem, xmax - 1, mem_eof);
102  }
103 
104  if (UNLIKELY(((scan[0][BLU] << 8) | val) != xmax)) {
105  return NULL;
106  }
107 
108  for (size_t i = 0; i < 4; i++) {
109  if (UNLIKELY(mem_eof - mem < 2)) {
110  return NULL;
111  }
112  for (size_t j = 0; j < xmax;) {
113  int code = *mem++;
114  if (code > 128) {
115  code &= 127;
116  if (UNLIKELY(code + j > xmax)) {
117  return NULL;
118  }
119  val = *mem++;
120  while (code--) {
121  scan[j++][i] = (unsigned char)val;
122  }
123  }
124  else {
125  if (UNLIKELY(mem_eof - mem < code)) {
126  return NULL;
127  }
128  if (UNLIKELY(code + j > xmax)) {
129  return NULL;
130  }
131  while (code--) {
132  scan[j++][i] = *mem++;
133  }
134  }
135  }
136  }
137 
138  return mem;
139 }
140 
141 /* helper functions */
142 
143 /* rgbe -> float color */
144 static void RGBE2FLOAT(RGBE rgbe, fCOLOR fcol)
145 {
146  if (rgbe[EXP] == 0) {
147  fcol[RED] = fcol[GRN] = fcol[BLU] = 0;
148  }
149  else {
150  float f = ldexp(1.0, rgbe[EXP] - (COLXS + 8));
151  fcol[RED] = f * (rgbe[RED] + 0.5f);
152  fcol[GRN] = f * (rgbe[GRN] + 0.5f);
153  fcol[BLU] = f * (rgbe[BLU] + 0.5f);
154  }
155 }
156 
157 /* float color -> rgbe */
158 static void FLOAT2RGBE(const fCOLOR fcol, RGBE rgbe)
159 {
160  int e;
161  float d = (fcol[RED] > fcol[GRN]) ? fcol[RED] : fcol[GRN];
162  if (fcol[BLU] > d) {
163  d = fcol[BLU];
164  }
165  if (d <= 1e-32f) {
166  rgbe[RED] = rgbe[GRN] = rgbe[BLU] = rgbe[EXP] = 0;
167  }
168  else {
169  d = (float)frexp(d, &e) * 256.0f / d;
170  rgbe[RED] = (unsigned char)(fcol[RED] * d);
171  rgbe[GRN] = (unsigned char)(fcol[GRN] * d);
172  rgbe[BLU] = (unsigned char)(fcol[BLU] * d);
173  rgbe[EXP] = (unsigned char)(e + COLXS);
174  }
175 }
176 
177 /* ImBuf read */
178 
179 bool imb_is_a_hdr(const unsigned char *buf, const size_t size)
180 {
181  /* NOTE: `#?RADIANCE` is used by other programs such as `ImageMagik`,
182  * Although there are some files in the wild that only use `#?` (from looking online).
183  * If this is ever a problem we could check for the longer header since this is part of the spec.
184  *
185  * We could check `32-bit_rle_rgbe` or `32-bit_rle_xyze` too since this is part of the format.
186  * Currently this isn't needed.
187  *
188  * See: http://paulbourke.net/dataformats/pic/
189  */
190  const unsigned char magic[2] = {'#', '?'};
191  if (size < sizeof(magic)) {
192  return false;
193  }
194  return memcmp(buf, magic, sizeof(magic)) == 0;
195 }
196 
197 struct ImBuf *imb_loadhdr(const unsigned char *mem,
198  size_t size,
199  int flags,
200  char colorspace[IM_MAX_SPACE])
201 {
202  struct ImBuf *ibuf;
203  RGBE *sline;
204  fCOLOR fcol;
205  float *rect_float;
206  int found = 0;
207  int width = 0, height = 0;
208  const unsigned char *ptr, *mem_eof = mem + size;
209  char oriY[3], oriX[3];
210 
211  if (!imb_is_a_hdr(mem, size)) {
212  return NULL;
213  }
214 
216 
217  /* find empty line, next line is resolution info */
218  size_t x;
219  for (x = 1; x < size; x++) {
220  if ((mem[x - 1] == '\n') && (mem[x] == '\n')) {
221  found = 1;
222  break;
223  }
224  }
225 
226  if ((found && (x < (size - 1))) == 0) {
227  /* Data not found! */
228  return NULL;
229  }
230 
231  x++;
232 
233  /* sscanf requires a null-terminated buffer argument */
234  char buf[32] = {0};
235  memcpy(buf, &mem[x], MIN2(sizeof(buf) - 1, size - x));
236 
237  if (sscanf(buf, "%2s %d %2s %d", (char *)&oriY, &height, (char *)&oriX, &width) != 4) {
238  return NULL;
239  }
240 
241  if (width < 1 || height < 1) {
242  return NULL;
243  }
244 
245  /* Checking that width x height does not extend past mem_eof is not easily possible
246  * since the format uses RLE compression. Can cause excessive memory allocation to occur. */
247 
248  /* find end of this line, data right behind it */
249  ptr = (const unsigned char *)strchr((const char *)&mem[x], '\n');
250  if (ptr == NULL || ptr >= mem_eof) {
251  return NULL;
252  }
253  ptr++;
254 
255  if (flags & IB_test) {
256  ibuf = IMB_allocImBuf(width, height, 32, 0);
257  }
258  else {
259  ibuf = IMB_allocImBuf(width, height, 32, (flags & IB_rect) | IB_rectfloat);
260  }
261 
262  if (UNLIKELY(ibuf == NULL)) {
263  return NULL;
264  }
265 
266  ibuf->ftype = IMB_FTYPE_RADHDR;
267 
268  if (flags & IB_alphamode_detect) {
269  ibuf->flags |= IB_alphamode_premul;
270  }
271 
272  if (flags & IB_test) {
273  return ibuf;
274  }
275 
276  /* read in and decode the actual data */
277  sline = (RGBE *)MEM_mallocN(sizeof(*sline) * width, __func__);
278  rect_float = ibuf->rect_float;
279 
280  for (size_t y = 0; y < height; y++) {
281  ptr = freadcolrs(sline, ptr, width, mem_eof);
282  if (ptr == NULL) {
283  printf("WARNING! HDR decode error, image may be just truncated, or completely wrong...\n");
284  break;
285  }
286  for (x = 0; x < width; x++) {
287  /* Convert to LDR. */
288  RGBE2FLOAT(sline[x], fcol);
289  *rect_float++ = fcol[RED];
290  *rect_float++ = fcol[GRN];
291  *rect_float++ = fcol[BLU];
292  *rect_float++ = 1.0f;
293  }
294  }
295  MEM_freeN(sline);
296  if (oriY[0] == '-') {
297  IMB_flipy(ibuf);
298  }
299 
300  if (flags & IB_rect) {
301  IMB_rect_from_float(ibuf);
302  }
303 
304  return ibuf;
305 }
306 
307 /* ImBuf write */
308 static int fwritecolrs(
309  FILE *file, int width, int channels, const unsigned char *ibufscan, const float *fpscan)
310 {
311  int beg, c2, count = 0;
312  fCOLOR fcol;
313  RGBE rgbe, *rgbe_scan;
314 
315  if (UNLIKELY((ibufscan == NULL) && (fpscan == NULL))) {
316  return 0;
317  }
318 
319  rgbe_scan = (RGBE *)MEM_mallocN(sizeof(RGBE) * width, "radhdr_write_tmpscan");
320 
321  /* Convert scan-line. */
322  for (size_t i = 0, j = 0; i < width; i++) {
323  if (fpscan) {
324  fcol[RED] = fpscan[j];
325  fcol[GRN] = (channels >= 2) ? fpscan[j + 1] : fpscan[j];
326  fcol[BLU] = (channels >= 3) ? fpscan[j + 2] : fpscan[j];
327  }
328  else {
329  fcol[RED] = (float)ibufscan[j] / 255.0f;
330  fcol[GRN] = (float)((channels >= 2) ? ibufscan[j + 1] : ibufscan[j]) / 255.0f;
331  fcol[BLU] = (float)((channels >= 3) ? ibufscan[j + 2] : ibufscan[j]) / 255.0f;
332  }
333  FLOAT2RGBE(fcol, rgbe);
334  COPY_RGBE(rgbe, rgbe_scan[i]);
335  j += channels;
336  }
337 
338  if ((width < MINELEN) | (width > MAXELEN)) { /* OOBs, write out flat */
339  int x = fwrite((char *)rgbe_scan, sizeof(RGBE), width, file) - width;
340  MEM_freeN(rgbe_scan);
341  return x;
342  }
343  /* put magic header */
344  putc(2, file);
345  putc(2, file);
346  putc((unsigned char)(width >> 8), file);
347  putc((unsigned char)(width & 255), file);
348  /* put components separately */
349  for (size_t i = 0; i < 4; i++) {
350  for (size_t j = 0; j < width; j += count) { /* find next run */
351  for (beg = j; beg < width; beg += count) {
352  for (count = 1; (count < 127) && ((beg + count) < width) &&
353  (rgbe_scan[beg + count][i] == rgbe_scan[beg][i]);
354  count++) {
355  /* pass */
356  }
357  if (count >= MINRUN) {
358  break; /* long enough */
359  }
360  }
361  if (((beg - j) > 1) && ((beg - j) < MINRUN)) {
362  c2 = j + 1;
363  while (rgbe_scan[c2++][i] == rgbe_scan[j][i]) {
364  if (c2 == beg) { /* short run */
365  putc((unsigned char)(128 + beg - j), file);
366  putc((unsigned char)(rgbe_scan[j][i]), file);
367  j = beg;
368  break;
369  }
370  }
371  }
372  while (j < beg) { /* write out non-run */
373  if ((c2 = beg - j) > 128) {
374  c2 = 128;
375  }
376  putc((unsigned char)(c2), file);
377  while (c2--) {
378  putc(rgbe_scan[j++][i], file);
379  }
380  }
381  if (count >= MINRUN) { /* write out run */
382  putc((unsigned char)(128 + count), file);
383  putc(rgbe_scan[beg][i], file);
384  }
385  else {
386  count = 0;
387  }
388  }
389  }
390  MEM_freeN(rgbe_scan);
391  return (ferror(file) ? -1 : 0);
392 }
393 
394 static void writeHeader(FILE *file, int width, int height)
395 {
396  fprintf(file, "#?RADIANCE");
397  fputc(10, file);
398  fprintf(file, "# %s", "Created with Blender");
399  fputc(10, file);
400  fprintf(file, "EXPOSURE=%25.13f", 1.0);
401  fputc(10, file);
402  fprintf(file, "FORMAT=32-bit_rle_rgbe");
403  fputc(10, file);
404  fputc(10, file);
405  fprintf(file, "-Y %d +X %d", height, width);
406  fputc(10, file);
407 }
408 
409 bool imb_savehdr(struct ImBuf *ibuf, const char *filepath, int flags)
410 {
411  FILE *file = BLI_fopen(filepath, "wb");
412  float *fp = NULL;
413  size_t width = ibuf->x, height = ibuf->y;
414  unsigned char *cp = NULL;
415 
416  (void)flags; /* unused */
417 
418  if (file == NULL) {
419  return 0;
420  }
421 
423 
424  if (ibuf->rect) {
425  cp = (unsigned char *)ibuf->rect + ibuf->channels * (height - 1) * width;
426  }
427  if (ibuf->rect_float) {
428  fp = ibuf->rect_float + ibuf->channels * (height - 1) * width;
429  }
430 
431  for (size_t y = 0; y < height; y++) {
432  if (fwritecolrs(file, width, ibuf->channels, cp, fp) < 0) {
433  fclose(file);
434  printf("HDR write error\n");
435  return 0;
436  }
437  if (cp) {
438  cp -= ibuf->channels * width;
439  }
440  if (fp) {
441  fp -= ibuf->channels * width;
442  }
443  }
444 
445  fclose(file);
446  return 1;
447 }
typedef float(TangentPoint)[2]
File and directory operations.
FILE * BLI_fopen(const char *filepath, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: fileops.c:906
#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 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
_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
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:500
void IMB_rect_from_float(struct ImBuf *ibuf)
Definition: divers.c:696
#define IM_MAX_SPACE
Definition: IMB_imbuf.h:49
void IMB_flipy(struct ImBuf *ibuf)
Definition: rotate.c:16
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.
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert a producing a negative Combine Generate a color from its and blue channels(Deprecated)") DefNode(ShaderNode
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
SyclQueue void void size_t num_bytes void
int len
Definition: draw_manager.c:108
int count
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
static const unsigned char * oldreadcolrs(RGBE *scan, const unsigned char *mem, int xmax, const unsigned char *mem_eof)
Definition: radiance_hdr.c:44
#define COLXS
Definition: radiance_hdr.c:35
static const unsigned char * freadcolrs(RGBE *scan, const unsigned char *mem, int xmax, const unsigned char *mem_eof)
Definition: radiance_hdr.c:75
static void writeHeader(FILE *file, int width, int height)
Definition: radiance_hdr.c:394
struct ImBuf * imb_loadhdr(const unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
Definition: radiance_hdr.c:197
static void FLOAT2RGBE(const fCOLOR fcol, RGBE rgbe)
Definition: radiance_hdr.c:158
bool imb_is_a_hdr(const unsigned char *buf, const size_t size)
Definition: radiance_hdr.c:179
#define MINELEN
Definition: radiance_hdr.c:28
#define MINRUN
Definition: radiance_hdr.c:30
static int fwritecolrs(FILE *file, int width, int channels, const unsigned char *ibufscan, const float *fpscan)
Definition: radiance_hdr.c:308
unsigned char RGBE[4]
Definition: radiance_hdr.c:36
bool imb_savehdr(struct ImBuf *ibuf, const char *filepath, int flags)
Definition: radiance_hdr.c:409
#define EXP
Definition: radiance_hdr.c:34
#define RED
Definition: radiance_hdr.c:31
#define MAXELEN
Definition: radiance_hdr.c:29
#define COPY_RGBE(c1, c2)
Definition: radiance_hdr.c:40
float fCOLOR[3]
Definition: radiance_hdr.c:37
#define BLU
Definition: radiance_hdr.c:33
#define GRN
Definition: radiance_hdr.c:32
static void RGBE2FLOAT(RGBE rgbe, fCOLOR fcol)
Definition: radiance_hdr.c:144
int channels
enum eImbFileType ftype
unsigned int * rect
float * rect_float
static int magic(const Tex *tex, const float texvec[3], TexResult *texres)
PointerRNA * ptr
Definition: wm_files.c:3480