imageio_rgbe.c 20.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
    This file is part of darktable,
    copyright (c) 2009--2010 johannes hanika.

    darktable is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    darktable is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with darktable.  If not, see <http://www.gnu.org/licenses/>.
*/
18
#ifdef HAVE_CONFIG_H
19
#include "config.h"
20 21
#endif
#include "common/imageio_rgbe.h"
22
#include <ctype.h>
23 24
#include <math.h>
#include <stdio.h>
johannes hanika's avatar
johannes hanika committed
25
#include <stdlib.h>
26
#include <string.h>
27 28 29 30 31 32 33 34 35


/* THIS CODE CARRIES NO GUARANTEE OF USABILITY OR FITNESS FOR ANY PURPOSE.
 * WHILE THE AUTHORS HAVE TRIED TO ENSURE THE PROGRAM WORKS CORRECTLY,
 * IT IS STRICTLY USE AT YOUR OWN RISK.
 *
 * based on code written by Greg Ward */


36 37
typedef struct
{
38
  int valid;            /* indicate which fields are valid */
39 40 41
  char programtype[16]; /* listed at beginning of file to identify it
                         * after "#?".  defaults to "RGBE" */
  float gamma;          /* image has already been gamma corrected with
42 43
                         * given gamma.  defaults to 1.0 (no correction) */
  float exposure;       /* a value of 1.0 in an image corresponds to
44
                         * <exposure> watts/steradian/m^2.
45
                         * defaults to 1.0 */
46 47 48
  float primaries[8];   /* xy for R, G an B primaries plus white point
                         * defaults to:
                         * 0.640 0.330 0.290 0.600 0.150 0.060 0.333 0.333 */
49 50 51 52
} rgbe_header_info;

/* flags indicating which fields in an rgbe_header_info are valid */
#define RGBE_VALID_PROGRAMTYPE 0x01
53 54
#define RGBE_VALID_GAMMA 0x02
#define RGBE_VALID_EXPOSURE 0x04
55 56 57 58 59

/* return codes for rgbe routines */
#define RGBE_RETURN_SUCCESS 0
#define RGBE_RETURN_FAILURE -1

60 61 62
#define RGBE_DATA_RED 0
#define RGBE_DATA_GREEN 1
#define RGBE_DATA_BLUE 2
63
/* number of floats per pixel */
64
#define RGBE_DATA_SIZE 3
65

66 67
enum rgbe_error_codes
{
68 69 70 71 72 73 74 75 76
  rgbe_read_error,
  rgbe_write_error,
  rgbe_format_error,
  rgbe_memory_error,
};

/* default error routine.  change this to change error handling */
static int rgbe_error(int rgbe_error_code, char *msg)
{
77
  switch(rgbe_error_code)
78
  {
79 80 81 82 83 84 85
    case rgbe_read_error:
      perror("RGBE read error");
      break;
    case rgbe_write_error:
      perror("RGBE write error");
      break;
    case rgbe_format_error:
86
      fprintf(stderr, "RGBE bad file format: %s\n", msg);
87 88 89
      break;
    default:
    case rgbe_memory_error:
90
      fprintf(stderr, "RGBE error: %s\n", msg);
91 92 93 94 95 96 97
  }
  return RGBE_RETURN_FAILURE;
}

#if 0
/* standard conversion from float pixels to rgbe pixels */
/* note: you can remove the "inline"s if your compiler complains about it */
98
static void
99 100 101 102 103 104 105 106
float2rgbe(unsigned char rgbe[4], float red, float green, float blue)
{
  float v;
  int e;

  v = red;
  if (green > v) v = green;
  if (blue > v) v = blue;
107 108
  if (v < 1e-32)
  {
109 110
    rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
  }
111 112
  else
  {
113 114 115 116 117 118 119 120 121 122 123 124
    v = frexp(v,&e) * 256.0/v;
    rgbe[0] = (unsigned char) (red * v);
    rgbe[1] = (unsigned char) (green * v);
    rgbe[2] = (unsigned char) (blue * v);
    rgbe[3] = (unsigned char) (e + 128);
  }
}
#endif

