/** * Copyright (c) 2019, Timothy Stack * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Timothy Stack nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "styling.hh" #include "ansi-palette-json.h" #include "config.h" #include "fmt/format.h" #include "xterm-palette-json.h" #include "yajlpp/yajlpp.hh" #include "yajlpp/yajlpp_def.hh" static const struct json_path_container term_color_rgb_handler = { yajlpp::property_handler("r").for_field(&rgb_color::rc_r), yajlpp::property_handler("g").for_field(&rgb_color::rc_g), yajlpp::property_handler("b").for_field(&rgb_color::rc_b), }; static const struct json_path_container term_color_handler = { yajlpp::property_handler("colorId").for_field(&term_color::xc_id), yajlpp::property_handler("name").for_field(&term_color::xc_name), yajlpp::property_handler("hexString").for_field(&term_color::xc_hex), yajlpp::property_handler("rgb") .for_child(&term_color::xc_color) .with_children(term_color_rgb_handler), }; static const struct json_path_container root_color_handler = { yajlpp::property_handler("#") .with_obj_provider>( [](const yajlpp_provider_context& ypc, std::vector* palette) { if (ypc.ypc_index >= palette->size()) { palette->resize(ypc.ypc_index + 1); } return &((*palette)[ypc.ypc_index]); }) .with_children(term_color_handler), }; term_color_palette* xterm_colors() { static term_color_palette retval(xterm_palette_json.get_name(), xterm_palette_json.to_string_fragment()); return &retval; } term_color_palette* ansi_colors() { static term_color_palette retval(ansi_palette_json.get_name(), ansi_palette_json.to_string_fragment()); return &retval; } Result rgb_color::from_str(const string_fragment& sf) { if (sf.empty()) { return Ok(rgb_color()); } rgb_color rgb_out; if (sf[0] == '#') { switch (sf.length()) { case 4: if (sscanf(sf.data(), "#%1hx%1hx%1hx", &rgb_out.rc_r, &rgb_out.rc_g, &rgb_out.rc_b) == 3) { rgb_out.rc_r |= rgb_out.rc_r << 4; rgb_out.rc_g |= rgb_out.rc_g << 4; rgb_out.rc_b |= rgb_out.rc_b << 4; return Ok(rgb_out); } break; case 7: if (sscanf(sf.data(), "#%2hx%2hx%2hx", &rgb_out.rc_r, &rgb_out.rc_g, &rgb_out.rc_b) == 3) { return Ok(rgb_out); } break; } return Err(fmt::format(FMT_STRING("Could not parse color: {}"), sf)); } for (const auto& xc : xterm_colors()->tc_palette) { if (sf.iequal(xc.xc_name)) { return Ok(xc.xc_color); } } return Err(fmt::format( FMT_STRING( "Unknown color: '{}'. " "See https://jonasjacek.github.io/colors/ for a list of supported " "color names"), sf)); } bool rgb_color::operator<(const rgb_color& rhs) const { if (rc_r < rhs.rc_r) return true; if (rhs.rc_r < rc_r) return false; if (rc_g < rhs.rc_g) return true; if (rhs.rc_g < rc_g) return false; return rc_b < rhs.rc_b; } bool rgb_color::operator>(const rgb_color& rhs) const { return rhs < *this; } bool rgb_color::operator<=(const rgb_color& rhs) const { return !(rhs < *this); } bool rgb_color::operator>=(const rgb_color& rhs) const { return !(*this < rhs); } bool rgb_color::operator==(const rgb_color& rhs) const { return rc_r == rhs.rc_r && rc_g == rhs.rc_g && rc_b == rhs.rc_b; } bool rgb_color::operator!=(const rgb_color& rhs) const { return !(rhs == *this); } term_color_palette::term_color_palette(const char* name, const string_fragment& json) { yajlpp_parse_context ypc_xterm(intern_string::lookup(name), &root_color_handler); yajl_handle handle; handle = yajl_alloc(&ypc_xterm.ypc_callbacks, nullptr, &ypc_xterm); ypc_xterm.with_ignore_unused(true) .with_obj(this->tc_palette) .with_handle(handle); yajl_status st = ypc_xterm.parse(json); ensure(st == yajl_status_ok); st = ypc_xterm.complete_parse(); ensure(st == yajl_status_ok); yajl_free(handle); for (auto& xc : this->tc_palette) { xc.xc_lab_color = lab_color(xc.xc_color); } } short term_color_palette::match_color(const lab_color& to_match) { double lowest = 1000.0; short lowest_id = -1; for (auto& xc : this->tc_palette) { double xc_delta = xc.xc_lab_color.deltaE(to_match); if (lowest_id == -1) { lowest = xc_delta; lowest_id = xc.xc_id; continue; } if (xc_delta < lowest) { lowest = xc_delta; lowest_id = xc.xc_id; } } return lowest_id; } namespace styling { Result color_unit::from_str(const string_fragment& sf) { if (sf == "semantic()") { return Ok(color_unit{semantic{}}); } auto retval = TRY(rgb_color::from_str(sf)); return Ok(color_unit{retval}); } } // namespace styling