Blame libs/rgbe/rgbe.c

Packit bc1512
/* This file is an image processing operation for GEGL
Packit bc1512
 *
Packit bc1512
 * GEGL is free software; you can redistribute it and/or
Packit bc1512
 * modify it under the terms of the GNU Lesser General Public
Packit bc1512
 * License as published by the Free Software Foundation; either
Packit bc1512
 * version 3 of the License, or (at your option) any later version.
Packit bc1512
 *
Packit bc1512
 * GEGL is distributed in the hope that it will be useful,
Packit bc1512
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit bc1512
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit bc1512
 * Lesser General Public License for more details.
Packit bc1512
 *
Packit bc1512
 * You should have received a copy of the GNU Lesser General Public
Packit bc1512
 * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
Packit bc1512
 *
Packit bc1512
 * Copyright 2010 Danny Robson <danny@blubinc.net>
Packit bc1512
 */
Packit bc1512
Packit bc1512
#include "config.h"
Packit bc1512
Packit bc1512
#include "rgbe.h"
Packit bc1512
Packit bc1512
#include <errno.h>
Packit bc1512
#include <stdio.h>
Packit bc1512
#include <string.h>
Packit bc1512
#include <math.h>
Packit bc1512
Packit bc1512
Packit bc1512
/* Scanlines are limited to 2^(16 - 1), as RLE encoded lines only have the
Packit bc1512
 * lower 15 bits of the R&G components to store its length.
Packit bc1512
 */
Packit bc1512
#define RGBE_MAX_SCANLINE_WIDTH    (1 << 15)
Packit bc1512
Packit bc1512
#define RGBE_MAX_SOFTWARE_LEN      63
Packit bc1512
#define RGBE_NUM_RGB               3
Packit bc1512
#define RGBE_NUM_RGBE              4
Packit bc1512
#define RGBE_MAX_VARIABLE_LINE_LEN 24
Packit bc1512
Packit bc1512
Packit bc1512
/* Describes the colour space of the pixel components. These values must
Packit bc1512
 * index correctly into the array RGBE_FORMAT_STRINGS for correct operation.
Packit bc1512
 */
Packit bc1512
typedef enum
Packit bc1512
{
Packit bc1512
  FORMAT_RGBE,
Packit bc1512
  FORMAT_XYZE,
Packit bc1512
  FORMAT_UNKNOWN,
Packit bc1512
Packit bc1512
  NUM_RGBE_FORMATS = FORMAT_XYZE
Packit bc1512
} rgbe_format;
Packit bc1512
Packit bc1512
/* Describes the behaviour of indices across, and between scanlines. */
Packit bc1512
typedef enum
Packit bc1512
{
Packit bc1512
  ORIENT_DECREASING,
Packit bc1512
  ORIENT_INCREASING,
Packit bc1512
  ORIENT_UNKNOWN
Packit bc1512
} rgbe_orientation;
Packit bc1512
Packit bc1512
enum
Packit bc1512
{
Packit bc1512
  OFFSET_R = 0, OFFSET_X = OFFSET_R,
Packit bc1512
  OFFSET_G = 1, OFFSET_Y = OFFSET_G,
Packit bc1512
  OFFSET_B = 2, OFFSET_Z = OFFSET_B,
Packit bc1512
  OFFSET_E = 3,
Packit bc1512
  OFFSET_A = 3
Packit bc1512
};
Packit bc1512
Packit bc1512
typedef struct
Packit bc1512
{
Packit bc1512
    rgbe_orientation orient;
Packit bc1512
    guint16          size;
Packit bc1512
} rgbe_axis;
Packit bc1512
Packit bc1512
typedef struct
Packit bc1512
{
Packit bc1512
  rgbe_format   format;
Packit bc1512
Packit bc1512
  gchar         software[RGBE_MAX_SOFTWARE_LEN + 1];
Packit bc1512
Packit bc1512
  gfloat        exposure;
Packit bc1512
  gfloat        colorcorr[RGBE_NUM_RGB];
Packit bc1512
  /* TODO: xyz primaries   */
Packit bc1512
Packit bc1512
  /* TODO: view parameters */
Packit bc1512
Packit bc1512
  /* resolution parameters */
Packit bc1512
  rgbe_axis     x_axis,
Packit bc1512
                y_axis;
Packit bc1512
  gfloat        pixel_aspect;
Packit bc1512
} rgbe_header;
Packit bc1512
Packit bc1512
struct _rgbe_file
Packit bc1512
{
Packit bc1512
  rgbe_header  header;
Packit bc1512
Packit bc1512
  GMappedFile *file;
Packit bc1512
  /* Stores the address of the scanlines, or NULL */
Packit bc1512
  const void  *scanlines;
Packit bc1512
};
Packit bc1512
Packit bc1512
Packit bc1512
static const gchar RADIANCE_MAGIC[] = "#?RADIANCE";
Packit bc1512
Packit bc1512
static const gchar *RGBE_FORMAT_STRINGS[] =
Packit bc1512
{
Packit bc1512
  "32-bit_rle_rgbe",
Packit bc1512
  "32-bit_rle_xyze",
Packit bc1512
  NULL
Packit bc1512
};
Packit bc1512
Packit bc1512
Packit bc1512
/**
Packit bc1512
 * rgbe_mapped_file_remaining:
Packit bc1512
 * @f:    the file to read the image data from
Packit bc1512
 * @data: the current file read cursor
Packit bc1512
 *
Packit bc1512
 * Calculates the number of bytes remaining to be read in a mapped file.
Packit bc1512
 **/
Packit bc1512
static guint
Packit bc1512
rgbe_mapped_file_remaining (GMappedFile *f,
Packit bc1512
                            const void  *data)
