/* * Basic Types definition * Nana C++ Library(http://www.nanapro.org) * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * * @file: nana/basic_types.cpp * @contributos: Jan */ #include #if defined(USE_STD_REGEX) #include #else #include #endif #include #include namespace nana { //class color color::color(colors clr) : color((static_cast(clr)& 0xFF0000) >> 16, (static_cast(clr)& 0xFF00) >> 8, static_cast(clr)& 0xFF) {} color::color(colors clr, double alpha) : color((static_cast(clr)& 0xFF0000) >> 16, (static_cast(clr)& 0xFF00) >> 8, static_cast(clr)& 0xFF, alpha) {} color::color(color_rgb rgb) : r_((static_cast(rgb) >> 16) & 0xFF), g_((static_cast(rgb) >> 8) & 0xFF), b_(static_cast(rgb) & 0xFF), a_(1.0) {} color::color(color_argb argb) : r_((static_cast(argb) >> 16) & 0xFF), g_((static_cast(argb) >> 8) & 0xFF), b_(static_cast(argb) & 0xFF), a_(((static_cast(argb) >> 24) & 0xFF) / 255.0) {} color::color(color_rgba rgba) : r_((static_cast(rgba) >> 24) & 0xFF), g_((static_cast(rgba) >> 16) & 0xFF), b_((static_cast(rgba) >> 8) & 0xFF), a_((static_cast(rgba) & 0xFF) / 255.0) {} color::color(unsigned red, unsigned green, unsigned blue) : r_(red), g_(green), b_(blue), a_(1.0) { } color::color(unsigned red, unsigned green, unsigned blue, double alpha) : r_(red), g_(green), b_(blue), a_(alpha) { if (alpha < 0.0) a_ = 0.0; else if (alpha > 1.0) a_ = 1.0; } #if !defined(USE_STD_REGEX) std::string read_number(std::string& str, std::size_t& pos) { pos = str.find_first_of("0123456789", pos); if (pos == str.npos) return{}; auto end = str.find_first_not_of("0123456789", pos + 1); //integer part if (end == str.npos) { pos = end; return str.substr(pos); } if (str[end] == '.') { auto decimal_end = str.find_first_not_of("0123456789", end + 1); if ((decimal_end == str.npos) || (decimal_end == end + 1)) //Because of missing % return{}; end = decimal_end; } auto ch = str[end]; if (ch == '%' || ch == ' ' || ch == ',' || ch == ')') { auto start = pos; pos = end + (ch == '%' ? 1 : 0); return str.substr(start, pos - start); } return{}; } #endif //Initializes the color with a CSS-like string //contributor: BigDave(mortis2007 at hotmail co uk) //date: February 3, 2015 //maintainor: Jinhao, extended the support of CSS-spec color::color(std::string css_color) : a_(1.0) { const char * excpt_what = "color: invalid rgb format"; auto pos = css_color.find_first_not_of(' '); if (pos == css_color.npos) throw std::invalid_argument(excpt_what); if ('#' == css_color[pos]) { if (css_color.size() < pos + 4) throw std::invalid_argument(excpt_what); auto endpos = css_color.find_first_not_of("0123456789abcdefABCDEF", pos + 1); if (endpos == css_color.npos) endpos = static_cast(css_color.size()); if ((endpos - pos != 4) && (endpos - pos != 7)) throw std::invalid_argument(excpt_what); auto n = std::stoi(css_color.substr(pos + 1, endpos - pos - 1), nullptr, 16); if (endpos - pos == 4) { r_ = ((0xF00 & n) >> 4) | ((0xF00 & n) >> 8); g_ = (0xF0 & n) | ((0xF0 & n) >> 4); b_ = (0xF & n) | ((0xF & n) << 4); } else { r_ = (0xFF0000 & n) >> 16; g_ = (0xFF00 & n) >> 8; b_ = (0xFF & n); } return; } //std::tolower is not allowed because of concept requirements std::transform(css_color.begin(), css_color.end(), css_color.begin(), [](char ch){ if('A' <= ch && ch <= 'Z') return static_cast(ch - ('A' - 'a')); return ch; }); auto endpos = css_color.find(' ', pos + 1); if (endpos == css_color.npos) endpos = css_color.size(); if ((endpos - pos == 11) && (css_color.substr(pos, 11) == "transparent")) { r_ = 0; g_ = 0; b_ = 0; a_ = 0; return; } auto type_end = css_color.find_first_of(" (", pos + 1); if (type_end == css_color.npos || ((type_end - pos != 3) && (type_end - pos != 4))) //rgb/hsl = 3, rgba/hsla = 4 throw std::invalid_argument(excpt_what); bool has_alpha = false; if (type_end - pos == 4) //maybe rgba/hsla { if (css_color[pos + 3] != 'a') throw std::invalid_argument(excpt_what); has_alpha = true; } #if defined(USE_STD_REGEX) std::regex pat; std::regex_iterator i, end; auto type_name = css_color.substr(pos, 3); if ("rgb" == type_name) { pat.assign("(\\d*\\.)?\\d+\\%?"); i = std::regex_iterator(css_color.begin() + pos, css_color.end(), pat); if (i == end) throw std::invalid_argument(excpt_what); std::vector rgb; rgb.emplace_back(i->str()); const bool is_real = (rgb.back().back() == '%'); pat.assign(is_real ? "(\\d*\\.)?\\d+\\%" : "\\d+"); for (++i; i != end; ++i) { rgb.emplace_back(i->str()); if (rgb.size() == 3) break; } if (rgb.size() != 3) throw std::invalid_argument(excpt_what); if (is_real) { auto pr = ::nana::stod(rgb[0].substr(0, rgb[0].size() - 1)); r_ = (pr > 100 ? 255.0 : 2.55 * pr); pr = ::nana::stod(rgb[1].substr(0, rgb[1].size() - 1)); g_ = (pr > 100 ? 255.0 : 2.55 * pr); pr = ::nana::stod(rgb[2].substr(0, rgb[2].size() - 1)); b_ = (pr > 100 ? 255.0 : 2.55 * pr); } else { r_ = ::nana::stod(rgb[0]); if (r_ > 255.0) r_ = 255; g_ = ::nana::stod(rgb[1]); if (g_ > 255.0) g_ = 255; b_ = ::nana::stod(rgb[2]); if (b_ > 255.0) b_ = 255; } } else if ("hsl" == type_name) { pat.assign("(\\d*\\.)?\\d+"); i = std::regex_iterator(css_color.begin() + pos, css_color.end(), pat); if (i == end) throw std::invalid_argument(excpt_what); auto h = ::nana::stod(i->str()); pat.assign("(\\d*\\.)?\\d+\\%"); if (++i == end) throw std::invalid_argument(excpt_what); auto str = i->str(); auto s = ::nana::stod(str.substr(0, str.size() - 1)); if (++i == end) throw std::invalid_argument(excpt_what); str = i->str(); auto l = ::nana::stod(str.substr(0, str.size() - 1)); from_hsl(h, s / 100, l / 100); } else throw std::invalid_argument(excpt_what); //invalid color type if (has_alpha) { pat.assign("(\\d*\\.)?\\d+"); if (++i == end) throw std::invalid_argument(excpt_what); //invalid alpha value a_ = ::nana::stod(i->str()); } #else auto type_name = css_color.substr(pos, 3); pos = css_color.find_first_not_of(' ', type_end); if (pos == css_color.npos || css_color[pos] != '(') throw std::invalid_argument(excpt_what); auto str = read_number(css_color, ++pos); if (str.empty()) throw std::invalid_argument(excpt_what); if ("rgb" == type_name) { std::vector rgb; rgb.emplace_back(std::move(str)); const bool is_real = (rgb.back().back() == '%'); for (int i = 0; i < 2; ++i) { pos = css_color.find_first_not_of(' ', pos); if (pos == css_color.npos || css_color[pos] != ',') throw std::invalid_argument(excpt_what); str = read_number(css_color, ++pos); if (str.empty()) throw std::invalid_argument(excpt_what); rgb.emplace_back(std::move(str)); if (rgb.size() == 3) break; } if (rgb.size() != 3) throw std::invalid_argument(excpt_what); if (is_real) { auto pr = std::stod(rgb[0].substr(0, rgb[0].size() - 1)); r_ = (pr > 100 ? 255.0 : 2.55 * pr); pr = std::stod(rgb[1].substr(0, rgb[1].size() - 1)); g_ = (pr > 100 ? 255.0 : 2.55 * pr); pr = std::stod(rgb[2].substr(0, rgb[2].size() - 1)); b_ = (pr > 100 ? 255.0 : 2.55 * pr); } else { r_ = std::stod(rgb[0]); if (r_ > 255.0) r_ = 255; g_ = std::stod(rgb[1]); if (g_ > 255.0) g_ = 255; b_ = std::stod(rgb[2]); if (b_ > 255.0) b_ = 255; } } else if ("hsl" == type_name) { if (str.back() == '%') throw std::invalid_argument(excpt_what); auto h = std::stod(str); pos = css_color.find_first_not_of(' ', pos); if (pos == css_color.npos || css_color[pos] != ',') throw std::invalid_argument(excpt_what); str = read_number(css_color, ++pos); if (str.empty() || str.back() != '%') throw std::invalid_argument(excpt_what); auto s = std::stod(str.substr(0, str.size() - 1)); pos = css_color.find_first_not_of(' ', pos); if (pos == css_color.npos || css_color[pos] != ',') throw std::invalid_argument(excpt_what); str = read_number(css_color, ++pos); if (str.empty() || str.back() != '%') throw std::invalid_argument(excpt_what); auto l = std::stod(str.substr(0, str.size() - 1)); from_hsl(h, s / 100, l / 100); } else throw std::invalid_argument(excpt_what); //invalid color type if (has_alpha) { str = read_number(css_color, ++pos); if (str.empty() || str.back() == '%') throw std::invalid_argument(excpt_what); //invalid alpha value a_ = std::stod(str); } #endif } color& color::from_rgb(unsigned red, unsigned green, unsigned blue) { r_ = red; g_ = green; b_ = blue; return *this; } double rgb_from_hue(double v1, double v2, double h) { if (h < 0.0) h += 1.0; else if (h > 1.0) h -= 1.0; if (h < 0.1666666) return v1 + (v2 - v1) * (6.0 * h); if (h < 0.5) return v2; if (h < 0.6666666) return v1 + (v2 - v1) * (4.0 - h * 6.0); return v1; } color& color::from_hsl(double hue, double saturation, double lightness) { if (0.0 == saturation) { r_ = lightness * 255.0; g_ = r_; b_ = r_; } else { double var2; if (lightness < 0.5) var2 = lightness * (1.0 + saturation); else var2 = (lightness + saturation) - (saturation * lightness); double var1 = 2.0 * lightness - var2; hue /= 360; r_ = 255.0 * rgb_from_hue(var1, var2, hue + 0.33333); g_ = 255.0 * rgb_from_hue(var1, var2, hue); b_ = 255.0 * rgb_from_hue(var1, var2, hue - 0.33333); } return *this; } color& color::alpha(double al) { if (al < 0.0) a_ = 0.0; else if (al > 1.0) a_ = 1.0; else a_ = al; return *this; } color color::blend(const color& bgcolor, bool ignore_bgcolor_alpha) const { if (a_ < 1.0) { color result; if (0.0 < a_) { if (ignore_bgcolor_alpha || (1.0 == bgcolor.b_)) { result.r_ = r_ * a_ + bgcolor.r_ * (1.0 - a_); result.g_ = g_ * a_ + bgcolor.g_ * (1.0 - a_); result.b_ = b_ * a_ + bgcolor.b_ * (1.0 - a_); result.a_ = 1.0; } else { result.r_ = r_ * a_ + bgcolor.r_ * bgcolor.a_ * (1.0 - a_); result.g_ = g_ * a_ + bgcolor.g_ * bgcolor.a_ * (1.0 - a_); result.b_ = b_ * a_ + bgcolor.b_ * bgcolor.a_ * (1.0 - a_); result.a_ = a_ + (bgcolor.a_ * (1.0 - a_)); } } else { result.r_ = bgcolor.r_; result.g_ = bgcolor.g_; result.b_ = bgcolor.b_; result.a_ = (ignore_bgcolor_alpha ? 1.0 : bgcolor.a_); } return result; } return *this; } color color::blend(const color& bgcolor, double alpha) const { color result; result.r_ = r_ * alpha + bgcolor.r_ * (1.0 - alpha); result.g_ = g_ * alpha + bgcolor.g_ * (1.0 - alpha); result.b_ = b_ * alpha + bgcolor.b_ * (1.0 - alpha); result.a_ = 1.0; return result; } bool color::invisible() const { return (a_ == 0.0); } pixel_color_t color::px_color() const { return argb(); } pixel_argb_t color::argb() const { pixel_argb_t argb; argb.element.red = static_cast(r_); argb.element.green = static_cast(g_); argb.element.blue = static_cast(b_); argb.element.alpha_channel = static_cast(a_ * 255); return argb; } pixel_rgba_t color::rgba() const { pixel_rgba_t rgba; rgba.element.red = static_cast(r_); rgba.element.green = static_cast(g_); rgba.element.blue = static_cast(b_); rgba.element.alpha_channel = static_cast(a_ * 255); return rgba; } const double& color::r() const { return r_; } const double& color::g() const { return g_; } const double& color::b() const { return b_; } const double& color::a() const { return a_; } bool color::operator==(const color& other) const { return (px_color().value == other.px_color().value); } bool color::operator!=(const color& other) const { return (px_color().value != other.px_color().value); } color operator+(const color& x, const color& y) { double a = x.a_ + y.a_; auto r = static_cast(x.r_ + y.r_); auto g = static_cast(x.g_ + y.g_); auto b = static_cast(x.b_ + y.b_); return color{ r > 255 ? 255 : r, g > 255 ? 255 : g, b > 255 ? 255 : b, a > 1.0 ? 1.0 : a }; } //end class color //struct size size::size():width(0), height(0){} size::size(value_type width, value_type height) : width(width), height(height){} bool size::empty() const { return (0 == width || 0 == height); } bool size::is_hit(const point& pos) const { return (0 <= pos.x && pos.x < static_cast(width) && 0 <= pos.y && pos.y < static_cast(height)); } size& size::shift() { std::swap(width, height); return *this; } bool size::operator==(const size& rhs) const { return (width == rhs.width) && (height == rhs.height); } bool size::operator!=(const size& rhs) const { return (width != rhs.width) || (height != rhs.height); } size size::operator+(const size& sz) const { return{width + sz.width, height + sz.height}; } //end struct size //struct rectangle rectangle::rectangle() :x(0), y(0), width(0), height(0) {} rectangle::rectangle(int x, int y, unsigned width, unsigned height) :x(x), y(y), width(width), height(height) {} rectangle::rectangle(const size & sz) :x(0), y(0), width(sz.width), height(sz.height) {} rectangle::rectangle(const point & pos, const size& sz) : x(pos.x), y(pos.y), width(sz.width), height(sz.height) {} bool rectangle::operator==(const rectangle& rhs) const { return (width == rhs.width) && (height == rhs.height) && (x == rhs.x) && (y == rhs.y); } bool rectangle::operator!=(const rectangle& rhs) const { return (width != rhs.width) || (height != rhs.height) || (x != rhs.x) || (y != rhs.y); } rectangle & rectangle::operator=(const point& pos) { x = pos.x; y = pos.y; return *this; } rectangle & rectangle::operator=(const size & sz) { width = sz.width; height = sz.height; return *this; } point rectangle::position() const noexcept { return{ x, y }; } rectangle& rectangle::position(const point& p) noexcept { x = p.x; y = p.y; return *this; } size rectangle::dimension() const noexcept { return{width, height}; } rectangle& rectangle::dimension(const size& sz) noexcept { width = sz.width; height = sz.height; return *this; } /* rectangle& rectangle::set_size(const size& sz) { width = sz.width; height = sz.height; return *this; } */ rectangle& rectangle::pare_off(int pixels) { x += pixels; y += pixels; width -= (pixels << 1); height -= (pixels << 1); return *this; } int rectangle::right() const { return x + static_cast(width); } int rectangle::bottom() const { return y + static_cast(height); } bool rectangle::is_hit(int pos_x, int pos_y) const { return (x <= pos_x && pos_x < x + static_cast(width)) && (y <= pos_y && pos_y < y + static_cast(height)); } bool rectangle::is_hit(const point& pos) const { return (x <= pos.x && pos.x < x + static_cast(width)) && (y <= pos.y && pos.y < y + static_cast(height)); } bool rectangle::empty() const { return (0 == width) || (0 == height); } rectangle& rectangle::shift() { std::swap(x, y); std::swap(width, height); return *this; } //end struct rectangle //class rectangle_rotator rectangle_rotator::rectangle_rotator(bool rotated, const ::nana::rectangle& area) : rotated_(rotated), area_(area) {} int rectangle_rotator::x() const { return (rotated_ ? area_.y : area_.x); } int & rectangle_rotator::x_ref() { return (rotated_ ? area_.y : area_.x); } int rectangle_rotator::y() const { return (rotated_ ? area_.x : area_.y); } int & rectangle_rotator::y_ref() { return (rotated_ ? area_.x : area_.y); } unsigned rectangle_rotator::w() const { return (rotated_ ? area_.height : area_.width); } unsigned & rectangle_rotator::w_ref() { return (rotated_ ? area_.height : area_.width); } unsigned rectangle_rotator::h() const { return (rotated_ ? area_.width : area_.height); } unsigned & rectangle_rotator::h_ref() { return (rotated_ ? area_.width : area_.height); } int rectangle_rotator::right() const { return (rotated_ ? area_.y + static_cast(area_.height) : area_.x + static_cast(area_.width)); } int rectangle_rotator::bottom() const { return (rotated_ ? area_.x + static_cast(area_.width) : area_.y + static_cast(area_.height)); } const ::nana::rectangle& rectangle_rotator::result() const { return area_; } //end class rectangle_rotator }