1 /**
2 * Copyright (c) 2019, Timothy Stack
3 *
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * * Redistributions of source code must retain the above copyright notice, this
10 * list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 * * Neither the name of Timothy Stack nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "config.h"
31
32 #include <string>
33
34 #include "fmt/format.h"
35 #include "yajlpp/yajlpp.hh"
36 #include "yajlpp/yajlpp_def.hh"
37 #include "styling.hh"
38 #include "ansi-palette-json.h"
39 #include "xterm-palette-json.h"
40
41 using namespace std;
42
43 static struct json_path_container term_color_rgb_handler = {
44 yajlpp::property_handler("r")
45 .FOR_FIELD(rgb_color, rc_r),
46 yajlpp::property_handler("g")
47 .FOR_FIELD(rgb_color, rc_g),
48 yajlpp::property_handler("b")
49 .FOR_FIELD(rgb_color, rc_b)
50 };
51
52 static struct json_path_container term_color_handler = {
53 yajlpp::property_handler("colorId")
54 .FOR_FIELD(term_color, xc_id),
55 yajlpp::property_handler("name")
56 .FOR_FIELD(term_color, xc_name),
57 yajlpp::property_handler("hexString")
58 .FOR_FIELD(term_color, xc_hex),
59 yajlpp::property_handler("rgb")
60 .with_obj_provider<rgb_color, term_color>(
__anon41b579290102(const auto &pc, term_color *xc) 61 [](const auto &pc, term_color *xc) { return &xc->xc_color; })
62 .with_children(term_color_rgb_handler)
63 };
64
65 static struct json_path_container root_color_handler = {
66 yajlpp::property_handler("#")
67 .with_obj_provider<term_color, vector<term_color>>(
__anon41b579290202(const yajlpp_provider_context &ypc, vector<term_color> *palette) 68 [](const yajlpp_provider_context &ypc, vector<term_color> *palette) {
69 palette->resize(ypc.ypc_index + 1);
70 return &((*palette)[ypc.ypc_index]);
71 })
72 .with_children(term_color_handler)
73 };
74
xterm_colors()75 term_color_palette *xterm_colors()
76 {
77 static term_color_palette retval(xterm_palette_json.to_string_fragment());
78
79 return &retval;
80 }
81
ansi_colors()82 term_color_palette *ansi_colors()
83 {
84 static term_color_palette retval(ansi_palette_json.to_string_fragment());
85
86 return &retval;
87 }
88
from_str(const string_fragment & sf)89 Result<rgb_color, std::string> rgb_color::from_str(const string_fragment &sf)
90 {
91 if (sf.empty()) {
92 return Ok(rgb_color());
93 }
94
95 rgb_color rgb_out;
96
97 if (sf[0] == '#') {
98 switch (sf.length()) {
99 case 4:
100 if (sscanf(sf.data(), "#%1hx%1hx%1hx",
101 &rgb_out.rc_r, &rgb_out.rc_g, &rgb_out.rc_b) == 3) {
102 rgb_out.rc_r |= rgb_out.rc_r << 4;
103 rgb_out.rc_g |= rgb_out.rc_g << 4;
104 rgb_out.rc_b |= rgb_out.rc_b << 4;
105 return Ok(rgb_out);
106 }
107 break;
108 case 7:
109 if (sscanf(sf.data(), "#%2hx%2hx%2hx",
110 &rgb_out.rc_r, &rgb_out.rc_g, &rgb_out.rc_b) == 3) {
111 return Ok(rgb_out);
112 }
113 break;
114 }
115
116 return Err(fmt::format("Could not parse color: {}", sf));
117 }
118
119 for (const auto &xc : xterm_colors()->tc_palette) {
120 if (sf.iequal(xc.xc_name)) {
121 return Ok(xc.xc_color);
122 }
123 }
124
125 return Err(fmt::format(
126 "Unknown color: '{}'. "
127 "See https://jonasjacek.github.io/colors/ for a list of supported "
128 "color names", sf));
129 }
130
operator <(const rgb_color & rhs) const131 bool rgb_color::operator<(const rgb_color &rhs) const
132 {
133 if (rc_r < rhs.rc_r)
134 return true;
135 if (rhs.rc_r < rc_r)
136 return false;
137 if (rc_g < rhs.rc_g)
138 return true;
139 if (rhs.rc_g < rc_g)
140 return false;
141 return rc_b < rhs.rc_b;
142 }
143
operator >(const rgb_color & rhs) const144 bool rgb_color::operator>(const rgb_color &rhs) const
145 {
146 return rhs < *this;
147 }
148
operator <=(const rgb_color & rhs) const149 bool rgb_color::operator<=(const rgb_color &rhs) const
150 {
151 return !(rhs < *this);
152 }
153
operator >=(const rgb_color & rhs) const154 bool rgb_color::operator>=(const rgb_color &rhs) const
155 {
156 return !(*this < rhs);
157 }
158
operator ==(const rgb_color & rhs) const159 bool rgb_color::operator==(const rgb_color &rhs) const
160 {
161 return rc_r == rhs.rc_r &&
162 rc_g == rhs.rc_g &&
163 rc_b == rhs.rc_b;
164 }
165
operator !=(const rgb_color & rhs) const166 bool rgb_color::operator!=(const rgb_color &rhs) const
167 {
168 return !(rhs == *this);
169 }
170
term_color_palette(const string_fragment & json)171 term_color_palette::term_color_palette(const string_fragment& json)
172 {
173 yajlpp_parse_context ypc_xterm("palette.json", &root_color_handler);
174 yajl_handle handle;
175
176 handle = yajl_alloc(&ypc_xterm.ypc_callbacks, nullptr, &ypc_xterm);
177 ypc_xterm
178 .with_ignore_unused(true)
179 .with_obj(this->tc_palette)
180 .with_handle(handle);
181 yajl_status st = ypc_xterm.parse(json);
182 ensure(st == yajl_status_ok);
183 st = ypc_xterm.complete_parse();
184 ensure(st == yajl_status_ok);
185 yajl_free(handle);
186
187 for (auto &xc : this->tc_palette) {
188 xc.xc_lab_color = lab_color(xc.xc_color);
189 }
190 }
191
match_color(const lab_color & to_match)192 short term_color_palette::match_color(const lab_color &to_match)
193 {
194 double lowest = 1000.0;
195 short lowest_id = -1;
196
197 for (auto &xc : this->tc_palette) {
198 double xc_delta = xc.xc_lab_color.deltaE(to_match);
199
200 if (lowest_id == -1) {
201 lowest = xc_delta;
202 lowest_id = xc.xc_id;
203 continue;
204 }
205
206 if (xc_delta < lowest) {
207 lowest = xc_delta;
208 lowest_id = xc.xc_id;
209 }
210 }
211
212 return lowest_id;
213 }
214
215 namespace styling {
216
from_str(const string_fragment & sf)217 Result<color_unit, std::string> color_unit::from_str(const string_fragment &sf)
218 {
219 if (sf == "semantic()") {
220 return Ok(color_unit{ semantic{} });
221 }
222
223 auto retval = TRY(rgb_color::from_str(sf));
224
225 return Ok(color_unit{ retval });
226 }
227
228 }
229