Packit bc1512
{
Packit bc1512
  g_return_val_if_fail (f, 0);
Packit bc1512
  g_return_val_if_fail (GPOINTER_TO_UINT (data) >
Packit bc1512
                        GPOINTER_TO_UINT (g_mapped_file_get_contents (f)), 0);
Packit bc1512
Packit bc1512
  return GPOINTER_TO_UINT (data) -
Packit bc1512
         GPOINTER_TO_UINT (g_mapped_file_get_contents (f)) -
Packit bc1512
         g_mapped_file_get_length (f);
Packit bc1512
}
Packit bc1512
Packit bc1512
static void
Packit bc1512
rgbe_header_init (rgbe_header *header)
Packit bc1512
{
Packit bc1512
  g_return_if_fail (header);
Packit bc1512
Packit bc1512
  header->format              = FORMAT_UNKNOWN;
Packit bc1512
  memset (header->software, '\0', G_N_ELEMENTS (header->software));
Packit bc1512
Packit bc1512
  header->exposure            = 1.0;
Packit bc1512
  header->colorcorr[OFFSET_R] = 1.0;
Packit bc1512
  header->colorcorr[OFFSET_G] = 1.0;
Packit bc1512
  header->colorcorr[OFFSET_B] = 1.0;
Packit bc1512
Packit bc1512
  header->pixel_aspect        = 1.0;
Packit bc1512
Packit bc1512
  header->x_axis.orient = header->y_axis.orient = ORIENT_UNKNOWN;
Packit bc1512
  header->x_axis.size   = header->y_axis.size   = 0;
Packit bc1512
}
Packit bc1512
Packit bc1512
static gboolean
Packit bc1512
rgbe_file_init (rgbe_file   *file,
Packit bc1512
                const gchar *path)
