From: Alexander Ebert Date: Sun, 25 Sep 2016 10:30:15 +0000 (+0200) Subject: Approximate color using a Delta E (CIE 1994) diff X-Git-Tag: 3.0.0_Beta_2~151 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=701517480b29d182649664f166ffc4728c99408a;p=GitHub%2FWoltLab%2FWCF.git Approximate color using a Delta E (CIE 1994) diff --- diff --git a/wcfsetup/install/files/lib/system/html/input/node/HtmlInputNodeWoltlabColor.class.php b/wcfsetup/install/files/lib/system/html/input/node/HtmlInputNodeWoltlabColor.class.php new file mode 100644 index 0000000000..f0f0b640d7 --- /dev/null +++ b/wcfsetup/install/files/lib/system/html/input/node/HtmlInputNodeWoltlabColor.class.php @@ -0,0 +1,207 @@ +` to check for valid color arguments. + * + * @author Alexander Ebert + * @copyright 2001-2016 WoltLab GmbH + * @license GNU Lesser General Public License + * @package WoltLabSuite\Core\System\Html\Input\Node + * @since 3.0 + */ +class HtmlInputNodeWoltlabColor extends AbstractHtmlInputNode { + /** + * @inheritDoc + */ + protected $tagName = 'woltlab-color'; + + public static $validColors = [ + '000000', '000080', '0000CD', '0000FF', '006400', '008000', '008080', '00FF00', + '00FFFF', '2F4F4F', '40E0D0', '4B0082', '696969', '800000', '800080', '808080', + '8B4513', 'A52A2A', 'A9A9A9', 'ADD8E6', 'AFEEEE', 'B22222', 'D3D3D3', 'DAA520', + 'DDA0DD', 'E6E6FA', 'EE82EE', 'F0F8FF', 'F0FFF0', 'F0FFFF', 'FAEBD7', 'FF0000', + 'FF8C00', 'FFA07A', 'FFA500', 'FFD700', 'FFF0F5', 'FFFF00', 'FFFFE0', 'FFFFFF' + ]; + + protected static $colorsToLab = []; + + /** + * @inheritDoc + */ + public function isAllowed(AbstractHtmlNodeProcessor $htmlNodeProcessor) { + if (BBCodeHandler::getInstance()->isAvailableBBCode('color')) { + return []; + } + + if (!$htmlNodeProcessor->getDocument()->getElementsByTagName('woltlab-color')->length) { + return []; + } + + return ['color']; + } + + /** + * @inheritDoc + */ + public function process(array $elements, AbstractHtmlNodeProcessor $htmlNodeProcessor) { + /** @var \DOMElement $element */ + foreach ($elements as $element) { + if (preg_match('~\bwoltlab-color-([a-z0-9]{6})\b~i', $element->getAttribute('class'), $matches)) { + $color = strtoupper($matches[1]); + if (!in_array($color, self::$validColors)) { + $color = $this->getClosestColor($color); + } + + $element->setAttribute('class', 'woltlab-color-'.$color); + } + else { + DOMUtil::removeNode($element, true); + } + } + } + + protected function getClosestColor($hex) { + if (empty(self::$colorsToLab)) { + // build static lookup table + foreach (self::$validColors as $color) { + self::$colorsToLab[$color] = $this->hexToLab($color); + } + } + + $lab1 = $this->hexToLab($hex); + + $diff = 0; + $newColor = ''; + + foreach (self::$colorsToLab as $color => $lab2) { + $newDiff = $this->deltaE_cie1994($lab1, $lab2); + + if ($newColor === '' || $diff > $newDiff) { + $diff = $newDiff; + $newColor = $color; + } + } + + return $newColor; + } + + protected function hexToLab($hex) { + return $this->xyzToLab( + $this->rgbToXyz( + $this->hexToRgb($hex) + ) + ); + } + + protected function hexToRgb($hex) { + // [r, g, b] + return [ + hexdec($hex{0}.$hex{1}), + hexdec($hex{2}.$hex{3}), + hexdec($hex{4}.$hex{5}) + ]; + } + + protected function rgbToXyz($rgb) { + // convert into values between 0 and 1 + $red = $rgb[0] / 255; + $green = $rgb[1] / 255; + $blue = $rgb[2] / 255; + + if ($red > 0.04045) { + $red = ($red + 0.055) / 1.055; + $red = pow($red, 2.4); + } + else { + $red = $red / 12.92; + } + + if ($green > 0.04045) { + $green = ($green + 0.055) / 1.055; + $green = pow($green, 2.4); + } + else { + $green = $green / 12.92; + } + + if ($blue > 0.04045) { + $blue = ($blue + 0.055) / 1.055; + $blue = pow($blue, 2.4); + } + else { + $blue = $blue / 12.92; + } + + $red *= 100; + $green *= 100; + $blue *= 100; + + // [x, y, z] + return [ + $red * 0.4124 + $green * 0.3576 + $blue * 0.1805, + $red * 0.2126 + $green * 0.7152 + $blue * 0.0722, + $red * 0.0193 + $green * 0.1192 + $blue * 0.9505 + ]; + } + + protected function xyzToLab($xyz) { + $x = $xyz[0] / 95.047; + $y = $xyz[1] / 100; + $z = $xyz[2] / 108.883; + + if ($x > 0.008856) { + $x = pow($x, 1 / 3); + } + else { + $x = 7.787 * $x + 16 / 116; + } + + if ($y > 0.008856) { + $y = pow($y, 1 / 3); + } + else { + $y = (7.787 * $y) + (16 / 116); + } + + if ($z > 0.008856) { + $z = pow($z, 1 / 3); + } + else { + $z = 7.787 * $z + 16 / 116; + } + + // [l, a, b] + return [ + 116 * $y - 16, + 500 * ($x - $y), + 200 * ($y - $z) + ]; + } + + protected function deltaE_cie1994($lab1, $lab2) { + // Delta E (CIE 1994) difference of two colors + // http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CIE94.html + $c1 = sqrt($lab1[1] * $lab1[1] + $lab1[2] * $lab1[2]); + $c2 = sqrt($lab2[1] * $lab2[1] + $lab2[2] * $lab2[2]); + + $dc = $c1 - $c2; + $dl = $lab1[0] - $lab2[0]; + $da = $lab1[1] - $lab2[1]; + $db = $lab1[2] - $lab2[2]; + $dh = ($da * $da) + ($db * $db) - ($dc * $dc); + $dh = ($dh < 0) ? 0 : sqrt($dh); + + $first = $dl; + $second = $dc / (1 + 0.045 * $c1); + $third = $dh / (1 + 0.015 * $c1); + + return sqrt($first * $first + $second * $second + $third * $third); + } +}