Skip to content

Commit e67717c

Browse files
committed
c++ port
0 parents  commit e67717c

File tree

5 files changed

+373
-0
lines changed

5 files changed

+373
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.o
2+
/color

Makefile

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
CXXFLAGS = -std=c++11 -Wall -Wextra -Wno-unused-parameter
2+
3+
BIN = color
4+
SRCS = main.cpp
5+
SRCS += csscolorparser.cpp
6+
OBJS = $(patsubst %.cpp,%.o,$(SRCS))
7+
8+
build: $(BIN)
9+
10+
$(BIN): $(OBJS)
11+
$(CXX) $(CXXFLAGS) -o $@ $^
12+
13+
%.o: %.cpp
14+
$(CXX) $(CXXFLAGS) -c -o $@ $^
15+
16+
clean:
17+
rm -rf *.o $(BIN)
18+
19+
.PHONY: clean

csscolorparser.cpp

Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
// (c) Dean McNamee <dean@gmail.com>, 2012.
2+
// C++ port by Konstantin Käfer <mail@kkaefer.com>, 2014.
3+
//
4+
// https://github.com/deanm/css-color-parser-js
5+
// https://github.com/kkaefer/css-color-parser-cpp
6+
//
7+
// Permission is hereby granted, free of charge, to any person obtaining a copy
8+
// of this software and associated documentation files (the "Software"), to
9+
// deal in the Software without restriction, including without limitation the
10+
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11+
// sell copies of the Software, and to permit persons to whom the Software is
12+
// furnished to do so, subject to the following conditions:
13+
//
14+
// The above copyright notice and this permission notice shall be included in
15+
// all copies or substantial portions of the Software.
16+
//
17+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23+
// IN THE SOFTWARE.
24+
25+
#include "csscolorparser.hpp"
26+
27+
#include <cstdint>
28+
#include <vector>
29+
#include <sstream>
30+
#include <cmath>
31+
#include <map>
32+
33+
using namespace CSSColorParser;
34+
35+
// http://www.w3.org/TR/css3-color/
36+
const std::map<std::string, Color> kCSSColorTable = {
37+
{ "transparent", { 0, 0, 0, 0 } }, { "aliceblue", { 240, 248, 255, 1 } },
38+
{ "antiquewhite", { 250, 235, 215, 1 } }, { "aqua", { 0, 255, 255, 1 } },
39+
{ "aquamarine", { 127, 255, 212, 1 } }, { "azure", { 240, 255, 255, 1 } },
40+
{ "beige", { 245, 245, 220, 1 } }, { "bisque", { 255, 228, 196, 1 } },
41+
{ "black", { 0, 0, 0, 1 } }, { "blanchedalmond", { 255, 235, 205, 1 } },
42+
{ "blue", { 0, 0, 255, 1 } }, { "blueviolet", { 138, 43, 226, 1 } },
43+
{ "brown", { 165, 42, 42, 1 } }, { "burlywood", { 222, 184, 135, 1 } },
44+
{ "cadetblue", { 95, 158, 160, 1 } }, { "chartreuse", { 127, 255, 0, 1 } },
45+
{ "chocolate", { 210, 105, 30, 1 } }, { "coral", { 255, 127, 80, 1 } },
46+
{ "cornflowerblue", { 100, 149, 237, 1 } }, { "cornsilk", { 255, 248, 220, 1 } },
47+
{ "crimson", { 220, 20, 60, 1 } }, { "cyan", { 0, 255, 255, 1 } },
48+
{ "darkblue", { 0, 0, 139, 1 } }, { "darkcyan", { 0, 139, 139, 1 } },
49+
{ "darkgoldenrod", { 184, 134, 11, 1 } }, { "darkgray", { 169, 169, 169, 1 } },
50+
{ "darkgreen", { 0, 100, 0, 1 } }, { "darkgrey", { 169, 169, 169, 1 } },
51+
{ "darkkhaki", { 189, 183, 107, 1 } }, { "darkmagenta", { 139, 0, 139, 1 } },
52+
{ "darkolivegreen", { 85, 107, 47, 1 } }, { "darkorange", { 255, 140, 0, 1 } },
53+
{ "darkorchid", { 153, 50, 204, 1 } }, { "darkred", { 139, 0, 0, 1 } },
54+
{ "darksalmon", { 233, 150, 122, 1 } }, { "darkseagreen", { 143, 188, 143, 1 } },
55+
{ "darkslateblue", { 72, 61, 139, 1 } }, { "darkslategray", { 47, 79, 79, 1 } },
56+
{ "darkslategrey", { 47, 79, 79, 1 } }, { "darkturquoise", { 0, 206, 209, 1 } },
57+
{ "darkviolet", { 148, 0, 211, 1 } }, { "deeppink", { 255, 20, 147, 1 } },
58+
{ "deepskyblue", { 0, 191, 255, 1 } }, { "dimgray", { 105, 105, 105, 1 } },
59+
{ "dimgrey", { 105, 105, 105, 1 } }, { "dodgerblue", { 30, 144, 255, 1 } },
60+
{ "firebrick", { 178, 34, 34, 1 } }, { "floralwhite", { 255, 250, 240, 1 } },
61+
{ "forestgreen", { 34, 139, 34, 1 } }, { "fuchsia", { 255, 0, 255, 1 } },
62+
{ "gainsboro", { 220, 220, 220, 1 } }, { "ghostwhite", { 248, 248, 255, 1 } },
63+
{ "gold", { 255, 215, 0, 1 } }, { "goldenrod", { 218, 165, 32, 1 } },
64+
{ "gray", { 128, 128, 128, 1 } }, { "green", { 0, 128, 0, 1 } },
65+
{ "greenyellow", { 173, 255, 47, 1 } }, { "grey", { 128, 128, 128, 1 } },
66+
{ "honeydew", { 240, 255, 240, 1 } }, { "hotpink", { 255, 105, 180, 1 } },
67+
{ "indianred", { 205, 92, 92, 1 } }, { "indigo", { 75, 0, 130, 1 } },
68+
{ "ivory", { 255, 255, 240, 1 } }, { "khaki", { 240, 230, 140, 1 } },
69+
{ "lavender", { 230, 230, 250, 1 } }, { "lavenderblush", { 255, 240, 245, 1 } },
70+
{ "lawngreen", { 124, 252, 0, 1 } }, { "lemonchiffon", { 255, 250, 205, 1 } },
71+
{ "lightblue", { 173, 216, 230, 1 } }, { "lightcoral", { 240, 128, 128, 1 } },
72+
{ "lightcyan", { 224, 255, 255, 1 } }, { "lightgoldenrodyellow", { 250, 250, 210, 1 } },
73+
{ "lightgray", { 211, 211, 211, 1 } }, { "lightgreen", { 144, 238, 144, 1 } },
74+
{ "lightgrey", { 211, 211, 211, 1 } }, { "lightpink", { 255, 182, 193, 1 } },
75+
{ "lightsalmon", { 255, 160, 122, 1 } }, { "lightseagreen", { 32, 178, 170, 1 } },
76+
{ "lightskyblue", { 135, 206, 250, 1 } }, { "lightslategray", { 119, 136, 153, 1 } },
77+
{ "lightslategrey", { 119, 136, 153, 1 } }, { "lightsteelblue", { 176, 196, 222, 1 } },
78+
{ "lightyellow", { 255, 255, 224, 1 } }, { "lime", { 0, 255, 0, 1 } },
79+
{ "limegreen", { 50, 205, 50, 1 } }, { "linen", { 250, 240, 230, 1 } },
80+
{ "magenta", { 255, 0, 255, 1 } }, { "maroon", { 128, 0, 0, 1 } },
81+
{ "mediumaquamarine", { 102, 205, 170, 1 } }, { "mediumblue", { 0, 0, 205, 1 } },
82+
{ "mediumorchid", { 186, 85, 211, 1 } }, { "mediumpurple", { 147, 112, 219, 1 } },
83+
{ "mediumseagreen", { 60, 179, 113, 1 } }, { "mediumslateblue", { 123, 104, 238, 1 } },
84+
{ "mediumspringgreen", { 0, 250, 154, 1 } }, { "mediumturquoise", { 72, 209, 204, 1 } },
85+
{ "mediumvioletred", { 199, 21, 133, 1 } }, { "midnightblue", { 25, 25, 112, 1 } },
86+
{ "mintcream", { 245, 255, 250, 1 } }, { "mistyrose", { 255, 228, 225, 1 } },
87+
{ "moccasin", { 255, 228, 181, 1 } }, { "navajowhite", { 255, 222, 173, 1 } },
88+
{ "navy", { 0, 0, 128, 1 } }, { "oldlace", { 253, 245, 230, 1 } },
89+
{ "olive", { 128, 128, 0, 1 } }, { "olivedrab", { 107, 142, 35, 1 } },
90+
{ "orange", { 255, 165, 0, 1 } }, { "orangered", { 255, 69, 0, 1 } },
91+
{ "orchid", { 218, 112, 214, 1 } }, { "palegoldenrod", { 238, 232, 170, 1 } },
92+
{ "palegreen", { 152, 251, 152, 1 } }, { "paleturquoise", { 175, 238, 238, 1 } },
93+
{ "palevioletred", { 219, 112, 147, 1 } }, { "papayawhip", { 255, 239, 213, 1 } },
94+
{ "peachpuff", { 255, 218, 185, 1 } }, { "peru", { 205, 133, 63, 1 } },
95+
{ "pink", { 255, 192, 203, 1 } }, { "plum", { 221, 160, 221, 1 } },
96+
{ "powderblue", { 176, 224, 230, 1 } }, { "purple", { 128, 0, 128, 1 } },
97+
{ "red", { 255, 0, 0, 1 } }, { "rosybrown", { 188, 143, 143, 1 } },
98+
{ "royalblue", { 65, 105, 225, 1 } }, { "saddlebrown", { 139, 69, 19, 1 } },
99+
{ "salmon", { 250, 128, 114, 1 } }, { "sandybrown", { 244, 164, 96, 1 } },
100+
{ "seagreen", { 46, 139, 87, 1 } }, { "seashell", { 255, 245, 238, 1 } },
101+
{ "sienna", { 160, 82, 45, 1 } }, { "silver", { 192, 192, 192, 1 } },
102+
{ "skyblue", { 135, 206, 235, 1 } }, { "slateblue", { 106, 90, 205, 1 } },
103+
{ "slategray", { 112, 128, 144, 1 } }, { "slategrey", { 112, 128, 144, 1 } },
104+
{ "snow", { 255, 250, 250, 1 } }, { "springgreen", { 0, 255, 127, 1 } },
105+
{ "steelblue", { 70, 130, 180, 1 } }, { "tan", { 210, 180, 140, 1 } },
106+
{ "teal", { 0, 128, 128, 1 } }, { "thistle", { 216, 191, 216, 1 } },
107+
{ "tomato", { 255, 99, 71, 1 } }, { "turquoise", { 64, 224, 208, 1 } },
108+
{ "violet", { 238, 130, 238, 1 } }, { "wheat", { 245, 222, 179, 1 } },
109+
{ "white", { 255, 255, 255, 1 } }, { "whitesmoke", { 245, 245, 245, 1 } },
110+
{ "yellow", { 255, 255, 0, 1 } }, { "yellowgreen", { 154, 205, 50, 1 } }
111+
};
112+
113+
114+
template <typename T>
115+
uint8_t clamp_css_byte(T i) { // Clamp to integer 0 .. 255.
116+
i = round(i); // Seems to be what Chrome does (vs truncation).
117+
return i < 0 ? 0 : i > 255 ? 255 : i;
118+
}
119+
120+
template <typename T>
121+
float clamp_css_float(T f) { // Clamp to float 0.0 .. 1.0.
122+
return f < 0 ? 0 : f > 1 ? 1 : f;
123+
}
124+
125+
float parseFloat(const std::string& str) {
126+
return strtof(str.c_str(), nullptr);
127+
}
128+
129+
int64_t parseInt(const std::string& str, uint8_t base = 10) {
130+
return strtoll(str.c_str(), nullptr, base);
131+
}
132+
133+
uint8_t parse_css_int(const std::string& str) { // int or percentage.
134+
if (str.length() && str.back() == '%') {
135+
return clamp_css_byte(parseFloat(str) / 100.0f * 255.0f);
136+
} else {
137+
return clamp_css_byte(parseInt(str));
138+
}
139+
}
140+
141+
float parse_css_float(const std::string& str) { // float or percentage.
142+
if (str.length() && str.back() == '%') {
143+
return clamp_css_float(parseFloat(str) / 100.0f);
144+
} else {
145+
return clamp_css_float(parseFloat(str));
146+
}
147+
}
148+
149+
float css_hue_to_rgb(float m1, float m2, float h) {
150+
if (h < 0.0f) {
151+
h += 1.0f;
152+
} else if (h > 1.0f) {
153+
h -= 1.0f;
154+
}
155+
156+
if (h * 6.0f < 1.0f) {
157+
return m1 + (m2 - m1) * h * 6.0f;
158+
}
159+
if (h * 2.0f < 1.0f) {
160+
return m2;
161+
}
162+
if (h * 3.0f < 2.0f) {
163+
return m1 + (m2 - m1) * (2.0 / 3.0 - h) * 6.0f;
164+
}
165+
return m1;
166+
}
167+
168+
169+
170+
std::vector<std::string> split(const std::string& s, char delim) {
171+
std::vector<std::string> elems;
172+
std::stringstream ss(s);
173+
std::string item;
174+
while (std::getline(ss, item, delim)) {
175+
elems.push_back(item);
176+
}
177+
return elems;
178+
}
179+
180+
Color CSSColorParser::parse(const std::string& css_str) {
181+
std::string str = css_str;
182+
183+
// Remove all whitespace, not compliant, but should just be more accepting.
184+
str.erase(std::remove(str.begin(), str.end(), ' '), str.end());
185+
186+
// Convert to lowercase.
187+
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
188+
189+
// Color keywords (and transparent) lookup.
190+
auto it = kCSSColorTable.find(str);
191+
if (it != kCSSColorTable.end()) {
192+
return it->second;
193+
}
194+
195+
// #abc and #abc123 syntax.
196+
if (str.length() && str.front() == '#') {
197+
if (str.length() == 4) {
198+
int64_t iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing.
199+
if (!(iv >= 0 && iv <= 0xfff)) {
200+
return {};
201+
} else {
202+
return {
203+
static_cast<uint8_t>(((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8)),
204+
static_cast<uint8_t>((iv & 0xf0) | ((iv & 0xf0) >> 4)),
205+
static_cast<uint8_t>((iv & 0xf) | ((iv & 0xf) << 4)),
206+
1
207+
};
208+
}
209+
} else if (str.length() == 7) {
210+
int64_t iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing.
211+
if (!(iv >= 0 && iv <= 0xffffff)) {
212+
return {}; // Covers NaN.
213+
} else {
214+
return {
215+
static_cast<uint8_t>((iv & 0xff0000) >> 16),
216+
static_cast<uint8_t>((iv & 0xff00) >> 8),
217+
static_cast<uint8_t>(iv & 0xff),
218+
1
219+
};
220+
}
221+
}
222+
223+
return {};
224+
}
225+
226+
size_t op = str.find_first_of('('), ep = str.find_first_of(')');
227+
if (op != std::string::npos && ep + 1 == str.length()) {
228+
const std::string fname = str.substr(0, op);
229+
const std::vector<std::string> params = split(str.substr(op + 1, ep - (op + 1)), ',');
230+
231+
float alpha = 1.0f;
232+
233+
if (fname == "rgba" || fname == "rgb") {
234+
if (fname == "rgba") {
235+
if (params.size() != 4) {
236+
return {};
237+
}
238+
alpha = parse_css_float(params.back());
239+
} else {
240+
if (params.size() != 3) {
241+
return {};
242+
}
243+
}
244+
245+
return {
246+
parse_css_int(params[0]),
247+
parse_css_int(params[1]),
248+
parse_css_int(params[2]),
249+
alpha
250+
};
251+
252+
} else if (fname == "hsla" || fname == "hsl") {
253+
if (fname == "hsla") {
254+
if (params.size() != 4) {
255+
return {};
256+
}
257+
alpha = parse_css_float(params.back());
258+
} else {
259+
if (params.size() != 3) {
260+
return {};
261+
}
262+
}
263+
264+
float h = parseFloat(params[0]) / 360.0f;
265+
while (h < 0.0f) h++;
266+
while (h > 1.0f) h--;
267+
268+
// NOTE(deanm): According to the CSS spec s/l should only be
269+
// percentages, but we don't bother and let float or percentage.
270+
float s = parse_css_float(params[1]);
271+
float l = parse_css_float(params[2]);
272+
273+
float m2 = l <= 0.5f ? l * (s + 1.0f) : l + s - l * s;
274+
float m1 = l * 2.0f - m2;
275+
276+
return {
277+
clamp_css_byte(css_hue_to_rgb(m1, m2, h + 1.0f / 3.0f) * 255.0f),
278+
clamp_css_byte(css_hue_to_rgb(m1, m2, h) * 255.0f),
279+
clamp_css_byte(css_hue_to_rgb(m1, m2, h - 1.0f / 3.0f) * 255.0f),
280+
alpha
281+
};
282+
}
283+
}
284+
285+
return {};
286+
}

csscolorparser.hpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// (c) Dean McNamee <dean@gmail.com>, 2012.
2+
// C++ port by Konstantin Käfer <mail@kkaefer.com>, 2014.
3+
//
4+
// https://github.com/deanm/css-color-parser-js
5+
// https://github.com/kkaefer/css-color-parser-cpp
6+
//
7+
// Permission is hereby granted, free of charge, to any person obtaining a copy
8+
// of this software and associated documentation files (the "Software"), to
9+
// deal in the Software without restriction, including without limitation the
10+
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11+
// sell copies of the Software, and to permit persons to whom the Software is
12+
// furnished to do so, subject to the following conditions:
13+
//
14+
// The above copyright notice and this permission notice shall be included in
15+
// all copies or substantial portions of the Software.
16+
//
17+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23+
// IN THE SOFTWARE.
24+
25+
#ifndef CSS_COLOR_PARSER_CPP
26+
#define CSS_COLOR_PARSER_CPP
27+
28+
#include <string>
29+
30+
namespace CSSColorParser {
31+
32+
struct Color {
33+
inline Color() {}
34+
inline Color(unsigned char r, unsigned char g, unsigned char b, float a)
35+
: r(r), g(g), b(b), a(a) {}
36+
unsigned char r = 0, g = 0, b = 0;
37+
float a = 1.0f;
38+
};
39+
40+
Color parse(const std::string& css_str);
41+
42+
}
43+
44+
#endif

main.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#include "csscolorparser.hpp"
2+
3+
#include <iostream>
4+
5+
std::ostream& operator<<(std::ostream& os, const CSSColorParser::Color& color) {
6+
return os << "rgba(" << (int)color.r << ", " << (int)color.g << ", " << (int)color.b << ", " << color.a << ")";
7+
}
8+
9+
int main() {
10+
using namespace CSSColorParser;
11+
12+
std::cout << "rgba(255, 128, 12, 0.5) == " << parse(" rgba (255, 128, 12, 0.5)") << std::endl;
13+
std::cout << "rgba(255, 255, 255, 1) == " << parse("#fff") << std::endl;
14+
std::cout << "rgba(255, 0, 17, 1) == " << parse("#ff0011") << std::endl;
15+
std::cout << "rgba(106, 90, 205, 1) == " << parse("slateblue") << std::endl;
16+
std::cout << "rgba(0, 0, 0, 1) == " << parse("blah") << std::endl;
17+
std::cout << "rgba(0, 0, 0, 1) == " << parse("ffffff") << std::endl;
18+
std::cout << "rgba(226, 233, 233, 0.5) == " << parse("hsla(900, 15%, 90%, 0.5)") << std::endl;
19+
std::cout << "rgba(0, 0, 0, 1) == " << parse("hsla(900, 15%, 90%)") << std::endl;
20+
std::cout << "rgba(226, 233, 233, 1) == " << parse("hsl(900, 15%, 90%)") << std::endl;
21+
std::cout << "rgba(226, 233, 233, 1) == " << parse("hsl(900, 0.15, 90%)") << std::endl; // NOTE: not spec compliant.
22+
}

0 commit comments

Comments
 (0)