/* standard conversion from rgbe to float pixels */
/* note: Ward uses ldexp(col+0.5,exp-(128+8)).  However we wanted pixels */
/*       in the range [0,1] to map back into the range [0,1].            */
125
static void rgbe2float(float *red, float *green, float *blue, unsigned char rgbe[4])
126 127 128
{
  float f;

129
  if(rgbe[3]) /*nonzero pixel*/
130
  {
131
    f = ldexp(1.0, rgbe[3] - (int)(128 + 8));
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
    *red = rgbe[0] * f;
    *green = rgbe[1] * f;
    *blue = rgbe[2] * f;
  }
  else
    *red = *green = *blue = 0.0;
}

#if 0
/* default minimal header. modify if you want more information in header */
int RGBE_WriteHeader(FILE *fp, int width, int height, rgbe_header_info *info)
{
  char *programtype = "RGBE";

  if (info && (info->valid & RGBE_VALID_PROGRAMTYPE))
    programtype = info->programtype;
  if (fprintf(fp,"#?%s\n",programtype) < 0)
    return rgbe_error(rgbe_write_error,NULL);
  /* The #? is to identify file type, the programtype is optional. */
151 152
  if (info && (info->valid & RGBE_VALID_GAMMA))
  {
153 154 155
    if (fprintf(fp,"GAMMA=%g\n",info->gamma) < 0)
      return rgbe_error(rgbe_write_error,NULL);
  }
156 157
  if (info && (info->valid & RGBE_VALID_EXPOSURE))
  {
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
    if (fprintf(fp,"EXPOSURE=%g\n",info->exposure) < 0)
      return rgbe_error(rgbe_write_error,NULL);
  }
  if (fprintf(fp,"FORMAT=32-bit_rle_rgbe\n\n") < 0)
    return rgbe_error(rgbe_write_error,NULL);
  if (fprintf(fp, "-Y %d +X %d\n", height, width) < 0)
    return rgbe_error(rgbe_write_error,NULL);
  return RGBE_RETURN_SUCCESS;
}
#endif

/* minimal header reading.  modify if you want to parse more information */
int RGBE_ReadHeader(FILE *fp, int *width, int *height, rgbe_header_info *info)
{
  char buf[128];

174
  if(info)
175
  {
176 177 178
    info->valid = 0;
    info->programtype[0] = 0;
    info->gamma = info->exposure = 1.0;
179 180
    static const float default_primaries[] = { 0.640, 0.330, 0.290, 0.600, 0.150, 0.060, 0.333, 0.333 };
    memcpy(info->primaries, default_primaries, sizeof(info->primaries));
181
  }
182 183
  if(fgets(buf, sizeof(buf) / sizeof(buf[0]), fp) == NULL) return rgbe_error(rgbe_read_error, NULL);
  if((buf[0] != '#') || (buf[1] != '?'))
184
  {
185 186 187
    /* if you want to require the magic token then uncomment the next line */
    /*return rgbe_error(rgbe_format_error,"bad initial token"); */
  }
188
  else if(info)
189
  {
190
    info->valid |= RGBE_VALID_PROGRAMTYPE;
191
    size_t i;
192
    for(i = 0; i < sizeof(info->programtype) - 1; i++)
193
    {
194 195
      if((buf[i + 2] == 0) || isspace(buf[i + 2])) break;
      info->programtype[i] = buf[i + 2];
196 197
    }
    info->programtype[i] = 0;
198
    if(fgets(buf, sizeof(buf) / sizeof(buf[0]), fp) == 0) return rgbe_error(rgbe_read_error, NULL);
199
  }
200
  gboolean format_is_rgbe = FALSE;
201 202
  for(;;)
  {
203
    if((buf[0] == 0) || (buf[0] == '\n'))
204
      break;
205
    else if(strcmp(buf, "FORMAT=32-bit_rle_rgbe\n") == 0)
206 207
      format_is_rgbe = TRUE;
    else if(info)
208
    {
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
      if(g_str_has_prefix(buf, "GAMMA="))
      {
        char *startptr = buf + strlen("GAMMA="), *endptr;
        float tmp = g_ascii_strtod(startptr, &endptr);
        if(startptr != endptr)
        {
          info->gamma = tmp;
          info->valid |= RGBE_VALID_GAMMA;
        }
      }
      else if(g_str_has_prefix(buf, "EXPOSURE="))
      {
        char *startptr = buf + strlen("EXPOSURE="), *endptr;
        float tmp = g_ascii_strtod(startptr, &endptr);
        if(startptr != endptr)
        {
          info->exposure = tmp;
          info->valid |= RGBE_VALID_EXPOSURE;
        }
      }
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
      else if(g_str_has_prefix(buf, "PRIMARIES="))
      {
        float tmp[8];
        gboolean all_ok = TRUE;
        char *startptr = buf + strlen("PRIMARIES="), *endptr;
        for(int i = 0; i < 8; i++)
        {
          tmp[i] = g_ascii_strtod(startptr, &endptr);
          if(startptr == endptr)
          {
            all_ok = FALSE;
            break;
          }
          startptr = endptr;
        }
        if(all_ok) memcpy(info->primaries, tmp, sizeof(info->primaries));
      }
246
    }
247

248
    if(fgets(buf, sizeof(buf) / sizeof(buf[0]), fp) == 0) return rgbe_error(rgbe_read_error, NULL);
249
  }
250 251 252
  if(!format_is_rgbe)
    return rgbe_error(rgbe_format_error, "no FORMAT specifier found or it's not 32-bit_rle_rgbe");
  while(!strcmp(buf, "\n")) // be nice and accept more than one blank line
253 254 255
    if(fgets(buf, sizeof(buf) / sizeof(buf[0]), fp) == 0) return rgbe_error(rgbe_read_error, NULL);
  if(sscanf(buf, "-Y %d +X %d", height, width) < 2)
    return rgbe_error(rgbe_format_error, "missing image size specifier");
256 257 258 259 260 261 262 263 264 265 266
  return RGBE_RETURN_SUCCESS;
}

