summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@gmail.com>2009-03-20 04:06:39 +0000
committerTavian Barnes <tavianator@gmail.com>2009-03-20 04:06:39 +0000
commit047d3248896d375a8fbc80dbbf573b81a3e5a927 (patch)
treecdfdeebf0477ac62711683a5c267235f0a286ecd
parent5ac6158f8ff999d4db18fb805c02b5c733e75ddb (diff)
downloaddimension-047d3248896d375a8fbc80dbbf573b81a3e5a927.tar.xz
Revamp color handling.
-rw-r--r--libdimension-png/png.c88
-rw-r--r--libdimension/Makefile.am2
-rw-r--r--libdimension/canvas.c166
-rw-r--r--libdimension/color.c259
-rw-r--r--libdimension/dimension.h4
-rw-r--r--libdimension/dimension/canvas.h11
-rw-r--r--libdimension/dimension/color.h51
-rw-r--r--libdimension/dimension/geometry.h16
-rw-r--r--libdimension/inlines.c22
-rw-r--r--tests/png.c46
10 files changed, 428 insertions, 237 deletions
diff --git a/libdimension-png/png.c b/libdimension-png/png.c
index 750b5af..dbcd21a 100644
--- a/libdimension-png/png.c
+++ b/libdimension-png/png.c
@@ -32,7 +32,8 @@ dmnsn_png_write_canvas(const dmnsn_canvas *canvas, FILE *file)
png_uint_32 width, height;
unsigned int x, y;
uint16_t *row = NULL;
- dmnsn_pixel *pixel;
+ dmnsn_color *pixel;
+ dmnsn_sRGB sRGB;
width = canvas->x;
height = canvas->y;
@@ -80,16 +81,27 @@ dmnsn_png_write_canvas(const dmnsn_canvas *canvas, FILE *file)
for (x = 0; x < width; ++x) {
/* Invert the rows. PNG coordinates are fourth quadrant. */
pixel = canvas->pixels + (height - y - 1)*width + x;
+ sRGB = dmnsn_sRGB_from_color(*pixel);
- row[4*x] = pixel->r;
- row[4*x + 1] = pixel->g;
- row[4*x + 2] = pixel->b;
+ if (sRGB.R < 1.0) {
+ row[4*x] = sRGB.R*UINT16_MAX;
+ } else {
+ row[4*x] = UINT16_MAX;
+ }
- if (pixel->a > pixel->t) {
- row[4*x + 3] = pixel->a;
+ if (sRGB.G < 1.0) {
+ row[4*x + 1] = sRGB.G*UINT16_MAX;
+ } else {
+ row[4*x + 1] = UINT16_MAX;
+ }
+
+ if (sRGB.B < 1.0) {
+ row[4*x + 2] = sRGB.B*UINT16_MAX;
} else {
- row[4*x + 3] = pixel->t;
+ row[4*x + 2] = UINT16_MAX;
}
+
+ row[4*x + 3] = (pixel->filter + pixel->trans)*UINT16_MAX;
}
png_write_row(png_ptr, (png_bytep)row);
}
@@ -112,7 +124,8 @@ dmnsn_png_read_canvas(dmnsn_canvas **canvas, FILE *file)
png_bytep image = NULL;
png_bytep *row_pointers = NULL;
unsigned int x, y;
- dmnsn_pixel *pixel;
+ dmnsn_color *pixel;
+ dmnsn_sRGB sRGB;
png_bytep png_pixel;
/* Ensure that *canvas can always be deleted. */
@@ -203,11 +216,18 @@ dmnsn_png_read_canvas(dmnsn_canvas **canvas, FILE *file)
pixel = (*canvas)->pixels + (height - y - 1)*width + x;
png_pixel = image + 8*(y*width + x);
- pixel->r = (png_pixel[0] << UINT16_C(8)) + png_pixel[1];
- pixel->g = (png_pixel[2] << UINT16_C(8)) + png_pixel[3];
- pixel->b = (png_pixel[4] << UINT16_C(8)) + png_pixel[5];
- pixel->a = (png_pixel[6] << UINT16_C(8)) + png_pixel[7];
- pixel->t = 0;
+ sRGB.R = ((double)((png_pixel[0] << UINT16_C(8)) + png_pixel[1]))
+ /UINT16_MAX;
+ sRGB.G = ((double)((png_pixel[2] << UINT16_C(8)) + png_pixel[3]))
+ /UINT16_MAX;
+ sRGB.B = ((double)((png_pixel[4] << UINT16_C(8)) + png_pixel[5]))
+ /UINT16_MAX;
+
+ *pixel = dmnsn_color_from_sRGB(sRGB);
+
+ pixel->filter = 0.0;
+ pixel->trans = ((double)((png_pixel[6] << UINT16_C(8))
+ + png_pixel[7]))/UINT16_MAX;
}
}
} else {
@@ -216,11 +236,17 @@ dmnsn_png_read_canvas(dmnsn_canvas **canvas, FILE *file)
pixel = (*canvas)->pixels + (height - y - 1)*width + x;
png_pixel = image + 6*(y*width + x);
- pixel->r = (png_pixel[0] << UINT16_C(8)) + png_pixel[1];
- pixel->g = (png_pixel[2] << UINT16_C(8)) + png_pixel[3];
- pixel->b = (png_pixel[4] << UINT16_C(8)) + png_pixel[5];
- pixel->a = 0;
- pixel->t = 0;
+ sRGB.R = ((double)((png_pixel[0] << UINT16_C(8)) + png_pixel[1]))
+ /UINT16_MAX;
+ sRGB.G = ((double)((png_pixel[2] << UINT16_C(8)) + png_pixel[3]))
+ /UINT16_MAX;
+ sRGB.B = ((double)((png_pixel[4] << UINT16_C(8)) + png_pixel[5]))
+ /UINT16_MAX;
+
+ *pixel = dmnsn_color_from_sRGB(sRGB);
+
+ pixel->filter = 0.0;
+ pixel->trans = 0.0;
}
}
}
@@ -232,11 +258,14 @@ dmnsn_png_read_canvas(dmnsn_canvas **canvas, FILE *file)
pixel = (*canvas)->pixels + (height - y - 1)*width + x;
png_pixel = image + 4*(y*width + x);
- pixel->r = png_pixel[0] << UINT16_C(8);
- pixel->g = png_pixel[1] << UINT16_C(8);
- pixel->b = png_pixel[2] << UINT16_C(8);
- pixel->a = png_pixel[3] << UINT16_C(8);
- pixel->t = 0;
+ sRGB.R = ((double)png_pixel[0])/UINT8_MAX;
+ sRGB.G = ((double)png_pixel[1])/UINT8_MAX;
+ sRGB.B = ((double)png_pixel[2])/UINT8_MAX;
+
+ *pixel = dmnsn_color_from_sRGB(sRGB);
+
+ pixel->filter = 0.0;
+ pixel->trans = ((double)png_pixel[3])/UINT8_MAX;
}
}
} else {
@@ -245,11 +274,14 @@ dmnsn_png_read_canvas(dmnsn_canvas **canvas, FILE *file)
pixel = (*canvas)->pixels + (height - y - 1)*width + x;
png_pixel = image + 3*(y*width + x);
- pixel->r = png_pixel[0] << UINT16_C(8);
- pixel->g = png_pixel[1] << UINT16_C(8);
- pixel->b = png_pixel[2] << UINT16_C(8);
- pixel->a = 0;
- pixel->t = 0;
+ sRGB.R = ((double)png_pixel[0])/UINT8_MAX;
+ sRGB.G = ((double)png_pixel[1])/UINT8_MAX;
+ sRGB.B = ((double)png_pixel[2])/UINT8_MAX;
+
+ *pixel = dmnsn_color_from_sRGB(sRGB);
+
+ pixel->filter = 0.0;
+ pixel->trans = 0.0;
}
}
}
diff --git a/libdimension/Makefile.am b/libdimension/Makefile.am
index 293093c..9338b38 100644
--- a/libdimension/Makefile.am
+++ b/libdimension/Makefile.am
@@ -21,5 +21,5 @@ nobase_include_HEADERS = dimension.h dimension/geometry.h dimension/color.h dime
lib_LTLIBRARIES = libdimension.la
-libdimension_la_SOURCES = dimension.h dimension/geometry.h dimension/color.h dimension/canvas.h canvas.c
+libdimension_la_SOURCES = dimension.h dimension/geometry.h dimension/color.h dimension/canvas.h canvas.c color.c inlines.c
libdimension_la_LDFLAGS = -version-info 0:0:0
diff --git a/libdimension/canvas.c b/libdimension/canvas.c
index 4e3f89f..21d6d86 100644
--- a/libdimension/canvas.c
+++ b/libdimension/canvas.c
@@ -19,169 +19,7 @@
*************************************************************************/
#include "dimension.h"
-#include <math.h>
-#include <stdlib.h>
-
-/* Conversions between CIE 1931 XYZ and sRGB color. */
-
-dmnsn_pixel
-dmnsn_pixel_from_color(dmnsn_color color)
-{
- double X, Y, Z; /* CIE XYZ values */
- double Rlinear, Glinear, Blinear; /* Linear RGB values - no gamma */
- double R, G, B; /* sRGB values */
- dmnsn_pixel pixel;
-
- /* Convert from CIE xyY to CIE XYZ */
- Y = color.Y;
- X = Y*color.x/color.y;
- Z = Y*(1.0 - color.x - color.y)/color.y;
-
- /*
- * First, the linear conversion. Expressed as matrix multiplication, it looks
- * like this:
- *
- * [Rlinear] [ 3.2410 -1.5374 -0.4986] [X]
- * [Glinear] = [-0.9692 1.8760 0.0416]*[Y]
- * [Blinear] [ 0.0556 -0.2040 1.0570] [Z]
- */
- Rlinear = 3.2410*X - 1.5374*Y - 0.4986*Z;
- Glinear = -0.9692*X + 1.8760*Y + 0.0416*Z;
- Blinear = 0.0556*X - 0.2040*Y + 1.0570*Z;
-
- /*
- * If C represents R, G, and B, then the sRGB values are now found as follows:
- *
- * { 12.92*Clinear, Clinear <= 0.0031308
- * Csrgb = { 1/2.4
- * { (1.055)*Clinear - 0.055, Clinear > 0.0031308
- */
-
- if (Rlinear <= 0.0031308) {
- R = 12.92*Rlinear;
- } else {
- R = 1.055*pow(Rlinear, 1.0/2.4) - 0.055;
- }
-
- if (Glinear <= 0.0031308) {
- G = 12.92*Glinear;
- } else {
- G = 1.055*pow(Glinear, 1.0/2.4) - 0.055;
- }
-
- if (Blinear <= 0.0031308) {
- B = 12.92*Blinear;
- } else {
- B = 1.055*pow(Blinear, 1.0/2.4) - 0.055;
- }
-
- /* Now we go from unlimited to limited light, saturating at UINT16_MAX */
-
- if (R < 0.0) {
- pixel.r = 0.0;
- } else if (R > 1.0) {
- pixel.r = UINT16_MAX;
- } else {
- pixel.r = UINT16_MAX*R;
- }
-
- if (G < 0.0) {
- pixel.g = 0.0;
- } else if (G > 1.0) {
- pixel.g = UINT16_MAX;
- } else {
- pixel.g = UINT16_MAX*G;
- }
-
- if (B < 0.0) {
- pixel.b = 0.0;
- } else if (B > 1.0) {
- pixel.b = UINT16_MAX;
- } else {
- pixel.b = UINT16_MAX*B;
- }
-
- if (color.filter < 0.0) {
- pixel.a = 0.0;
- } else if (color.filter > 1.0) {
- pixel.a = UINT16_MAX;
- } else {
- pixel.a = UINT16_MAX*color.filter;
- }
-
- if (color.trans < 0.0) {
- pixel.t = 0.0;
- } else if (color.trans > 1.0) {
- pixel.t = UINT16_MAX;
- } else {
- pixel.t = UINT16_MAX*color.trans;
- }
-
- return pixel;
-}
-
-dmnsn_color
-dmnsn_color_from_pixel(dmnsn_pixel pixel)
-{
- double R, G, B; /* sRGB values */
- double Rlinear, Glinear, Blinear; /* Linear RGB values - no gamma */
- double X, Y, Z; /* CIE XYZ values */
- dmnsn_color color;
-
- /* Conversion back to unlimited light */
- R = ((double)pixel.r)/UINT16_MAX;
- G = ((double)pixel.g)/UINT16_MAX;
- B = ((double)pixel.b)/UINT16_MAX;
-
- /*
- * If C represents R, G, and B, then the Clinear values are now found as
- * follows:
- *
- * { Csrgb/12.92, Csrgb <= 0.04045
- * Clinear = { 1/2.4
- * { ((Csrgb + 0.055)/1.055) , Csrgb > 0.04045
- */
-
- if (R <= 0.04045) {
- Rlinear = R/19.92;
- } else {
- Rlinear = pow((R + 0.055)/1.055, 2.4);
- }
-
- if (G <= 0.04045) {
- Glinear = G/19.92;
- } else {
- Glinear = pow((G + 0.055)/1.055, 2.4);
- }
-
- if (B <= 0.04045) {
- Blinear = B/19.92;
- } else {
- Blinear = pow((B + 0.055)/1.055, 2.4);
- }
-
- /*
- * Now, the linear conversion. Expressed as matrix multiplication, it looks
- * like this:
- *
- * [X] [0.4124 0.3576 0.1805] [Rlinear]
- * [Y] = [0.2126 0.7152 0.0722]*[Glinear]
- * [X] [0.0193 0.1192 0.9505] [Blinear]
- */
-
- X = 0.4124*Rlinear + 0.3576*Glinear + 0.1805*Blinear;
- Y = 0.2126*Rlinear + 0.7152*Glinear + 0.0722*Blinear;
- Z = 0.0193*Rlinear + 0.1192*Glinear + 0.9505*Blinear;
-
- /* Now convert to CIE xyY colorspace */
- color.x = X/(X + Y + Z);
- color.y = Y/(X + Y + Z);
- color.Y = Y;
- color.filter = ((double)pixel.a)/UINT16_MAX;
- color.trans = ((double)pixel.t)/UINT16_MAX;
-
- return color;
-}
+#include <stdlib.h> /* For malloc(), free() */
dmnsn_canvas *
dmnsn_new_canvas(unsigned int x, unsigned int y)
@@ -191,7 +29,7 @@ dmnsn_new_canvas(unsigned int x, unsigned int y)
if (canvas) {
canvas->x = x;
canvas->y = y;
- canvas->pixels = malloc(sizeof(dmnsn_pixel)*x*y);
+ canvas->pixels = malloc(sizeof(dmnsn_color)*x*y);
if (canvas->pixels) {
return canvas;
diff --git a/libdimension/color.c b/libdimension/color.c
new file mode 100644
index 0000000..bbde48d
--- /dev/null
+++ b/libdimension/color.c
@@ -0,0 +1,259 @@
+/*************************************************************************
+ * Copyright (C) 2008 Tavian Barnes <tavianator@gmail.com> *
+ * *
+ * This file is part of Dimension. *
+ * *
+ * Dimension is free software; you can redistribute it and/or modify it *
+ * under the terms of the GNU Lesser General Public License as published *
+ * by the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * Dimension 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with this program. If not, see *
+ * <http://www.gnu.org/licenses/>. *
+ *************************************************************************/
+
+#include "dimension.h"
+#include <math.h> /* For pow() */
+
+dmnsn_CIE_XYZ whitepoint = { 0.9505, 1, 1.089 };
+
+dmnsn_color
+dmnsn_color_from_XYZ(dmnsn_CIE_XYZ XYZ)
+{
+ dmnsn_color ret = { XYZ.X, XYZ.Y, XYZ.Z, 0.0, 0.0 };
+ return ret;
+}
+
+dmnsn_color
+dmnsn_color_from_xyY(dmnsn_CIE_xyY xyY)
+{
+ dmnsn_color ret = { xyY.Y*xyY.x/xyY.y,
+ xyY.Y,
+ xyY.Y*(1.0 - xyY.x - xyY.Y)/xyY.y,
+ 0.0, 0.0 };
+ return ret;
+}
+
+static double Lab_finv(double t) {
+ if (t > 6.0/29.0) {
+ return t*t*t;
+ } else {
+ return 108.0*(t - 16.0/116.0)/841.0;
+ }
+}
+
+dmnsn_color
+dmnsn_color_from_Lab(dmnsn_CIE_Lab Lab, dmnsn_CIE_XYZ white)
+{
+ double fx, fy, fz;
+ dmnsn_color ret;
+
+ fy = (Lab.L + 16.0)/116.0;
+ fx = fy + Lab.a/500.0;
+ fz = fy - Lab.b/200.0;
+
+ ret.X = white.X*Lab_finv(fx);
+ ret.Y = white.Y*Lab_finv(fy);
+ ret.Z = white.Z*Lab_finv(fz);
+
+ return ret;
+}
+
+dmnsn_color
+dmnsn_color_from_Luv(dmnsn_CIE_Luv Luv, dmnsn_CIE_XYZ white)
+{
+ double fy;
+ double uprime, unprime, vprime, vnprime;
+ dmnsn_color ret;
+
+ fy = (Luv.L + 16.0)/116.0;
+
+ unprime = 4.0*white.X/(white.X + 15.0*white.Y + 3.0*white.Z);
+ uprime = Luv.u/Luv.L/13.0 + unprime;
+ vnprime = 9.0*white.Y/(white.X + 15.0*white.Y + 3.0*white.Z);
+ vprime = Luv.v/Luv.L/13.0 + vnprime;
+
+ ret.Y = white.Y*Lab_finv(fy);
+ ret.X = ret.Y*9.0*uprime/vprime/4.0;
+ ret.Z = ret.Y*(12.0 - 3*uprime - 20*vprime)/vprime/4.0;
+
+ return ret;
+}
+
+dmnsn_color
+dmnsn_color_from_sRGB(dmnsn_sRGB sRGB)
+{
+ double Rlinear, Glinear, Blinear; /* Linear RGB values - no gamma */
+ dmnsn_color ret;
+
+ /*
+ * If C represents R, G, and B, then the Clinear values are now found as
+ * follows:
+ *
+ * { Csrgb/12.92, Csrgb <= 0.04045
+ * Clinear = { 1/2.4
+ * { ((Csrgb + 0.055)/1.055) , Csrgb > 0.04045
+ */
+
+ if (sRGB.R <= 0.04045) {
+ Rlinear = sRGB.R/19.92;
+ } else {
+ Rlinear = pow((sRGB.R + 0.055)/1.055, 2.4);
+ }
+
+ if (sRGB.G <= 0.04045) {
+ Glinear = sRGB.G/19.92;
+ } else {
+ Glinear = pow((sRGB.G + 0.055)/1.055, 2.4);
+ }
+
+ if (sRGB.B <= 0.04045) {
+ Blinear = sRGB.B/19.92;
+ } else {
+ Blinear = pow((sRGB.B + 0.055)/1.055, 2.4);
+ }
+
+ /*
+ * Now, the linear conversion. Expressed as matrix multiplication, it looks
+ * like this:
+ *
+ * [X] [0.4124 0.3576 0.1805] [Rlinear]
+ * [Y] = [0.2126 0.7152 0.0722]*[Glinear]
+ * [X] [0.0193 0.1192 0.9505] [Blinear]
+ */
+
+ ret.X = 0.4124*Rlinear + 0.3576*Glinear + 0.1805*Blinear;
+ ret.Y = 0.2126*Rlinear + 0.7152*Glinear + 0.0722*Blinear;
+ ret.Z = 0.0193*Rlinear + 0.1192*Glinear + 0.9505*Blinear;
+ ret.filter = 0.0;
+ ret.trans = 0.0;
+
+ return ret;
+}
+
+dmnsn_CIE_XYZ
+dmnsn_XYZ_from_color(dmnsn_color color)
+{
+ dmnsn_CIE_XYZ ret = { color.X, color.Y, color.Z };
+ return ret;
+}
+
+dmnsn_CIE_xyY
+dmnsn_xyY_from_color(dmnsn_color color)
+{
+ dmnsn_CIE_xyY ret = { color.X/(color.X + color.Y + color.Z),
+ color.Y/(color.X + color.Y + color.Z),
+ color.Y };
+ return ret;
+}
+
+static double Lab_f(double t) {
+ if (t > 216.0/24389.0) {
+ return pow(t, 1.0/3.0);
+ } else {
+ return 841.0*t/108.0 + 4.0/29.0;
+ }
+}
+
+dmnsn_CIE_Lab
+dmnsn_Lab_from_color(dmnsn_color color, dmnsn_CIE_XYZ white)
+{
+ dmnsn_CIE_Lab ret;
+
+ ret.L = 116.0*Lab_f(color.Y/white.Y) - 16.0;
+ ret.a = 500.0*(Lab_f(color.X/white.X) - Lab_f(color.Y/white.Y));
+ ret.b = 200.0*(Lab_f(color.Y/white.Y) - Lab_f(color.Z/white.Z));
+
+ return ret;
+}
+
+dmnsn_CIE_Luv
+dmnsn_Luv_from_color(dmnsn_color color, dmnsn_CIE_XYZ white)
+{
+ double uprime, unprime, vprime, vnprime;
+ dmnsn_CIE_Luv ret;
+
+ uprime = 4.0*color.X/(color.X + 15.0*color.Y + 3.0*color.Z);
+ unprime = 4.0*white.X/(white.X + 15.0*white.Y + 3.0*white.Z);
+ vprime = 9.0*color.Y/(color.X + 15.0*color.Y + 3.0*color.Z);
+ vnprime = 9.0*white.Y/(white.X + 15.0*white.Y + 3.0*white.Z);
+
+ ret.L = 116.0*Lab_f(color.Y/white.Y) - 16.0;
+ ret.u = 13.0*ret.L*(uprime - unprime);
+ ret.v = 13.0*ret.L*(vprime - vnprime);
+
+ return ret;
+}
+
+dmnsn_sRGB
+dmnsn_sRGB_from_color(dmnsn_color color)
+{
+ double Rlinear, Glinear, Blinear; /* Linear RGB values - no gamma */
+ dmnsn_sRGB ret;
+
+ /*
+ * First, the linear conversion. Expressed as matrix multiplication, it looks
+ * like this:
+ *
+ * [Rlinear] [ 3.2410 -1.5374 -0.4986] [X]
+ * [Glinear] = [-0.9692 1.8760 0.0416]*[Y]
+ * [Blinear] [ 0.0556 -0.2040 1.0570] [Z]
+ */
+ Rlinear = 3.2410*color.X - 1.5374*color.Y - 0.4986*color.Z;
+ Glinear = -0.9692*color.X + 1.8760*color.Y + 0.0416*color.Z;
+ Blinear = 0.0556*color.X - 0.2040*color.Y + 1.0570*color.Z;
+
+ /*
+ * If C represents R, G, and B, then the sRGB values are now found as follows:
+ *
+ * { 12.92*Clinear, Clinear <= 0.0031308
+ * Csrgb = { 1/2.4
+ * { (1.055)*Clinear - 0.055, Clinear > 0.0031308
+ */
+
+ if (Rlinear <= 0.0031308) {
+ ret.R = 12.92*Rlinear;
+ } else {
+ ret.R = 1.055*pow(Rlinear, 1.0/2.4) - 0.055;
+ }
+
+ if (Glinear <= 0.0031308) {
+ ret.G = 12.92*Glinear;
+ } else {
+ ret.G = 1.055*pow(Glinear, 1.0/2.4) - 0.055;
+ }
+
+ if (Blinear <= 0.0031308) {
+ ret.B = 12.92*Blinear;
+ } else {
+ ret.B = 1.055*pow(Blinear, 1.0/2.4) - 0.055;
+ }
+
+ return ret;
+}
+
+dmnsn_color
+dmnsn_color_add(dmnsn_color color1, dmnsn_color color2)
+{
+ dmnsn_CIE_Lab Lab, Lab1, Lab2;
+ dmnsn_color ret;
+
+ Lab1 = dmnsn_Lab_from_color(color1, whitepoint);
+ Lab2 = dmnsn_Lab_from_color(color2, whitepoint);
+
+ Lab.L = Lab1.L + Lab2.L;
+ Lab.a = (Lab1.L*Lab1.a + Lab2.L*Lab2.a)/Lab.L;
+ Lab.b = (Lab1.L*Lab1.b + Lab2.L*Lab2.b)/Lab.L;
+
+ ret = dmnsn_color_from_Lab(Lab, whitepoint);
+ ret.filter = (Lab1.L*color1.filter + Lab2.L*color2.filter)/Lab.L;
+ ret.trans = (Lab1.L*color1.trans + Lab2.L*color2.trans)/Lab.L;
+
+ return ret;
+}
diff --git a/libdimension/dimension.h b/libdimension/dimension.h
index 558c9ed..c52ae31 100644
--- a/libdimension/dimension.h
+++ b/libdimension/dimension.h
@@ -28,6 +28,10 @@
extern "C" {
#endif
+#ifndef DMNSN_INLINE
+#define DMNSN_INLINE extern inline
+#endif
+
#include <dimension/geometry.h>
#include <dimension/color.h>
#include <dimension/canvas.h>
diff --git a/libdimension/dimension/canvas.h b/libdimension/dimension/canvas.h
index 8959e12..21d56e4 100644
--- a/libdimension/dimension/canvas.h
+++ b/libdimension/dimension/canvas.h
@@ -25,15 +25,6 @@
#ifndef DIMENSION_CANVAS_H
#define DIMENSION_CANVAS_H
-/* 48-bit sRGB color for pixels. */
-typedef struct {
- uint16_t r, g, b; /* Red, green, blue */
- uint16_t a, t; /* Filtered transparancy, normal transparancy */
-} dmnsn_pixel;
-
-dmnsn_pixel dmnsn_pixel_from_color(dmnsn_color color);
-dmnsn_color dmnsn_color_from_pixel(dmnsn_pixel pixel);
-
typedef struct {
unsigned int x, y;
@@ -41,7 +32,7 @@ typedef struct {
* Stored in first-quadrant representation (origin is bottom-left). The pixel
* at (a,b) is accessible as pixels[b*x + a].
*/
- dmnsn_pixel *pixels;
+ dmnsn_color *pixels;
} dmnsn_canvas;
dmnsn_canvas *dmnsn_new_canvas(unsigned int x, unsigned int y);
diff --git a/libdimension/dimension/color.h b/libdimension/dimension/color.h
index 34668e3..d637a81 100644
--- a/libdimension/dimension/color.h
+++ b/libdimension/dimension/color.h
@@ -29,16 +29,57 @@
extern "C" {
#endif
-/* CIE 1931 xyY color. */
+/* Internally, we use CIE 1931 XYZ color. */
+typedef struct {
+ double X, Y, Z;
+ double filter, trans; /* Filter transparancy only lets light of this color
+ through; regular transparancy lets all colors
+ through. filter + trans should be <= 1.0. */
+} dmnsn_color;
+
+typedef struct {
+ double X, Y, Z; /* X, Y, and Z are tristimulus values, unbounded above zero.
+ Diffuse white is (0.9505, 1, 1.089). */
+} dmnsn_CIE_XYZ;
+
typedef struct {
double x, y, Y; /* x and y are chromaticity coordinates, and Y is luminance,
in the CIE 1931 xyZ color space. We use an unlimited light
model, so x,y in [0, 1] and Y >= 0, with 1 = diffuse
white */
- double filter, trans; /* Filter transparancy only lets light of this color
- through; regular transparancy lets all colors
- through */
-} dmnsn_color;
+} dmnsn_CIE_xyY;
+
+typedef struct {
+ double L, a, b; /* L is luminence (100 = diffuse white); a and b are color-
+ opponent dimensions. This color space is used for color
+ arithmetic. */
+} dmnsn_CIE_Lab;
+
+typedef struct {
+ double L, u, v; /* L is luminence (100 = diffuse white); u and v are
+ chromaticity coordinates. */
+} dmnsn_CIE_Luv;
+
+typedef struct {
+ double R, G, B; /* sRGB R, G, and B values */
+} dmnsn_sRGB;
+
+/* Standard whitepoint, determined by the conversion of sRGB white to XYZ */
+extern dmnsn_CIE_XYZ whitepoint;
+
+dmnsn_color dmnsn_color_from_XYZ(dmnsn_CIE_XYZ XYZ);
+dmnsn_color dmnsn_color_from_xyY(dmnsn_CIE_xyY xyY);
+dmnsn_color dmnsn_color_from_Lab(dmnsn_CIE_Lab Lab, dmnsn_CIE_XYZ white);
+dmnsn_color dmnsn_color_from_Luv(dmnsn_CIE_Luv Luv, dmnsn_CIE_XYZ white);
+dmnsn_color dmnsn_color_from_sRGB(dmnsn_sRGB sRGB);
+
+dmnsn_CIE_XYZ dmnsn_XYZ_from_color(dmnsn_color color);
+dmnsn_CIE_xyY dmnsn_xyY_from_color(dmnsn_color color);
+dmnsn_CIE_Lab dmnsn_Lab_from_color(dmnsn_color color, dmnsn_CIE_XYZ white);
+dmnsn_CIE_Luv dmnsn_Luv_from_color(dmnsn_color color, dmnsn_CIE_XYZ white);
+dmnsn_sRGB dmnsn_sRGB_from_color(dmnsn_color color);
+
+dmnsn_color dmnsn_color_add(dmnsn_color color1, dmnsn_color color2);
#ifdef __cplusplus
}
diff --git a/libdimension/dimension/geometry.h b/libdimension/dimension/geometry.h
index 2c0c204..11a6daf 100644
--- a/libdimension/dimension/geometry.h
+++ b/libdimension/dimension/geometry.h
@@ -31,44 +31,44 @@ typedef struct { dmnsn_scalar x, y, z; } dmnsn_vector;
/* Vector arithmetic */
-inline dmnsn_vector
+DMNSN_INLINE dmnsn_vector
dmnsn_vector_construct(dmnsn_scalar x, dmnsn_scalar y, dmnsn_scalar z)
{
dmnsn_vector v = { .x = x, .y = y, .z = z };
return v;
}
-inline dmnsn_vector
+DMNSN_INLINE dmnsn_vector
dmnsn_vector_add(dmnsn_vector lhs, dmnsn_vector rhs)
{
return dmnsn_vector_construct(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z);
}
-inline dmnsn_vector
+DMNSN_INLINE dmnsn_vector
dmnsn_vector_sub(dmnsn_vector lhs, dmnsn_vector rhs)
{
return dmnsn_vector_construct(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z);
}
-inline dmnsn_vector
+DMNSN_INLINE dmnsn_vector
dmnsn_vector_mul(dmnsn_scalar lhs, dmnsn_vector rhs)
{
return dmnsn_vector_construct(lhs*rhs.x, lhs*rhs.y, lhs*rhs.z);
}
-inline dmnsn_vector
+DMNSN_INLINE dmnsn_vector
dmnsn_vector_div(dmnsn_vector lhs, dmnsn_scalar rhs)
{
return dmnsn_vector_construct(lhs.x/rhs, lhs.y/rhs, lhs.z/rhs);
}
-inline dmnsn_scalar
+DMNSN_INLINE dmnsn_scalar
dmnsn_vector_dot(dmnsn_vector lhs, dmnsn_vector rhs)
{
return lhs.x*rhs.x + lhs.y*rhs.y + lhs.z*rhs.z;
}
-inline dmnsn_vector
+DMNSN_INLINE dmnsn_vector
dmnsn_vector_cross(dmnsn_vector lhs, dmnsn_vector rhs)
{
return dmnsn_vector_construct(lhs.y*rhs.z - lhs.z*rhs.y,
@@ -83,7 +83,7 @@ typedef struct {
} dmnsn_line;
/* A point on a line, defined by x0 + t*n */
-inline dmnsn_vector
+DMNSN_INLINE dmnsn_vector
dmnsn_line_point(dmnsn_line l, dmnsn_scalar t)
{
return dmnsn_vector_add(l.x0, dmnsn_vector_mul(t, l.n));
diff --git a/libdimension/inlines.c b/libdimension/inlines.c
new file mode 100644
index 0000000..51348fa
--- /dev/null
+++ b/libdimension/inlines.c
@@ -0,0 +1,22 @@
+/*************************************************************************
+ * Copyright (C) 2008 Tavian Barnes <tavianator@gmail.com> *
+ * *
+ * This file is part of Dimension. *
+ * *
+ * Dimension is free software; you can redistribute it and/or modify it *
+ * under the terms of the GNU Lesser General Public License as published *
+ * by the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * Dimension 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with this program. If not, see *
+ * <http://www.gnu.org/licenses/>. *
+ *************************************************************************/
+
+#define DMNSN_INLINE
+#include "dimension.h"
diff --git a/tests/png.c b/tests/png.c
index e16ee8e..93cb266 100644
--- a/tests/png.c
+++ b/tests/png.c
@@ -6,6 +6,7 @@
int main() {
dmnsn_canvas *canvas;
+ dmnsn_sRGB sRGB;
FILE *ofile, *ifile;
unsigned int x, y;
@@ -15,37 +16,40 @@ int main() {
return EXIT_FAILURE;
}
- ofile = fopen("dimension.png", "wb");
+ ofile = fopen("dimension1.png", "wb");
if (!ofile) {
- fprintf(stderr, "--- Opening 'dimension.png' for writing failed! ---\n");
+ fprintf(stderr, "--- Opening 'dimension1.png' for writing failed! ---\n");
return EXIT_FAILURE;
}
for (x = 0; x < canvas->x; ++x) {
for (y = 0; y < canvas->y; ++y) {
if (x < canvas->x/3) {
- canvas->pixels[y*canvas->x + x].r = UINT16_MAX;
- canvas->pixels[y*canvas->x + x].g = 0;
- canvas->pixels[y*canvas->x + x].b = 0;
+ sRGB.R = 1.0;
+ sRGB.G = 0.0;
+ sRGB.B = 0.0;
+ canvas->pixels[y*canvas->x + x] = dmnsn_color_from_sRGB(sRGB);
} else if (x < 2*canvas->x/3) {
- canvas->pixels[y*canvas->x + x].r = 0;
- canvas->pixels[y*canvas->x + x].g = UINT16_MAX;
- canvas->pixels[y*canvas->x + x].b = 0;
+ sRGB.R = 0.0;
+ sRGB.G = 1.0;
+ sRGB.B = 0.0;
+ canvas->pixels[y*canvas->x + x] = dmnsn_color_from_sRGB(sRGB);
} else {
- canvas->pixels[y*canvas->x + x].r = 0;
- canvas->pixels[y*canvas->x + x].g = 0;
- canvas->pixels[y*canvas->x + x].b = UINT16_MAX;
+ sRGB.R = 0.0;
+ sRGB.G = 0.0;
+ sRGB.B = 1.0;
+ canvas->pixels[y*canvas->x + x] = dmnsn_color_from_sRGB(sRGB);
}
if (y < canvas->y/3) {
- canvas->pixels[y*canvas->x + x].a = 0;
- canvas->pixels[y*canvas->x + x].t = 0;
+ canvas->pixels[y*canvas->x + x].filter = 0.0;
+ canvas->pixels[y*canvas->x + x].trans = 0.0;
} else if (y < 2*canvas->y/3) {
- canvas->pixels[y*canvas->x + x].a = UINT16_MAX/4;
- canvas->pixels[y*canvas->x + x].t = 0;
+ canvas->pixels[y*canvas->x + x].filter = 1.0/3.0;
+ canvas->pixels[y*canvas->x + x].trans = 0.0;
} else {
- canvas->pixels[y*canvas->x + x].a = UINT16_MAX/4;
- canvas->pixels[y*canvas->x + x].t = UINT16_MAX/2;
+ canvas->pixels[y*canvas->x + x].filter = 1.0/3.0;
+ canvas->pixels[y*canvas->x + x].trans = 1.0/3.0;
}
}
}
@@ -58,9 +62,9 @@ int main() {
dmnsn_delete_canvas(canvas);
fclose(ofile);
- ifile = fopen("dimension.png", "rb");
+ ifile = fopen("dimension1.png", "rb");
if (!ifile) {
- fprintf(stderr, "--- Opening 'dimension.png' for reading failed! ---\n");
+ fprintf(stderr, "--- Opening 'dimension1.png' for reading failed! ---\n");
return EXIT_FAILURE;
}
@@ -70,9 +74,9 @@ int main() {
}
fclose(ifile);
- ofile = fopen("dimension.png", "wb");
+ ofile = fopen("dimension2.png", "wb");
if (!ofile) {
- fprintf(stderr, "--- Opening 'dimension.png' for writing failed! ---\n");
+ fprintf(stderr, "--- Opening 'dimension2.png' for writing failed! ---\n");
return EXIT_FAILURE;
}