blob: d810dec90dbc64135b5b1043c64a51a154b3fd4e [file] [log] [blame]
/**
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright (C) 2000 and onwards Google, Inc.
//
// Author: Feng Hu
//
// .cc for the HtmlColor class
#include "webutil/html/htmlcolor.h"
#include <string.h>
#include <cmath>
#include "base/stringprintf.h"
#include "strings/ascii_ctype.h"
#include "strings/case.h"
#include "strings/escaping.h"
typedef struct RgbValue {
unsigned char r_;
unsigned char g_;
unsigned char b_;
} RgbValue;
// color table
// when making change to known_color_values, please
// also change the GetKnownColorValue function because
// entire table is hardcoded into the function for efficiency
// clang-format off
static const RgbValue known_color_values[] = {
/* 49 aliceblue */{240, 248, 255},
/* 50 antiquewhite */{250, 235, 215},
/* 51 aqua */{ 0, 255, 255},
/* 52 aquamarine */{127, 255, 212},
/* 53 azure */{240, 255, 255},
/* 54 beige */{245, 245, 220},
/* 55 bisque */{255, 228, 196},
/* 56 black */{ 0, 0, 0},
/* 57 blanchedalmond */{255, 235, 205},
/* 58 blue */{ 0, 0, 255},
/* 59 blueviolet */{138, 43, 226},
/* 60 brown */{165, 42, 42},
/* 61 burlywood */{222, 184, 135},
/* 62 cadetblue */{ 95, 158, 160},
/* 63 chartreuse */{127, 255, 0},
/* 64 chocolate */{210, 105, 30},
/* 65 coral */{255, 127, 80},
/* 66 cornflowerblue */{100, 149, 237},
/* 67 cornsilk */{255, 248, 220},
/* 68 crimson */{220, 20, 60},
/* 69 cyan */{ 0, 255, 255},
/* 70 darkblue */{ 0, 0, 139},
/* 71 darkcyan */{ 0, 139, 139},
/* 72 darkgoldenrod */{184, 134, 11},
/* 73 darkgray */{169, 169, 169},
/* 74 darkgreen */{ 0, 100, 0},
/* 75 darkgrey */{169, 169, 169},
/* 76 darkkhaki */{189, 183, 107},
/* 77 darkmagenta */{139, 0, 139},
/* 78 darkolivegreen */{ 85, 107, 47},
/* 79 darkorange */{255, 140, 0},
/* 80 darkorchid */{153, 50, 204},
/* 81 darkred */{139, 0, 0},
/* 82 darksalmon */{233, 150, 122},
/* 83 darkseagreen */{143, 188, 143},
/* 84 darkslateblue */{ 72, 61, 139},
/* 85 darkslategray */{ 47, 79, 79},
/* 86 darkslategrey */{ 47, 79, 79},
/* 87 darkturquoise */{ 0, 206, 209},
/* 88 darkviolet */{148, 0, 211},
/* 89 deeppink */{255, 20, 147},
/* 90 deepskyblue */{ 0, 191, 255},
/* 91 dimgray */{105, 105, 105},
/* 92 dimgrey */{105, 105, 105},
/* 93 dodgerblue */{ 30, 144, 255},
/* 94 firebrick */{178, 34, 34},
/* 95 floralwhite */{255, 250, 240},
/* 96 forestgreen */{ 34, 139, 34},
/* 97 fuchsia */{255, 0, 255},
/* 98 gainsboro */{220, 220, 220},
/* 99 ghostwhite */{248, 248, 255},
/*100 gold */{255, 215, 0},
/*101 goldenrod */{218, 165, 32},
/*102 gray */{128, 128, 128},
/*103 green */{ 0, 128, 0},
/*104 grey */{128, 128, 128},
/*105 greenyellow */{173, 255, 47},
/*106 honeydew */{240, 255, 240},
/*107 hotpink */{255, 105, 180},
/*108 indianred */{205, 92, 92},
/*109 indigo */{ 75, 0, 130},
/*110 ivory */{255, 255, 240},
/*111 khaki */{240, 230, 140},
/*112 lavender */{230, 230, 250},
/*113 lavenderblush */{255, 240, 245},
/*114 lawngreen */{124, 252, 0},
/*115 lemonchiffon */{255, 250, 205},
/*116 lightblue */{173, 216, 230},
/*117 lightcoral */{240, 128, 128},
/*118 lightcyan */{224, 255, 255},
/*119 lightgoldenrodyellow */{250, 250, 210},
/*120 lightgray */{211, 211, 211},
/*121 lightgreen */{144, 238, 144},
/*122 lightgrey */{211, 211, 211},
/*123 lightpink */{255, 182, 193},
/*124 lightsalmon */{255, 160, 122},
/*125 lightseagreen */{ 32, 178, 170},
/*126 lightskyblue */{135, 206, 250},
/*127 lightslategray */{119, 136, 153},
/*128 lightslategrey */{119, 136, 153},
/*129 lightsteelblue */{176, 196, 222},
/*130 lightyellow */{255, 255, 224},
/*131 lime */{ 0, 255, 0},
/*132 limegreen */{ 50, 205, 50},
/*133 linen */{250, 240, 230},
/*134 magenta */{255, 0, 255},
/*135 maroon */{128, 0, 0},
/*136 mediumaquamarine */{102, 205, 170},
/*137 mediumblue */{ 0, 0, 205},
/*138 mediumorchid */{186, 85, 211},
/*139 mediumpurple */{147, 112, 219},
/*140 mediumseagreen */{ 60, 179, 113},
/*141 mediumslateblue */{123, 104, 238},
/*142 mediumspringgreen */{ 0, 250, 154},
/*143 mediumturquoise */{ 72, 209, 204},
/*144 mediumvioletred */{199, 21, 133},
/*145 midnightblue */{ 25, 25, 112},
/*146 mintcream */{245, 255, 250},
/*147 mistyrose */{255, 228, 225},
/*148 moccasin */{255, 228, 181},
/*149 navajowhite */{255, 222, 173},
/*150 navy */{ 0, 0, 128},
/*151 oldlace */{253, 245, 230},
/*152 olive */{128, 128, 0},
/*153 olivedrab */{107, 142, 35},
/*154 orange */{255, 165, 0},
/*155 orangered */{255, 69, 0},
/*156 orchid */{218, 112, 214},
/*157 palegoldenrod */{238, 232, 170},
/*158 palegreen */{152, 251, 152},
/*159 paleturquoise */{175, 238, 238},
/*160 palevioletred */{219, 112, 147},
/*161 papayawhip */{255, 239, 213},
/*162 peachpuff */{255, 218, 185},
/*163 peru */{205, 133, 63},
/*164 pink */{255, 192, 203},
/*165 plum */{221, 160, 221},
/*166 powderblue */{176, 224, 230},
/*167 purple */{128, 0, 128},
/*168 red */{255, 0, 0},
/*169 rosybrown */{188, 143, 143},
/*170 royalblue */{ 65, 105, 225},
/*171 saddlebrown */{139, 69, 19},
/*172 salmon */{250, 128, 114},
/*173 sandybrown */{244, 164, 96},
/*174 seagreen */{ 46, 139, 87},
/*175 seashell */{255, 245, 238},
/*176 sienna */{160, 82, 45},
/*177 silver */{192, 192, 192},
/*178 skyblue */{135, 206, 235},
/*179 slateblue */{106, 90, 205},
/*180 slategray */{112, 128, 144},
/*181 slategrey */{112, 128, 144},
/*182 snow */{255, 250, 250},
/*183 springgreen */{ 0, 255, 127},
/*184 steelblue */{ 70, 130, 180},
/*185 tan */{210, 180, 140},
/*186 teal */{ 0, 128, 128},
/*187 thistle */{216, 191, 216},
/*188 tomato */{255, 99, 71},
/*189 turquoise */{ 64, 224, 208},
/*190 violet */{238, 130, 238},
/*191 wheat */{245, 222, 179},
/*192 white */{255, 255, 255},
/*193 whitesmoke */{245, 245, 245},
/*194 yellow */{255, 255, 0},
/*195 yellowgreen */{154, 205, 50},
}; // clang-format on
// here the entire table is hardcoded into the function
// mainly because of consideration of efficiency
static const RgbValue* GetKnownColorValue(StringPiece colorstr) {
if (colorstr.size() <= 2) {
return NULL;
}
switch (ascii_tolower(colorstr[0])) {
case 'a':
switch (ascii_tolower(colorstr[1])) {
case 'l':
if (CaseEqual("aliceblue", colorstr)) {
return &known_color_values[0];
}
return NULL;
case 'n':
if (CaseEqual("antiquewhite", colorstr)) {
return &known_color_values[1];
}
return NULL;
case 'q':
if (CaseEqual("aqua", colorstr)) {
return &known_color_values[2];
} else if (CaseEqual("aquamarine", colorstr)) {
return &known_color_values[3];
}
return NULL;
case 'z':
if (CaseEqual("azure", colorstr)) {
return &known_color_values[4];
}
return NULL;
}
return NULL;
case 'b':
switch (ascii_tolower(colorstr[1])) {
case 'e':
if (CaseEqual("beige", colorstr)) {
return &known_color_values[5];
}
return NULL;
case 'i':
if (CaseEqual("bisque", colorstr)) {
return &known_color_values[6];
}
return NULL;
case 'l':
if (CaseEqual("black", colorstr)) {
return &known_color_values[7];
} else if (CaseEqual("blanchedalmond", colorstr)) {
return &known_color_values[8];
} else if (CaseEqual("blue", colorstr)) {
return &known_color_values[9];
} else if (CaseEqual("blueviolet", colorstr)) {
return &known_color_values[10];
}
return NULL;
case 'r':
if (CaseEqual("brown", colorstr)) {
return &known_color_values[11];
}
return NULL;
case 'u':
if (CaseEqual("burlywood", colorstr)) {
return &known_color_values[12];
}
return NULL;
}
return NULL;
case 'c':
switch (ascii_tolower(colorstr[1])) {
case 'a':
if (CaseEqual("cadetblue", colorstr)) {
return &known_color_values[13];
}
return NULL;
case 'h':
if (CaseEqual("chartreuse", colorstr)) {
return &known_color_values[14];
} else if (CaseEqual("chocolate", colorstr)) {
return &known_color_values[15];
}
return NULL;
case 'o':
if (CaseEqual("coral", colorstr)) {
return &known_color_values[16];
} else if (CaseEqual("cornflowerblue", colorstr)) {
return &known_color_values[17];
} else if (CaseEqual("cornsilk", colorstr)) {
return &known_color_values[18];
}
return NULL;
case 'r':
if (CaseEqual("crimson", colorstr)) {
return &known_color_values[19];
}
return NULL;
case 'y':
if (CaseEqual("cyan", colorstr)) {
return &known_color_values[20];
}
return NULL;
}
return NULL;
case 'd':
switch (ascii_tolower(colorstr[1])) {
case 'a':
if (CaseEqual("darkblue", colorstr)) {
return &known_color_values[21];
} else if (CaseEqual("darkcyan", colorstr)) {
return &known_color_values[22];
} else if (CaseEqual("darkgoldenrod", colorstr)) {
return &known_color_values[23];
} else if (CaseEqual("darkgray", colorstr)) {
return &known_color_values[24];
} else if (CaseEqual("darkgreen", colorstr)) {
return &known_color_values[25];
} else if (CaseEqual("darkgrey", colorstr)) {
return &known_color_values[26];
} else if (CaseEqual("darkkhaki", colorstr)) {
return &known_color_values[27];
} else if (CaseEqual("darkmagenta", colorstr)) {
return &known_color_values[28];
} else if (CaseEqual("darkolivegreen", colorstr)) {
return &known_color_values[29];
} else if (CaseEqual("darkorange", colorstr)) {
return &known_color_values[30];
} else if (CaseEqual("darkorchid", colorstr)) {
return &known_color_values[31];
} else if (CaseEqual("darkred", colorstr)) {
return &known_color_values[32];
} else if (CaseEqual("darksalmon", colorstr)) {
return &known_color_values[33];
} else if (CaseEqual("darkseagreen", colorstr)) {
return &known_color_values[34];
} else if (CaseEqual("darkslateblue", colorstr)) {
return &known_color_values[35];
} else if (CaseEqual("darkslategray", colorstr)) {
return &known_color_values[36];
} else if (CaseEqual("darkslategrey", colorstr)) {
return &known_color_values[37];
} else if (CaseEqual("darkturquoise", colorstr)) {
return &known_color_values[38];
} else if (CaseEqual("darkviolet", colorstr)) {
return &known_color_values[39];
}
return NULL;
case 'e':
if (CaseEqual("deeppink", colorstr)) {
return &known_color_values[40];
} else if (CaseEqual("deepskyblue", colorstr)) {
return &known_color_values[41];
}
return NULL;
case 'i':
if (CaseEqual("dimgray", colorstr)) {
return &known_color_values[42];
} else if (CaseEqual("dimgrey", colorstr)) {
return &known_color_values[43];
}
return NULL;
case 'o':
if (CaseEqual("dodgerblue", colorstr)) {
return &known_color_values[44];
}
return NULL;
}
return NULL;
case 'f':
switch (ascii_tolower(colorstr[1])) {
case 'i':
if (CaseEqual("firebrick", colorstr)) {
return &known_color_values[45];
}
return NULL;
case 'l':
if (CaseEqual("floralwhite", colorstr)) {
return &known_color_values[46];
}
return NULL;
case 'o':
if (CaseEqual("forestgreen", colorstr)) {
return &known_color_values[47];
}
return NULL;
case 'u':
if (CaseEqual("fuchsia", colorstr)) {
return &known_color_values[48];
}
return NULL;
}
return NULL;
case 'g':
switch (ascii_tolower(colorstr[1])) {
case 'a':
if (CaseEqual("gainsboro", colorstr)) {
return &known_color_values[49];
}
return NULL;
case 'h':
if (CaseEqual("ghostwhite", colorstr)) {
return &known_color_values[50];
}
return NULL;
case 'o':
if (CaseEqual("gold", colorstr)) {
return &known_color_values[51];
} else if (CaseEqual("goldenrod", colorstr)) {
return &known_color_values[52];
}
return NULL;
case 'r':
if (CaseEqual("gray", colorstr)) {
return &known_color_values[53];
} else if (CaseEqual("green", colorstr)) {
return &known_color_values[54];
} else if (CaseEqual("grey", colorstr)) {
return &known_color_values[55];
} else if (CaseEqual("greenyellow", colorstr)) {
return &known_color_values[56];
}
return NULL;
}
return NULL;
case 'h':
if (CaseEqual("honeydew", colorstr)) {
return &known_color_values[57];
} else if (CaseEqual("hotpink", colorstr)) {
return &known_color_values[58];
}
return NULL;
case 'i':
if (CaseEqual("indianred", colorstr)) {
return &known_color_values[59];
} else if (CaseEqual("indigo", colorstr)) {
return &known_color_values[60];
} else if (CaseEqual("ivory", colorstr)) {
return &known_color_values[61];
}
return NULL;
case 'k':
if (CaseEqual("khaki", colorstr)) {
return &known_color_values[62];
}
return NULL;
case 'l':
switch (ascii_tolower(colorstr[1])) {
case 'a':
if (CaseEqual("lavender", colorstr)) {
return &known_color_values[63];
} else if (CaseEqual("lavenderblush", colorstr)) {
return &known_color_values[64];
} else if (CaseEqual("lawngreen", colorstr)) {
return &known_color_values[65];
}
return NULL;
case 'e':
if (CaseEqual("lemonchiffon", colorstr)) {
return &known_color_values[66];
}
return NULL;
case 'i':
if (CaseEqual("lightblue", colorstr)) {
return &known_color_values[67];
} else if (CaseEqual("lightcoral", colorstr)) {
return &known_color_values[68];
} else if (CaseEqual("lightcyan", colorstr)) {
return &known_color_values[69];
} else if (CaseEqual("lightgoldenrodyellow", colorstr)) {
return &known_color_values[70];
} else if (CaseEqual("lightgray", colorstr)) {
return &known_color_values[71];
} else if (CaseEqual("lightgreen", colorstr)) {
return &known_color_values[72];
} else if (CaseEqual("lightgrey", colorstr)) {
return &known_color_values[73];
} else if (CaseEqual("lightpink", colorstr)) {
return &known_color_values[74];
} else if (CaseEqual("lightsalmon", colorstr)) {
return &known_color_values[75];
} else if (CaseEqual("lightseagreen", colorstr)) {
return &known_color_values[76];
} else if (CaseEqual("lightskyblue", colorstr)) {
return &known_color_values[77];
} else if (CaseEqual("lightslategray", colorstr)) {
return &known_color_values[78];
} else if (CaseEqual("lightslategrey", colorstr)) {
return &known_color_values[79];
} else if (CaseEqual("lightsteelblue", colorstr)) {
return &known_color_values[80];
} else if (CaseEqual("lightyellow", colorstr)) {
return &known_color_values[81];
} else if (CaseEqual("lime", colorstr)) {
return &known_color_values[82];
} else if (CaseEqual("limegreen", colorstr)) {
return &known_color_values[83];
} else if (CaseEqual("linen", colorstr)) {
return &known_color_values[84];
}
return NULL;
}
return NULL;
case 'm':
switch (ascii_tolower(colorstr[1])) {
case 'a':
if (CaseEqual("magenta", colorstr)) {
return &known_color_values[85];
} else if (CaseEqual("maroon", colorstr)) {
return &known_color_values[86];
}
return NULL;
case 'e':
if (CaseEqual("mediumaquamarine", colorstr)) {
return &known_color_values[87];
} else if (CaseEqual("mediumblue", colorstr)) {
return &known_color_values[88];
} else if (CaseEqual("mediumorchid", colorstr)) {
return &known_color_values[89];
} else if (CaseEqual("mediumpurple", colorstr)) {
return &known_color_values[90];
} else if (CaseEqual("mediumseagreen", colorstr)) {
return &known_color_values[91];
} else if (CaseEqual("mediumslateblue", colorstr)) {
return &known_color_values[92];
} else if (CaseEqual("mediumspringgreen", colorstr)) {
return &known_color_values[93];
} else if (CaseEqual("mediumturquoise", colorstr)) {
return &known_color_values[94];
} else if (CaseEqual("mediumvioletred", colorstr)) {
return &known_color_values[95];
}
return NULL;
case 'i':
if (CaseEqual("midnightblue", colorstr)) {
return &known_color_values[96];
} else if (CaseEqual("mintcream", colorstr)) {
return &known_color_values[97];
} else if (CaseEqual("mistyrose", colorstr)) {
return &known_color_values[98];
}
return NULL;
case 'o':
if (CaseEqual("moccasin", colorstr)) {
return &known_color_values[99];
}
return NULL;
}
return NULL;
case 'n':
if (CaseEqual("navajowhite", colorstr)) {
return &known_color_values[100];
} else if (CaseEqual("navy", colorstr)) {
return &known_color_values[101];
}
return NULL;
case 'o':
switch (ascii_tolower(colorstr[1])) {
case 'l':
if (CaseEqual("oldlace", colorstr)) {
return &known_color_values[102];
} else if (CaseEqual("olive", colorstr)) {
return &known_color_values[103];
} else if (CaseEqual("olivedrab", colorstr)) {
return &known_color_values[104];
}
return NULL;
case 'r':
if (CaseEqual("orange", colorstr)) {
return &known_color_values[105];
} else if (CaseEqual("orangered", colorstr)) {
return &known_color_values[106];
} else if (CaseEqual("orchid", colorstr)) {
return &known_color_values[107];
}
return NULL;
}
return NULL;
case 'p':
switch (ascii_tolower(colorstr[1])) {
case 'a':
if (CaseEqual("palegoldenrod", colorstr)) {
return &known_color_values[108];
} else if (CaseEqual("palegreen", colorstr)) {
return &known_color_values[109];
} else if (CaseEqual("paleturquoise", colorstr)) {
return &known_color_values[110];
} else if (CaseEqual("palevioletred", colorstr)) {
return &known_color_values[111];
} else if (CaseEqual("papayawhip", colorstr)) {
return &known_color_values[112];
}
return NULL;
case 'e':
if (CaseEqual("peachpuff", colorstr)) {
return &known_color_values[113];
} else if (CaseEqual("peru", colorstr)) {
return &known_color_values[114];
}
return NULL;
case 'i':
if (CaseEqual("pink", colorstr)) {
return &known_color_values[115];
}
return NULL;
case 'l':
if (CaseEqual("plum", colorstr)) {
return &known_color_values[116];
}
return NULL;
case 'o':
if (CaseEqual("powderblue", colorstr)) {
return &known_color_values[117];
}
return NULL;
case 'u':
if (CaseEqual("purple", colorstr)) {
return &known_color_values[118];
}
return NULL;
}
return NULL;
case 'r':
if (CaseEqual("red", colorstr)) {
return &known_color_values[119];
} else if (CaseEqual("rosybrown", colorstr)) {
return &known_color_values[120];
} else if (CaseEqual("royalblue", colorstr)) {
return &known_color_values[121];
}
return NULL;
case 's':
switch (ascii_tolower(colorstr[1])) {
case 'a':
if (CaseEqual("saddlebrown", colorstr)) {
return &known_color_values[122];
} else if (CaseEqual("salmon", colorstr)) {
return &known_color_values[123];
} else if (CaseEqual("sandybrown", colorstr)) {
return &known_color_values[124];
}
return NULL;
case 'e':
if (CaseEqual("seagreen", colorstr)) {
return &known_color_values[125];
} else if (CaseEqual("seashell", colorstr)) {
return &known_color_values[126];
}
return NULL;
case 'i':
if (CaseEqual("sienna", colorstr)) {
return &known_color_values[127];
} else if (CaseEqual("silver", colorstr)) {
return &known_color_values[128];
}
return NULL;
case 'k':
if (CaseEqual("skyblue", colorstr)) {
return &known_color_values[129];
}
return NULL;
case 'l':
if (CaseEqual("slateblue", colorstr)) {
return &known_color_values[130];
} else if (CaseEqual("slategray", colorstr)) {
return &known_color_values[131];
} else if (CaseEqual("slategrey", colorstr)) {
return &known_color_values[132];
}
return NULL;
case 'n':
if (CaseEqual("snow", colorstr)) {
return &known_color_values[133];
}
return NULL;
case 'p':
if (CaseEqual("springgreen", colorstr)) {
return &known_color_values[134];
}
return NULL;
case 't':
if (CaseEqual("steelblue", colorstr)) {
return &known_color_values[135];
}
return NULL;
}
return NULL;
case 't':
switch (ascii_tolower(colorstr[1])) {
case 'a':
if (CaseEqual("tan", colorstr)) {
return &known_color_values[136];
}
return NULL;
case 'e':
if (CaseEqual("teal", colorstr)) {
return &known_color_values[137];
}
return NULL;
case 'h':
if (CaseEqual("thistle", colorstr)) {
return &known_color_values[138];
}
return NULL;
case 'o':
if (CaseEqual("tomato", colorstr)) {
return &known_color_values[139];
}
return NULL;
case 'u':
if (CaseEqual("turquoise", colorstr)) {
return &known_color_values[140];
}
return NULL;
}
return NULL;
case 'v':
if (CaseEqual("violet", colorstr)) {
return &known_color_values[141];
}
return NULL;
case 'w':
if (CaseEqual("wheat", colorstr)) {
return &known_color_values[142];
} else if (CaseEqual("white", colorstr)) {
return &known_color_values[143];
} else if (CaseEqual("whitesmoke", colorstr)) {
return &known_color_values[144];
}
return NULL;
case 'y':
if (CaseEqual("yellow", colorstr)) {
return &known_color_values[145];
} else if (CaseEqual("yellowgreen", colorstr)) {
return &known_color_values[146];
}
return NULL;
}
return NULL;
} // NOLINT
const unsigned char HtmlColor::kGoodColorValue;
const unsigned char HtmlColor::kBadColorName;
const unsigned char HtmlColor::kBadColorHex;
static inline int TwoXDigitsToNum(StringPiece xstr) {
return (strings::hex_digit_to_int(xstr[0]) * 16 +
strings::hex_digit_to_int(xstr[1]));
}
void HtmlColor::SetValueFromHexStr(StringPiece hexstr) {
int hexstr_len = hexstr.size();
const char* finalstr = hexstr.data();
char hexbuf[7];
if (hexstr_len == 3) {
for (int i = 0; i < 3; i++) {
if (!ascii_isxdigit(hexstr[i])) {
SetBadHexValue();
return;
}
hexbuf[2 * i] = hexstr[i];
hexbuf[2 * i + 1] = hexstr[i];
}
hexbuf[6] = '\0';
finalstr = hexbuf;
} else if (hexstr_len == 6) {
for (int i = 0; i < 6; i++) {
if (!ascii_isxdigit(hexstr[i])) {
SetBadHexValue();
return;
}
}
} else {
SetBadHexValue();
return;
}
r_ = static_cast<unsigned char>(TwoXDigitsToNum(finalstr));
g_ = static_cast<unsigned char>(TwoXDigitsToNum(finalstr + 2));
b_ = static_cast<unsigned char>(TwoXDigitsToNum(finalstr + 4));
is_bad_value_ = kGoodColorValue;
}
HtmlColor::HtmlColor(StringPiece str) { SetValueFromStr(str); }
HtmlColor::HtmlColor(const char* str, int colorstrlen) {
SetValueFromStr(StringPiece(str, colorstrlen));
}
void HtmlColor::SetValueFromStr(StringPiece colorstr) {
if (colorstr.size() > 0 && colorstr[0] == '#') {
// rgb value
colorstr.remove_prefix(1);
SetValueFromHexStr(colorstr);
} else {
SetValueFromName(colorstr);
if (!IsDefined() && colorstr.size() == 6) {
SetValueFromHexStr(colorstr);
if (!IsDefined()) SetBadNameValue();
}
}
}
void HtmlColor::SetValueFromName(StringPiece str) {
const RgbValue* p_rgb = GetKnownColorValue(str);
if (p_rgb) {
r_ = p_rgb->r_;
g_ = p_rgb->g_;
b_ = p_rgb->b_;
is_bad_value_ = kGoodColorValue;
} else {
SetBadNameValue();
}
}
void HtmlColor::SetValueFromRGB(unsigned char r, unsigned char g,
unsigned char b) {
r_ = r;
g_ = g;
b_ = b;
is_bad_value_ = kGoodColorValue;
}
static bool HSLtoRGB(double hue, double saturation, double lightness,
unsigned char* red, unsigned char* green,
unsigned char* blue) {
// Imlementation follows wikipedia:
// https://en.wikipedia.org/wiki/HSL_and_HSV#From_HSL
// The difference being that hue is in [0, 1) instead of [0, 360).
if (hue < 0.0 || hue >= 1.0 || saturation < 0.0 || saturation > 1.0 ||
lightness < 0.0 || lightness > 1.0) {
return false;
}
const double C = (1.0 - fabs(2.0 * lightness - 1.0)) * saturation;
const double m = lightness - (C / 2.0);
const double hue6 = hue * 6.0;
const double X = C * (1.0 - fabs(fmod(hue6, 2.0) - 1.0));
// Add m already to C and X before rounding.
const unsigned char Cbyte =
static_cast<unsigned char>(floor((C + m) * 255.0 + 0.5));
const unsigned char Xbyte =
static_cast<unsigned char>(floor((X + m) * 255.0 + 0.5));
const unsigned char Mbyte = static_cast<unsigned char>(floor(m * 255 + 0.5));
if (hue6 < 1.0) {
*red = Cbyte;
*green = Xbyte;
*blue = Mbyte;
} else if (hue6 < 2.0) {
*red = Xbyte;
*green = Cbyte;
*blue = Mbyte;
} else if (hue6 < 3.0) {
*red = Mbyte;
*green = Cbyte;
*blue = Xbyte;
} else if (hue6 < 4.0) {
*red = Mbyte;
*green = Xbyte;
*blue = Cbyte;
} else if (hue6 < 5.0) {
*red = Xbyte;
*green = Mbyte;
*blue = Cbyte;
} else { // hue6 < 6.0
*red = Cbyte;
*green = Mbyte;
*blue = Xbyte;
}
return true;
}
void HtmlColor::SetValueFromHSL(double hue, double saturation,
double lightness) {
if (HSLtoRGB(hue, saturation, lightness, &r_, &g_, &b_)) {
is_bad_value_ = kGoodColorValue;
} else {
SetBadHslValue();
}
}
HtmlColor::HtmlColor(unsigned char r, unsigned char g, unsigned char b) {
SetValueFromRGB(r, g, b);
}
static const float kLumR = 0.30;
static const float kLumG = 0.59;
static const float kLumB = 0.11;
// Converts from RGB to HSL. This is an algorithm derived from Fundamentals of
// Interactive Computer Graphics by Foley and van Dam (1982). A (slightly)
// modified formula can be found at
// http://en.wikipedia.org/wiki/HSL_color_space
// We adopt the notation that HSL values are expressed in the range of 0 to 1.
// Specifically, H is in [0, 1), while S and L are in [0, 1].
// (Another popular scheme normalizes HSL to [0, 360), [0, 100], and [0, 100],
// respectively).
static void RGBtoHSL(const HtmlColor& rgb, double* h, double* s, double* l) {
int r = rgb.r();
int g = rgb.g();
int b = rgb.b();
int max_v = (r < g) ? ((g < b) ? b : g) : ((b < r) ? r : b);
int min_v = (r < g) ? ((r < b) ? r : b) : ((b < g) ? b : g);
int sum = max_v + min_v;
double delta = static_cast<double>(max_v - min_v);
double dR = (max_v - r) / delta;
double dG = (max_v - g) / delta;
double dB = (max_v - b) / delta;
if (min_v == max_v)
*h = 0.0;
else if (r == max_v)
*h = (dB - dG) / 6.0;
else if (g == max_v)
*h = (2.0 + dR - dB) / 6.0;
else
*h = (4.0 + dG - dR) / 6.0;
if (*h < 0.0) *h += 1.0;
if (*h >= 1.0) *h -= 1.0;
*l = 0.5 * sum / 255.0;
if (max_v == 0 || min_v == 255)
*s = 0.0;
else if (sum <= 255)
*s = delta / sum;
else
*s = delta / (2 * 255 - sum);
}
// Calculates the Euclidean distance between two color vectors on a HSL sphere.
// A demo of the sphere can also be found at:
// http://en.wikipedia.org/wiki/HSL_color_space
// In short, a vector for color (H, S, L) in this system can be expressed as
// (S*L'*cos(2*PI*H), S*L'*sin(2*PI*H), L), where L' = abs(L - 0.5).
// And we simply calculate the l-2 distance using these coordinates.
static double HSLDistance(double h1, double s1, double l1, double h2, double s2,
double l2) {
double sl1, sl2;
if (l1 <= 0.5)
sl1 = s1 * l1;
else
sl1 = s1 * (1.0 - l1);
if (l2 <= 0.5)
sl2 = s2 * l2;
else
sl2 = s2 * (1.0 - l2);
double dH = (h1 - h2) * 2.0 * M_PI;
return (l1 - l2) * (l1 - l2) + sl1 * sl1 + sl2 * sl2 -
2 * sl1 * sl2 * cos(dH);
}
bool HtmlColor::IsSimilarInHSL(const HtmlColor& color, double level) const {
double h1, s1, l1, h2, s2, l2;
RGBtoHSL(*this, &h1, &s1, &l1);
RGBtoHSL(color, &h2, &s2, &l2);
return HSLDistance(h1, s1, l1, h2, s2, l2) <= level;
}
bool HtmlColor::GetValueInHSL(double* hue, double* saturation,
double* lightness) const {
if (!IsDefined()) return false;
RGBtoHSL(*this, hue, saturation, lightness);
return true;
}
// Calculate the luminance of the color
// Luminance is an integer value from 0 - 255, which
// represents the gray level that most closely corresponds
// to the perceived brightness of the color, based on
// the way the human eye sees color. The weights in
// the function below are pretty standard. See
// http://www.google.com/search?q=rgb+luminance+formula
int HtmlColor::Luminance() const {
if (!IsDefined()) return 0;
float luminance = (kLumR * static_cast<float>(r_)) +
(kLumG * static_cast<float>(g_)) +
(kLumB * static_cast<float>(b_));
return static_cast<int>(luminance);
}
void HtmlColor::Lighten(float factor) {
HtmlColor white("ffffff", 6);
BlendWithColor(1.0 - factor, white);
}
void HtmlColor::Darken(float factor) {
HtmlColor black("000000", 6);
BlendWithColor(1.0 - factor, black);
}
void HtmlColor::Desaturate(float factor) {
if (!IsDefined() || factor < 0 || factor > 1) return;
unsigned char lum = static_cast<unsigned char>(Luminance());
HtmlColor gray(lum, lum, lum);
BlendWithColor(1.0 - factor, gray);
}
void HtmlColor::BlendWithColor(float factor, const HtmlColor& c) {
if (!IsDefined() || factor < 0 || factor > 1) return;
r_ = static_cast<unsigned char>(factor * r_ + (1 - factor) * c.r_);
g_ = static_cast<unsigned char>(factor * g_ + (1 - factor) * c.g_);
b_ = static_cast<unsigned char>(factor * b_ + (1 - factor) * c.b_);
}
// Return the color as a string for use in HTML
// This isn't the most efficient function. If you're using it a lot,
// you might want to rewrite it.
string HtmlColor::ToString() const {
return StringPrintf("#%02x%02x%02x", r_, g_, b_);
}
bool HtmlColor::Equals(const HtmlColor& color) const {
if (IsDefined() != color.IsDefined()) return false;
if (IsDefined()) return rgb() == color.rgb();
return true;
}
//
// == HtmlColorUtils ==
//
string HtmlColorUtils::MaybeConvertToCssShorthand(StringPiece orig_color) {
HtmlColor color(orig_color);
if (!color.IsDefined()) return orig_color.as_string();
string shorthand = MaybeConvertToCssShorthand(color);
if (shorthand.size() < orig_color.size()) {
return shorthand;
} else {
return orig_color.as_string();
}
}
string HtmlColorUtils::MaybeConvertToCssShorthand(const HtmlColor& color) {
// There are 16 color names which are supported by all known CSS compliant
// browsers. Of these 16, 9 are shorter than their hex equivalents. For
// this reason, we prefer to use the shorter color names in order to save
// bytes.
switch (color.rgb()) {
case 0x000080:
return "navy";
case 0x008000:
return "green";
case 0x008080:
return "teal";
case 0x800000:
return "maroon";
case 0x800080:
return "purple";
case 0x808000:
return "olive";
case 0x808080:
return "gray";
case 0xC0C0C0:
return "silver";
case 0xFF0000:
return "red";
}
if ((color.r() >> 4) != (color.r() & 0xF) ||
(color.g() >> 4) != (color.g() & 0xF) ||
(color.b() >> 4) != (color.b() & 0xF))
return color.ToString();
return StringPrintf("#%01x%01x%01x", color.r() & 0xF, color.g() & 0xF,
color.b() & 0xF);
}