#if 0
/* simple write routine that does not use run length encoding */
/* These routines can be made faster by allocating a larger buffer and
   fread-ing and fwrite-ing the data in larger chunks */
int RGBE_WritePixels(FILE *fp, float *data, int numpixels)
{
  unsigned char rgbe[4];

267 268
  while (numpixels-- > 0)
  {
269
    float2rgbe(rgbe,data[RGBE_DATA_RED],
270
               data[RGBE_DATA_GREEN],data[RGBE_DATA_BLUE]);
271 272 273 274 275 276 277 278 279 280 281 282 283
    data += RGBE_DATA_SIZE;
    if (fwrite(rgbe, sizeof(rgbe), 1, fp) < 1)
      return rgbe_error(rgbe_write_error,NULL);
  }
  return RGBE_RETURN_SUCCESS;
}
#endif

/* simple read routine.  will not correctly handle run length encoding */
int RGBE_ReadPixels(FILE *fp, float *data, int numpixels)
{
  unsigned char rgbe[4];

284 285
  while(numpixels-- > 0)
  {
286 287
    if(fread(rgbe, sizeof(rgbe), 1, fp) < 1) return rgbe_error(rgbe_read_error, NULL);
    rgbe2float(&data[RGBE_DATA_RED], &data[RGBE_DATA_GREEN], &data[RGBE_DATA_BLUE], rgbe);
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
    data += RGBE_DATA_SIZE;
  }
  return RGBE_RETURN_SUCCESS;
}

#if 0
/* The code below is only needed for the run-length encoded files. */
/* Run length encoding adds considerable complexity but does */
/* save some space.  For each scanline, each channel (r,g,b,e) is */
/* encoded separately for better compression. */

