[PATCH] [For-Review] CMYK profile gamut support
- From: James Cloos <cloos jhcloos com>
- To: gnome-color-manager-list gnome org
- Subject: [PATCH] [For-Review] CMYK profile gamut support
- Date: Mon, 17 Oct 2011 15:30:23 -0400
Here is an intial proof-of-concept patch. I still need to update
gcm_cie_widget_draw_line() for cmyk and to split it into a sequence
of commits, but the hexagon hull of the gamut is drawn.
My tests show that the r-y-g-c-b-m hexagon is only an approximation of
the profile’s gamut, but it is a reasonable one. *Very* few colours
escape the hexagon, and only just barely.
A spherical polygon may be more accurate than a planar polygon, but
converting the edges of a spheroid hexagon into planar cubic curve_to
calls is more complex than this probably needs to be.
>From 90c7a8d6e1d7bdfaf2dfa457aa5d0baeaa54b579 Mon Sep 17 00:00:00 2001
From: James Cloos <cloos jhcloos com>
Date: Sat, 15 Oct 2011 19:42:24 -0400
Subject: [PATCH 1/1] Render the CIE gamut of CMYK profiles
Signed-off-by: James Cloos <cloos jhcloos com>
---
src/gcm-cie-widget.c | 106 ++++++++++++++++++++++++
src/gcm-profile.c | 218 +++++++++++++++++++++++++++++++++++++++++++++++++-
src/gcm-profile.h | 4 +
src/gcm-viewer.c | 6 ++
4 files changed, 333 insertions(+), 1 deletions(-)
diff --git a/src/gcm-cie-widget.c b/src/gcm-cie-widget.c
index 8fc4b85..0eaa9ef 100644
--- a/src/gcm-cie-widget.c
+++ b/src/gcm-cie-widget.c
@@ -52,7 +52,12 @@ struct GcmCieWidgetPrivate
CdColorYxy *green; /* green primary illuminant */
CdColorYxy *blue; /* blue primary illuminant */
CdColorYxy *white; /* white point */
+ CdColorYxy *cyan; /* cyan colourant */
+ CdColorYxy *magenta; /* magenta colourant */
+ CdColorYxy *yellow; /* yellow colourant */
+ CdColorYxy *key; /* key colourant */
gdouble gamma; /* gamma of nonlinear correction */
+ CdColorspace colorspace; /* color space of the profile */
};
/* The following table gives the spectral chromaticity co-ordinates
@@ -399,6 +404,10 @@ enum
PROP_GREEN,
PROP_BLUE,
PROP_WHITE,
+ PROP_CYAN,
+ PROP_MAGENTA,
+ PROP_YELLOW,
+ PROP_KEY,
PROP_LAST
};
@@ -450,6 +459,18 @@ gcm_cie_set_property (GObject *object, guint prop_id, const GValue *value, GPara
case PROP_WHITE:
cd_color_copy_yxy (g_value_get_boxed (value), priv->white);
break;
+ case PROP_CYAN:
+ cd_color_copy_yxy (g_value_get_boxed (value), priv->cyan);
+ break;
+ case PROP_MAGENTA:
+ cd_color_copy_yxy (g_value_get_boxed (value), priv->magenta);
+ break;
+ case PROP_YELLOW:
+ cd_color_copy_yxy (g_value_get_boxed (value), priv->yellow);
+ break;
+ case PROP_KEY:
+ cd_color_copy_yxy (g_value_get_boxed (value), priv->key);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -507,6 +528,26 @@ gcm_cie_widget_class_init (GcmCieWidgetClass *class)
g_param_spec_boxed ("white", NULL, NULL,
CD_TYPE_COLOR_YXY,
G_PARAM_WRITABLE));
+ g_object_class_install_property (object_class,
+ PROP_CYAN,
+ g_param_spec_boxed ("cyan", NULL, NULL,
+ CD_TYPE_COLOR_YXY,
+ G_PARAM_WRITABLE));
+ g_object_class_install_property (object_class,
+ PROP_MAGENTA,
+ g_param_spec_boxed ("magenta", NULL, NULL,
+ CD_TYPE_COLOR_YXY,
+ G_PARAM_WRITABLE));
+ g_object_class_install_property (object_class,
+ PROP_YELLOW,
+ g_param_spec_boxed ("yellow", NULL, NULL,
+ CD_TYPE_COLOR_YXY,
+ G_PARAM_WRITABLE));
+ g_object_class_install_property (object_class,
+ PROP_KEY,
+ g_param_spec_boxed ("key", NULL, NULL,
+ CD_TYPE_COLOR_YXY,
+ G_PARAM_WRITABLE));
}
/**
@@ -520,6 +561,11 @@ gcm_cie_widget_set_from_profile (GtkWidget *widget, GcmProfile *profile)
CdColorXYZ *red;
CdColorXYZ *green;
CdColorXYZ *blue;
+ CdColorXYZ *cyan;
+ CdColorXYZ *magenta;
+ CdColorXYZ *yellow;
+ CdColorXYZ *key;
+ CdColorspace colorspace;
/* get the new details from the profile */
g_object_get (profile,
@@ -527,6 +573,11 @@ gcm_cie_widget_set_from_profile (GtkWidget *widget, GcmProfile *profile)
"red", &red,
"green", &green,
"blue", &blue,
+ "cyan", &cyan,
+ "magenta", &magenta,
+ "yellow", &yellow,
+ "key", &key,
+ "colorspace", &colorspace,
NULL);
/* copy into this widget */
@@ -534,6 +585,12 @@ gcm_cie_widget_set_from_profile (GtkWidget *widget, GcmProfile *profile)
cd_color_convert_xyz_to_yxy (red, cie->priv->red);
cd_color_convert_xyz_to_yxy (green, cie->priv->green);
cd_color_convert_xyz_to_yxy (blue, cie->priv->blue);
+ cd_color_convert_xyz_to_yxy (cyan, cie->priv->cyan);
+ cd_color_convert_xyz_to_yxy (magenta, cie->priv->magenta);
+ cd_color_convert_xyz_to_yxy (yellow, cie->priv->yellow);
+ cd_color_convert_xyz_to_yxy (key, cie->priv->key);
+
+ cie->priv->colorspace = colorspace;
/* hide if we have no data */
if (cie->priv->white->x > 0.001) {
@@ -548,6 +605,10 @@ gcm_cie_widget_set_from_profile (GtkWidget *widget, GcmProfile *profile)
cd_color_xyz_free (red);
cd_color_xyz_free (green);
cd_color_xyz_free (blue);
+ cd_color_xyz_free (cyan);
+ cd_color_xyz_free (magenta);
+ cd_color_xyz_free (yellow);
+ cd_color_xyz_free (key);
}
/**
@@ -569,6 +630,10 @@ gcm_cie_widget_init (GcmCieWidget *cie)
cie->priv->green = cd_color_yxy_new ();
cie->priv->blue = cd_color_yxy_new ();
cie->priv->white = cd_color_yxy_new ();
+ cie->priv->cyan = cd_color_yxy_new ();
+ cie->priv->magenta = cd_color_yxy_new ();
+ cie->priv->yellow = cd_color_yxy_new ();
+ cie->priv->key = cd_color_yxy_new ();
cie->priv->red->x = 0.64;
cie->priv->red->y = 0.33;
cie->priv->green->x = 0.30;
@@ -578,6 +643,15 @@ gcm_cie_widget_init (GcmCieWidget *cie)
cie->priv->white->x = 0.3127;
cie->priv->white->y = 0.3291;
cie->priv->gamma = 0.0;
+ /* zero CMYK values by default */
+ cie->priv->cyan->x = 0.0;
+ cie->priv->cyan->y = 0.0;
+ cie->priv->magenta->x = 0.0;
+ cie->priv->magenta->y = 0.0;
+ cie->priv->yellow->x = 0.0;
+ cie->priv->yellow->y = 0.0;
+ cie->priv->key->x = 0.0;
+ cie->priv->key->y = 0.0;
/* do pango stuff */
context = gtk_widget_get_pango_context (GTK_WIDGET (cie));
@@ -602,6 +676,10 @@ gcm_cie_widget_finalize (GObject *object)
cd_color_yxy_free (cie->priv->red);
cd_color_yxy_free (cie->priv->green);
cd_color_yxy_free (cie->priv->blue);
+ cd_color_yxy_free (cie->priv->cyan);
+ cd_color_yxy_free (cie->priv->magenta);
+ cd_color_yxy_free (cie->priv->yellow);
+ cd_color_yxy_free (cie->priv->key);
g_ptr_array_unref (cie->priv->tongue_buffer);
G_OBJECT_CLASS (gcm_cie_widget_parent_class)->finalize (object);
}
@@ -972,6 +1050,10 @@ gcm_cie_widget_draw_gamut_outline (GcmCieWidget *cie, cairo_t *cr)
gdouble wx;
gdouble wy;
GcmCieWidgetPrivate *priv = cie->priv;
+ gboolean cmyk = FALSE;
+
+ if (cie->priv->colorspace == CD_COLORSPACE_CMYK || cie->priv->colorspace == CD_COLORSPACE_CMY)
+ cmyk = TRUE;
cairo_save (cr);
@@ -983,16 +1065,40 @@ gcm_cie_widget_draw_gamut_outline (GcmCieWidget *cie, cairo_t *cr)
goto out;
cairo_move_to (cr, wx, wy);
+ if (cmyk)
+ {
+ gcm_cie_widget_map_to_display (cie, priv->yellow->x, priv->yellow->y, &wx, &wy);
+ if (wx < 0 || wy < 0)
+ goto out;
+ cairo_line_to (cr, wx, wy);
+ }
+
gcm_cie_widget_map_to_display (cie, priv->green->x, priv->green->y, &wx, &wy);
if (wx < 0 || wy < 0)
goto out;
cairo_line_to (cr, wx, wy);
+ if (cmyk)
+ {
+ gcm_cie_widget_map_to_display (cie, priv->cyan->x, priv->cyan->y, &wx, &wy);
+ if (wx < 0 || wy < 0)
+ goto out;
+ cairo_line_to (cr, wx, wy);
+ }
+
gcm_cie_widget_map_to_display (cie, priv->blue->x, priv->blue->y, &wx, &wy);
if (wx < 0 || wy < 0)
goto out;
cairo_line_to (cr, wx, wy);
+ if (cmyk)
+ {
+ gcm_cie_widget_map_to_display (cie, priv->magenta->x, priv->magenta->y, &wx, &wy);
+ if (wx < 0 || wy < 0)
+ goto out;
+ cairo_line_to (cr, wx, wy);
+ }
+
cairo_close_path (cr);
cairo_stroke (cr);
out:
diff --git a/src/gcm-profile.c b/src/gcm-profile.c
index 5a6c2f2..c17a48c 100644
--- a/src/gcm-profile.c
+++ b/src/gcm-profile.c
@@ -62,6 +62,10 @@ struct _GcmProfilePrivate
CdColorXYZ *red;
CdColorXYZ *green;
CdColorXYZ *blue;
+ CdColorXYZ *cyan;
+ CdColorXYZ *magenta;
+ CdColorXYZ *yellow;
+ CdColorXYZ *key;
GFile *file;
GFileMonitor *monitor;
gboolean has_mlut;
@@ -86,6 +90,10 @@ enum {
PROP_GREEN,
PROP_BLUE,
PROP_TEMPERATURE,
+ PROP_CYAN,
+ PROP_MAGENTA,
+ PROP_YELLOW,
+ PROP_KEY,
PROP_LAST
};
@@ -241,6 +249,66 @@ gcm_profile_get_white (GcmProfile *profile)
}
/**
+ * gcm_profile_get_cyan:
+ * @profile: a valid #GcmProfile instance
+ *
+ * Gets the cyan colorant chromaticity value.
+ *
+ * Return value: the #CdColorXYZ value
+ **/
+const CdColorXYZ *
+gcm_profile_get_cyan (GcmProfile *profile)
+{
+ g_return_val_if_fail (GCM_IS_PROFILE (profile), NULL);
+ return profile->priv->cyan;
+}
+
+/**
+ * gcm_profile_get_magenta:
+ * @profile: a valid #GcmProfile instance
+ *
+ * Gets the magenta colorant chromaticity value.
+ *
+ * Return value: the #CdColorXYZ value
+ **/
+const CdColorXYZ *
+gcm_profile_get_magenta (GcmProfile *profile)
+{
+ g_return_val_if_fail (GCM_IS_PROFILE (profile), NULL);
+ return profile->priv->magenta;
+}
+
+/**
+ * gcm_profile_get_yellow:
+ * @profile: a valid #GcmProfile instance
+ *
+ * Gets the yellow colorant chromaticity value.
+ *
+ * Return value: the #CdColorXYZ value
+ **/
+const CdColorXYZ *
+gcm_profile_get_yellow (GcmProfile *profile)
+{
+ g_return_val_if_fail (GCM_IS_PROFILE (profile), NULL);
+ return profile->priv->yellow;
+}
+
+/**
+ * gcm_profile_get_key:
+ * @profile: a valid #GcmProfile instance
+ *
+ * Gets the key colorant chromaticity value.
+ *
+ * Return value: the #CdColorXYZ value
+ **/
+const CdColorXYZ *
+gcm_profile_get_key (GcmProfile *profile)
+{
+ g_return_val_if_fail (GCM_IS_PROFILE (profile), NULL);
+ return profile->priv->key;
+}
+
+/**
* gcm_profile_get_file:
* @profile: A valid #GcmProfile
*
@@ -612,8 +680,10 @@ gcm_profile_parse_data (GcmProfile *profile, const guint8 *data, gsize length, G
cmsHTRANSFORM transform;
gchar *text = NULL;
gchar *checksum = NULL;
+ gboolean got_colorants = FALSE;
gboolean got_illuminants = FALSE;
GcmProfilePrivate *priv = profile->priv;
+ cmsCIEXYZ cmyk_cyan, cmyk_magenta, cmyk_yellow, cmyk_key;
g_return_val_if_fail (GCM_IS_PROFILE (profile), FALSE);
g_return_val_if_fail (data != NULL, FALSE);
@@ -745,8 +815,74 @@ gcm_profile_parse_data (GcmProfile *profile, const guint8 *data, gsize length, G
} else {
g_debug ("failed to get illuminants");
}
- }
+ } else if (color_space == cmsSigCmykData || color_space == cmsSigCmyData) {
+ /* must get the colorants via a transform */
+
+ gdouble cmyk_values[4];
+
+ /* create a transform from profile to XYZ */
+ xyz_profile = cmsCreateXYZProfile ();
+ transform = cmsCreateTransform (priv->lcms_profile, TYPE_CMYK_DBL, xyz_profile,
+ TYPE_XYZ_DBL, INTENT_PERCEPTUAL, 0);
+ if (transform != NULL) {
+ /* cyan */
+ cmyk_values[0] = 100.0;
+ cmyk_values[1] = 0.0;
+ cmyk_values[2] = 0.0;
+ cmyk_values[3] = 0.0;
+ cmsDoTransform (transform, cmyk_values, &cmyk_cyan, 1);
+
+ /* magenta */
+ cmyk_values[0] = 0.0;
+ cmyk_values[1] = 100.0;
+ cmyk_values[2] = 0.0;
+ cmyk_values[3] = 0.0;
+ cmsDoTransform (transform, cmyk_values, &cmyk_magenta, 1);
+
+ /* yellow */
+ cmyk_values[0] = 0.0;
+ cmyk_values[1] = 0.0;
+ cmyk_values[2] = 100.0;
+ cmyk_values[3] = 0.0;
+ cmsDoTransform (transform, cmyk_values, &cmyk_yellow, 1);
+
+ /* key */
+ cmyk_values[0] = 0.0;
+ cmyk_values[1] = 0.0;
+ cmyk_values[2] = 0.0;
+ cmyk_values[3] = 100.0;
+ cmsDoTransform (transform, cmyk_values, &cmyk_key, 1);
+
+ /* red == magenta+yellow == anti-cyan */
+ cmyk_values[0] = 0.0;
+ cmyk_values[1] = 100.0;
+ cmyk_values[2] = 100.0;
+ cmyk_values[3] = 0.0;
+ cmsDoTransform (transform, cmyk_values, &cie_illum.Red, 1);
+
+ /* green == cyan+yellow == anti-magenta */
+ cmyk_values[0] = 100.0;
+ cmyk_values[1] = 0.0;
+ cmyk_values[2] = 100.0;
+ cmyk_values[3] = 0.0;
+ cmsDoTransform (transform, cmyk_values, &cie_illum.Green, 1);
+
+ /* blue == cyan+magenta == anti-yellow */
+ cmyk_values[0] = 100.0;
+ cmyk_values[1] = 100.0;
+ cmyk_values[2] = 0.0;
+ cmyk_values[3] = 0.0;
+ cmsDoTransform (transform, cmyk_values, &cie_illum.Blue, 1);
+
+ /* we're done */
+ cmsDeleteTransform (transform);
+ got_illuminants = TRUE;
+ got_colorants = TRUE;
+ } else {
+ g_debug ("failed to run through profile");
+ }
+ }
/* get the illuminants by running it through the profile */
if (!got_illuminants && color_space == cmsSigRgbData) {
gdouble rgb_values[3];
@@ -799,6 +935,22 @@ gcm_profile_parse_data (GcmProfile *profile, const guint8 *data, gsize length, G
cd_color_clear_xyz (priv->green);
cd_color_clear_xyz (priv->blue);
}
+ if (got_colorants) {
+ cd_color_set_xyz (priv->cyan,
+ cmyk_cyan.X, cmyk_cyan.Y, cmyk_cyan.Z);
+ cd_color_set_xyz (priv->magenta,
+ cmyk_magenta.X, cmyk_magenta.Y, cmyk_magenta.Z);
+ cd_color_set_xyz (priv->yellow,
+ cmyk_yellow.X, cmyk_yellow.Y, cmyk_yellow.Z);
+ cd_color_set_xyz (priv->key,
+ cmyk_key.X, cmyk_key.Y, cmyk_key.Z);
+ } else {
+ g_debug ("failed to get cmyk colorants values");
+ cd_color_clear_xyz (priv->cyan);
+ cd_color_clear_xyz (priv->magenta);
+ cd_color_clear_xyz (priv->yellow);
+ cd_color_clear_xyz (priv->key);
+ }
/* get the profile created time and date */
ret = cmsGetHeaderCreationDateTime (priv->lcms_profile, &created);
@@ -2128,6 +2280,18 @@ gcm_profile_get_property (GObject *object, guint prop_id, GValue *value, GParamS
case PROP_BLUE:
g_value_set_boxed (value, g_boxed_copy (CD_TYPE_COLOR_XYZ, priv->blue));
break;
+ case PROP_CYAN:
+ g_value_set_boxed (value, g_boxed_copy (CD_TYPE_COLOR_XYZ, priv->cyan));
+ break;
+ case PROP_MAGENTA:
+ g_value_set_boxed (value, g_boxed_copy (CD_TYPE_COLOR_XYZ, priv->magenta));
+ break;
+ case PROP_YELLOW:
+ g_value_set_boxed (value, g_boxed_copy (CD_TYPE_COLOR_XYZ, priv->yellow));
+ break;
+ case PROP_KEY:
+ g_value_set_boxed (value, g_boxed_copy (CD_TYPE_COLOR_XYZ, priv->key));
+ break;
case PROP_TEMPERATURE:
g_value_set_uint (value, priv->temperature);
break;
@@ -2186,6 +2350,18 @@ gcm_profile_set_property (GObject *object, guint prop_id, const GValue *value, G
case PROP_BLUE:
cd_color_copy_xyz (g_value_get_boxed (value), priv->blue);
break;
+ case PROP_CYAN:
+ cd_color_copy_xyz (g_value_get_boxed (value), priv->cyan);
+ break;
+ case PROP_MAGENTA:
+ cd_color_copy_xyz (g_value_get_boxed (value), priv->magenta);
+ break;
+ case PROP_YELLOW:
+ cd_color_copy_xyz (g_value_get_boxed (value), priv->yellow);
+ break;
+ case PROP_KEY:
+ cd_color_copy_xyz (g_value_get_boxed (value), priv->key);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -2325,6 +2501,38 @@ gcm_profile_class_init (GcmProfileClass *klass)
g_object_class_install_property (object_class, PROP_BLUE, pspec);
/**
+ * GcmProfile:cyan:
+ */
+ pspec = g_param_spec_boxed ("cyan", NULL, NULL,
+ CD_TYPE_COLOR_XYZ,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_CYAN, pspec);
+
+ /**
+ * GcmProfile:magenta:
+ */
+ pspec = g_param_spec_boxed ("magenta", NULL, NULL,
+ CD_TYPE_COLOR_XYZ,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_MAGENTA, pspec);
+
+ /**
+ * GcmProfile:yellow:
+ */
+ pspec = g_param_spec_boxed ("yellow", NULL, NULL,
+ CD_TYPE_COLOR_XYZ,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_YELLOW, pspec);
+
+ /**
+ * GcmProfile:key:
+ */
+ pspec = g_param_spec_boxed ("key", NULL, NULL,
+ CD_TYPE_COLOR_XYZ,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_KEY, pspec);
+
+ /**
* GcmProfile:temperature:
*/
pspec = g_param_spec_uint ("temperature", NULL, NULL,
@@ -2351,6 +2559,10 @@ gcm_profile_init (GcmProfile *profile)
profile->priv->red = cd_color_xyz_new ();
profile->priv->green = cd_color_xyz_new ();
profile->priv->blue = cd_color_xyz_new ();
+ profile->priv->cyan = cd_color_xyz_new ();
+ profile->priv->magenta = cd_color_xyz_new ();
+ profile->priv->yellow = cd_color_xyz_new ();
+ profile->priv->key = cd_color_xyz_new ();
/* setup LCMS */
cmsSetLogErrorHandler (gcm_profile_error_cb);
@@ -2377,6 +2589,10 @@ gcm_profile_finalize (GObject *object)
cd_color_xyz_free (priv->red);
cd_color_xyz_free (priv->green);
cd_color_xyz_free (priv->blue);
+ cd_color_xyz_free (priv->cyan);
+ cd_color_xyz_free (priv->magenta);
+ cd_color_xyz_free (priv->yellow);
+ cd_color_xyz_free (priv->key);
g_hash_table_destroy (profile->priv->dict);
if (priv->file != NULL)
g_object_unref (priv->file);
diff --git a/src/gcm-profile.h b/src/gcm-profile.h
index f3f290c..e5c16a8 100644
--- a/src/gcm-profile.h
+++ b/src/gcm-profile.h
@@ -136,6 +136,10 @@ const CdColorXYZ *gcm_profile_get_red (GcmProfile *profile);
const CdColorXYZ *gcm_profile_get_green (GcmProfile *profile);
const CdColorXYZ *gcm_profile_get_blue (GcmProfile *profile);
const CdColorXYZ *gcm_profile_get_white (GcmProfile *profile);
+const CdColorXYZ *gcm_profile_get_cyan (GcmProfile *profile);
+const CdColorXYZ *gcm_profile_get_magenta (GcmProfile *profile);
+const CdColorXYZ *gcm_profile_get_yellow (GcmProfile *profile);
+const CdColorXYZ *gcm_profile_get_key (GcmProfile *profile);
const gchar *gcm_profile_get_data (GcmProfile *profile,
const gchar *key);
void gcm_profile_set_data (GcmProfile *profile,
diff --git a/src/gcm-viewer.c b/src/gcm-viewer.c
index a84efed..0d74ff0 100644
--- a/src/gcm-viewer.c
+++ b/src/gcm-viewer.c
@@ -950,6 +950,12 @@ gcm_viewer_set_profile (GcmViewerPrivate *viewer, CdProfile *profile)
gcm_cie_widget_set_from_profile (viewer->cie_widget,
gcm_profile);
gtk_widget_show (widget);
+ } else if ((cd_profile_get_colorspace (profile) == CD_COLORSPACE_CMY ||
+ cd_profile_get_colorspace (profile) == CD_COLORSPACE_CMYK ) &&
+ cd_profile_get_kind (profile) != CD_PROFILE_KIND_NAMED_COLOR) {
+ gcm_cie_widget_set_from_profile (viewer->cie_widget,
+ gcm_profile);
+ gtk_widget_show (widget);
} else {
gtk_widget_hide (widget);
}
--
1.7.6.1
-JimC
--
James Cloos <cloos jhcloos com> OpenPGP: 1024D/ED7DAEA6
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]