From bfbe9e43e108f6816c17b9b7764b75284ac78313 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 14 Dec 2011 19:27:22 -0500 Subject: Re-think colors. Color is a property of light, and thus doesn't include information about transparency. But canvas pixels and object pigments represent a color and a degree of transparency. The new type dmnsn_tcolor/ TColor encapsulates that information. Also, fix the transparent shadow implementation. --- libdimension/dimension/canvas.h | 18 ++-- libdimension/dimension/color.h | 192 ++++++++++++++++++++++++++------------ libdimension/dimension/finish.h | 8 +- libdimension/dimension/finishes.h | 7 -- libdimension/dimension/pigment.h | 10 +- libdimension/dimension/pigments.h | 11 +-- libdimension/dimension/tcolor.h | 100 ++++++++++++++++++++ 7 files changed, 249 insertions(+), 97 deletions(-) create mode 100644 libdimension/dimension/tcolor.h (limited to 'libdimension/dimension') diff --git a/libdimension/dimension/canvas.h b/libdimension/dimension/canvas.h index 48a64c1..ba06b8c 100644 --- a/libdimension/dimension/canvas.h +++ b/libdimension/dimension/canvas.h @@ -26,7 +26,7 @@ #include /** A canvas, or image. */ -typedef struct { +typedef struct dmnsn_canvas { size_t width; /**< Canvas width. */ size_t height; /**< Canvas height. */ @@ -38,7 +38,7 @@ typedef struct { * Stored in first-quadrant representation (origin is bottom-left). The pixel * at (a,b) is accessible as pixels[b*width + a]. */ - dmnsn_color *pixels; + dmnsn_tcolor *pixels; dmnsn_refcount refcount; /**< @internal Reference count. */ } dmnsn_canvas; @@ -94,9 +94,9 @@ void dmnsn_canvas_optimize(dmnsn_canvas *canvas, * @param[in] canvas The canvas to access. * @param[in] x The x coordinate. * @param[in] y The y coordinate. - * @return The color of the canvas at (\p x, \p y). + * @return The color of the pixel at (\p x, \p y). */ -DMNSN_INLINE dmnsn_color +DMNSN_INLINE dmnsn_tcolor dmnsn_canvas_get_pixel(const dmnsn_canvas *canvas, size_t x, size_t y) { dmnsn_assert(x < canvas->width && y < canvas->height, @@ -105,18 +105,18 @@ dmnsn_canvas_get_pixel(const dmnsn_canvas *canvas, size_t x, size_t y) } /** - * Set the color of a pixel. + * Set the value of a pixel. * @param[in,out] canvas The canvas to modify. * @param[in] x The x coordinate of the pixel. * @param[in] y The y coordinate of the pixel. - * @param[in] color The color to set the pixel at (\p x, \p y) to. + * @param[in] tcolor The value to set the pixel at (\p x, \p y) to. */ void dmnsn_canvas_set_pixel(dmnsn_canvas *canvas, size_t x, size_t y, - dmnsn_color color); + dmnsn_tcolor tcolor); /** * Clear a canvas uniformly with a given color. * @param[in,out] canvas The canvas to erase. - * @param[in] color The color to paint it with. + * @param[in] tcolor The color to paint it with. */ -void dmnsn_canvas_clear(dmnsn_canvas *canvas, dmnsn_color color); +void dmnsn_canvas_clear(dmnsn_canvas *canvas, dmnsn_tcolor tcolor); diff --git a/libdimension/dimension/color.h b/libdimension/dimension/color.h index 295400b..34f3884 100644 --- a/libdimension/dimension/color.h +++ b/libdimension/dimension/color.h @@ -20,97 +20,167 @@ /** * @file - * Color-related types and operations. + * Colors. */ #include /** A color value. */ typedef struct { - double R; /**< Red. */ - double G; /**< Green. */ - double B; /**< Blue. */ - - double trans; /**< Transparency. */ - double filter; /**< Degree of filtering. */ + double R; /**< Red component. */ + double G; /**< Green component. */ + double B; /**< Blue component. */ } dmnsn_color; /** A standard format string for colors. */ -#define DMNSN_COLOR_FORMAT \ - "" +#define DMNSN_COLOR_FORMAT "Color<%g, %g, %g>" /** The appropriate arguements to printf() a color. */ -#define DMNSN_COLOR_PRINTF(c) (c).R, (c).G, (c).B, (c).trans, (c).filter - -/* Standard colors */ -extern const dmnsn_color dmnsn_black; /**< Black. */ -extern const dmnsn_color dmnsn_white; /**< White. */ -extern const dmnsn_color dmnsn_clear; /**< Clear. */ -extern const dmnsn_color dmnsn_red; /**< Red. */ -extern const dmnsn_color dmnsn_green; /**< Green. */ -extern const dmnsn_color dmnsn_blue; /**< Blue. */ -extern const dmnsn_color dmnsn_magenta; /**< Magenta. */ -extern const dmnsn_color dmnsn_orange; /**< Orange. */ -extern const dmnsn_color dmnsn_yellow; /**< Yellow. */ -extern const dmnsn_color dmnsn_cyan; /**< Cyan. */ - -/* Color construction */ +#define DMNSN_COLOR_PRINTF(c) (c).R, (c).G, (c).B /** Construct a new color. */ DMNSN_INLINE dmnsn_color dmnsn_new_color(double R, double G, double B) { - dmnsn_color ret = { R, G, B, 0.0, 0.0 }; + dmnsn_color ret = { R, G, B }; return ret; } -/** Construct a new color with transparent components. */ -DMNSN_INLINE dmnsn_color -dmnsn_new_color5(double R, double G, double B, double trans, double filter) +/** Apply sRGB gamma */ +DMNSN_INLINE double +dmnsn_sRGB_gamma(double Clinear) { - dmnsn_color ret = { R, G, B, trans, filter }; - return ret; + /* + * 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 1.055*pow(Clinear, 1.0/2.4) - 0.055; + } else { + return 12.92*Clinear; + } } -/** Saturate the color components to [0.0, 1.0]. */ +/** Convert to sRGB space. */ DMNSN_INLINE dmnsn_color -dmnsn_color_saturate(dmnsn_color color) +dmnsn_color_to_sRGB(dmnsn_color color) { - color.R = dmnsn_min(dmnsn_max(color.R, 0.0), 1.0); - color.G = dmnsn_min(dmnsn_max(color.G, 0.0), 1.0); - color.B = dmnsn_min(dmnsn_max(color.B, 0.0), 1.0); - color.trans = dmnsn_min(dmnsn_max(color.trans, 0.0), 1.0); - color.filter = dmnsn_min(dmnsn_max(color.filter, 0.0), 1.0); - return color; + return dmnsn_new_color( + dmnsn_sRGB_gamma(color.R), + dmnsn_sRGB_gamma(color.G), + dmnsn_sRGB_gamma(color.B) + ); } -/* Perceptual color manipulation */ - -/** Apply sRGB gamma */ -double dmnsn_sRGB_gamma(double Clinear); -/** Convert to sRGB space. */ -dmnsn_color dmnsn_color_to_sRGB(dmnsn_color color); /** Remove sRGB gamma */ -double dmnsn_sRGB_inverse_gamma(double CsRGB); +DMNSN_INLINE double +dmnsn_sRGB_inverse_gamma(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_INLINE dmnsn_color +dmnsn_color_from_sRGB(dmnsn_color color) +{ + return dmnsn_new_color( + dmnsn_sRGB_inverse_gamma(color.R), + dmnsn_sRGB_inverse_gamma(color.G), + dmnsn_sRGB_inverse_gamma(color.B) + ); +} /** Greyscale color intensity. */ -double dmnsn_color_intensity(dmnsn_color color); +DMNSN_INLINE double +dmnsn_color_intensity(dmnsn_color color) +{ + return 0.2126*color.R + 0.7152*color.G + 0.0722*color.B; +} + /** Add two colors together. */ -dmnsn_color dmnsn_color_add(dmnsn_color lhs, dmnsn_color rhs); +DMNSN_INLINE dmnsn_color +dmnsn_color_add(dmnsn_color lhs, dmnsn_color rhs) +{ + return dmnsn_new_color(lhs.R + rhs.R, lhs.G + rhs.G, lhs.B + rhs.B); +} + /** Subtract two colors. */ -dmnsn_color dmnsn_color_sub(dmnsn_color lhs, dmnsn_color rhs); -/** Multiply a color's intensity by \p n. */ -dmnsn_color dmnsn_color_mul(double n, dmnsn_color color); +DMNSN_INLINE dmnsn_color +dmnsn_color_sub(dmnsn_color lhs, dmnsn_color rhs) +{ + return dmnsn_new_color(lhs.R - rhs.R, lhs.G - rhs.G, lhs.B - rhs.B); +} + +/** Scale a color's intensity. */ +DMNSN_INLINE dmnsn_color +dmnsn_color_mul(double n, dmnsn_color color) +{ + return dmnsn_new_color(n*color.R, n*color.G, n*color.B); +} + /** Return the color at \p n on a gradient from \p c1 at 0 to \p c2 at 1. */ -dmnsn_color dmnsn_color_gradient(dmnsn_color c1, dmnsn_color c2, double n); -/** Filter \p light through \p filter. */ -dmnsn_color dmnsn_filter_light(dmnsn_color light, dmnsn_color filter); -/** Add the background contribution \p filtered to \p filter. */ -dmnsn_color dmnsn_apply_transparency(dmnsn_color filtered, dmnsn_color filter); -/** Add the background contribution of \p color to \p filter. */ -dmnsn_color dmnsn_apply_filter(dmnsn_color color, dmnsn_color filter); -/** Convert the color into a close equivalent with only transmittance. */ -dmnsn_color dmnsn_remove_filter(dmnsn_color color); +DMNSN_INLINE dmnsn_color +dmnsn_color_gradient(dmnsn_color c1, dmnsn_color c2, double n) +{ + return dmnsn_new_color( + n*(c2.R - c1.R) + c1.R, + n*(c2.G - c1.G) + c1.G, + n*(c2.B - c1.B) + c1.B + ); +} + /** Illuminate \p color with \p light. */ -dmnsn_color dmnsn_color_illuminate(dmnsn_color light, dmnsn_color color); +DMNSN_INLINE dmnsn_color +dmnsn_color_illuminate(dmnsn_color light, dmnsn_color color) +{ + return dmnsn_new_color(light.R*color.R, light.G*color.G, light.B*color.B); +} + +/** Saturate the color components to [0.0, 1.0]. */ +DMNSN_INLINE dmnsn_color +dmnsn_color_saturate(dmnsn_color color) +{ + color.R = dmnsn_min(dmnsn_max(color.R, 0.0), 1.0); + color.G = dmnsn_min(dmnsn_max(color.G, 0.0), 1.0); + color.B = dmnsn_min(dmnsn_max(color.B, 0.0), 1.0); + return color; +} + +/* Standard colors */ + +/** Black. */ +#define dmnsn_black dmnsn_new_color(0.0, 0.0, 0.0) +/** White. */ +#define dmnsn_white dmnsn_new_color(1.0, 1.0, 1.0) +/** Red. */ +#define dmnsn_red dmnsn_new_color(1.0, 0.0, 0.0) +/** Green. */ +#define dmnsn_green dmnsn_new_color(0.0, 1.0, 0.0) +/** Blue. */ +#define dmnsn_blue dmnsn_new_color(0.0, 0.0, 1.0) +/** Magenta. */ +#define dmnsn_magenta dmnsn_new_color(1.0, 0.0, 1.0) +/** Orange. */ +#define dmnsn_orange dmnsn_new_color(1.0, 0.21404114048223255, 0.0) +/** Yellow. */ +#define dmnsn_yellow dmnsn_new_color(1.0, 1.0, 0.0) +/** Cyan. */ +#define dmnsn_cyan dmnsn_new_color(0.0, 1.0, 1.0) diff --git a/libdimension/dimension/finish.h b/libdimension/dimension/finish.h index 2e3b48c..4ac9239 100644 --- a/libdimension/dimension/finish.h +++ b/libdimension/dimension/finish.h @@ -38,14 +38,12 @@ typedef dmnsn_color dmnsn_ambient_fn(const dmnsn_ambient *ambient, /** Ambient finish component. */ struct dmnsn_ambient { - dmnsn_ambient_fn *ambient_fn; /**< Ambient callback. */ - dmnsn_free_fn *free_fn; /**< Destructor callback. */ - void *ptr; /**< Generic data pointer. */ - dmnsn_refcount refcount; /**< @internal Reference count. */ + dmnsn_color ambient; /**< Ambient light. */ + dmnsn_refcount refcount; /**< @internal Reference count. */ }; /** Allocate a dummy ambient component. */ -dmnsn_ambient *dmnsn_new_ambient(void); +dmnsn_ambient *dmnsn_new_ambient(dmnsn_color ambient); /** Delete an ambient component. */ void dmnsn_delete_ambient(dmnsn_ambient *ambient); diff --git a/libdimension/dimension/finishes.h b/libdimension/dimension/finishes.h index d9cd14c..7ed66d3 100644 --- a/libdimension/dimension/finishes.h +++ b/libdimension/dimension/finishes.h @@ -23,13 +23,6 @@ * Pre-defined finishes. */ -/** - * Ambient finish. - * @param[in] ambient The color of the ambient light. - * @return An ambient finish component. - */ -dmnsn_ambient *dmnsn_new_basic_ambient(dmnsn_color ambient); - /** * Regular diffuse finish. * @param[in] diffuse The diffuse reflection coefficient. diff --git a/libdimension/dimension/pigment.h b/libdimension/dimension/pigment.h index 716c28b..176ff28 100644 --- a/libdimension/dimension/pigment.h +++ b/libdimension/dimension/pigment.h @@ -32,8 +32,8 @@ typedef struct dmnsn_pigment dmnsn_pigment; * @param[in] v The point to color. * @return The color of the pigment at \p v. */ -typedef dmnsn_color dmnsn_pigment_fn(const dmnsn_pigment *pigment, - dmnsn_vector v); +typedef dmnsn_tcolor dmnsn_pigment_fn(const dmnsn_pigment *pigment, + dmnsn_vector v); /** * Pigment initializer callback. @@ -51,7 +51,7 @@ struct dmnsn_pigment { dmnsn_matrix trans_inv; /**< The inverse of the transformation matrix. */ /** Quick color -- used for low-quality renders. */ - dmnsn_color quick_color; + dmnsn_tcolor quick_color; /** Generic pointer. */ void *ptr; @@ -86,5 +86,5 @@ void dmnsn_pigment_initialize(dmnsn_pigment *pigment); * @param[in] v The point to color. * @return The color at \p v. */ -dmnsn_color dmnsn_pigment_evaluate(const dmnsn_pigment *pigment, - dmnsn_vector v); +dmnsn_tcolor dmnsn_pigment_evaluate(const dmnsn_pigment *pigment, + dmnsn_vector v); diff --git a/libdimension/dimension/pigments.h b/libdimension/dimension/pigments.h index 3584167..383e38b 100644 --- a/libdimension/dimension/pigments.h +++ b/libdimension/dimension/pigments.h @@ -28,7 +28,7 @@ * @param[in] color The color of the pigment. * @return A pigment with the color \p color everywhere. */ -dmnsn_pigment *dmnsn_new_solid_pigment(dmnsn_color color); +dmnsn_pigment *dmnsn_new_solid_pigment(dmnsn_tcolor color); /** * An image map. The image (regardless of its real dimensions) is projected @@ -52,15 +52,6 @@ typedef enum dmnsn_pigment_map_flags { */ dmnsn_map *dmnsn_new_pigment_map(void); -/** - * Add a raw color to a pigment map. - * Shorthand for creating a solid pigment and adding it manually. - * @param[in,out] map The pigment map to add to. - * @param[in] n The index of the entry. - * @param[in] color The value of the entry. - */ -void dmnsn_pigment_map_add_color(dmnsn_map *map, double n, dmnsn_color color); - /** * A pigment-mapped pigment. * @param[in,out] pattern The pattern of the pigment. diff --git a/libdimension/dimension/tcolor.h b/libdimension/dimension/tcolor.h new file mode 100644 index 0000000..d1b7338 --- /dev/null +++ b/libdimension/dimension/tcolor.h @@ -0,0 +1,100 @@ +/************************************************************************* + * Copyright (C) 2009-2011 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library 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. * + * * + * The Dimension Library 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 * + * . * + *************************************************************************/ + +/** + * @file + * Colors with transparency information. + */ + +/** A transparent color. */ +typedef struct dmnsn_tcolor { + dmnsn_color c; /**< Color. */ + double T; /**< Transparency. */ + double F; /**< Proportion of filtered transparency. */ +} dmnsn_tcolor; + +/** Create a tcolor. */ +DMNSN_INLINE dmnsn_tcolor +dmnsn_new_tcolor(dmnsn_color c, double T, double F) +{ + dmnsn_tcolor ret = { c, T, F }; + return ret; +} + +/** Convert a dmnsn_color into a dmnsn_tcolor. */ +#define DMNSN_TCOLOR(c) dmnsn_new_tcolor(c, 0.0, 0.0) + +/** Create a tcolor with individually-specified components. */ +DMNSN_INLINE dmnsn_tcolor +dmnsn_new_tcolor5(double R, double G, double B, double T, double F) +{ + dmnsn_tcolor ret = { dmnsn_new_color(R, G, B), T, F }; + return ret; +} + +/** Return the color at \p n on a gradient from \p c1 at 0 to \p c2 at 1. */ +DMNSN_INLINE dmnsn_tcolor +dmnsn_tcolor_gradient(dmnsn_tcolor c1, dmnsn_tcolor c2, double n) +{ + return dmnsn_new_tcolor( + dmnsn_color_gradient(c1.c, c2.c, n), + n*(c2.T - c1.T) + c1.T, + n*(c2.F - c1.F) + c1.F + ); +} + +/** Filter \p light through \p filter. */ +DMNSN_INLINE dmnsn_color +dmnsn_tcolor_filter(dmnsn_color light, dmnsn_tcolor filter) +{ + dmnsn_color filtered = + dmnsn_color_mul(filter.T*filter.F, dmnsn_color_illuminate(light, filter.c)); + dmnsn_color transmitted = dmnsn_color_mul(filter.T*(1.0 - filter.F), light); + return dmnsn_color_add(filtered, transmitted); +} + +/** Remove the filtered component of a tcolor. */ +DMNSN_INLINE dmnsn_tcolor +dmnsn_tcolor_remove_filter(dmnsn_tcolor tcolor) +{ + double intensity = dmnsn_color_intensity(tcolor.c); + double newtrans = (1.0 - (1.0 - intensity)*tcolor.F)*tcolor.T; + if (1.0 - newtrans >= dmnsn_epsilon) { + tcolor.c = dmnsn_color_mul((1.0 - tcolor.T)/(1.0 - newtrans), tcolor.c); + } + tcolor.T = newtrans; + tcolor.F = 0.0; + return tcolor; +} + +/** Saturate the tcolor components to [0.0, 1.0]. */ +DMNSN_INLINE dmnsn_tcolor +dmnsn_tcolor_saturate(dmnsn_tcolor tcolor) +{ + tcolor.c = dmnsn_color_saturate(tcolor.c); + tcolor.T = dmnsn_min(dmnsn_max(tcolor.T, 0.0), 1.0); + tcolor.F = dmnsn_min(dmnsn_max(tcolor.F, 0.0), 1.0); + return tcolor; +} + +/* Standard tcolors */ + +/** Clear. */ +#define dmnsn_clear dmnsn_new_tcolor5(0.0, 0.0, 0.0, 0.0, 0.0) -- cgit v1.2.3