static int RGBE_WriteBytes_RLE(FILE *fp, unsigned char *data, int numbytes)
{
#define MINRUNLENGTH 4
  int cur, beg_run, run_count, old_run_count, nonrun_count;
  unsigned char buf[2];

  cur = 0;
306 307
  while(cur < numbytes)
  {
308 309 310
    beg_run = cur;
    /* find next run of length at least 4 if one exists */
    run_count = old_run_count = 0;
311 312
    while((run_count < MINRUNLENGTH) && (beg_run < numbytes))
    {
313 314 315
      beg_run += run_count;
      old_run_count = run_count;
      run_count = 1;
316 317
      while((data[beg_run] == data[beg_run + run_count])
            && (beg_run + run_count < numbytes) && (run_count < 127))
318 319 320
        run_count++;
    }
    /* if data before next big run is a short run then write it as such */
321 322
    if ((old_run_count > 1)&&(old_run_count == beg_run - cur))
    {
323 324 325 326 327 328 329
      buf[0] = 128 + old_run_count;   /*write short run*/
      buf[1] = data[cur];
      if (fwrite(buf,sizeof(buf[0])*2,1,fp) < 1)
        return rgbe_error(rgbe_write_error,NULL);
      cur = beg_run;
    }
    /* write out bytes until we reach the start of the next run */
330 331
    while(cur < beg_run)
    {
332
      nonrun_count = beg_run - cur;
333
      if (nonrun_count > 128)
334 335 336 337 338 339 340 341 342
        nonrun_count = 128;
      buf[0] = nonrun_count;
      if (fwrite(buf,sizeof(buf[0]),1,fp) < 1)
        return rgbe_error(rgbe_write_error,NULL);
      if (fwrite(&data[cur],sizeof(data[0])*nonrun_count,1,fp) < 1)
        return rgbe_error(rgbe_write_error,NULL);
      cur += nonrun_count;
    }
    /* write out next run if one was found */
343 344
    if (run_count >= MINRUNLENGTH)
    {
345 346 347 348 349 350 351 352 353 354 355 356
      buf[0] = 128 + run_count;
      buf[1] = data[beg_run];
      if (fwrite(buf,sizeof(buf[0])*2,1,fp) < 1)
        return rgbe_error(rgbe_write_error,NULL);
      cur += run_count;
    }
  }
  return RGBE_RETURN_SUCCESS;
#undef MINRUNLENGTH
}

int RGBE_WritePixels_RLE(FILE *fp, float *data, int scanline_width,
357
                         int num_scanlines)
358 359 360 361 362 363 364 365 366
{
  unsigned char rgbe[4];
  unsigned char *buffer;
  int i, err;

  if ((scanline_width < 8)||(scanline_width > 0x7fff))
    /* run length encoding is not allowed so write flat*/
    return RGBE_WritePixels(fp,data,scanline_width*num_scanlines);
  buffer = (unsigned char *)malloc(sizeof(unsigned char)*4*scanline_width);
367
  if (buffer == NULL)
368 369
    /* no buffer space so write flat */
    return RGBE_WritePixels(fp,data,scanline_width*num_scanlines);
370 371
  while(num_scanlines-- > 0)
  {
372 373 374 375
    rgbe[0] = 2;
    rgbe[1] = 2;
    rgbe[2] = scanline_width >> 8;
    rgbe[3] = scanline_width & 0xFF;
376 377
    if (fwrite(rgbe, sizeof(rgbe), 1, fp) < 1)
    {
378 379 380
      free(buffer);
      return rgbe_error(rgbe_write_error,NULL);
    }
381 382
    for(i=0; i<scanline_width; i++)
    {
383
      float2rgbe(rgbe,data[RGBE_DATA_RED],
384
                 data[RGBE_DATA_GREEN],data[RGBE_DATA_BLUE]);
385 386 387 388 389 390 391 392
      buffer[i] = rgbe[0];
      buffer[i+scanline_width] = rgbe[1];
      buffer[i+2*scanline_width] = rgbe[2];
      buffer[i+3*scanline_width] = rgbe[3];
      data += RGBE_DATA_SIZE;
    }
    /* write out each of the four channels separately run length encoded */
    /* first red, then green, then blue, then exponent */
393 394
    for(i=0; i<4; i++)
    {
395
      if ((err = RGBE_WriteBytes_RLE(fp,&buffer[i*scanline_width],
396 397
                                     scanline_width)) != RGBE_RETURN_SUCCESS)
      {
398 399 400 401 402 403 404 405 406 407
        free(buffer);
        return err;
      }
    }
  }
  free(buffer);
  return RGBE_RETURN_SUCCESS;
}
#endif

