summaryrefslogtreecommitdiffstats
path: root/libdimension
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@gmail.com>2011-05-28 18:05:05 -0600
committerTavian Barnes <tavianator@gmail.com>2011-05-28 18:05:05 -0600
commitefd3c1c2b42ffda0c8f7e5cd9b03fba07eead1ea (patch)
treec522f2bafe5def3eb68a79005c049e130ef76b24 /libdimension
parent1bf306d4d93cc21c220a3f31835023e49e84dd2d (diff)
downloaddimension-efd3c1c2b42ffda0c8f7e5cd9b03fba07eead1ea.tar.xz
Handle sRGB gamma correctly.
Diffstat (limited to 'libdimension')
-rw-r--r--libdimension/color.c95
-rw-r--r--libdimension/color_map.c17
-rw-r--r--libdimension/dimension/color.h4
-rw-r--r--libdimension/dimension/pigments.h14
-rw-r--r--libdimension/gl.c9
-rw-r--r--libdimension/pigment_map.c17
-rw-r--r--libdimension/png.c6
-rw-r--r--libdimension/tests/render.c24
8 files changed, 162 insertions, 24 deletions
diff --git a/libdimension/color.c b/libdimension/color.c
index 6de8d0a..eea005b 100644
--- a/libdimension/color.c
+++ b/libdimension/color.c
@@ -77,7 +77,7 @@ const dmnsn_color dmnsn_magenta = {
};
const dmnsn_color dmnsn_orange = {
.R = 1.0,
- .G = 0.5,
+ .G = 0.21404114048223255,
.B = 0.0,
.trans = 0.0,
.filter = 0.0,
@@ -97,6 +97,75 @@ const dmnsn_color dmnsn_cyan = {
.trans = 0.0,
};
+/** Inverse function of sRGB's `C' function, for the reverse conversion. */
+static double
+dmnsn_sRGB_C_inv(double CsRGB)
+{
+ /*
+ * 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 (CsRGB == 1.0) {
+ return 1.0; /* Map 1.0 to 1.0 instead of 0.9999999999999999 */
+ } else if (CsRGB <= 0.040449936) {
+ return CsRGB/12.92;
+ } else {
+ return pow((CsRGB + 0.055)/1.055, 2.4);
+ }
+}
+
+/* Convert from sRGB space */
+dmnsn_color
+dmnsn_color_from_sRGB(dmnsn_color color)
+{
+ dmnsn_color ret = {
+ .R = dmnsn_sRGB_C_inv(color.R),
+ .G = dmnsn_sRGB_C_inv(color.G),
+ .B = dmnsn_sRGB_C_inv(color.B),
+ .trans = color.trans,
+ .filter = color.filter,
+ };
+ return ret;
+}
+
+/** sRGB's `C' function. */
+static double
+dmnsn_sRGB_C(double Clinear)
+{
+ /*
+ * 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 (Clinear == 1.0) {
+ return 1.0; /* Map 1.0 to 1.0 instead of 0.9999999999999999 */
+ } else if (Clinear <= 0.0031308) {
+ return 12.92*Clinear;
+ } else {
+ return 1.055*pow(Clinear, 1.0/2.4) - 0.055;
+ }
+}
+
+/* Convert to sRGB space */
+dmnsn_color
+dmnsn_color_to_sRGB(dmnsn_color color)
+{
+ dmnsn_color ret = {
+ .R = dmnsn_sRGB_C(color.R),
+ .G = dmnsn_sRGB_C(color.G),
+ .B = dmnsn_sRGB_C(color.B),
+ .trans = color.trans,
+ .filter = color.filter,
+ };
+ return ret;
+}
+
/* Greyscale color intensity */
double
dmnsn_color_intensity(dmnsn_color color)
@@ -111,7 +180,7 @@ dmnsn_color_add(dmnsn_color c1, dmnsn_color c2)
{
dmnsn_color ret = dmnsn_new_color(c1.R + c2.R, c1.G + c2.G, c1.B + c2.B);
- /* Switch back into absolute filter and transmittance space */
+ /* Switch into absolute filter and transmittance space */
double n1 = dmnsn_color_intensity(c1), n2 = dmnsn_color_intensity(c2);
double f1 = c1.filter*c1.trans, f2 = c2.filter*c2.trans;
double t1 = c1.trans - f1, t2 = c2.trans - f2;
@@ -142,13 +211,25 @@ dmnsn_color_mul(double n, dmnsn_color color)
dmnsn_color
dmnsn_color_gradient(dmnsn_color c1, dmnsn_color c2, double n)
{
- return dmnsn_new_color5(
+ dmnsn_color ret = dmnsn_new_color(
n*(c2.R - c1.R) + c1.R,
n*(c2.G - c1.G) + c1.G,
- n*(c2.B - c1.B) + c1.B,
- n*(c2.trans - c1.trans) + c1.trans,
- n*(c2.filter - c1.filter) + c1.filter
+ n*(c2.B - c1.B) + c1.B
);
+
+ /* Switch into absolute filter and transmittance space */
+ double f1 = c1.filter*c1.trans, f2 = c2.filter*c2.trans;
+ double t1 = c1.trans - f1, t2 = c2.trans - f2;
+ double f = n*(f2 - f1) + f1;
+ double t = n*(t2 - t1) + t1;
+
+ /* Switch back */
+ ret.trans = f + t;
+ ret.filter = 0.0;
+ if (ret.trans >= dmnsn_epsilon)
+ ret.filter = f/ret.trans;
+
+ return ret;
}
/* Filters `light' through `filter' */
@@ -165,7 +246,7 @@ dmnsn_filter_light(dmnsn_color light, dmnsn_color filter)
);
dmnsn_color ret = dmnsn_color_add(transmitted, filtered);
- /* Switch back into absolute filter and transmittance space */
+ /* Switch into absolute filter and transmittance space */
double lf = light.filter*light.trans, ff = filter.filter*filter.trans;
double lt = light.trans - lf, ft = filter.trans - ff;
double f = lf*(dmnsn_color_intensity(filtered) + ft) + lt*ff;
diff --git a/libdimension/color_map.c b/libdimension/color_map.c
index 6390fe5..e7a25a6 100644
--- a/libdimension/color_map.c
+++ b/libdimension/color_map.c
@@ -35,6 +35,7 @@ dmnsn_new_color_map(void)
typedef struct dmnsn_color_map_payload {
dmnsn_pattern *pattern;
dmnsn_map *map;
+ dmnsn_pigment_map_flags flags;
} dmnsn_color_map_payload;
/** Free a color_map payload. */
@@ -56,7 +57,17 @@ dmnsn_color_map_pigment_fn(const dmnsn_pigment *pigment, dmnsn_vector v)
dmnsn_color color1, color2;
dmnsn_evaluate_map(payload->map, dmnsn_pattern_value(payload->pattern, v),
&n, &color1, &color2);
- return dmnsn_color_gradient(color1, color2, n);
+
+ if (payload->flags == DMNSN_PIGMENT_MAP_SRGB) {
+ color1 = dmnsn_color_to_sRGB(color1);
+ color2 = dmnsn_color_to_sRGB(color2);
+ }
+ dmnsn_color ret = dmnsn_color_gradient(color1, color2, n);
+ if (payload->flags == DMNSN_PIGMENT_MAP_SRGB) {
+ ret = dmnsn_color_from_sRGB(ret);
+ }
+
+ return ret;
}
/** color_map initialization callback. */
@@ -70,7 +81,8 @@ dmnsn_color_map_initialize_fn(dmnsn_pigment *pigment)
}
dmnsn_pigment *
-dmnsn_new_color_map_pigment(dmnsn_pattern *pattern, dmnsn_map *map)
+dmnsn_new_color_map_pigment(dmnsn_pattern *pattern, dmnsn_map *map,
+ dmnsn_pigment_map_flags flags)
{
dmnsn_pigment *pigment = dmnsn_new_pigment();
@@ -78,6 +90,7 @@ dmnsn_new_color_map_pigment(dmnsn_pattern *pattern, dmnsn_map *map)
= dmnsn_malloc(sizeof(dmnsn_color_map_payload));
payload->pattern = pattern;
payload->map = map;
+ payload->flags = flags;
pigment->pigment_fn = dmnsn_color_map_pigment_fn;
pigment->initialize_fn = dmnsn_color_map_initialize_fn;
diff --git a/libdimension/dimension/color.h b/libdimension/dimension/color.h
index 98d1530..d4e0402 100644
--- a/libdimension/dimension/color.h
+++ b/libdimension/dimension/color.h
@@ -77,6 +77,10 @@ dmnsn_color_is_black(dmnsn_color color)
/* Perceptual color manipulation */
+/** Convert from sRGB space. */
+dmnsn_color dmnsn_color_from_sRGB(dmnsn_color color);
+/** Convert to sRGB space. */
+dmnsn_color dmnsn_color_to_sRGB(dmnsn_color color);
/** Greyscale color intensity. */
double dmnsn_color_intensity(dmnsn_color color);
/** Add two colors together. */
diff --git a/libdimension/dimension/pigments.h b/libdimension/dimension/pigments.h
index 26b75f8..e62963e 100644
--- a/libdimension/dimension/pigments.h
+++ b/libdimension/dimension/pigments.h
@@ -45,13 +45,22 @@ dmnsn_pigment *dmnsn_new_canvas_pigment(dmnsn_canvas *canvas);
dmnsn_map *dmnsn_new_color_map(void);
/**
+ * pigment_map flags.
+ */
+typedef enum dmnsn_pigment_map_flags {
+ DMNSN_PIGMENT_MAP_REGULAR, /**< Calculate linear color gradients. */
+ DMNSN_PIGMENT_MAP_SRGB /**< Calculate sRGB color gradients. */
+} dmnsn_pigment_map_flags;
+
+/**
* A color-mapped pigment.
* @param[in,out] pattern The pattern of the pigment.
* @param[in,out] map The color map to apply to the pattern.
* @return A pigment mapping the pattern to color values.
*/
dmnsn_pigment *dmnsn_new_color_map_pigment(dmnsn_pattern *pattern,
- dmnsn_map *map);
+ dmnsn_map *map,
+ dmnsn_pigment_map_flags flags);
/**
* Construct a pigment map.
@@ -66,4 +75,5 @@ dmnsn_map *dmnsn_new_pigment_map(void);
* @return A pigment mapping the pattern to other pigments.
*/
dmnsn_pigment *dmnsn_new_pigment_map_pigment(dmnsn_pattern *pattern,
- dmnsn_map *map);
+ dmnsn_map *map,
+ dmnsn_pigment_map_flags flags);
diff --git a/libdimension/gl.c b/libdimension/gl.c
index f5f1c92..a968d75 100644
--- a/libdimension/gl.c
+++ b/libdimension/gl.c
@@ -82,7 +82,9 @@ dmnsn_gl_write_canvas(const dmnsn_canvas *canvas)
for (size_t x = 0; x < width; ++x) {
pixel = pixels + 4*(y*width + x);
- color = dmnsn_remove_filter(dmnsn_get_pixel(canvas, x, y));
+ color = dmnsn_get_pixel(canvas, x, y);
+ color = dmnsn_remove_filter(color);
+ color = dmnsn_color_to_sRGB(color);
/* Saturate R, G, and B to [0, UINT16_MAX] */
@@ -153,6 +155,7 @@ dmnsn_gl_read_canvas(size_t x0, size_t y0,
(double)pixel[2]/UINT16_MAX,
(double)pixel[3]/UINT16_MAX,
0.0);
+ color = dmnsn_color_from_sRGB(color);
dmnsn_set_pixel(canvas, x, y, color);
}
}
@@ -167,7 +170,9 @@ dmnsn_gl_optimizer_fn(const dmnsn_canvas *canvas,
dmnsn_canvas_optimizer optimizer, size_t x, size_t y)
{
GLushort *pixel = (GLushort *)optimizer.ptr + 4*(y*canvas->width + x);
- dmnsn_color color = dmnsn_remove_filter(dmnsn_get_pixel(canvas, x, y));
+ dmnsn_color color = dmnsn_get_pixel(canvas, x, y);
+ color = dmnsn_remove_filter(color);
+ color = dmnsn_color_to_sRGB(color);
/* Saturate R, G, and B to [0, UINT16_MAX] */
diff --git a/libdimension/pigment_map.c b/libdimension/pigment_map.c
index 9864152..e97553f 100644
--- a/libdimension/pigment_map.c
+++ b/libdimension/pigment_map.c
@@ -53,6 +53,7 @@ dmnsn_new_pigment_map(void)
typedef struct dmnsn_pigment_map_payload {
dmnsn_pattern *pattern;
dmnsn_map *map;
+ dmnsn_pigment_map_flags flags;
} dmnsn_pigment_map_payload;
/** Free a pigment_map payload. */
@@ -76,7 +77,17 @@ dmnsn_pigment_map_pigment_fn(const dmnsn_pigment *pigment, dmnsn_vector v)
&n, &pigment1, &pigment2);
dmnsn_color color1 = pigment1->pigment_fn(pigment1, v);
dmnsn_color color2 = pigment2->pigment_fn(pigment2, v);
- return dmnsn_color_gradient(color1, color2, n);
+
+ if (payload->flags == DMNSN_PIGMENT_MAP_SRGB) {
+ color1 = dmnsn_color_to_sRGB(color1);
+ color2 = dmnsn_color_to_sRGB(color2);
+ }
+ dmnsn_color ret = dmnsn_color_gradient(color1, color2, n);
+ if (payload->flags == DMNSN_PIGMENT_MAP_SRGB) {
+ ret = dmnsn_color_from_sRGB(ret);
+ }
+
+ return ret;
}
/** pigment_map initialization callback. */
@@ -91,7 +102,8 @@ dmnsn_pigment_map_initialize_fn(dmnsn_pigment *pigment)
}
dmnsn_pigment *
-dmnsn_new_pigment_map_pigment(dmnsn_pattern *pattern, dmnsn_map *map)
+dmnsn_new_pigment_map_pigment(dmnsn_pattern *pattern, dmnsn_map *map,
+ dmnsn_pigment_map_flags flags)
{
dmnsn_pigment *pigment = dmnsn_new_pigment();
@@ -99,6 +111,7 @@ dmnsn_new_pigment_map_pigment(dmnsn_pattern *pattern, dmnsn_map *map)
= dmnsn_malloc(sizeof(dmnsn_pigment_map_payload));
payload->pattern = pattern;
payload->map = map;
+ payload->flags = flags;
pigment->pigment_fn = dmnsn_pigment_map_pigment_fn;
pigment->initialize_fn = dmnsn_pigment_map_initialize_fn;
diff --git a/libdimension/png.c b/libdimension/png.c
index 5b7f625..6a56a87 100644
--- a/libdimension/png.c
+++ b/libdimension/png.c
@@ -64,7 +64,9 @@ dmnsn_png_optimizer_fn(const dmnsn_canvas *canvas,
dmnsn_color color;
uint16_t *pixel = (uint16_t *)optimizer.ptr + 4*(y*canvas->width + x);
- color = dmnsn_remove_filter(dmnsn_get_pixel(canvas, x, y));
+ color = dmnsn_get_pixel(canvas, x, y);
+ color = dmnsn_remove_filter(color);
+ color = dmnsn_color_to_sRGB(color);
/* Saturate R, G, and B to [0, UINT16_MAX] */
@@ -272,6 +274,7 @@ dmnsn_png_write_canvas_thread(void *ptr)
/* Invert the rows. PNG coordinates are fourth quadrant. */
dmnsn_color color = dmnsn_get_pixel(payload->canvas, x, height - y - 1);
color = dmnsn_remove_filter(color);
+ color = dmnsn_color_to_sRGB(color);
/* Saturate R, G, and B to [0, UINT16_MAX] */
@@ -477,6 +480,7 @@ dmnsn_png_read_canvas_thread(void *ptr)
}
}
+ color = dmnsn_color_from_sRGB(color);
dmnsn_set_pixel(*payload->canvas, x, height - y - 1, color);
}
diff --git a/libdimension/tests/render.c b/libdimension/tests/render.c
index a79c85c..de5b6a4 100644
--- a/libdimension/tests/render.c
+++ b/libdimension/tests/render.c
@@ -34,9 +34,9 @@ dmnsn_new_test_scene(void)
/* Default finish */
scene->default_texture->finish = dmnsn_new_finish_combination(
dmnsn_new_ambient_finish(
- dmnsn_color_mul(0.1, dmnsn_white)
+ dmnsn_color_mul(0.01, dmnsn_white)
),
- dmnsn_new_diffuse_finish(0.6)
+ dmnsn_new_diffuse_finish(0.3)
);
/* Allocate a canvas */
@@ -74,10 +74,13 @@ dmnsn_new_test_scene(void)
dmnsn_pattern *sky_gradient = dmnsn_new_gradient_pattern(dmnsn_y);
dmnsn_map *sky_gradient_color_map = dmnsn_new_color_map();
dmnsn_add_map_entry(sky_gradient_color_map, 0.0, &dmnsn_orange);
- dmnsn_color background = dmnsn_new_color5(0.0, 0.1, 0.2, 0.1, 1.0);
+ dmnsn_color background = dmnsn_color_from_sRGB(
+ dmnsn_new_color5(0.0, 0.1, 0.2, 0.1, 0.0)
+ );
dmnsn_add_map_entry(sky_gradient_color_map, 0.35, &background);
dmnsn_pigment *sky_pigment
- = dmnsn_new_color_map_pigment(sky_gradient, sky_gradient_color_map);
+ = dmnsn_new_color_map_pigment(sky_gradient, sky_gradient_color_map,
+ DMNSN_PIGMENT_MAP_SRGB);
dmnsn_array_push(scene->sky_sphere->pigments, &sky_pigment);
/* Light source */
@@ -144,7 +147,8 @@ dmnsn_new_test_scene(void)
dmnsn_add_map_entry(gradient_color_map, 1.0, &dmnsn_red);
arrow->texture = dmnsn_new_texture();
arrow->texture->pigment
- = dmnsn_new_color_map_pigment(gradient, gradient_color_map);
+ = dmnsn_new_color_map_pigment(gradient, gradient_color_map,
+ DMNSN_PIGMENT_MAP_SRGB);
arrow->texture->trans =
dmnsn_matrix_mul(
dmnsn_translation_matrix(dmnsn_new_vector(0.0, -1.25, 0.0)),
@@ -186,15 +190,19 @@ dmnsn_new_test_scene(void)
dmnsn_add_map_entry(checker_color_map, 1.0, &dmnsn_white);
dmnsn_pigment *pigment1 = dmnsn_new_solid_pigment(dmnsn_white);
dmnsn_pigment *pigment2
- = dmnsn_new_color_map_pigment(checker1, checker_color_map);
+ = dmnsn_new_color_map_pigment(checker1, checker_color_map,
+ DMNSN_PIGMENT_MAP_REGULAR);
pigment2->trans
= dmnsn_scale_matrix(dmnsn_new_vector(1.0/3.0, 1.0/3.0, 1.0/3.0));
dmnsn_map *checker_pigment_map = dmnsn_new_pigment_map();
dmnsn_add_map_entry(checker_pigment_map, 0.0, &pigment1);
dmnsn_add_map_entry(checker_pigment_map, 1.0, &pigment2);
plane->texture->pigment
- = dmnsn_new_pigment_map_pigment(checker2, checker_pigment_map);
- plane->texture->pigment->quick_color = dmnsn_new_color(1.0, 0.5, 0.75);
+ = dmnsn_new_pigment_map_pigment(checker2, checker_pigment_map,
+ DMNSN_PIGMENT_MAP_REGULAR);
+ plane->texture->pigment->quick_color = dmnsn_color_from_sRGB(
+ dmnsn_new_color(1.0, 0.5, 0.75)
+ );
dmnsn_scene_add_object(scene, plane);
return scene;