bg = array of CSS color values // $this->bg[0] is the bg color // $this->bg['+1'..'+5'] are lighter colors // $this->bg['-1'..'-5'] are darker colors var $bg = array(); // $this->fg = array of foreground colors. // Each color corresponds to a background color. var $fg = array(); // brightDiff is the minimum brightness difference // between the background and the foreground. // Note: you should not change this directly, // instead use setBrightDiff() and getBrightDiff() var $minBrightDiff = 128; // colorDiff is the minimum color difference // between the background and the foreground. // Note: you should not change this directly, // instead use setColorDiff() and getColorDiff() var $minColorDiff = 100; //================================================== //==CONSTRUCTOR===================================== //================================================== function __construct($bgHex, $fgHex='') { // This is the constructor method for the class, // which is called when a new object is created. // Initialize this PEAR object so I can // use the PEAR error return mechanism // Initialize the palette $this->setPalette($bgHex, $fgHex); } //================================================== //==METHODS========================================= //================================================== //-------------------------------------------------- function setPalette($bgHex, $fgHex = '') { // Initialize the color palettes // If a foreground color was not specified, // just use the background color. if (!$fgHex) { $fgHex = $bgHex; } // Clear the existing palette $this->bg = array(); $this->fg = array(); // Make sure we got a valid hex value if ( ! $this->isHex( $bgHex ) ) { error_log("background color '$bgHex' is not a hex color value"); return false; } // Set the bg color $this->bg[0] = $bgHex; $this->bg['+1'] = $this->lighten($bgHex, .85); $this->bg['+2'] = $this->lighten($bgHex, .75); $this->bg['+3'] = $this->lighten($bgHex, .5); $this->bg['+4'] = $this->lighten($bgHex, .25); $this->bg['+5'] = $this->lighten($bgHex, .1); $this->bg['-1'] = $this->darken($bgHex, .85); $this->bg['-2'] = $this->darken($bgHex, .75); $this->bg['-3'] = $this->darken($bgHex, .5); $this->bg['-4'] = $this->darken($bgHex, .25); $this->bg['-5'] = $this->darken($bgHex, .1); // Make sure we got a valid hex value if (!$this->isHex($fgHex)) { $this->raiseError("background color '$bgHex' is not a hex color value", __FUNCTION__, __LINE__); return false; } // Set up the foreground colors $this->fg[0] = $this->calcFG( $this->bg[0], $fgHex); $this->fg['+1'] = $this->calcFG( $this->bg['+1'], $fgHex); $this->fg['+2'] = $this->calcFG( $this->bg['+2'], $fgHex); $this->fg['+3'] = $this->calcFG( $this->bg['+3'], $fgHex); $this->fg['+4'] = $this->calcFG( $this->bg['+4'], $fgHex); $this->fg['+5'] = $this->calcFG( $this->bg['+5'], $fgHex); $this->fg['-1'] = $this->calcFG( $this->bg['-1'], $fgHex); $this->fg['-2'] = $this->calcFG( $this->bg['-2'], $fgHex); $this->fg['-3'] = $this->calcFG( $this->bg['-3'], $fgHex); $this->fg['-4'] = $this->calcFG( $this->bg['-4'], $fgHex); $this->fg['-5'] = $this->calcFG( $this->bg['-5'], $fgHex); } //-------------------------------------------------- function lighten($hex, $percent) { return $this->mix($hex, $percent, 255); } //-------------------------------------------------- function darken($hex, $percent) { return $this->mix($hex, $percent, 0); } //-------------------------------------------------- function mix($hex, $percent, $mask) { // Make sure inputs are valid if (!is_numeric($percent) || $percent < 0 || $percent > 1) { $this->raiseError("percent=$percent is not valid", __FUNCTION__, __LINE__); return false; } if (!is_int($mask) || $mask < 0 || $mask > 255) { $this->raiseError("mask=$mask is not valid", __FUNCTION__, __LINE__); return false; } $rgb = $this->hex2RGB($hex); if (!is_array($rgb)) { // hex2RGB will raise an error return false; } for ($i=0; $i<3; $i++) { $rgb[$i] = round($rgb[$i] * $percent) + round($mask * (1-$percent)); // In case rounding up causes us to go to 256 if ($rgb[$i] > 255) { $rgb[$i] = 255; } } return $this->RGB2Hex($rgb); } //-------------------------------------------------- function hex2RGB($hex) { // // Given a hex color (rrggbb or rgb), // returns an array (r, g, b) with decimal values // If $hex is not the correct format, // returns false. // // example: // $d = hex2RGB('#abc'); // if (!$d) { error } // Regexp for a valid hex digit $d = '[a-fA-F0-9]'; // Make sure $hex is valid if (preg_match("/^($d$d)($d$d)($d$d)\$/", $hex, $rgb)) { return array( hexdec($rgb[1]), hexdec($rgb[2]), hexdec($rgb[3]) ); } if (preg_match("/^($d)($d)($d)$/", $hex, $rgb)) { return array( hexdec($rgb[1] . $rgb[1]), hexdec($rgb[2] . $rgb[2]), hexdec($rgb[3] . $rgb[3]) ); } $this->raiseError("cannot convert hex '$hex' to RGB", __FUNCTION__, __LINE__); return false; } //-------------------------------------------------- function RGB2Hex($rgb) { // Given an array(rval,gval,bval) consisting of // decimal color values (0-255), returns a hex string // suitable for use with CSS. // Returns false if the input is not in the correct format. // Example: // $h = RGB2Hex(array(255,0,255)); // if (!$h) { error }; // Make sure the input is valid if(!$this->isRGB($rgb)) { $this->raiseError("RGB value is not valid", __FUNCTION__, __LINE__); return false; } $hex = ""; for($i=0; $i < 3; $i++) { // Convert the decimal digit to hex $hexDigit = dechex($rgb[$i]); // Add a leading zero if necessary if(strlen($hexDigit) == 1) { $hexDigit = "0" . $hexDigit; } // Append to the hex string $hex .= $hexDigit; } // Return the complete hex string return $hex; } //-------------------------------------------------- function isHex($hex) { // Returns true if $hex is a valid CSS hex color. // The "#" character at the start is optional. // Regexp for a valid hex digit $d = '[a-fA-F0-9]'; // Make sure $hex is valid if (preg_match("/^#?$d$d$d$d$d$d\$/", $hex) || preg_match("/^#?$d$d$d\$/", $hex)) { return true; } return false; } //-------------------------------------------------- function isRGB($rgb) { // Returns true if $rgb is an array with three valid // decimal color digits. if (!is_array($rgb) || count($rgb) != 3) { return false; } for($i=0; $i < 3; $i++) { // Get the decimal digit $dec = intval($rgb[$i]); // Make sure the decimal digit is between 0 and 255 if (!is_int($dec) || $dec < 0 || $dec > 255) { return false; } } return true; } //-------------------------------------------------- function calcFG($bgHex, $fgHex) { // Given a background color $bgHex and a foreground color $fgHex, // modifies the foreground color so it will have enough contrast // to be seen against the background color. // // The following parameters are used: // $this->minBrightDiff // $this->minColorDiff // Loop through brighter and darker versions // of the foreground color. // The numbers here represent the amount of // foreground color to mix with black and white. foreach (array(1, 0.75, 0.5, 0.25, 0) as $percent) { $darker = $this->darken($fgHex, $percent); $lighter = $this->lighten($fgHex, $percent); $darkerBrightDiff = $this->brightnessDiff($bgHex, $darker); $lighterBrightDiff = $this->brightnessDiff($bgHex, $lighter); if ($lighterBrightDiff > $darkerBrightDiff) { $newFG = $lighter; $newFGBrightDiff = $lighterBrightDiff; } else { $newFG = $darker; $newFGBrightDiff = $darkerBrightDiff; } $newFGColorDiff = $this->colorDiff($bgHex, $newFG); if ($newFGBrightDiff >= $this->minBrightDiff && $newFGColorDiff >= $this->minColorDiff) { break; } } return $newFG; } //-------------------------------------------------- function getMinBrightDiff() { return $this->minBrightDiff; } function setMinBrightDiff($b, $resetPalette = true) { $this->minBrightDiff = $b; if ($resetPalette) { $this->setPalette($this->bg[0],$this->fg[0]); } } //-------------------------------------------------- function getMinColorDiff() { return $this->minColorDiff; } function setMinColorDiff($d, $resetPalette = true) { $this->minColorDiff = $d; if ($resetPalette) { $this->setPalette($this->bg[0],$this->fg[0]); } } //-------------------------------------------------- function brightness($hex) { // Returns the brightness value for a color, // a number between zero and 178. // To allow for maximum readability, the difference between // the background brightness and the foreground brightness // should be greater than 125. $rgb = $this->hex2RGB($hex); if (!is_array($rgb)) { // hex2RGB will raise an error return false; } return( (($rgb[0] * 299) + ($rgb[1] * 587) + ($rgb[2] * 114)) / 1000 ); } //-------------------------------------------------- function brightnessDiff($hex1, $hex2) { // Returns the brightness value for a color, // a number between zero and 178. // To allow for maximum readability, the difference between // the background brightness and the foreground brightness // should be greater than 125. $b1 = $this->brightness($hex1); $b2 = $this->brightness($hex2); if (is_bool($b1) || is_bool($b2)) { return false; } return abs($b1 - $b2); } //-------------------------------------------------- function colorDiff($hex1, $hex2) { // Returns the contrast between two colors, // an integer between 0 and 675. // To allow for maximum readability, the difference between // the background and the foreground color should be > 500. $rgb1 = $this->hex2RGB($hex1); $rgb2 = $this->hex2RGB($hex2); if (!is_array($rgb1) || !is_array($rgb2)) { // hex2RGB will raise an error return -1; } $r1 = $rgb1[0]; $g1 = $rgb1[1]; $b1 = $rgb1[2]; $r2 = $rgb2[0]; $g2 = $rgb2[1]; $b2 = $rgb2[2]; return(abs($r1-$r2) + abs($g1-$g2) + abs($b1-$b2)); } } ?>