408
int RGBE_ReadPixels_RLE(FILE *fp, float *data, int scanline_width, int num_scanlines)
409 410 411 412 413
{
  unsigned char rgbe[4], *scanline_buffer, *ptr, *ptr_end;
  int i, count;
  unsigned char buf[2];

414 415
  if((scanline_width < 8) || (scanline_width > 0x7fff)) /* run length encoding is not allowed so read flat*/
    return RGBE_ReadPixels(fp, data, scanline_width * num_scanlines);
416 417
  scanline_buffer = NULL;
  /* read in each successive scanline */
418 419
  while(num_scanlines > 0)
  {
420
    if(fread(rgbe, sizeof(rgbe), 1, fp) < 1)
421
    {
422
      free(scanline_buffer);
423
      return rgbe_error(rgbe_read_error, NULL);
424
    }
425
    if((rgbe[0] != 2) || (rgbe[1] != 2) || (rgbe[2] & 0x80))
426
    {
427
      /* this file is not run length encoded */
428
      rgbe2float(&data[0], &data[1], &data[2], rgbe);
429 430
      data += RGBE_DATA_SIZE;
      free(scanline_buffer);
431
      return RGBE_ReadPixels(fp, data, scanline_width * num_scanlines - 1);
432
    }
433
    if((((int)rgbe[2]) << 8 | rgbe[3]) != scanline_width)
434
    {
435
      free(scanline_buffer);
436
      return rgbe_error(rgbe_format_error, "wrong scanline width");
437
    }
438 439 440
    if(scanline_buffer == NULL)
      scanline_buffer = (unsigned char *)malloc(sizeof(unsigned char) * 4 * scanline_width);
    if(scanline_buffer == NULL) return rgbe_error(rgbe_memory_error, "unable to allocate buffer space");
441 442 443

    ptr = &scanline_buffer[0];
    /* read each of the four channels for the scanline into the buffer */
444
    for(i = 0; i < 4; i++)
445
    {
446
      ptr_end = &scanline_buffer[(i + 1) * scanline_width];
447 448
      while(ptr < ptr_end)
      {
449
        if(fread(buf, sizeof(buf[0]) * 2, 1, fp) < 1)
450
        {
451
          free(scanline_buffer);
452
          return rgbe_error(rgbe_read_error, NULL);
453
        }
454
        if(buf[0] > 128)
455
        {
456
          /* a run of the same value */
457 458
          count = buf[0] - 128;
          if((count == 0) || (count > ptr_end - ptr))
459
          {
460
            free(scanline_buffer);
461
            return rgbe_error(rgbe_format_error, "bad scanline data");
462
          }
463
          while(count-- > 0) *ptr++ = buf[1];
464
        }
465 466
        else
        {
467 468
          /* a non-run */
          count = buf[0];
469
          if((count == 0) || (count > ptr_end - ptr))
470
          {
471
            free(scanline_buffer);
472
            return rgbe_error(rgbe_format_error, "bad scanline data");
473 474
          }
          *ptr++ = buf[1];
475
          if(--count > 0)
476
          {
477
            if(fread(ptr, sizeof(*ptr) * count, 1, fp) < 1)
478
            {
479
              free(scanline_buffer);
480
              return rgbe_error(rgbe_read_error, NULL);
481 482 483 484 485 486 487
            }
            ptr += count;
          }
        }
      }
    }
    /* now convert data from buffer into floats */
488
    for(i = 0; i < scanline_width; i++)
489
    {
490
      rgbe[0] = scanline_buffer[i];
491 492 493 494
      rgbe[1] = scanline_buffer[i + scanline_width];
      rgbe[2] = scanline_buffer[i + 2 * scanline_width];
      rgbe[3] = scanline_buffer[i + 3 * scanline_width];
      rgbe2float(&data[RGBE_DATA_RED], &data[RGBE_DATA_GREEN], &data[RGBE_DATA_BLUE], rgbe);
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514
      data += RGBE_DATA_SIZE;
    }
    num_scanlines--;
  }
  free(scanline_buffer);
  return RGBE_RETURN_SUCCESS;
}

#undef RGBE_VALID_PROGRAMTYPE
#undef RGBE_VALID_GAMMA
#undef RGBE_VALID_EXPOSURE

#undef RGBE_RETURN_SUCCESS
#undef RGBE_RETURN_FAILURE

#undef RGBE_DATA_RED
#undef RGBE_DATA_GREEN
#undef RGBE_DATA_BLUE
#undef RGBE_DATA_SIZE

