{-# LANGUAGE OverloadedStrings #-} ----------------------------------------------------------------------------- -- | -- Module : Hasmin.Types.Color -- Copyright : (c) 2017 Cristian Adrián Ontivero -- License : BSD3 -- Stability : experimental -- Portability : unknown -- ----------------------------------------------------------------------------- module Hasmin.Types.Color ( Color(Named) , mkHex3 , mkHex4 , mkHex6 , mkHex8 , mkNamed , mkHSL , mkHSLA , mkRGBInt , mkRGBPer , mkRGBAInt , mkRGBAPer , keywordColors , minifyColor ) where import Control.Arrow (first) import Control.Monad.Reader (asks) import Data.Monoid ((<>)) import Data.Char (isHexDigit, digitToInt, intToDigit, toLower) import Data.Maybe (fromMaybe) import Data.Ratio ((%)) import Data.Text (Text) import Data.Text.Lazy.Builder (Builder, singleton) import qualified Data.Text.Lazy.Builder as B import Data.Word (Word8) import qualified Data.Map.Strict as Map import qualified Data.Text as T import Hasmin.Class import Hasmin.Config import Hasmin.Types.Numeric import Hasmin.Utils -- | CSS > -- data type. data Color = Hex3 Char Char Char | Hex4 Char Char Char Char | Hex6 String String String | Hex8 String String String String | Named Text | RGBInt Word8 Word8 Word8 | RGBPer Percentage Percentage Percentage | RGBAInt Word8 Word8 Word8 Alphavalue | RGBAPer Percentage Percentage Percentage Alphavalue | HSL Int Percentage Percentage | HSLA Int Percentage Percentage Alphavalue deriving (Show) -- | Equality is slightly relaxed, since percentages and real numbers are mapped -- to the [0,255] integer range, and then compared instance Eq Color where (Hex6 r1 g1 b1) == (Hex6 r2 g2 b2) = r1 == r2 && g1 == g2 && b1 == b2 (Hex8 r1 g1 b1 a1) == (Hex8 r2 g2 b2 a2) = r1 == r2 && g1 == g2 && b1 == b2 && a1 == a2 (Hex8 r1 g1 b1 a) == (Hex6 r2 g2 b2) | a == "ff" = r1 == r2 && g1 == g2 && b1 == b2 | otherwise = False (Hex6 r1 g1 b1) == (Hex8 r2 g2 b2 a) | a == "ff" = r1 == r2 && g1 == g2 && b1 == b2 | otherwise = False c1 == (Named s) = Map.lookup (T.toLower s) colorMap == Just c1 (Named s) == c2 = c2 == Named s a == b = toLongHex a == toLongHex b instance Ord Color where (Hex6 r1 g1 b1) <= (Hex6 r2 g2 b2) = r1 < r2 || r1 == r2 && (g1 < g2 || (g1 == g2 && b1 <= b2)) (Hex8 r1 g1 b1 a1) <= (Hex8 r2 g2 b2 a2) = r1 < r2 || r1 == r2 && (g1 < g2 || (g1 == g2 && (b1 < b2 || (b1 == b2 && a1 <= a2)))) (Hex8 r1 g1 b1 a) <= (Hex6 r2 g2 b2) | a == "ff" = r1 < r2 || r1 == r2 && (g1 < g2 || (g1 == g2 && b1 <= b2)) | otherwise = True (Hex6 r1 g1 b1) <= (Hex8 r2 g2 b2 a) | a == "ff" = r1 < r2 || r1 == r2 && (g1 < g2 || (g1 == g2 && b1 <= b2)) | otherwise = False c1 <= c2 = toLongHex c1 <= toLongHex c2 instance Minifiable Color where minify c = do colSettings <- asks colorSettings pure $ case colSettings of ColorMinOn -> minifyColor c ColorMinOff -> c minifyColor :: Color -> Color minifyColor c@Hex6{} = fromMaybe (toHexShorthand c) (Map.lookup c minimalColorMap) minifyColor c@(Hex8 r g b a) | a == "ff" = minifyColor (Hex6 r g b) | otherwise = toHexShorthand c minifyColor c@(RGBAPer r g b a) | a >= 1 = minifyColor (RGBPer r g b) | otherwise = minifyColor $ toLongHex c minifyColor c@(RGBAInt r g b a) | a >= 1 = minifyColor (RGBInt r g b) | otherwise = minifyColor $ toLongHex c minifyColor c@(HSLA h s l a) | a >= 1 = minifyColor (HSL h s l) | otherwise = minifyColor $ toLongHex c minifyColor c = case toLongHex c of n@(Named _) -> n other -> minifyColor other instance ToText Color where toBuilder (RGBInt r g b) = "rgb(" <> values <> singleton ')' where values = toBuilderWithCommas [toText r, toText g, toText b] toBuilder (RGBAInt r g b a) = "rgba(" <> values <> singleton ')' where values = toBuilderWithCommas [toText r, toText g, toText b, toText a] toBuilder (RGBPer r g b) = "rgb(" <> values <> singleton ')' where values = toBuilderWithCommas [toText r, toText g, toText b] toBuilder (RGBAPer r g b a) = "rgba(" <> values <> singleton ')' where values = toBuilderWithCommas [toText r, toText g, toText b, toText a] toBuilder (HSL h s l) = "hsl(" <> values <> singleton ')' where values = toBuilderWithCommas [toText h, toText s, toText l] toBuilder (HSLA h s l a) = "hsla(" <> values <> singleton ')' where values = toBuilderWithCommas [toText h, toText s, toText l, toText a] toBuilder (Named a) = B.fromText a toBuilder (Hex3 r g b) = singleton '#' <> singleton r <> singleton g <> singleton b toBuilder (Hex4 r g b a) = singleton '#' <> singleton r <> singleton g <> singleton b <> singleton a toBuilder (Hex6 r g b) = B.fromString $ mconcat ["#", r, g, b] toBuilder (Hex8 r g b a) = B.fromString $ mconcat ["#", r, g, b, a] toBuilderWithCommas :: [Text] -> Builder toBuilderWithCommas = mconcatIntersperse B.fromText (singleton ',') -- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -- Smart constructors -- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% mkHex3 :: Char -> Char -> Char -> Color mkHex3 r g b | isHexDigit r && isHexDigit g && isHexDigit b = Hex3 (toLower r) (toLower g) (toLower b) | otherwise = error "passing non hexadecimal arguments to mkHex3" mkHex6 :: String -> String -> String -> Color mkHex6 r g b | allHex r && allHex g && allHex b = Hex6 (strToLower r) (strToLower g) (strToLower b) | otherwise = error "passing non hexadecimal arguments to mkHex6" mkHex4 :: Char -> Char -> Char -> Char -> Color mkHex4 r g b a | isHexDigit r && isHexDigit g && isHexDigit b && isHexDigit a = Hex4 (toLower r) (toLower g) (toLower b) (toLower a) | otherwise = error "passing non hexadecimal arguments to mkHex4" mkHex8 :: String -> String -> String -> String -> Color mkHex8 r g b a | allHex r && allHex g && allHex b = Hex8 (strToLower r) (strToLower g) (strToLower b) (strToLower a) | otherwise = error "passing non hexadecimal arguments to mkHex6" mkNamed :: Text -> Maybe Color mkNamed colorName | Map.member name colorMap = Just (Named name) | otherwise = Nothing where name = T.toLower colorName mkHSL :: Int -> Percentage -> Percentage -> Color mkHSL h s l = HSL (h `mod` 360) (bound s) (bound l) where bound = restrict 0 100 mkHSLA :: Int -> Percentage -> Percentage -> Alphavalue -> Color mkHSLA h s l = HSLA (h `mod` 360) (bound s) (bound l) where bound = restrict 0 100 mkRGBInt :: Word8 -> Word8 -> Word8 -> Color mkRGBInt r g b = RGBInt (bound r) (bound g) (bound b) where bound = restrict 0 255 mkRGBPer :: Percentage -> Percentage -> Percentage -> Color mkRGBPer r g b = RGBPer (bound r) (bound g) (bound b) where bound = restrict 0 100 mkRGBAInt :: Word8 -> Word8 -> Word8 -> Alphavalue -> Color mkRGBAInt r g b = RGBAInt (bound r) (bound g) (bound b) where bound = restrict 0 255 mkRGBAPer :: Percentage -> Percentage -> Percentage -> Alphavalue -> Color mkRGBAPer r g b = RGBAPer (bound r) (bound g) (bound b) where bound = restrict 0 100 allHex :: String -> Bool allHex = all isHexDigit strToLower :: String -> String strToLower = map toLower ------------------------------------------------------------------------------- toHexShorthand :: Color -> Color toHexShorthand c@(Hex6 [r1,r2] [g1,g2] [b1,b2]) | r1 == r2 && g1 == g2 && b1 == b2 = Hex3 r1 g1 b1 | otherwise = c toHexShorthand c@(Hex8 [r1,r2] [g1,g2] [b1,b2] [a1,a2]) | r1 == r2 && g1 == g2 && b1 == b2 && a1 == a2 = Hex4 r1 g1 b1 a1 | otherwise = c toHexShorthand h = h -- Returns hexadecimal equivalent as a string of two characters -- (i.e. for values in the range [0,15], a leading zero is added). word8ToHex :: Word8 -> String word8ToHex n | 0 <= n && n < 16 = '0':[intToDigit num] | otherwise = intToDigit sndRemainder : [intToDigit fstRemainder] where num = fromIntegral n fstRemainder = num `mod` 16 sndRemainder = (num `quot` 16) `mod` 16 -- Takes a color to a Hex6 or Hex8 representation unless it's an invalid -- keyword, in which case it remains the same toLongHex :: Color -> Color toLongHex c@(Named s) = fromMaybe c (Map.lookup (T.toLower s) colorMap) toLongHex (RGBAInt r g b a) = Hex8 (word8ToHex r) (word8ToHex g) (word8ToHex b) (ratToHex a) where ratToHex :: Alphavalue -> String ratToHex n = word8ToHex . round $ toRational n * 255 toLongHex (RGBInt r g b) = Hex6 (word8ToHex r) (word8ToHex g) (word8ToHex b) toLongHex c@RGBPer{} = toLongHex $ toRGBAInt c toLongHex c@RGBAPer{} = toLongHex $ toRGBAInt c toLongHex c@HSL{} = toLongHex $ toRGBAInt c toLongHex c@HSLA{} = toLongHex $ toRGBAInt c toLongHex (Hex3 r g b) = Hex6 [r,r] [g,g] [b,b] toLongHex (Hex4 r g b a) = Hex8 [r,r] [g,g] [b,b] [a,a] toLongHex a = a -- This fold works in general to convert hexadecimals into Integers, but we -- only need it for Word8 hexToWord8 :: String -> Word8 hexToWord8 = fromIntegral . foldl (\s c -> s*16 + digitToInt c) 0 toRGBAInt :: Color -> Color toRGBAInt (Named s) = maybe (error e) toRGBAInt (Map.lookup t colorMap) where e = T.unpack $ "Invalid color keyword (" <> s <> "). Can't convert to rgba" t = T.toLower s toRGBAInt (Hex3 r g b) = RGBAInt (f [r,r]) (f [g,g]) (f [b,b]) 1 where f = fromIntegral . hexToWord8 toRGBAInt (Hex6 r g b) = RGBAInt (hexToWord8 r) (hexToWord8 g) (hexToWord8 b) 1 toRGBAInt (Hex4 r g b a) = RGBAInt (f [r,r]) (f [g,g]) (f [b,b]) (h [a,a]) where f = fromIntegral . hexToWord8 h = toAlphavalue . hexToWord8 toRGBAInt (Hex8 r g b a) = RGBAInt (hexToWord8 r) (hexToWord8 g) (hexToWord8 b) (toAlphavalue $ toRational (hexToWord8 a) / 255) toRGBAInt (RGBInt r g b) = RGBAInt r g b 1 toRGBAInt (RGBPer r g b) = RGBAInt (f r) (f g) (f b) 1 where f = round . (2.55*) toRGBAInt (RGBAPer r g b a) = RGBAInt (f r) (f g) (f b) a where f = round . (2.55*) toRGBAInt c@RGBAInt{} = c toRGBAInt (HSL h s l) = withAlpha 1 $ hslToRgb (h, s, l) toRGBAInt (HSLA h s l a) = withAlpha a $ hslToRgb (h, s, l) withAlpha :: Alphavalue -> (Word8, Word8, Word8) -> Color withAlpha a (r, g, b) = RGBAInt r g b a hslToRgb :: (Int, Percentage, Percentage) -> (Word8, Word8, Word8) hslToRgb (hue, sat, light) | s == 0 = (lumToRgb, lumToRgb, lumToRgb) | l <= 0.5 = hslToRgb' h l (l * (s+1)) | otherwise = hslToRgb' h l (l + s - l*s) where h = toPercentage hue / 360 s = sat / 100 l = light / 100 lumToRgb = round (l * 255) hslToRgb' :: Percentage -> Percentage -> Percentage -> (Word8, Word8, Word8) hslToRgb' h l t2 = (r, g, b) where t1 = l*2 - t2 r = round $ 255 * hueToRgb t1 t2 (h + Percentage (1 % 3)) g = round $ 255 * hueToRgb t1 t2 h b = round $ 255 * hueToRgb t1 t2 (h - Percentage (1 % 3)) hueToRgb :: Percentage -> Percentage -> Percentage -> Percentage hueToRgb t1 t2 hue | hue < 0 = test t1 t2 (hue+1) | hue > 1 = test t1 t2 (hue-1) | otherwise = test t1 t2 hue where test :: Percentage -> Percentage -> Percentage -> Percentage test a b h | h * 6 < 1 = a + (b-a) * 6 * h | h * 2 < 1 = b | h * 3 < 2 = a + (b-a) * (Percentage (2 % 3) - h) * 6 | otherwise = a -- | A map with hex values as keys, and their minimal colorname as value minimalColorMap :: Map.Map Color Color minimalColorMap = Map.fromList minimalColors where minimalColors = [(Hex6 "ff" "00" "00", Named "red") ,(Hex6 "d2" "b4" "8c", Named "tan") ,(Hex6 "00" "ff" "ff", Named "aqua") ,(Hex6 "00" "00" "ff", Named "blue") ,(Hex6 "00" "ff" "ff", Named "cyan") ,(Hex6 "ff" "d7" "00", Named "gold") ,(Hex6 "80" "80" "80", Named "gray") ,(Hex6 "80" "80" "80", Named "grey") ,(Hex6 "00" "ff" "00", Named "lime") ,(Hex6 "00" "00" "80", Named "navy") ,(Hex6 "cd" "85" "3f", Named "peru") ,(Hex6 "ff" "c0" "cb", Named "pink") ,(Hex6 "dd" "a0" "dd", Named "plum") ,(Hex6 "ff" "fa" "fa", Named "snow") ,(Hex6 "00" "80" "80", Named "teal") ,(Hex6 "f0" "ff" "ff", Named "azure") ,(Hex6 "f5" "f5" "dc", Named "beige") ,(Hex6 "a5" "2a" "2a", Named "brown") ,(Hex6 "ff" "7f" "50", Named "coral") ,(Hex6 "00" "80" "00", Named "green") ,(Hex6 "ff" "ff" "f0", Named "ivory") ,(Hex6 "f0" "e6" "8c", Named "khaki") ,(Hex6 "fa" "f0" "e6", Named "linen") ,(Hex6 "80" "80" "00", Named "olive") ,(Hex6 "f5" "de" "b3", Named "wheat") ,(Hex6 "ff" "e4" "c4", Named "bisque") ,(Hex6 "4b" "00" "82", Named "indigo") ,(Hex6 "80" "00" "00", Named "maroon") ,(Hex6 "ff" "a5" "00", Named "orange") ,(Hex6 "da" "70" "d6", Named "orchid") ,(Hex6 "80" "00" "80", Named "purple") ,(Hex6 "fa" "80" "72", Named "salmon") ,(Hex6 "a0" "52" "2d", Named "sienna") ,(Hex6 "c0" "c0" "c0", Named "silver") ,(Hex6 "ff" "63" "47", Named "tomato") ,(Hex6 "ee" "82" "ee", Named "violet")] -- | Mapping between color names and hex values colorMap :: Map.Map Text Color colorMap = Map.fromList keywordColors -- | Pairs of color keywords, and their equivalent hexadecimal value. keywordColors :: [(Text, Color)] keywordColors = map (first T.toLower) [("aliceblue", Hex6 "f0" "f8" "ff") ,("antiquewhite", Hex6 "fa" "eb" "d7") ,("aqua", Hex6 "00" "ff" "ff") ,("aquamarine", Hex6 "7f" "ff" "d4") ,("azure", Hex6 "f0" "ff" "ff") ,("beige", Hex6 "f5" "f5" "dc") ,("bisque", Hex6 "ff" "e4" "c4") ,("black", Hex6 "00" "00" "00") ,("blanchedalmond", Hex6 "ff" "eb" "cd") ,("blue", Hex6 "00" "00" "ff") ,("blueviolet", Hex6 "8a" "2b" "e2") ,("brown", Hex6 "a5" "2a" "2a") ,("burlywood", Hex6 "de" "b8" "87") ,("cadetblue", Hex6 "5f" "9e" "a0") ,("chartreuse", Hex6 "7f" "ff" "00") ,("chocolate", Hex6 "d2" "69" "1e") ,("coral", Hex6 "ff" "7f" "50") ,("cornflowerblue", Hex6 "64" "95" "ed") ,("cornsilk", Hex6 "ff" "f8" "dc") ,("crimson", Hex6 "dc" "14" "3c") ,("cyan", Hex6 "00" "ff" "ff") ,("darkblue", Hex6 "00" "00" "8b") ,("darkcyan", Hex6 "00" "8b" "8b") ,("darkgoldenrod", Hex6 "b8" "86" "0b") ,("darkgray", Hex6 "a9" "a9" "a9") ,("darkgrey", Hex6 "a9" "a9" "a9") ,("darkgreen", Hex6 "00" "64" "00") ,("darkkhaki", Hex6 "bd" "b7" "6b") ,("darkmagenta", Hex6 "8b" "00" "8b") ,("darkolivegreen", Hex6 "55" "6b" "2f") ,("darkorange", Hex6 "ff" "8c" "00") ,("darkorchid", Hex6 "99" "32" "cc") ,("darkred", Hex6 "8b" "00" "00") ,("darksalmon", Hex6 "e9" "96" "7a") ,("darkseagreen", Hex6 "8f" "bc" "8f") ,("darkslateblue", Hex6 "48" "3d" "8b") ,("darkslategray", Hex6 "2f" "4f" "4f") ,("darkslategrey", Hex6 "2f" "4f" "4f") ,("darkturquoise", Hex6 "00" "ce" "d1") ,("darkviolet", Hex6 "94" "00" "d3") ,("deeppink", Hex6 "ff" "14" "93") ,("deepskyblue", Hex6 "00" "bf" "ff") ,("dimgray", Hex6 "69" "69" "69") ,("dimgrey", Hex6 "69" "69" "69") ,("dodgerblue", Hex6 "1e" "90" "ff") ,("firebrick", Hex6 "b2" "22" "22") ,("floralwhite", Hex6 "ff" "fa" "f0") ,("forestgreen", Hex6 "22" "8b" "22") ,("fuchsia", Hex6 "ff" "00" "ff") ,("gainsboro", Hex6 "dc" "dc" "dc") ,("ghostwhite", Hex6 "f8" "f8" "ff") ,("gold", Hex6 "ff" "d7" "00") ,("goldenrod", Hex6 "da" "a5" "20") ,("gray", Hex6 "80" "80" "80") ,("grey", Hex6 "80" "80" "80") ,("green", Hex6 "00" "80" "00") ,("greenyellow", Hex6 "ad" "ff" "2f") ,("honeydew", Hex6 "f0" "ff" "f0") ,("hotpink", Hex6 "ff" "69" "b4") ,("indianred", Hex6 "cd" "5c" "5c") ,("indigo", Hex6 "4b" "00" "82") ,("ivory", Hex6 "ff" "ff" "f0") ,("khaki", Hex6 "f0" "e6" "8c") ,("lavender", Hex6 "e6" "e6" "fa") ,("lavenderblush", Hex6 "ff" "f0" "f5") ,("lawngreen", Hex6 "7c" "fc" "00") ,("lemonchiffon", Hex6 "ff" "fa" "cd") ,("lightblue", Hex6 "ad" "d8" "e6") ,("lightcoral", Hex6 "f0" "80" "80") ,("lightcyan", Hex6 "e0" "ff" "ff") ,("lightgoldenrodyellow", Hex6 "fa" "fa" "d2") ,("lightgray", Hex6 "d3" "d3" "d3") ,("lightgrey", Hex6 "d3" "d3" "d3") ,("lightgreen", Hex6 "90" "ee" "90") ,("lightpink", Hex6 "ff" "b6" "c1") ,("lightsalmon", Hex6 "ff" "a0" "7a") ,("lightseagreen", Hex6 "20" "b2" "aa") ,("lightskyblue", Hex6 "87" "ce" "fa") ,("lightslategray", Hex6 "77" "88" "99") ,("lightslategrey", Hex6 "77" "88" "99") ,("lightsteelblue", Hex6 "b0" "c4" "de") ,("lightyellow", Hex6 "ff" "ff" "e0") ,("lime", Hex6 "00" "ff" "00") ,("limegreen", Hex6 "32" "cd" "32") ,("linen", Hex6 "fa" "f0" "e6") ,("magenta", Hex6 "ff" "00" "ff") ,("maroon", Hex6 "80" "00" "00") ,("mediumaquamarine", Hex6 "66" "cd" "aa") ,("mediumblue", Hex6 "00" "00" "cd") ,("mediumorchid", Hex6 "ba" "55" "d3") ,("mediumpurple", Hex6 "93" "70" "d8") ,("mediumseagreen", Hex6 "3c" "b3" "71") ,("mediumslateblue", Hex6 "7b" "68" "ee") ,("mediumspringgreen", Hex6 "00" "fa" "9a") ,("mediumturquoise", Hex6 "48" "d1" "cc") ,("mediumvioletred", Hex6 "c7" "15" "85") ,("midnightblue", Hex6 "19" "19" "70") ,("mintcream", Hex6 "f5" "ff" "fa") ,("mistyrose", Hex6 "ff" "e4" "e1") ,("moccasin", Hex6 "ff" "e4" "b5") ,("navajowhite", Hex6 "ff" "de" "ad") ,("navy", Hex6 "00" "00" "80") ,("oldlace", Hex6 "fd" "f5" "e6") ,("olive", Hex6 "80" "80" "00") ,("olivedrab", Hex6 "6b" "8e" "23") ,("orange", Hex6 "ff" "a5" "00") ,("orangered", Hex6 "ff" "45" "00") ,("orchid", Hex6 "da" "70" "d6") ,("palegoldenrod", Hex6 "ee" "e8" "aa") ,("palegreen", Hex6 "98" "fb" "98") ,("paleturquoise", Hex6 "af" "ee" "ee") ,("palevioletred", Hex6 "d8" "70" "93") ,("papayawhip", Hex6 "ff" "ef" "d5") ,("peachpuff", Hex6 "ff" "da" "b9") ,("peru", Hex6 "cd" "85" "3f") ,("pink", Hex6 "ff" "c0" "cb") ,("plum", Hex6 "dd" "a0" "dd") ,("powderblue", Hex6 "b0" "e0" "e6") ,("purple", Hex6 "80" "00" "80") ,("red", Hex6 "ff" "00" "00") ,("rosybrown", Hex6 "bc" "8f" "8f") ,("royalblue", Hex6 "41" "69" "e1") ,("saddlebrown", Hex6 "8b" "45" "13") ,("salmon", Hex6 "fa" "80" "72") ,("sandybrown", Hex6 "f4" "a4" "60") ,("seagreen", Hex6 "2e" "8b" "57") ,("seashell", Hex6 "ff" "f5" "ee") ,("sienna", Hex6 "a0" "52" "2d") ,("silver", Hex6 "c0" "c0" "c0") ,("skyblue", Hex6 "87" "ce" "eb") ,("slateblue", Hex6 "6a" "5a" "cd") ,("slategray", Hex6 "70" "80" "90") ,("slategrey", Hex6 "70" "80" "90") ,("snow", Hex6 "ff" "fa" "fa") ,("springgreen", Hex6 "00" "ff" "7f") ,("steelblue", Hex6 "46" "82" "b4") ,("tan", Hex6 "d2" "b4" "8c") ,("teal", Hex6 "00" "80" "80") ,("thistle", Hex6 "d8" "bf" "d8") ,("transparent", Hex8 "00" "00" "00" "00") ,("tomato", Hex6 "ff" "63" "47") ,("turquoise", Hex6 "40" "e0" "d0") ,("violet", Hex6 "ee" "82" "ee") ,("wheat", Hex6 "f5" "de" "b3") ,("white", Hex6 "ff" "ff" "ff") ,("whitesmoke", Hex6 "f5" "f5" "f5") ,("yellow", Hex6 "ff" "ff" "00") ,("yellowgreen", Hex6 "9a" "cd" "32")]