Author: abrander
Date: 2009-09-23 17:18:13 +0200 (Wed, 23 Sep 2009)
New Revision: 2654
Added:
trunk/plugins/dcp/
trunk/plugins/dcp/Makefile.am
trunk/plugins/dcp/dcp-huesat-map.c
trunk/plugins/dcp/dcp-huesat-map.h
trunk/plugins/dcp/dcp.c
Modified:
trunk/configure.in
trunk/plugins/Makefile.am
Log:
Added a (proof-of-concept) DNG Camera Profile filter.
Modified: trunk/configure.in
===================================================================
--- trunk/configure.in 2009-09-23 15:14:46 UTC (rev 2653)
+++ trunk/configure.in 2009-09-23 15:18:13 UTC (rev 2654)
@@ -78,6 +78,7 @@
plugins/basic-render/Makefile
plugins/cache/Makefile
plugins/crop/Makefile
+plugins/dcp/Makefile
plugins/demosaic/Makefile
plugins/denoise/Makefile
plugins/exposure-mask/Makefile
Modified: trunk/plugins/Makefile.am
===================================================================
--- trunk/plugins/Makefile.am 2009-09-23 15:14:46 UTC (rev 2653)
+++ trunk/plugins/Makefile.am 2009-09-23 15:18:13 UTC (rev 2654)
@@ -2,6 +2,7 @@
basic-render \
cache \
crop \
+ dcp \
demosaic \
denoise \
exposure-mask \
Added: trunk/plugins/dcp/Makefile.am
===================================================================
--- trunk/plugins/dcp/Makefile.am (rev 0)
+++ trunk/plugins/dcp/Makefile.am 2009-09-23 15:18:13 UTC (rev 2654)
@@ -0,0 +1,22 @@
+plugindir = $(libdir)/rawstudio/plugins
+
+AM_CFLAGS =\
+ -Wall \
+ -g3 \
+ -O4
+
+AM_CXXFLAGS = $(AM_CFLAGS)
+
+INCLUDES = \
+ -DPACKAGE_DATA_DIR=\""$(datadir)"\" \
+ -DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \
+ @PACKAGE_CFLAGS@ \
+ -I../../librawstudio/
+
+lib_LTLIBRARIES = dcp.la
+
+libdir = $(datadir)/rawstudio/plugins/
+
+dcp_la_LIBADD = @PACKAGE_LIBS@
+dcp_la_LDFLAGS = -module -avoid-version
+dcp_la_SOURCES = dcp.c dcp-huesat-map.c dcp-huesat-map.h
Added: trunk/plugins/dcp/dcp-huesat-map.c
===================================================================
--- trunk/plugins/dcp/dcp-huesat-map.c (rev 0)
+++ trunk/plugins/dcp/dcp-huesat-map.c 2009-09-23 15:18:13 UTC (rev 2654)
@@ -0,0 +1,190 @@
+#include <rawstudio.h>
+#include "dcp-huesat-map.h"
+
+G_DEFINE_TYPE (RSHuesatMap, rs_huesat_map, G_TYPE_OBJECT)
+
+static void
+rs_huesat_map_finalize(GObject *object)
+{
+ RSHuesatMap *map = RS_HUESAT_MAP(object);
+
+ if (map->deltas)
+ g_free(map->deltas);
+
+ G_OBJECT_CLASS (rs_huesat_map_parent_class)->finalize (object);
+}
+
+static void
+rs_huesat_map_class_init(RSHuesatMapClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = rs_huesat_map_finalize;
+}
+
+static void
+rs_huesat_map_init(RSHuesatMap *self)
+{
+}
+
+RSHuesatMap *
+rs_huesat_map_new(guint hue_divisions, guint sat_division, guint val_divisions)
+{
+ RSHuesatMap *map = g_object_new(RS_TYPE_HUESAT_MAP, NULL);
+
+ if (val_divisions == 0)
+ val_divisions = 1;
+
+ map->hue_divisions = hue_divisions;
+ map->sat_divisions = sat_division;
+ map->val_divisions = val_divisions;
+
+ map->hue_step = sat_division;
+ map->val_step = hue_divisions * map->hue_step;
+
+ map->deltas = g_new0(RS_VECTOR3, rs_huesat_map_get_deltacount(map));
+
+ return map;
+}
+
+RSHuesatMap *
+rs_huesat_map_new_from_dcp(RSTiff *tiff, const guint ifd, const gushort
dims_tag, const gushort table_tag)
+{
+ RSHuesatMap *map = NULL;
+ RSTiffIfdEntry *entry;
+ guint hue_count = 0, sat_count = 0, val_count = 0;
+
+ g_assert(RS_IS_TIFF(tiff));
+
+ entry = rs_tiff_get_ifd_entry(tiff, ifd, dims_tag);
+ if (entry && (entry->count > 1))
+ {
+ hue_count = rs_tiff_get_uint(tiff, entry->value_offset);
+ sat_count = rs_tiff_get_uint(tiff, entry->value_offset+4);
+
+ if (entry->count > 2)
+ val_count = rs_tiff_get_uint(tiff,
entry->value_offset+8);
+
+ entry = rs_tiff_get_ifd_entry(tiff, ifd, table_tag);
+ if (entry && (entry->count == (hue_count * sat_count *
val_count * 3)))
+ {
+ gboolean skipSat0 = FALSE; /* FIXME */
+ gint val, hue, sat;
+ gint offset = entry->value_offset;
+
+ map = rs_huesat_map_new(hue_count, sat_count,
val_count);
+ for (val = 0; val < val_count; val++)
+ {
+ for (hue = 0; hue < hue_count; hue++)
+ {
+ for (sat = (skipSat0) ? 1 : 0; sat <
sat_count; sat++)
+ {
+ RS_VECTOR3 modify;
+
+ modify.h =
rs_tiff_get_float(tiff, offset);
+ modify.s =
rs_tiff_get_float(tiff, offset+4);
+ modify.v =
rs_tiff_get_float(tiff, offset+8);
+ offset += 12;
+
+ rs_huesat_map_set_delta(map,
hue, sat, val, &modify);
+ }
+ }
+ }
+ }
+ }
+
+ return map;
+}
+
+RSHuesatMap *
+rs_huesat_map_new_interpolated(const RSHuesatMap *map1, RSHuesatMap *map2,
gfloat weight1)
+{
+ RSHuesatMap *map = NULL;
+
+ g_assert(RS_IS_HUESAT_MAP(map1));
+ g_assert(RS_IS_HUESAT_MAP(map2));
+
+ if (weight1 >= 1.0)
+ return RS_HUESAT_MAP(g_object_ref(G_OBJECT(map1)));
+ else if (weight1 <= 0.0)
+ return RS_HUESAT_MAP(g_object_ref(G_OBJECT(map2)));
+
+ if ((map1->hue_divisions == map2->hue_divisions) &&
+ (map1->sat_divisions == map2->sat_divisions) &&
+ (map1->val_divisions == map2->val_divisions))
+ {
+ map = rs_huesat_map_new(map1->hue_divisions,
map1->sat_divisions, map1->val_divisions);
+ gfloat weight2 = 1.0 - weight1;
+
+ const RS_VECTOR3 *data1 = map1->deltas;
+ const RS_VECTOR3 *data2 = map1->deltas;
+ RS_VECTOR3 *data3 = map1->deltas;
+ gint count = map1->hue_divisions * map1->sat_divisions *
map1->val_divisions;
+
+ gint index;
+ for (index = 0; index < count; index++)
+ {
+ data3->h = weight1 * data1->h + weight2 * data2->h;
+ data3->s = weight1 * data1->s + weight2 * data2->s;
+ data3->v = weight1 * data1->v + weight2 * data2->v;
+
+ data1++;
+ data2++;
+ data3++;
+ }
+ }
+
+ return map;
+}
+
+guint
+rs_huesat_map_get_deltacount(RSHuesatMap *map)
+{
+ return map->val_divisions * map->hue_divisions * map->sat_divisions;
+}
+
+void
+rs_huesat_map_get_delta(RSHuesatMap *map, const guint hue_div, const guint
sat_div, const guint val_div, RS_VECTOR3 *modify)
+{
+ g_assert(RS_IS_HUESAT_MAP(map));
+ if (hue_div >= map->hue_divisions || sat_div >= map->sat_divisions ||
val_div >= map->val_divisions)
+ {
+ modify->h = 0.0;
+ modify->s = 1.0;
+ modify->v = 1.0;
+
+ return;
+ }
+
+ gint offset = val_div * map->val_step + hue_div * map->hue_step +
sat_div;
+
+ *modify = map->deltas[offset];
+}
+
+void
+rs_huesat_map_set_delta(RSHuesatMap *map, const guint hue_div, const guint
sat_div, const guint val_div, const RS_VECTOR3 *modify)
+{
+ g_assert(RS_IS_HUESAT_MAP(map));
+
+ if (hue_div >= map->hue_divisions || sat_div >= map->sat_divisions ||
val_div >= map->val_divisions)
+ return;
+
+ gint offset = val_div * map->val_step + hue_div * map->hue_step +
sat_div;
+
+ map->deltas[offset] = *modify;
+
+ if (sat_div == 0)
+ map->deltas[offset].v = 1.0;
+
+ else if (sat_div == 1)
+ {
+ RS_VECTOR3 zero_sat_modify;
+ rs_huesat_map_get_delta(map, hue_div, 0, val_div,
&zero_sat_modify);
+ if (zero_sat_modify.v != 1.0f)
+ {
+ zero_sat_modify = *modify;
+ zero_sat_modify.v = 1.0;
+ rs_huesat_map_set_delta(map, hue_div, 0, val_div,
&zero_sat_modify);
+ }
+ }
+}
Added: trunk/plugins/dcp/dcp-huesat-map.h
===================================================================
--- trunk/plugins/dcp/dcp-huesat-map.h (rev 0)
+++ trunk/plugins/dcp/dcp-huesat-map.h 2009-09-23 15:18:13 UTC (rev 2654)
@@ -0,0 +1,45 @@
+#ifndef RS_HUESAT_MAP_H
+#define RS_HUESAT_MAP_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define RS_TYPE_HUESAT_MAP rs_huesat_map_get_type()
+#define RS_HUESAT_MAP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
RS_TYPE_HUESAT_MAP, RSHuesatMap))
+#define RS_HUESAT_MAP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),
RS_TYPE_HUESAT_MAP, RSHuesatMapClass))
+#define RS_IS_HUESAT_MAP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
RS_TYPE_HUESAT_MAP))
+#define RS_IS_HUESAT_MAP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),
RS_TYPE_HUESAT_MAP))
+#define RS_HUESAT_MAP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
RS_TYPE_HUESAT_MAP, RSHuesatMapClass))
+
+typedef struct {
+ GObject parent;
+
+ guint hue_divisions;
+ guint sat_divisions;
+ guint val_divisions;
+
+ guint hue_step;
+ guint val_step;
+
+ RS_VECTOR3 *deltas;
+} RSHuesatMap;
+
+typedef struct {
+ GObjectClass parent_class;
+} RSHuesatMapClass;
+
+GType rs_huesat_map_get_type(void);
+
+RSHuesatMap *rs_huesat_map_new(guint hue_divisions, guint sat_division, guint
val_divisions);
+
+RSHuesatMap *rs_huesat_map_new_from_dcp(RSTiff *tiff, const guint ifd, const
gushort dims_tag, const gushort table_tag);
+
+guint rs_huesat_map_get_deltacount(RSHuesatMap *map);
+
+void rs_huesat_map_get_delta(RSHuesatMap *map, const guint hue_div, const
guint sat_div, const guint val_div, RS_VECTOR3 *modify);
+void rs_huesat_map_set_delta(RSHuesatMap *map, const guint hue_div, const
guint sat_div, const guint val_div, const RS_VECTOR3 *modify);
+
+G_END_DECLS
+
+#endif /* RS_HUESAT_MAP_H */
Added: trunk/plugins/dcp/dcp.c
===================================================================
--- trunk/plugins/dcp/dcp.c (rev 0)
+++ trunk/plugins/dcp/dcp.c 2009-09-23 15:18:13 UTC (rev 2654)
@@ -0,0 +1,1142 @@
+/*
+ * Copyright (C) 2006-2009 Anders Brander <[email protected]> and
+ * Anders Kvist <[email protected]>
+ *
+ * This program 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
USA.
+ */
+
+/* Plugin tmpl version 4 */
+
+#include <rawstudio.h>
+#include <math.h> /* pow() */
+#include "dcp-huesat-map.h"
+
+#define RS_TYPE_DCP (rs_dcp_type)
+#define RS_DCP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), RS_TYPE_DCP, RSDcp))
+#define RS_DCP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), RS_TYPE_DCP,
RSDcpClass))
+#define RS_IS_DCP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), RS_TYPE_DCP))
+
+typedef struct _RSDcp RSDcp;
+typedef struct _RSDcpClass RSDcpClass;
+
+struct _RSDcp {
+ RSFilter parent;
+
+ gfloat exposure;
+ gfloat saturation;
+ gfloat hue;
+
+ RS_xy_COORD white_xy;
+
+ gint nknots;
+ gfloat *curve_samples;
+
+ gfloat temp1;
+ gfloat temp2;
+
+ rs_spline_t *baseline_exposure;
+ gfloat *baseline_exposure_lut;
+
+ gboolean has_color_matrix1;
+ gboolean has_color_matrix2;
+ RS_MATRIX3 color_matrix1;
+ RS_MATRIX3 color_matrix2;
+
+ gboolean has_reduction_matrix1;
+ gboolean has_reduction_matrix2;
+ RS_MATRIX3 reduction_matrix1;
+ RS_MATRIX3 reduction_matrix2;
+ RS_MATRIX3 reduction_matrix;
+
+ gboolean has_forward_matrix1;
+ gboolean has_forward_matrix2;
+ RS_MATRIX3 forward_matrix1;
+ RS_MATRIX3 forward_matrix2;
+ RS_MATRIX3 forward_matrix;
+
+ RSHuesatMap *looktable;
+
+ RSHuesatMap *huesatmap;
+ RSHuesatMap *huesatmap1;
+ RSHuesatMap *huesatmap2;
+
+ RS_MATRIX3 camera_to_pcs;
+
+ RS_VECTOR3 camera_white;
+ RS_MATRIX3 camera_to_prophoto;
+
+ RS_MATRIX3 prophoto_to_srgb;
+};
+
+struct _RSDcpClass {
+ RSFilterClass parent_class;
+};
+
+RS_DEFINE_FILTER(rs_dcp, RSDcp)
+
+enum {
+ PROP_0,
+ PROP_SETTINGS,
+ PROP_PROFILE
+};
+
+static void get_property (GObject *object, guint property_id, GValue *value,
GParamSpec *pspec);
+static void set_property (GObject *object, guint property_id, const GValue
*value, GParamSpec *pspec);
+static RSFilterResponse *get_image(RSFilter *filter, const RSFilterParam
*param);
+static void settings_changed(RSSettings *settings, RSSettingsMask mask, RSDcp
*dcp);
+static RS_xy_COORD neutral_to_xy(RSDcp *dcp, const RS_VECTOR3 *neutral);
+static RS_MATRIX3 find_xyz_to_camera(RSDcp *dcp, const RS_xy_COORD *white_xy,
RS_MATRIX3 *forward_matrix, RS_MATRIX3 *reduction_matrix, RS_MATRIX3
*camera_calibration);
+static void set_white_xy(RSDcp *dcp, const RS_xy_COORD *xy);
+static void precalc(RSDcp *dcp);
+static void render(RSDcp *dcp, RS_IMAGE16 *image);
+static void read_profile(RSDcp *dcp, const gchar *filename);
+
+G_MODULE_EXPORT void
+rs_plugin_load(RSPlugin *plugin)
+{
+ rs_dcp_get_type(G_TYPE_MODULE(plugin));
+}
+
+static void
+finalize(GObject *object)
+{
+ RSDcp *dcp = RS_DCP(object);
+
+ g_free(dcp->curve_samples);
+}
+
+static void
+rs_dcp_class_init(RSDcpClass *klass)
+{
+ RSFilterClass *filter_class = RS_FILTER_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+ object_class->finalize = finalize;
+
+ g_object_class_install_property(object_class,
+ PROP_SETTINGS, g_param_spec_object(
+ "settings", "Settings", "Settings to render from",
+ RS_TYPE_SETTINGS, G_PARAM_READWRITE)
+ );
+
+ g_object_class_install_property(object_class,
+ PROP_PROFILE, g_param_spec_string(
+ "profile", "profile", "DCP Profile",
+ NULL, G_PARAM_READWRITE)
+ );
+
+ filter_class->name = "Adobe DNG camera profile filter";
+ filter_class->get_image = get_image;
+}
+
+static void
+settings_changed(RSSettings *settings, RSSettingsMask mask, RSDcp *dcp)
+{
+ gboolean changed = FALSE;
+
+ if (mask & MASK_EXPOSURE)
+ {
+ g_object_get(settings, "exposure", &dcp->exposure, NULL);
+ changed = TRUE;
+ }
+
+ if (mask & MASK_SATURATION)
+ {
+ g_object_get(settings, "saturation", &dcp->saturation, NULL);
+ changed = TRUE;
+ }
+
+ if (mask & MASK_HUE)
+ {
+ g_object_get(settings, "hue", &dcp->hue, NULL);
+ dcp->hue /= 60.0;
+ changed = TRUE;
+ }
+
+ if ((mask & MASK_WB) || (mask & MASK_CHANNELMIXER))
+ {
+ const gfloat warmth;
+ gfloat tint;
+ const gfloat channelmixer_red;
+ const gfloat channelmixer_green;
+ const gfloat channelmixer_blue;
+
+ g_object_get(settings,
+ "warmth", &warmth,
+ "tint", &tint,
+ "channelmixer_red", &channelmixer_red,
+ "channelmixer_green", &channelmixer_green,
+ "channelmixer_blue", &channelmixer_blue,
+ NULL);
+
+ RS_xy_COORD whitepoint;
+ RS_VECTOR3 pre_mul;
+ /* This is messy, but we're essentially converting from
warmth/tint to cameraneutral */
+ pre_mul.x = (1.0+warmth)*(2.0-tint)*(channelmixer_red/100.0);
+ pre_mul.y = 1.0*(channelmixer_green/100.0);
+ pre_mul.z = (1.0-warmth)*(2.0-tint)*(channelmixer_blue/100.0);
+ RS_VECTOR3 neutral;
+ neutral.x = 1.0 / CLAMP(pre_mul.x, 0.001, 100.00);
+ neutral.y = 1.0 / CLAMP(pre_mul.y, 0.001, 100.00);
+ neutral.z = 1.0 / CLAMP(pre_mul.z, 0.001, 100.00);
+ gfloat max = vector3_max(&neutral);
+ neutral.x = neutral.x / max;
+ neutral.y = neutral.y / max;
+ neutral.z = neutral.z / max;
+ whitepoint = neutral_to_xy(dcp, &neutral);
+
+ set_white_xy(dcp, &whitepoint);
+ precalc(dcp);
+ changed = TRUE;
+ }
+
+ if (mask & MASK_CURVE)
+ {
+ const gint nknots = rs_settings_get_curve_nknots(settings);
+
+ if (nknots > 1)
+ {
+ gfloat *knots = rs_settings_get_curve_knots(settings);
+ if (knots)
+ {
+ dcp->nknots = nknots;
+ rs_spline_t *spline = rs_spline_new(knots,
dcp->nknots, NATURAL);
+ rs_spline_sample(spline, dcp->curve_samples,
65536);
+ rs_spline_destroy(spline);
+ g_free(knots);
+ }
+ }
+ else
+ {
+ gint i;
+ for(i=0;i<65536;i++)
+ dcp->curve_samples[i] = ((gfloat)i)/65536.0;
+ }
+ changed = TRUE;
+ }
+
+ if (changed)
+ rs_filter_changed(RS_FILTER(dcp), RS_FILTER_CHANGED_PIXELDATA);
+}
+
+static void
+rs_dcp_init(RSDcp *dcp)
+{
+ gint i;
+
+ dcp->curve_samples = g_new(gfloat, 65536);
+
+ for(i=0;i<65536;i++)
+ dcp->curve_samples[i] = ((gfloat)i)/65536.0;
+}
+
+static void
+get_property(GObject *object, guint property_id, GValue *value, GParamSpec
*pspec)
+{
+// RSDcp *dcp = RS_DCP(object);
+
+ switch (property_id)
+ {
+ case PROP_SETTINGS:
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id,
pspec);
+ }
+}
+
+static void
+set_property(GObject *object, guint property_id, const GValue *value,
GParamSpec *pspec)
+{
+ RSDcp *dcp = RS_DCP(object);
+// RSFilter *filter = RS_FILTER(dcp);
+ RSSettings *settings;
+ const gchar *profile_filename;
+
+ switch (property_id)
+ {
+ case PROP_SETTINGS:
+ settings = g_value_get_object(value);
+ g_signal_connect(settings, "settings-changed",
G_CALLBACK(settings_changed), dcp);
+ settings_changed(settings, MASK_ALL, dcp);
+ break;
+ case PROP_PROFILE:
+ profile_filename = g_value_get_string(value);
+ read_profile(dcp, profile_filename);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id,
pspec);
+ }
+}
+
+static RSFilterResponse *
+get_image(RSFilter *filter, const RSFilterParam *param)
+{
+ RSDcp *dcp = RS_DCP(filter);
+ GdkRectangle *roi;
+ RSFilterResponse *previous_response;
+ RSFilterResponse *response;
+ RS_IMAGE16 *input;
+ RS_IMAGE16 *output;
+ RS_IMAGE16 *tmp;
+
+ previous_response = rs_filter_get_image(filter->previous, param);
+
+ if (!RS_IS_FILTER(filter->previous))
+ return previous_response;
+
+ input = rs_filter_response_get_image(previous_response);
+ if (!input) return previous_response;
+ response = rs_filter_response_clone(previous_response);
+ g_object_unref(previous_response);
+
+ output = rs_image16_copy(input, TRUE);
+ g_object_unref(input);
+
+ rs_filter_response_set_image(response, output);
+ g_object_unref(output);
+
+ if ((roi = rs_filter_param_get_roi(param)))
+ tmp = rs_image16_new_subframe(output, roi);
+ else
+ tmp = g_object_ref(output);
+
+ render(dcp, tmp);
+
+ g_object_unref(tmp);
+
+ return response;
+}
+
+/* dng_color_spec::NeutralToXY */
+static RS_xy_COORD
+neutral_to_xy(RSDcp *dcp, const RS_VECTOR3 *neutral)
+{
+ const guint max_passes = 30;
+ guint pass;
+ RS_xy_COORD last;
+
+ last = XYZ_to_xy(&XYZ_WP_D50);
+
+ for(pass = 0; pass < max_passes; pass++)
+ {
+ RS_MATRIX3 xyz_to_camera = find_xyz_to_camera(dcp, &last, NULL,
NULL, NULL);
+ RS_MATRIX3 camera_to_xyz = matrix3_invert(&xyz_to_camera);
+
+ RS_XYZ_VECTOR tmp = vector3_multiply_matrix(neutral,
&camera_to_xyz);
+ RS_xy_COORD next = XYZ_to_xy(&tmp);
+
+ if (ABS(next.x - last.x) + ABS(next.y - last.y) < 0.0000001)
+ {
+ last = next;
+ break;
+ }
+
+ // If we reach the limit without converging, we are most likely
+ // in a two value oscillation. So take the average of the last
+ // two estimates and give up.
+ if (pass == max_passes - 1)
+ {
+ next.x = (last.x + next.x) * 0.5;
+ next.y = (last.y + next.y) * 0.5;
+ }
+ last = next;
+ }
+
+ return last;
+}
+
+inline void
+RGBtoHSV(gfloat r, gfloat g, gfloat b, gfloat *h, gfloat *s, gfloat *v)
+{
+ *v = MAX(r, MAX (g, b));
+
+ gfloat gap = *v - MIN (r, MIN (g, b));
+
+ if (gap > 0.0f)
+ {
+ if (r == *v)
+ {
+ *h = (g - b) / gap;
+
+ if (*h < 0.0f)
+ *h += 6.0f;
+ }
+ else if (g == *v)
+ *h = 2.0f + (b - r) / gap;
+ else
+ *h = 4.0f + (r - g) / gap;
+
+ *s = gap / *v;
+ }
+ else
+ {
+ *h = 0.0f;
+ *s = 0.0f;
+ }
+}
+
+inline void
+HSVtoRGB(gfloat h, gfloat s, gfloat v, gfloat *r, gfloat *g, gfloat *b)
+{
+ if (s > 0.0f)
+ {
+
+ if (h < 0.0f)
+ h += 6.0f;
+
+ if (h >= 6.0f)
+ h -= 6.0f;
+
+ gint i = (gint) h;
+ gfloat f = h - (gint) i;
+
+ gfloat p = v * (1.0f - s);
+
+#define q (v * (1.0f - s * f))
+#define t (v * (1.0f - s * (1.0f - f)))
+
+ switch (i)
+ {
+ case 0: *r = v; *g = t; *b = p; break;
+ case 1: *r = q; *g = v; *b = p; break;
+ case 2: *r = p; *g = v; *b = t; break;
+ case 3: *r = p; *g = q; *b = v; break;
+ case 4: *r = t; *g = p; *b = v; break;
+ case 5: *r = v; *g = p; *b = q; break;
+ }
+
+#undef q
+#undef t
+
+ }
+ else
+ {
+ *r = v;
+ *g = v;
+ *b = v;
+ }
+}
+
+#define _F(x) (x / 65535.0)
+#define _S(x) CLAMP(((gint) (x * 65535.0)), 0, 65535)
+
+static void
+huesat_map(RSHuesatMap *map, gfloat *h, gfloat *s, gfloat *v)
+{
+ g_assert(RS_IS_HUESAT_MAP(map));
+
+ gfloat hScale = (map->hue_divisions < 2) ? 0.0f : (map->hue_divisions *
(1.0f / 6.0f));
+ gfloat sScale = (gfloat) (map->sat_divisions - 1);
+ gfloat vScale = (gfloat) (map->val_divisions - 1);
+
+ gint maxHueIndex0 = map->hue_divisions - 1;
+ gint maxSatIndex0 = map->sat_divisions - 2;
+ gint maxValIndex0 = map->val_divisions - 2;
+
+ const RS_VECTOR3 *tableBase = map->deltas;
+
+ gint hueStep = map->sat_divisions;
+ gint valStep = map->hue_divisions * hueStep;
+
+ gfloat hueShift;
+ gfloat satScale;
+ gfloat valScale;
+
+ if (map->val_divisions < 2)
+ {
+ gfloat hScaled = *h * hScale;
+ gfloat sScaled = *s * sScale;
+
+ gint hIndex0 = (gint) hScaled;
+ gint sIndex0 = (gint) sScaled;
+
+ sIndex0 = MIN(sIndex0, maxSatIndex0);
+
+ gint hIndex1 = hIndex0 + 1;
+
+ if (hIndex0 >= maxHueIndex0)
+ {
+ hIndex0 = maxHueIndex0;
+ hIndex1 = 0;
+ }
+
+ gfloat hFract1 = hScaled - (gfloat) hIndex0;
+ gfloat sFract1 = sScaled - (gfloat) sIndex0;
+
+ gfloat hFract0 = 1.0f - hFract1;
+ gfloat sFract0 = 1.0f - sFract1;
+
+ const RS_VECTOR3 *entry00 = tableBase + hIndex0 * hueStep +
sIndex0;
+
+ const RS_VECTOR3 *entry01 = entry00 + (hIndex1 - hIndex0) *
hueStep;
+ gfloat hueShift0 = hFract0 * entry00->fHueShift +
+ hFract1 * entry01->fHueShift;
+
+ gfloat satScale0 = hFract0 * entry00->fSatScale +
+ hFract1 * entry01->fSatScale;
+
+ gfloat valScale0 = hFract0 * entry00->fValScale +
+ hFract1 * entry01->fValScale;
+
+ entry00++;
+ entry01++;
+
+ gfloat hueShift1 = hFract0 * entry00->fHueShift +
+ hFract1 * entry01->fHueShift;
+
+ gfloat satScale1 = hFract0 * entry00->fSatScale +
+ hFract1 * entry01->fSatScale;
+
+ gfloat valScale1 = hFract0 * entry00->fValScale +
+ hFract1 * entry01->fValScale;
+
+ hueShift = sFract0 * hueShift0 + sFract1 * hueShift1;
+ satScale = sFract0 * satScale0 + sFract1 * satScale1;
+ valScale = sFract0 * valScale0 + sFract1 * valScale1;
+ }
+ else
+ {
+ gfloat hScaled = *h * hScale;
+ gfloat sScaled = *s * sScale;
+ gfloat vScaled = *v * vScale;
+
+ gint hIndex0 = (gint) hScaled;
+ gint sIndex0 = (gint) sScaled;
+ gint vIndex0 = (gint) vScaled;
+
+ sIndex0 = MIN(sIndex0, maxSatIndex0);
+ vIndex0 = MIN(vIndex0, maxValIndex0);
+
+ gint hIndex1 = hIndex0 + 1;
+
+ if (hIndex0 >= maxHueIndex0)
+ {
+ hIndex0 = maxHueIndex0;
+ hIndex1 = 0;
+ }
+
+ gfloat hFract1 = hScaled - (gfloat) hIndex0;
+ gfloat sFract1 = sScaled - (gfloat) sIndex0;
+ gfloat vFract1 = vScaled - (gfloat) vIndex0;
+
+ gfloat hFract0 = 1.0f - hFract1;
+ gfloat sFract0 = 1.0f - sFract1;
+ gfloat vFract0 = 1.0f - vFract1;
+
+ const RS_VECTOR3 *entry00 = tableBase + vIndex0 * valStep +
hIndex0 * hueStep + sIndex0;
+ const RS_VECTOR3 *entry01 = entry00 + (hIndex1 - hIndex0) *
hueStep;
+
+ const RS_VECTOR3 *entry10 = entry00 + valStep;
+ const RS_VECTOR3 *entry11 = entry01 + valStep;
+
+ gfloat hueShift0 = vFract0 * (hFract0 * entry00->fHueShift +
+ hFract1 * entry01->fHueShift) +
+ vFract1 * (hFract0 * entry10->fHueShift +
+ hFract1 * entry11->fHueShift);
+
+ gfloat satScale0 = vFract0 * (hFract0 * entry00->fSatScale +
+ hFract1 * entry01->fSatScale) +
+ vFract1 * (hFract0 * entry10->fSatScale +
+ hFract1 * entry11->fSatScale);
+
+ gfloat valScale0 = vFract0 * (hFract0 * entry00->fValScale +
+ hFract1 * entry01->fValScale) +
+ vFract1 * (hFract0 * entry10->fValScale +
+ hFract1 * entry11->fValScale);
+
+ entry00++;
+ entry01++;
+ entry10++;
+ entry11++;
+
+ gfloat hueShift1 = vFract0 * (hFract0 * entry00->fHueShift +
+ hFract1 * entry01->fHueShift) +
+ vFract1 * (hFract0 * entry10->fHueShift +
+ hFract1 * entry11->fHueShift);
+
+ gfloat satScale1 = vFract0 * (hFract0 * entry00->fSatScale +
+ hFract1 * entry01->fSatScale) +
+ vFract1 * (hFract0 * entry10->fSatScale +
+ hFract1 * entry11->fSatScale);
+
+ gfloat valScale1 = vFract0 * (hFract0 * entry00->fValScale +
+ hFract1 * entry01->fValScale) +
+ vFract1 * (hFract0 * entry10->fValScale +
+ hFract1 * entry11->fValScale);
+
+ hueShift = sFract0 * hueShift0 + sFract1 * hueShift1;
+ satScale = sFract0 * satScale0 + sFract1 * satScale1;
+ valScale = sFract0 * valScale0 + sFract1 * valScale1;
+ }
+
+ hueShift *= (6.0f / 360.0f);
+
+ *h += hueShift;
+ *s = MIN(*s * satScale, 1.0);
+ *v = MIN(*v * valScale, 1.0);
+}
+
+/* RefBaselineRGBTone() */
+void
+rgb_tone(gfloat *_r, gfloat *_g, gfloat *_b, const gfloat * const tone_lut)
+{
+ gfloat r = *_r;
+ gfloat g = *_g;
+ gfloat b = *_b;
+ gfloat rr;
+ gfloat gg;
+ gfloat bb;
+
+ #define RGBTone(r, g, b, rr, gg, bb)\
+ {\
+ \
+/* DNG_ASSERT (r >= g && g >= b && r > b, "Logic Error
RGBTone");*/\
+ \
+ rr = tone_lut[_S(r)];\
+ bb = tone_lut[_S(b)];\
+ \
+ gg = bb + ((rr - bb) * (g - b) / (r - b));\
+ \
+ }
+
+ if (r >= g)
+ {
+
+ if (g > b)
+ {
+
+ // Case 1: r >= g > b
+
+ RGBTone (r, g, b, rr, gg, bb);
+
+ }
+
+ else if (b > r)
+ {
+
+ // Case 2: b > r >= g
+
+ RGBTone (b, r, g, bb, rr, gg);
+
+ }
+
+ else if (b > g)
+ {
+
+ // Case 3: r >= b > g
+
+ RGBTone (r, b, g, rr, bb, gg);
+
+ }
+
+ else
+ {
+
+ // Case 4: r >= g == b
+
+// DNG_ASSERT (r >= g && g == b, "Logic Error 2");
+
+ rr = tone_lut[_S(r)];
+ gg = tone_lut[_S(b)];
+// rr = table.Interpolate (r);
+// gg = table.Interpolate (g);
+ bb = gg;
+
+ }
+
+ }
+
+ else
+ {
+
+ if (r >= b)
+ {
+
+ // Case 5: g > r >= b
+
+ RGBTone (g, r, b, gg, rr, bb);
+
+ }
+
+ else if (b > g)
+ {
+
+ // Case 6: b > g > r
+
+ RGBTone (b, g, r, bb, gg, rr);
+
+ }
+
+ else
+ {
+
+ // Case 7: g >= b > r
+
+ RGBTone (g, b, r, gg, bb, rr);
+
+ }
+
+ }
+
+ #undef RGBTone
+
+ *_r = rr;
+ *_g = gg;
+ *_b = bb;
+
+}
+
+static void
+render(RSDcp *dcp, RS_IMAGE16 *image)
+{
+ gint x, y;
+ gfloat h, s, v;
+ gfloat r, g, b;
+ RS_VECTOR3 pix;
+
+ const gfloat exposure_comp = pow(2.0, dcp->exposure);
+
+ for(y = 0 ; y < image->h; y++)
+ {
+ for(x=0; x < image->w; x++)
+ {
+ gushort *pixel = GET_PIXEL(image, x, y);
+
+ /* Convert to float */
+ r = _F(pixel[R]);
+ g = _F(pixel[G]);
+ b = _F(pixel[B]);
+
+ r = MIN(dcp->camera_white.x, r);
+ g = MIN(dcp->camera_white.y, g);
+ b = MIN(dcp->camera_white.z, b);
+
+ pix.R = r;
+ pix.G = g;
+ pix.B = b;
+ pix = vector3_multiply_matrix(&pix,
&dcp->camera_to_prophoto);
+ r = pix.R;
+ g = pix.G;
+ b = pix.B;
+
+ r = CLAMP(pix.R, 0.0, 1.0);
+ g = CLAMP(pix.G, 0.0, 1.0);
+ b = CLAMP(pix.B, 0.0, 1.0);
+
+ /* Does it matter if we're above 1.0 at this point? */
+
+ /* To HSV */
+ RGBtoHSV(r, g, b, &h, &s, &v);
+
+ v = MIN(v * exposure_comp, 1.0);
+
+ if (dcp->huesatmap)
+ huesat_map(dcp->huesatmap, &h, &s, &v);
+
+ /* Saturation */
+ s *= dcp->saturation;
+ s = MIN(s, 1.0);
+
+ /* Hue */
+ h += dcp->hue;
+
+ /* Curve */
+ v = dcp->curve_samples[_S(v)];
+
+ if (dcp->looktable)
+ huesat_map(dcp->looktable, &h, &s, &v);
+
+ /* Back to RGB */
+ HSVtoRGB(h, s, v, &r, &g, &b);
+
+ pix.R = r;
+ pix.G = g;
+ pix.B = b;
+
+ pix = vector3_multiply_matrix(&pix,
&dcp->prophoto_to_srgb);
+
+ r = pix.R;
+ g = pix.G;
+ b = pix.B;
+
+ /* Save as gushort */
+ pixel[R] = _S(r);
+ pixel[G] = _S(g);
+ pixel[B] = _S(b);
+ }
+ }
+}
+
+#undef _F
+#undef _S
+
+static gfloat
+temp_from_exif_illuminant(guint illuminant)
+{
+ enum {
+ lsUnknown = 0,
+
+ lsDaylight = 1,
+ lsFluorescent = 2,
+ lsTungsten = 3,
+ lsFlash = 4,
+ lsFineWeather = 9,
+ lsCloudyWeather = 10,
+ lsShade = 11,
+ lsDaylightFluorescent = 12, // D 5700 - 7100K
+ lsDayWhiteFluorescent = 13, // N 4600 - 5400K
+ lsCoolWhiteFluorescent = 14, // W 3900 - 4500K
+ lsWhiteFluorescent = 15, // WW 3200 - 3700K
+ lsStandardLightA = 17,
+ lsStandardLightB = 18,
+ lsStandardLightC = 19,
+ lsD55 = 20,
+ lsD65 = 21,
+ lsD75 = 22,
+ lsD50 = 23,
+ lsISOStudioTungsten = 24,
+
+ lsOther = 255
+ };
+ switch (illuminant)
+ {
+ case lsStandardLightA:
+ case lsTungsten:
+ return 2850.0;
+
+ case lsISOStudioTungsten:
+ return 3200.0;
+
+ case lsD50:
+ return 5000.0;
+
+ case lsD55:
+ case lsDaylight:
+ case lsFineWeather:
+ case lsFlash:
+ case lsStandardLightB:
+ return 5500.0;
+ case lsD65:
+ case lsStandardLightC:
+ case lsCloudyWeather:
+ return 6500.0;
+
+ case lsD75:
+ case lsShade:
+ return 7500.0;
+
+ case lsDaylightFluorescent:
+ return (5700.0 + 7100.0) * 0.5;
+
+ case lsDayWhiteFluorescent:
+ return (4600.0 + 5400.0) * 0.5;
+
+ case lsCoolWhiteFluorescent:
+ case lsFluorescent:
+ return (3900.0 + 4500.0) * 0.5;
+
+ case lsWhiteFluorescent:
+ return (3200.0 + 3700.0) * 0.5;
+
+ default:
+ return 0.0;
+ }
+}
+
+/* dng_color_spec::FindXYZtoCamera */
+static RS_MATRIX3
+find_xyz_to_camera(RSDcp *dcp, const RS_xy_COORD *white_xy, RS_MATRIX3
*forward_matrix, RS_MATRIX3 *reduction_matrix, RS_MATRIX3 *camera_calibration)
+{
+ gfloat temp = 5000.0;
+
+ rs_color_whitepoint_to_temp(white_xy, &temp, NULL);
+
+ gfloat alpha = 0.0;
+
+ if (temp <= dcp->temp1)
+ alpha = 1.0;
+ else if (temp >= dcp->temp2)
+ alpha = 0.0;
+ else if ((dcp->temp2 > 0.0) && (dcp->temp1 > 0.0) && (temp > 0.0))
+ {
+ gdouble invT = 1.0 / temp;
+ alpha = (invT - (1.0 / dcp->temp2)) / ((1.0 / dcp->temp1) -
(1.0 / dcp->temp2));
+ }
+
+ RS_MATRIX3 color_matrix;
+
+ matrix3_interpolate(&dcp->color_matrix1, &dcp->color_matrix2, alpha,
&color_matrix);
+
+ if (forward_matrix)
+ {
+ if (dcp->has_forward_matrix1 && dcp->has_forward_matrix2)
+ matrix3_interpolate(&dcp->forward_matrix1,
&dcp->forward_matrix2, 1.0-alpha, forward_matrix);
+ else if (dcp->has_forward_matrix1)
+ *forward_matrix = dcp->forward_matrix1;
+ else if (dcp->has_forward_matrix2)
+ *forward_matrix = dcp->forward_matrix2;
+ }
+
+ if (reduction_matrix)
+ {
+ if (dcp->has_reduction_matrix1 && dcp->has_reduction_matrix2)
+ matrix3_interpolate(&dcp->reduction_matrix1,
&dcp->reduction_matrix2, alpha, reduction_matrix);
+ else if (dcp->has_reduction_matrix1)
+ *reduction_matrix = dcp->reduction_matrix1;
+ else if (dcp->has_reduction_matrix2)
+ *reduction_matrix = dcp->reduction_matrix2;
+ }
+
+ /* We don't have camera_calibration anyway! */
+
+ return color_matrix;
+}
+
+/* Verified to behave like dng_camera_profile::NormalizeForwardMatrix */
+static void
+normalize_forward_matrix(RS_MATRIX3 *matrix)
+{
+ RS_MATRIX3 tmp;
+ RS_VECTOR3 camera_one = {{1.0}, {1.0}, {1.0} };
+
+ RS_MATRIX3 pcs_to_xyz_dia = vector3_as_diagonal(&XYZ_WP_D50);
+ RS_VECTOR3 xyz = vector3_multiply_matrix(&camera_one, matrix);
+ RS_MATRIX3 xyz_as_dia = vector3_as_diagonal(&xyz);
+ RS_MATRIX3 xyz_as_dia_inv = matrix3_invert(&xyz_as_dia);
+
+ matrix3_multiply(&pcs_to_xyz_dia, &xyz_as_dia_inv, &tmp);
+ matrix3_multiply(&tmp, matrix, matrix);
+}
+
+/* dng_color_spec::SetWhiteXY */
+static void
+set_white_xy(RSDcp *dcp, const RS_xy_COORD *xy)
+{
+ RS_MATRIX3 color_matrix;
+ RS_MATRIX3 forward_matrix;
+ RS_MATRIX3 reduction_matrix;
+
+ dcp->white_xy = *xy;
+
+ color_matrix = find_xyz_to_camera(dcp, xy, &forward_matrix,
&reduction_matrix, NULL);
+
+ RS_XYZ_VECTOR white = xy_to_XYZ(xy);
+
+ dcp->camera_white = vector3_multiply_matrix(&white, &color_matrix);
+
+ gfloat white_scale = 1.0 / vector3_max(&dcp->camera_white);
+
+ dcp->camera_white.x = CLAMP(0.001, white_scale * dcp->camera_white.x,
1.0);
+ dcp->camera_white.y = CLAMP(0.001, white_scale * dcp->camera_white.y,
1.0);
+ dcp->camera_white.z = CLAMP(0.001, white_scale * dcp->camera_white.z,
1.0);
+
+ if (dcp->has_forward_matrix1 || dcp->has_forward_matrix2)
+ {
+ /* verified by DNG SDK */
+ RS_MATRIX3 refCameraWhite_diagonal =
vector3_as_diagonal(&dcp->camera_white);
+
+ RS_MATRIX3 refCameraWhite_diagonal_inv =
matrix3_invert(&refCameraWhite_diagonal); /* D */
+ matrix3_multiply(&forward_matrix, &refCameraWhite_diagonal_inv,
&dcp->camera_to_pcs);
+ }
+ else
+ {
+ /* FIXME: test this */
+ RS_xy_COORD PCStoXY = {0.3457, 0.3585};
+ RS_MATRIX3 map = rs_calculate_map_white_matrix(&PCStoXY, xy);
/* or &white?! */
+ RS_MATRIX3 pcs_to_camera;
+ matrix3_multiply(&color_matrix, &map, &pcs_to_camera);
+ RS_VECTOR3 tmp = vector3_multiply_matrix(&XYZ_WP_D50,
&pcs_to_camera);
+ gfloat scale = vector3_max(&tmp);
+ matrix3_scale(&pcs_to_camera, 1.0 / scale, &pcs_to_camera);
+ dcp->camera_to_pcs = matrix3_invert(&pcs_to_camera);
+ }
+
+}
+
+static void
+precalc(RSDcp *dcp)
+{
+ const static RS_MATRIX3 xyz_to_prophoto = {{
+ { 1.3459433, -0.2556075, -0.0511118 },
+ { -0.5445989, 1.5081673, 0.0205351 },
+ { 0.0000000, 0.0000000, 1.2118128 }
+ }};
+ const RS_MATRIX3 prophoto_to_xyz = matrix3_invert(&xyz_to_prophoto);
+ /* This HAS ben adopted for D50 -> D65 white point */
+ const static RS_MATRIX3 xyz_to_rgb = {{
+ { 3.1338582120812, - 1.6168645994761, - 0.4906125135547 },
+ { - 0.978769586326, 1.9161399511888, 0.0334523812116 },
+ { 0.0719452014624, - 0.2289912335361, 1.4052430533683 }
+ }};
+
+ /* Build Prophoto to sRGB */
+ matrix3_multiply(&xyz_to_rgb, &prophoto_to_xyz, &dcp->prophoto_to_srgb);
+
+ /* Camera to ProPhoto */
+ matrix3_multiply(&xyz_to_prophoto, &dcp->camera_to_pcs,
&dcp->camera_to_prophoto); /* verified by SDK */
+}
+
+static RS_MATRIX3
+read_matrix(RSTiff *tiff, guint offset)
+{
+ RS_MATRIX3 matrix;
+
+ matrix.coeff[0][0] = rs_tiff_get_rational(tiff, offset);
+ matrix.coeff[0][1] = rs_tiff_get_rational(tiff, offset+8);
+ matrix.coeff[0][2] = rs_tiff_get_rational(tiff, offset+16);
+ matrix.coeff[1][0] = rs_tiff_get_rational(tiff, offset+24);
+ matrix.coeff[1][1] = rs_tiff_get_rational(tiff, offset+32);
+ matrix.coeff[1][2] = rs_tiff_get_rational(tiff, offset+40);
+ matrix.coeff[2][0] = rs_tiff_get_rational(tiff, offset+48);
+ matrix.coeff[2][1] = rs_tiff_get_rational(tiff, offset+56);
+ matrix.coeff[2][2] = rs_tiff_get_rational(tiff, offset+64);
+
+ return matrix;
+}
+
+static void
+read_profile(RSDcp *dcp, const gchar *filename)
+{
+ RSTiff *tiff = rs_tiff_new_from_file(filename);
+ RSTiffIfdEntry *entry;
+
+ /* FIXME: Reset this properly */
+ dcp->has_color_matrix1 = FALSE;
+ dcp->has_color_matrix2 = FALSE;
+ dcp->has_reduction_matrix1 = FALSE;
+ dcp->has_reduction_matrix2 = FALSE;
+ dcp->has_forward_matrix1 = FALSE;
+ dcp->has_forward_matrix2 = FALSE;
+
+ /* ColorMatrix1 */
+ entry = rs_tiff_get_ifd_entry(tiff, 0, 0xc621);
+ if (entry)
+ {
+ dcp->color_matrix1 = read_matrix(tiff, entry->value_offset);
+ dcp->has_color_matrix1 = TRUE;
+ }
+ else
+ matrix3_identity(&dcp->color_matrix1);
+
+ /* ColorMatrix2 */
+ entry = rs_tiff_get_ifd_entry(tiff, 0, 0xc622);
+ if (entry)
+ {
+ dcp->color_matrix2 = read_matrix(tiff, entry->value_offset);
+ dcp->has_color_matrix2 = TRUE;
+ }
+ else
+ matrix3_identity(&dcp->color_matrix2);
+
+ /* ReductionMatrix1 */
+ entry = rs_tiff_get_ifd_entry(tiff, 0, 0xc725);
+ if (entry)
+ {
+ dcp->reduction_matrix1 = read_matrix(tiff, entry->value_offset);
+ dcp->has_reduction_matrix1 = TRUE;
+ }
+ else
+ matrix3_identity(&dcp->reduction_matrix1);
+
+ /* ReductionMatrix2 */
+ entry = rs_tiff_get_ifd_entry(tiff, 0, 0xc726);
+ if (entry)
+ {
+ dcp->reduction_matrix2 = read_matrix(tiff, entry->value_offset);
+ dcp->has_reduction_matrix2 = TRUE;
+ }
+ else
+ matrix3_identity(&dcp->reduction_matrix2);
+
+ /* CalibrationIlluminant1 */
+ entry = rs_tiff_get_ifd_entry(tiff, 0, 0xc65a);
+ if (entry)
+ dcp->temp1 = temp_from_exif_illuminant(entry->value_offset);
+ else
+ dcp->temp1 = 5000;
+
+ /* CalibrationIlluminant2 */
+ entry = rs_tiff_get_ifd_entry(tiff, 0, 0xc65b);
+ if (entry)
+ dcp->temp2 = temp_from_exif_illuminant(entry->value_offset);
+ else
+ dcp->temp2 = 5000;
+
+ /* ProfileToneCurve */
+ entry = rs_tiff_get_ifd_entry(tiff, 0, 0xc6fc);
+ if (entry)
+ {
+ gint i;
+ gint num_knots = entry->count / 2;
+ gfloat *knots = g_new0(gfloat, entry->count);
+
+ for(i = 0; i < entry->count; i++)
+ knots[i] = rs_tiff_get_float(tiff,
(entry->value_offset+(i*4)));
+
+ dcp->baseline_exposure = rs_spline_new(knots, num_knots,
NATURAL);
+ dcp->baseline_exposure_lut =
rs_spline_sample(dcp->baseline_exposure, NULL, 65536);
+ }
+ /* ForwardMatrix1 */
+ entry = rs_tiff_get_ifd_entry(tiff, 0, 0xc714);
+ if (entry)
+ {
+ dcp->forward_matrix1 = read_matrix(tiff, entry->value_offset);
+ dcp->has_forward_matrix1 = TRUE;
+ normalize_forward_matrix(&dcp->forward_matrix1);
+ }
+ else
+ matrix3_identity(&dcp->forward_matrix1);
+
+ /* ForwardMatrix2 */
+ entry = rs_tiff_get_ifd_entry(tiff, 0, 0xc715);
+ if (entry)
+ {
+ dcp->forward_matrix2 = read_matrix(tiff, entry->value_offset);
+ dcp->has_forward_matrix2 = TRUE;
+ }
+ else
+ matrix3_identity(&dcp->forward_matrix2);
+
+ dcp->looktable = rs_huesat_map_new_from_dcp(tiff, 0, 0xc725, 0xc726);
+ dcp->huesatmap1 = rs_huesat_map_new_from_dcp(tiff, 0, 0xc6f9, 0xc6fa);
+ dcp->huesatmap2 = rs_huesat_map_new_from_dcp(tiff, 0, 0xc6f9, 0xc6fb);
+ dcp->huesatmap = dcp->huesatmap1;
+ g_object_unref(tiff);
+
+ precalc(dcp);
+}
+
+/*
++ 0xc621 ColorMatrix1 (9 * SRATIONAL)
++ 0xc622 ColorMatrix2 (9 * SRATIONAL)
++ 0xc725 ReductionMatrix1 (9 * SRATIONAL)
++ 0xc726 ReductionMatrix2 (9 * SRATIONAL)
++ 0xc65a CalibrationIlluminant1 (1 * SHORT)
++ 0xc65b CalibrationIlluminant2 (1 * SHORT)
+• 0xc6f4 ProfileCalibrationSignature (ASCII or BYTE)
+• 0xc6f8 ProfileName (ASCII or BYTE)
+• 0xc6f9 ProfileHueSatMapDims (3 * LONG)
+• 0xc6fa ProfileHueSatMapData1 (FLOAT)
+• 0xc6fb ProfileHueSatMapData2 (FLOAT)
+• 0xc6fc ProfileToneCurve (FLOAT)
+• 0xc6fd ProfileEmbedPolicy (LONG)
+• 0xc6fe ProfileCopyright (ASCII or BYTE)
++ 0xc714 ForwardMatrix1 (SRATIONAL)
++ 0xc715 ForwardMatrix2 (SRATIONAL)
+• 0xc725 ProfileLookTableDims (3 * LONG)
+• 0xc726 ProfileLookTableData
+*/
_______________________________________________
Rawstudio-commit mailing list
[email protected]
http://rawstudio.org/cgi-bin/mailman/listinfo/rawstudio-commit