|
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 |
|