Packit bc1512
{
Packit bc1512
  g_return_val_if_fail (file != NULL, FALSE);
Packit bc1512
Packit bc1512
  rgbe_header_init (&file->header);
Packit bc1512
  file->file      = g_mapped_file_new (path, FALSE, NULL);
Packit bc1512
  file->scanlines = NULL;
Packit bc1512
Packit bc1512
  return file->file != NULL;
Packit bc1512
}
Packit bc1512
Packit bc1512
static rgbe_file*
Packit bc1512
rgbe_file_new (const gchar *path)
Packit bc1512
{
Packit bc1512
  rgbe_file *file;
Packit bc1512
Packit bc1512
  g_return_val_if_fail (path, NULL);
Packit bc1512
Packit bc1512
  file = g_new (rgbe_file, 1);
Packit bc1512
  if (!rgbe_file_init (file, path))
Packit bc1512
    {
Packit bc1512
      rgbe_file_free (file);
Packit bc1512
      file = NULL;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  return file;
Packit bc1512
}
Packit bc1512
Packit bc1512
void
Packit bc1512
rgbe_file_free (rgbe_file *file)
Packit bc1512
{
Packit bc1512
  if (!file)
Packit bc1512
      return;
Packit bc1512
Packit bc1512
  g_mapped_file_unref (file->file);
Packit bc1512
  file->scanlines = NULL;
Packit bc1512
Packit bc1512
  g_free (file);
Packit bc1512
}
Packit bc1512
Packit bc1512
/* Parse the variable initialisations for an rgbe header. Returns the offset
Packit bc1512
 * after the header delimiting newline in cursor. The incoming cursor should
Packit bc1512
 * (most likely) be zero.
Packit bc1512
 *
Packit bc1512
 * Updates cursor on success.
Packit bc1512
 */
Packit bc1512
static gboolean
Packit bc1512
rgbe_header_read_variables (rgbe_file *file,
Packit bc1512
                            goffset   *cursor)
Packit bc1512
{
Packit bc1512
  const gchar *data;
Packit bc1512
  gboolean     success = FALSE;
Packit bc1512
Packit bc1512
  g_return_val_if_fail (file,                  FALSE);
Packit bc1512
  g_return_val_if_fail (file->file,            FALSE);
Packit bc1512
  g_return_val_if_fail (cursor && *cursor > 0, FALSE);
Packit bc1512
Packit bc1512
  data = g_mapped_file_get_contents (file->file) + *cursor;
Packit bc1512
Packit bc1512
  /* Keep iterating if it looks like there's enough data to satisfy another
Packit bc1512
   * line (the estimate doesn't need to be exact, as we can run a little over
Packit bc1512
   * due to required resolution specification which will be coming up next).
Packit bc1512
   */
Packit bc1512
  while (rgbe_mapped_file_remaining (file->file, data) > RGBE_MAX_VARIABLE_LINE_LEN)
Packit bc1512
    {
Packit bc1512
      /* Check the colourspace/type of pixels in the file */
Packit bc1512
      if (g_str_has_prefix (data, "FORMAT="))
Packit bc1512
        {
Packit bc1512
          guint i;
Packit bc1512
          data += strlen ("FORMAT=");
Packit bc1512
Packit bc1512
          file->header.format = FORMAT_UNKNOWN;
Packit bc1512
          for (i = 0; i < NUM_RGBE_FORMATS; ++i)
Packit bc1512
            {
Packit bc1512
              if (g_str_has_prefix (data, RGBE_FORMAT_STRINGS[i]))
Packit bc1512
                {
Packit bc1512
                  file->header.format = (rgbe_format)i;
Packit bc1512
                  break;
Packit bc1512
                }
Packit bc1512
            }
Packit bc1512
Packit bc1512
          if (file->header.format != FORMAT_RGBE)
Packit bc1512
            {
Packit bc1512
              g_warning ("Unsupported color format for rgbe format");
Packit bc1512
              goto cleanup;
Packit bc1512
            }
Packit bc1512
          continue;
Packit bc1512
        }
Packit bc1512
Packit bc1512
      /* Check the exposure multiplier */
Packit bc1512
      else if (g_str_has_prefix (data, "EXPOSURE="))
Packit bc1512
        {
Packit bc1512
          gdouble exposure;
Packit bc1512
Packit bc1512
          data += strlen ("EXPOSURE=");
Packit bc1512
Packit bc1512
          errno    = 0;
Packit bc1512
          exposure = g_ascii_strtod (data, NULL);
Packit bc1512
          if (errno)
Packit bc1512
            {
Packit bc1512
              g_warning ("Invalid value for exposure in radiance image file");
Packit bc1512
              goto cleanup;
Packit bc1512
            }
Packit bc1512
          else
Packit bc1512
            {
Packit bc1512
              file->header.exposure *= exposure;
Packit bc1512
            }
Packit bc1512
        }
Packit bc1512
Packit bc1512
      /* Parse the component multipliers */
Packit bc1512
      else if (g_str_has_prefix (data, "COLORCORR="))
Packit bc1512
        {
Packit bc1512
          guint i;
Packit bc1512
          data += strlen ("COLORCORR=");
Packit bc1512
Packit bc1512
          for (i = 0; i < RGBE_NUM_RGB; ++i)
Packit bc1512
            {
Packit bc1512
              gdouble multiplier;
Packit bc1512
Packit bc1512
              errno = 0;
Packit bc1512
              multiplier = g_ascii_strtod (data, (gchar**)&data);
Packit bc1512
              if (errno)
Packit bc1512
                {
Packit bc1512
                  g_warning ("Invalid value for COLORCORR");
Packit bc1512
                  goto cleanup;
Packit bc1512
                }
Packit bc1512
Packit bc1512
              file->header.colorcorr[i] *= multiplier;
Packit bc1512
            }
Packit bc1512
        }
Packit bc1512
Packit bc1512
      /* Generating software identifier */
Packit bc1512
      else if (g_str_has_prefix (data, "SOFTWARE="))
Packit bc1512
        {
Packit bc1512
          gchar * lineend;
Packit bc1512
Packit bc1512
          data    += strlen ("SOFTWARE=");
Packit bc1512
          lineend  = g_strstr_len (data,
Packit bc1512
                                   MIN (rgbe_mapped_file_remaining (file->file,
Packit bc1512
                                                                    data),
Packit bc1512
                                        G_N_ELEMENTS (file->header.software)),
Packit bc1512
                                   "\n");
Packit bc1512
Packit bc1512
          if (!lineend)
Packit bc1512
            {
Packit bc1512
              g_warning ("Cannot find a usable value for SOFTWARE, ignoring");
Packit bc1512
            }
Packit bc1512
          else
Packit bc1512
            {
Packit bc1512
              guint linesize = lineend - data;
Packit bc1512
              strncpy (file->header.software, data,
Packit bc1512
                       MIN (linesize, G_N_ELEMENTS (file->header.software) - 1));
Packit bc1512
            }
Packit bc1512
        }
Packit bc1512
Packit bc1512
      /* Ratio of pixel height to width */
Packit bc1512
      else if (g_str_has_prefix (data, "PIXASPECT="))
Packit bc1512
        {
Packit bc1512
          gdouble aspect;
Packit bc1512
Packit bc1512
          data  += strlen ("PIXASPECT=");
Packit bc1512
Packit bc1512
          errno = 0;
Packit bc1512
          aspect = g_ascii_strtod (data, (gchar **)&data);
Packit bc1512
          if (errno)
Packit bc1512
            {
Packit bc1512
              g_warning ("Invalid pixel aspect ratio");
Packit bc1512
              goto cleanup;
Packit bc1512
            }
Packit bc1512
          else
Packit bc1512
            {
Packit bc1512
              file->header.pixel_aspect *= aspect;
Packit bc1512
            }
Packit bc1512
        }
Packit bc1512
Packit bc1512
      /* We reached a blank line, so it's the end of the header */
Packit bc1512
      else if (!strncmp (data, "\n", strlen ("\n")))
Packit bc1512
        {
Packit bc1512
          data += strlen ("\n");
Packit bc1512
          *cursor = GPOINTER_TO_UINT (data) -
Packit bc1512
                    GPOINTER_TO_UINT (g_mapped_file_get_contents (file->file));
Packit bc1512
          success = TRUE;
Packit bc1512
          goto cleanup;
Packit bc1512
        }
Packit bc1512
Packit bc1512
      /* Skip past the end of the line for the next variable */
Packit bc1512
      data = g_strstr_len (data,
Packit bc1512
                           rgbe_mapped_file_remaining (file->file, data),
Packit bc1512
                           "\n");
Packit bc1512
      if (!data)
Packit bc1512
          goto cleanup;
Packit bc1512
      data += strlen ("\n");
Packit bc1512
    }
Packit bc1512
Packit bc1512
cleanup:
Packit bc1512
  return success;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/* Convert from '-' or '+' to useful scanline index constants
Packit bc1512
 */
Packit bc1512
static rgbe_orientation
Packit bc1512
rgbe_char_to_orientation (gchar c)
Packit bc1512
{
Packit bc1512
  switch (c)
Packit bc1512
    {
Packit bc1512
      case '-':
Packit bc1512
        return ORIENT_DECREASING;
Packit bc1512
Packit bc1512
      case '+':
Packit bc1512
        return ORIENT_INCREASING;
Packit bc1512
Packit bc1512
      default:
Packit bc1512
        return ORIENT_UNKNOWN;
Packit bc1512
    }
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/* Return the axis which the scanline index character refers to.
Packit bc1512
 */
Packit bc1512
static rgbe_axis*
Packit bc1512
rgbe_char_to_axis (rgbe_file *file,
Packit bc1512
                   gchar      c)
Packit bc1512
{
Packit bc1512
  switch (c)
Packit bc1512
    {
Packit bc1512
      case 'y':
Packit bc1512
      case 'Y':
Packit bc1512
        return &file->header.y_axis;
Packit bc1512
Packit bc1512
      case 'x':
Packit bc1512
      case 'X':
Packit bc1512
        return &file->header.x_axis;
Packit bc1512
Packit bc1512
      default:
Packit bc1512
        return NULL;
Packit bc1512
    }
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/* Parse the orientation/resolution line. The following format is repeated
Packit bc1512
 * twice: "[+-][XY] \d+" It specifies column or row major ordering, and the
Packit bc1512
 * direction of pixel indices (eg, mirrored).
Packit bc1512
 *
Packit bc1512
 * Updates cursor on success.
Packit bc1512
 */
Packit bc1512
static gboolean
Packit bc1512
rgbe_header_read_orientation (rgbe_file *file,
Packit bc1512
                              goffset   *cursor)
Packit bc1512
{
Packit bc1512
  const gchar      *data;
Packit bc1512
  rgbe_orientation  orient;
Packit bc1512
  rgbe_axis        *axis;
Packit bc1512
  gchar             firstaxis = '?';
Packit bc1512
  gboolean          success = FALSE;
Packit bc1512
Packit bc1512
  g_return_val_if_fail (file,                  FALSE);
Packit bc1512
  g_return_val_if_fail (file->file,            FALSE);
Packit bc1512
  g_return_val_if_fail (cursor && *cursor > 0, FALSE);
Packit bc1512
Packit bc1512
  data = g_mapped_file_get_contents (file->file) + *cursor;
Packit bc1512
Packit bc1512
  /* Read each direction, axis, and size until a newline is reached */
Packit bc1512
  do
Packit bc1512
    {
Packit bc1512
      orient = rgbe_char_to_orientation (*data++);
Packit bc1512
      if (orient == ORIENT_UNKNOWN)
Packit bc1512
          goto cleanup;
Packit bc1512
Packit bc1512
      /* Axis can be ordered with X major, which we don't currently handle */
Packit bc1512
      if (firstaxis == '?' && *data != 'Y' && *data != 'y')
Packit bc1512
          goto cleanup;
Packit bc1512
      else
Packit bc1512
          firstaxis = *data;
Packit bc1512
Packit bc1512
      axis = rgbe_char_to_axis (file, *data++);
Packit bc1512
      if (!axis)
Packit bc1512
          goto cleanup;
Packit bc1512
      axis->orient = orient;
Packit bc1512
Packit bc1512
      if (*data++ != ' ')
Packit bc1512
          goto cleanup;
Packit bc1512
Packit bc1512
      errno = 0;
Packit bc1512
      axis->size = g_ascii_strtoull (data, (gchar **)&data, 0);
Packit bc1512
      if (errno)
Packit bc1512
          goto cleanup;
Packit bc1512
Packit bc1512
  /* The termination check is simplified to a space check, as each set of
Packit bc1512
   * axis parameters are space seperated. We double check for a newline next
Packit bc1512
   * though.
Packit bc1512
   */
Packit bc1512
  } while (*data++ == ' ');
Packit bc1512
Packit bc1512
  if (data[-1] != '\n')
Packit bc1512
      goto cleanup;
Packit bc1512
Packit bc1512
  *cursor = data - g_mapped_file_get_contents (file->file);
Packit bc1512
  success = TRUE;
Packit bc1512
Packit bc1512
cleanup:
Packit bc1512
  return success;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/* Read each component of an rgbe file header. A pointer to the scanlines,
Packit bc1512
 * immediately after the header, is cached on success.
Packit bc1512
 */
Packit bc1512
static gboolean
Packit bc1512
rgbe_header_read (rgbe_file *file)
Packit bc1512
{
Packit bc1512
  gchar    *data;
Packit bc1512
  gboolean  success = FALSE;
Packit bc1512
  goffset   cursor  = 0;
Packit bc1512
Packit bc1512
  g_return_val_if_fail (file,                   FALSE);
Packit bc1512
  g_return_val_if_fail (file->file,             FALSE);
Packit bc1512
Packit bc1512
  rgbe_header_init (&file->header);
Packit bc1512
Packit bc1512
  data = g_mapped_file_get_contents (file->file);
Packit bc1512
  if (strncmp (&data[cursor], RADIANCE_MAGIC, strlen (RADIANCE_MAGIC)))
Packit bc1512
      goto cleanup;
Packit bc1512
  cursor += strlen (RADIANCE_MAGIC);
Packit bc1512
Packit bc1512
  if (data[cursor] != '\n')
Packit bc1512
      goto cleanup;
Packit bc1512
  ++cursor;
Packit bc1512
Packit bc1512
  if (!rgbe_header_read_variables (file, &cursor))
Packit bc1512
      goto cleanup;
Packit bc1512
Packit bc1512
  if (!rgbe_header_read_orientation (file, &cursor))
Packit bc1512
      goto cleanup;
Packit bc1512
Packit bc1512
  file->scanlines = &data[cursor];
Packit bc1512
  success = TRUE;
Packit bc1512
cleanup:
Packit bc1512
  return success;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/* Convert an array of gfloat mantissas to their full values. Applies the
Packit bc1512
 * exponent, exposure compensation, and color channel compensation.
Packit bc1512
 */
Packit bc1512
static void
Packit bc1512
rgbe_apply_exponent (const rgbe_file *file,
Packit bc1512
                     gfloat          *rgb,
Packit bc1512
                     gfloat           e)
Packit bc1512
{
Packit bc1512
  gfloat mult;
Packit bc1512
Packit bc1512
  g_return_if_fail (file);
Packit bc1512
  g_return_if_fail (rgb);
Packit bc1512
Packit bc1512
  if (e == 0)
Packit bc1512
    {
Packit bc1512
      rgb[OFFSET_R] = rgb[OFFSET_G] = rgb[OFFSET_B] = 0;
Packit bc1512
      goto cleanup;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  mult = ldexp (1.0, e - (128 + 8));
Packit bc1512
  rgb[OFFSET_R] *= mult                  *
Packit bc1512
                   file->header.exposure *
Packit bc1512
                   file->header.colorcorr[OFFSET_R];
Packit bc1512
  rgb[OFFSET_G] *= mult                  *
Packit bc1512
                   file->header.exposure *
Packit bc1512
                   file->header.colorcorr[OFFSET_G];
Packit bc1512
  rgb[OFFSET_B] *= mult                  *
Packit bc1512
                   file->header.exposure *
Packit bc1512
                   file->header.colorcorr[OFFSET_B];
Packit bc1512
  rgb[OFFSET_A]  = 1.0f;
Packit bc1512
Packit bc1512
cleanup:
Packit bc1512
  return;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/* Convert an array of RGBE uints to their floating point format (applying
Packit bc1512
 * exponents and compensations as required).
Packit bc1512
 */
Packit bc1512
static void
Packit bc1512
rgbe_rgbe_to_float (const rgbe_file *file,
Packit bc1512
                    const guint8    *rgbe,
Packit bc1512
                    gfloat          *output)
Packit bc1512
{
Packit bc1512
  g_return_if_fail (file);
Packit bc1512
  g_return_if_fail (rgbe);
Packit bc1512
  g_return_if_fail (output);
Packit bc1512
Packit bc1512
  output[OFFSET_R] = rgbe[OFFSET_R];
Packit bc1512
  output[OFFSET_G] = rgbe[OFFSET_G];
Packit bc1512
  output[OFFSET_B] = rgbe[OFFSET_B];
Packit bc1512
  output[OFFSET_A] = 1.0f;
Packit bc1512
Packit bc1512
  rgbe_apply_exponent (file, output, rgbe[OFFSET_E]);
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/* Read one uncompressed scanline row. Updates cursor on success. */
Packit bc1512
static gboolean
Packit bc1512
rgbe_read_uncompressed (const rgbe_file *file,
Packit bc1512
                        goffset         *cursor,
Packit bc1512
                        gfloat          *pixels)
Packit bc1512
{
Packit bc1512
  const guint8 *data;
Packit bc1512
  guint         i;
Packit bc1512
Packit bc1512
  g_return_val_if_fail (file,                  FALSE);
Packit bc1512
  g_return_val_if_fail (file->file,            FALSE);
Packit bc1512
  g_return_val_if_fail (cursor && *cursor > 0, FALSE);
Packit bc1512
  g_return_val_if_fail (pixels,                FALSE);
Packit bc1512
Packit bc1512
  data = (guint8 *)g_mapped_file_get_contents (file->file) + *cursor;
Packit bc1512
Packit bc1512
  for (i = 0; i < file->header.x_axis.size; ++i)
Packit bc1512
    {
Packit bc1512
      rgbe_rgbe_to_float (file, data, pixels);
Packit bc1512
      data   += RGBE_NUM_RGBE;
Packit bc1512
      pixels += RGBE_NUM_RGBE;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  *cursor   = GPOINTER_TO_UINT (data) -
Packit bc1512
              GPOINTER_TO_UINT (g_mapped_file_get_contents (file->file));
Packit bc1512
  return TRUE;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/* Read an old style rle scanline row. Unimplemented */
Packit bc1512
static gboolean
Packit bc1512
rgbe_read_old_rle (const rgbe_file *file,
Packit bc1512
                   goffset         *cursor,
Packit bc1512
                   gfloat          *pixels)
Packit bc1512
{
Packit bc1512
  /* const gchar * data = g_mapped_file_get_contents (f) + *cursor; */
Packit bc1512
Packit bc1512
  g_return_val_if_fail (file,                  FALSE);
Packit bc1512
  g_return_val_if_fail (file->file,            FALSE);
Packit bc1512
  g_return_val_if_fail (cursor && *cursor > 0, FALSE);
Packit bc1512
  g_return_val_if_fail (pixels,                FALSE);
Packit bc1512
Packit bc1512
  g_return_val_if_reached (FALSE);
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/* Read one new style rle scanline row. Updates cursor on success. */
Packit bc1512
static gboolean
Packit bc1512
rgbe_read_new_rle (const rgbe_file *file,
Packit bc1512
                   goffset         *cursor,
Packit bc1512
                   gfloat          *pixels)
Packit bc1512
{
Packit bc1512
  const guint8 *data;
Packit bc1512
  guint16       linesize;
Packit bc1512
  guint         i;
Packit bc1512
  guint         component;
Packit bc1512
  gfloat       *pixoffset[RGBE_NUM_RGBE] =
Packit bc1512
    {
Packit bc1512
      pixels + OFFSET_R,
Packit bc1512
      pixels + OFFSET_G,
Packit bc1512
      pixels + OFFSET_B,
Packit bc1512
      pixels + OFFSET_E
Packit bc1512
    };
Packit bc1512
Packit bc1512
  g_return_val_if_fail (file,                  FALSE);
Packit bc1512
  g_return_val_if_fail (file->file,            FALSE);
Packit bc1512
  g_return_val_if_fail (cursor && *cursor > 0, FALSE);
Packit bc1512
  g_return_val_if_fail (pixels,                FALSE);
Packit bc1512
Packit bc1512
  /* Read the scanline header: two magic bytes, and two byte pixel count. We
Packit bc1512
   * can assert on the magic as it should have been checked before
Packit bc1512
   * dispatching to this decoding routine.
Packit bc1512
   */
Packit bc1512
  data     = (guint8 *)g_mapped_file_get_contents (file->file) + *cursor;
Packit bc1512
  g_return_val_if_fail (data[OFFSET_R] == 2 && data[OFFSET_G] == 2, FALSE);
Packit bc1512
  linesize = (data[OFFSET_B] << 8) | data[OFFSET_E];
Packit bc1512
Packit bc1512
  data += RGBE_NUM_RGBE;
Packit bc1512
Packit bc1512
  /* Decode the rle/dump sequences for each color channel, continuing until
Packit bc1512
   * we've reached the expected offsets for each channel. Stores the exponent
Packit bc1512
   * values in the alpha channel temporarily.
Packit bc1512
   */
Packit bc1512
  for (component = 0; component < RGBE_NUM_RGBE; ++component)
Packit bc1512
    {
Packit bc1512
      while (pixoffset[component] < pixels + RGBE_NUM_RGBE * linesize)
Packit bc1512
        {
Packit bc1512
          const guint HIGH_BIT = (1 << 7);
Packit bc1512
          gboolean         rle = *data &  HIGH_BIT;
Packit bc1512
          guint         length = *data & ~HIGH_BIT;
Packit bc1512
Packit bc1512
          /* A dump/run of 0 is a special marker for dump 128 */
Packit bc1512
          if (length == 0)
Packit bc1512
            {
Packit bc1512
              rle    = FALSE;
Packit bc1512
              length = 128;
Packit bc1512
            }
Packit bc1512
Packit bc1512
          data++;
Packit bc1512
Packit bc1512
          /* A compressed run */
Packit bc1512
          if (rle)
Packit bc1512
            {
Packit bc1512
              for (i = 0; i < length; ++i)
Packit bc1512
                {
Packit bc1512
                  *pixoffset[component]  = *data;
Packit bc1512
                   pixoffset[component] += RGBE_NUM_RGBE;
Packit bc1512
                }
Packit bc1512
Packit bc1512
              data++;
Packit bc1512
            }
Packit bc1512
          /* A dump of values */
Packit bc1512
          else
Packit bc1512
            {
Packit bc1512
              for (i = 0; i < length; ++i)
Packit bc1512
                {
Packit bc1512
                   *pixoffset[component]  = *data;
Packit bc1512
                    pixoffset[component] += RGBE_NUM_RGBE;
Packit bc1512
                   data++;
Packit bc1512
                }
Packit bc1512
            }
Packit bc1512
        }
Packit bc1512
    }
Packit bc1512
Packit bc1512
  /* Double check we encountered as many pixels as expected. Pixoffsets should
Packit bc1512
   * have been incremented to just past the final pixel for each component.
Packit bc1512
   */
Packit bc1512
  for (component = 0; component < RGBE_NUM_RGBE; ++component)
Packit bc1512
    {
Packit bc1512
      g_warn_if_fail (pixoffset[component] == pixels + RGBE_NUM_RGBE * linesize + component);
Packit bc1512
    }
Packit bc1512
Packit bc1512
  /* Multiply the colours by the exponent. Remove 'transparency' by setting
Packit bc1512
   * alpha high as a precaution, it should be discarded in any case.
Packit bc1512
   */
Packit bc1512
  for (i = 0; i < linesize; ++i)
Packit bc1512
    {
Packit bc1512
      gfloat *pixel = pixels + i * RGBE_NUM_RGBE;
Packit bc1512
      rgbe_apply_exponent (file, pixel, pixel[OFFSET_E]);
Packit bc1512
    }
Packit bc1512
Packit bc1512
  *cursor = GPOINTER_TO_UINT (data) -
Packit bc1512
            GPOINTER_TO_UINT (g_mapped_file_get_contents (file->file));
Packit bc1512
Packit bc1512
  return TRUE;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/* Write a null terminated string (with user provided trailing newline) to
Packit bc1512
 * the output file, freeing the line and returning an error if needed.
Packit bc1512
 */
Packit bc1512
static gboolean
Packit bc1512
rgbe_write_line (FILE *f, gchar *line)
Packit bc1512
{
Packit bc1512
  size_t written;
Packit bc1512
  guint  len = strlen (line);
Packit bc1512
Packit bc1512
  g_return_val_if_fail (g_str_has_suffix (line, "\n"), FALSE);
Packit bc1512
  written = fwrite (line, sizeof (line[0]), len, f);
Packit bc1512
  g_free (line);
Packit bc1512
Packit bc1512
  return written == len ? TRUE : FALSE;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/* Write all rgbe header variables (which aren't defaults) out to a file. */
Packit bc1512
static gboolean
Packit bc1512
rgbe_header_write (const rgbe_header *header,
Packit bc1512
                   FILE              *f)
Packit bc1512
{
Packit bc1512
  gchar    *line    = NULL;
Packit bc1512
  gboolean  success = FALSE;
Packit bc1512
  gint      len;
Packit bc1512
Packit bc1512
  g_return_val_if_fail (header, FALSE);
Packit bc1512
  g_return_val_if_fail (f,      FALSE);
Packit bc1512
Packit bc1512
  /* Magic header bytes */
Packit bc1512
  line = g_strconcat (RADIANCE_MAGIC, "\n", NULL);
Packit bc1512
  if (!rgbe_write_line (f, line))
Packit bc1512
      goto cleanup;
Packit bc1512
Packit bc1512
  /* Insert the package name as the software name if not present (zero len)
Packit bc1512
   * or we don't have a null terminated name length.
Packit bc1512
   */
Packit bc1512
  len = strlen (header->software);
Packit bc1512
  if (len  == 0 || len > RGBE_MAX_SOFTWARE_LEN - 1)
Packit bc1512
    {
Packit bc1512
      line = g_strconcat ("SOFTWARE=", PACKAGE_STRING, "\n", NULL);
Packit bc1512
    }
Packit bc1512
  else
Packit bc1512
    {
Packit bc1512
      line = g_strconcat ("SOFTWARE=", header->software, "\n", NULL);
Packit bc1512
    }
Packit bc1512
  if (!rgbe_write_line (f, line))
Packit bc1512
      goto cleanup;
Packit bc1512
Packit bc1512
  /* Type of pixel components */
Packit bc1512
  g_return_val_if_fail (header->format < FORMAT_UNKNOWN,                     FALSE);
Packit bc1512
  g_return_val_if_fail (header->format < G_N_ELEMENTS (RGBE_FORMAT_STRINGS), FALSE);
Packit bc1512
  line = g_strconcat ("FORMAT=",
Packit bc1512
                      RGBE_FORMAT_STRINGS[header->format],
Packit bc1512
                      "\n", NULL);
Packit bc1512
  if (!rgbe_write_line (f, line))
Packit bc1512
      goto cleanup;
Packit bc1512
Packit bc1512
  /* Exposure compensation */
Packit bc1512
  if (header->exposure != 1.0)
Packit bc1512
    {
Packit bc1512
      gchar exp_line[G_ASCII_DTOSTR_BUF_SIZE + 1];
Packit bc1512
      line = g_strconcat ("EXPOSURE=",
Packit bc1512
                          g_ascii_dtostr (exp_line,
Packit bc1512
                                          G_N_ELEMENTS (exp_line),
Packit bc1512
                                          header->exposure),
Packit bc1512
                          "\n",
Packit bc1512
                          NULL);
Packit bc1512
      if (!rgbe_write_line (f, line))
Packit bc1512
          goto cleanup;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  /* Color channel correction */
Packit bc1512
  if (header->colorcorr [OFFSET_R] != 1.0 &&
Packit bc1512
      header->colorcorr [OFFSET_G] != 1.0 &&
Packit bc1512
      header->colorcorr [OFFSET_B] != 1.0)
Packit bc1512
  {
Packit bc1512
    gchar corr_line[G_ASCII_DTOSTR_BUF_SIZE + 1][RGBE_NUM_RGB];
Packit bc1512
    line = g_strconcat ("COLORCORR=",
Packit bc1512
                        g_ascii_dtostr (corr_line[OFFSET_R],
Packit bc1512
                                        G_N_ELEMENTS (corr_line[OFFSET_R]),
Packit bc1512
                                        header->colorcorr[OFFSET_R]), " ",
Packit bc1512
                        g_ascii_dtostr (corr_line[OFFSET_G],
Packit bc1512
                                        G_N_ELEMENTS (corr_line[OFFSET_G]),
Packit bc1512
                                        header->colorcorr[OFFSET_G]), " ",
Packit bc1512
                        g_ascii_dtostr (corr_line[OFFSET_B],
Packit bc1512
                                        G_N_ELEMENTS (corr_line[OFFSET_B]),
Packit bc1512
                                        header->colorcorr[OFFSET_R]),
Packit bc1512
                        "\n",
Packit bc1512
                        NULL);
Packit bc1512
    if (!rgbe_write_line (f, line))
Packit bc1512
        goto cleanup;
Packit bc1512
  }
Packit bc1512
Packit bc1512
Packit bc1512
  /* Resolution specifier */
Packit bc1512
  {
Packit bc1512
    const guint res_line_sz = strlen ("\n")
Packit bc1512
                            + strlen ("-Y ") * 2
Packit bc1512
                            + strlen (G_STRINGIFY (RGBE_MAX_SCANLINE_WIDTH)) * 2
Packit bc1512
                            + strlen ("\n")
Packit bc1512
                            + 1;
Packit bc1512
    gint err;
Packit bc1512
Packit bc1512
    line = g_malloc (res_line_sz * sizeof (line[0]));
Packit bc1512
    err  = snprintf (line, res_line_sz,
Packit bc1512
                     "\n-Y %hu +X %hu\n",
Packit bc1512
                     header->y_axis.size,
Packit bc1512
                     header->x_axis.size);
Packit bc1512
    if (err < 0 || !rgbe_write_line (f, line))
Packit bc1512
        goto cleanup;
Packit bc1512
  }
Packit bc1512
Packit bc1512
  success = TRUE;
Packit bc1512
cleanup:
Packit bc1512
  return success;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/* Convert an array of floats to rgbe components for file output */
Packit bc1512
static void
Packit bc1512
rgbe_float_to_rgbe (const gfloat *f,
Packit bc1512
                    guint8       *rgbe)
Packit bc1512
{
Packit bc1512
  gint   e;
Packit bc1512
  gfloat frac, max;
Packit bc1512
Packit bc1512
  g_return_if_fail (f);
Packit bc1512
  g_return_if_fail (rgbe);
Packit bc1512
Packit bc1512
  max =           f[OFFSET_R];
Packit bc1512
  max = MAX (max, f[OFFSET_G]);
Packit bc1512
  max = MAX (max, f[OFFSET_B]);
Packit bc1512
Packit bc1512
  if (max < 1e-38)
Packit bc1512
    {
Packit bc1512
      rgbe[OFFSET_R] = rgbe[OFFSET_G] = rgbe[OFFSET_B] = 0;
Packit bc1512
      goto cleanup;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  frac  = frexp (max, &e) * 256.0 / max;
Packit bc1512
Packit bc1512
  rgbe[OFFSET_R] = f[OFFSET_R] * frac;
Packit bc1512
  rgbe[OFFSET_G] = f[OFFSET_G] * frac;
Packit bc1512
  rgbe[OFFSET_B] = f[OFFSET_B] * frac;
Packit bc1512
Packit bc1512
  rgbe[OFFSET_E] = e + 128;
Packit bc1512
Packit bc1512
cleanup:
Packit bc1512
  return;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/* Write the first scanline from pixels into the file. Does not use RLE. */
Packit bc1512
static gboolean
Packit bc1512
rgbe_write_uncompressed (const rgbe_header *header,
Packit bc1512
                         const gfloat      *pixels,
Packit bc1512
                         FILE              *f)
Packit bc1512
{
Packit bc1512
  guint    x, y;
Packit bc1512
  guint8   rgbe[RGBE_NUM_RGBE];
Packit bc1512
  gboolean success = TRUE;
Packit bc1512
Packit bc1512
  g_return_val_if_fail (header, FALSE);
Packit bc1512
  g_return_val_if_fail (pixels, FALSE);
Packit bc1512
  g_return_val_if_fail (f,      FALSE);
Packit bc1512
Packit bc1512
  for (y = 0; y < header->y_axis.size; ++y)
Packit bc1512
      for (x = 0; x < header->x_axis.size; ++x)
Packit bc1512
        {
Packit bc1512
          rgbe_float_to_rgbe (pixels, rgbe);
Packit bc1512
Packit bc1512
          /* Ensure we haven't inadvertantly triggered an rle scanline */
Packit bc1512
          g_warn_if_fail (rgbe[0] != 2 || rgbe[1] != 2);
Packit bc1512
          g_warn_if_fail (rgbe[0] != 1 || rgbe[1] != 1 || rgbe[2] != 1);
Packit bc1512
Packit bc1512
          if (G_N_ELEMENTS (rgbe) != fwrite (rgbe, sizeof (rgbe[0]), G_N_ELEMENTS (rgbe), f))
Packit bc1512
            success = FALSE;
Packit bc1512
          pixels += RGBE_NUM_RGB;
Packit bc1512
        }
Packit bc1512
Packit bc1512
  return success;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
gboolean
Packit bc1512
rgbe_save_path (const gchar *path,
Packit bc1512
                guint        width,
Packit bc1512
                guint        height,
Packit bc1512
                gfloat      *pixels)
Packit bc1512
{
Packit bc1512
  rgbe_header  header;
Packit bc1512
  FILE        *f       = NULL;
Packit bc1512
  gboolean     success = FALSE;
Packit bc1512
Packit bc1512
  f = (!strcmp (path, "-") ? stdout : fopen(path, "wb"));
Packit bc1512
  if (!f)
Packit bc1512
      goto cleanup;
Packit bc1512
Packit bc1512
  rgbe_header_init (&header);
Packit bc1512
  header.x_axis.orient = ORIENT_INCREASING;
Packit bc1512
  header.x_axis.size   = width;
Packit bc1512
  header.y_axis.orient = ORIENT_DECREASING;
Packit bc1512
  header.y_axis.size   = height;
Packit bc1512
  header.format        = FORMAT_RGBE;
Packit bc1512
Packit bc1512
  success = rgbe_header_write  (&header, f);
Packit bc1512
  if (!success)
Packit bc1512
      goto cleanup;
Packit bc1512
Packit bc1512
  success = rgbe_write_uncompressed (&header, pixels, f);
Packit bc1512
Packit bc1512
cleanup:
Packit bc1512
  if (f)
Packit bc1512
      fclose (f);
Packit bc1512
Packit bc1512
  return success;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
rgbe_file *
Packit bc1512
rgbe_load_path (const gchar *path)
Packit bc1512
{
Packit bc1512
  gboolean success = FALSE;
Packit bc1512
  rgbe_file *file;
Packit bc1512
Packit bc1512
  file = rgbe_file_new (path);
Packit bc1512
  if (!file)
Packit bc1512
      goto cleanup;
Packit bc1512
Packit bc1512
  if (!rgbe_header_read (file))
Packit bc1512
      goto cleanup;
Packit bc1512
  success = TRUE;
Packit bc1512
Packit bc1512
cleanup:
Packit bc1512
  if (!success)
Packit bc1512
    {
Packit bc1512
      rgbe_file_free (file);
Packit bc1512
      file = NULL;
Packit bc1512
    }
Packit bc1512
  return file;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
gboolean
Packit bc1512
rgbe_get_size (rgbe_file *file,
Packit bc1512
               guint     *x,
Packit bc1512
               guint     *y)
Packit bc1512
{
Packit bc1512
  g_return_val_if_fail (file, FALSE);
Packit bc1512
  *x = file->header.x_axis.size;
Packit bc1512
  *y = file->header.y_axis.size;
Packit bc1512
Packit bc1512
  return TRUE;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/* Peek on each scanline row to dispatch to decoders.
Packit bc1512
 *
Packit bc1512
 * - Assumes row major ordering.
Packit bc1512
 * - Assumes cursor is at the start of a scanline
Packit bc1512
 * - Updates cursor, which is undefined on error.
Packit bc1512
 */
Packit bc1512
gfloat *
Packit bc1512
rgbe_read_scanlines (rgbe_file *file)
Packit bc1512
{
Packit bc1512
  guint     i;
Packit bc1512
  gboolean  success = FALSE;
Packit bc1512
  gfloat   *pixels  = NULL,
Packit bc1512
           *pixel_cursor;
Packit bc1512
  goffset   offset;
Packit bc1512
Packit bc1512
  g_return_val_if_fail (file,            NULL);
Packit bc1512
  g_return_val_if_fail (file->scanlines, NULL);
Packit bc1512
Packit bc1512
  pixels = pixel_cursor = g_new (gfloat, file->header.x_axis.size *
Packit bc1512
                                         file->header.y_axis.size *
Packit bc1512
                                         RGBE_NUM_RGBE);
Packit bc1512
  offset = GPOINTER_TO_UINT (file->scanlines) -
Packit bc1512
           GPOINTER_TO_UINT (g_mapped_file_get_contents (file->file));
Packit bc1512
Packit bc1512
  for (i = 0; i < file->header.y_axis.size; ++i)
Packit bc1512
    {
Packit bc1512
      const gchar *data = g_mapped_file_get_contents (file->file);
Packit bc1512
Packit bc1512
      if (data[offset + OFFSET_R] == 1 &&
Packit bc1512
          data[offset + OFFSET_G] == 1 &&
Packit bc1512
          data[offset + OFFSET_B] == 1)
Packit bc1512
        success = rgbe_read_old_rle      (file, &offset, pixel_cursor);
Packit bc1512
      else if (data[offset + OFFSET_R] == 2 &&
Packit bc1512
               data[offset + OFFSET_G] == 2)
Packit bc1512
        success = rgbe_read_new_rle      (file, &offset, pixel_cursor);
Packit bc1512
      else
Packit bc1512
        success = rgbe_read_uncompressed (file, &offset, pixel_cursor);
Packit bc1512
Packit bc1512
      if (!success)
Packit bc1512
        {
Packit bc1512
          g_warning ("Unable to parse rgbe scanlines, fail at row %u\n", i);
Packit bc1512
          goto cleanup;
Packit bc1512
        }
Packit bc1512
      pixel_cursor += file->header.x_axis.size * RGBE_NUM_RGBE;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  success = TRUE;
Packit bc1512
Packit bc1512
cleanup:
Packit bc1512
  if (!success)
Packit bc1512
    {
Packit bc1512
      g_free (pixels);
Packit bc1512
      pixels = NULL;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  return pixels;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512