515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591
// this function is borrowed from OpenEXR code
///////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2003, Industrial Light & Magic, a division of Lucas
// Digital Ltd. LLC
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
// *       Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// *       Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// *       Neither the name of Industrial Light & Magic nor the names of
// its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
///////////////////////////////////////////////////////////////////////////
static void _xy2matrix(const float r[2], const float g[2], const float b[2],
                       const float w[2], const float Y, float M[4][4])
{
  float X = w[0] * Y / w[1];
  float Z = (1 - w[0] - w[1]) * Y / w[1];

  //
  // Scale factors for matrix rows
  //
  float d = r[0]   * (b[1]  - g[1]) +
  b[0]  * (g[1] - r[1]) +
  g[0] * (r[1]   - b[1]);
  float Sr = (X * (b[1] - g[1]) -
  g[0] * (Y * (b[1] - 1) +
  b[1]  * (X + Z)) +
  b[0]  * (Y * (g[1] - 1) +
  g[1] * (X + Z))) / d;
  float Sg = (X * (r[1] - b[1]) +
  r[0]   * (Y * (b[1] - 1) +
  b[1]  * (X + Z)) -
  b[0]  * (Y * (r[1] - 1) +
  r[1]   * (X + Z))) / d;
  float Sb = (X * (g[1] - r[1]) -
  r[0]   * (Y * (g[1] - 1) +
  g[1] * (X + Z)) +
  g[0] * (Y * (r[1] - 1) +
  r[1]   * (X + Z))) / d;

  //
  // Assemble the matrix
  //
  for(int i = 0; i < 4; i++) M[i][3] = M[3][i] = 0.0;
  M[3][3] = 1.0;
  M[0][0] = Sr * r[0];
  M[0][1] = Sr * r[1];
  M[0][2] = Sr * (1 - r[0] - r[1]);
  M[1][0] = Sg * g[0];
  M[1][1] = Sg * g[1];
  M[1][2] = Sg * (1 - g[0] - g[1]);
  M[2][0] = Sb * b[0];
  M[2][1] = Sb * b[1];
  M[2][2] = Sb * (1 - b[0] - b[1]);
}
592

593
dt_imageio_retval_t dt_imageio_open_rgbe(dt_image_t *img, const char *filename, dt_mipmap_buffer_t *mbuf)
594 595 596
{
  const char *ext = filename + strlen(filename);
  while(*ext != '.' && ext > filename) ext--;
597 598
  if(strncmp(ext, ".hdr", 4) && strncmp(ext, ".HDR", 4) && strncmp(ext, ".Hdr", 4))
    return DT_IMAGEIO_FILE_CORRUPTED;
599
  FILE *f = g_fopen(filename, "rb");
johannes hanika's avatar
johannes hanika committed
600
  if(!f) return DT_IMAGEIO_FILE_CORRUPTED;
601

602 603
  rgbe_header_info info;
  if(RGBE_ReadHeader(f, &img->width, &img->height, &info)) goto error_corrupt;
604

605
  float *buf = (float *)dt_mipmap_cache_alloc(mbuf, img);
606 607
  if(!buf) goto error_cache_full;
  if(RGBE_ReadPixels_RLE(f, buf, img->width, img->height))
608
  {
johannes hanika's avatar
johannes hanika committed
609
    goto error_corrupt;
610 611 612
  }
  fclose(f);
  // repair nan/inf etc
613 614
  for(size_t i = (size_t)img->width * img->height; i > 0; i--)
    for(int c = 0; c < 3; c++) buf[4 * (i - 1) + c] = fmaxf(0.0f, fminf(10000.0, buf[3 * (i - 1) + c]));
615 616 617 618 619 620 621 622 623 624 625 626 627 628 629

  // set the color matrix
  float m[4][4];
  _xy2matrix(&info.primaries[0], &info.primaries[2], &info.primaries[4], &info.primaries[6], 1.0, m);

  float mat[3][3];

  for(int i = 0; i < 3; i++)
    for(int j = 0; j < 3; j++)
    {
      mat[i][j] = m[j][i];
    }

  mat3inv((float *)img->d65_color_matrix, (float *)mat);

johannes hanika's avatar
johannes hanika committed
630 631 632 633 634 635
  return DT_IMAGEIO_OK;

error_corrupt:
  fclose(f);
  return DT_IMAGEIO_FILE_CORRUPTED;
error_cache_full:
636
  fclose(f);
johannes hanika's avatar
johannes hanika committed
637
  return DT_IMAGEIO_CACHE_FULL;
638
}
639

Richard Wonka's avatar
Richard Wonka committed
640
// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
641
// vim: shiftwidth=2 expandtab tabstop=2 cindent
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
642
// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;