/************************************************************************* * Copyright (C) 2009-2014 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. */ #include /** A color value. */ typedef struct { double R; /**< Red component. */ double G; /**< Green component. */ double B; /**< Blue component. */ } dmnsn_color; /** A standard format string for colors. */ #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 /** Construct a new color. */ DMNSN_INLINE dmnsn_color dmnsn_new_color(double R, double G, double B) { dmnsn_color ret = { R, G, B }; return ret; } /** Apply sRGB gamma */ DMNSN_INLINE double dmnsn_sRGB_gamma(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 1.055*pow(Clinear, 1.0/2.4) - 0.055; } else { return 12.92*Clinear; } } /** Convert to sRGB space. */ DMNSN_INLINE dmnsn_color dmnsn_color_to_sRGB(dmnsn_color color) { return dmnsn_new_color( dmnsn_sRGB_gamma(color.R), dmnsn_sRGB_gamma(color.G), dmnsn_sRGB_gamma(color.B) ); } /** Remove sRGB gamma */ 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 = { 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_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. */ 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_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_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_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_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_clamp(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; } /** Return whether a color contains any NaN components. */ DMNSN_INLINE bool dmnsn_color_isnan(dmnsn_color color) { return dmnsn_isnan(color.R) || dmnsn_isnan(color.G) || dmnsn_isnan(color.B); } /* 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)