#include "rays/ruby/color.h" #include #include #include "defs.h" RUCY_DEFINE_VALUE_OR_ARRAY_FROM_TO(Rays::Color) #define THIS to(self) #define CHECK RUCY_CHECK_OBJ(Rays::Color, self) static VALUE alloc(VALUE klass) { return new_type(klass); } static VALUE initialize(VALUE self) { CHECK; check_arg_count(__FILE__, __LINE__, "Color#initialize", argc, 0, 1, 2, 3, 4); if (argc >= 1) *THIS = to(argc, argv); return self; } static VALUE initialize_copy(VALUE self, VALUE obj) { CHECK; *THIS = to(obj); return self; } static VALUE set_red(VALUE self, VALUE red) { CHECK; return value(THIS->red = to(red)); } static VALUE get_red(VALUE self) { CHECK; return value(THIS->red); } static VALUE set_green(VALUE self, VALUE green) { CHECK; return value(THIS->green = to(green)); } static VALUE get_green(VALUE self) { CHECK; return value(THIS->green); } static VALUE set_blue(VALUE self, VALUE blue) { CHECK; return value(THIS->blue = to(blue)); } static VALUE get_blue(VALUE self) { CHECK; return value(THIS->blue); } static VALUE set_alpha(VALUE self, VALUE alpha) { CHECK; return value(THIS->alpha = to(alpha)); } static VALUE get_alpha(VALUE self) { CHECK; return value(THIS->alpha); } static VALUE to_hsv(VALUE self) { CHECK; float h, s, v; Rays::get_hsv(&h, &s, &v, *THIS); return array(h, s, v, THIS->alpha); } typedef std::map ColorMap; static ColorMap& get_color_map () { static ColorMap map; if (map.empty()) { map["no"] = map["none"] = map["transp"] = map["transparent"] = Rays::gray(0, 0); map["black"] = Rays::rgb8( 0, 0, 0); map["white"] = Rays::rgb8(255, 241, 232); map["gray"] = map["lightgray"] = Rays::rgb8(194, 195, 199); map["darkgray"] = Rays::rgb8( 95, 87, 79); map["brown"] = Rays::rgb8(171, 82, 54); map["red"] = Rays::rgb8(255, 0, 77); map["orange"] = Rays::rgb8(255, 163, 0); map["yellow"] = Rays::rgb8(255, 236, 39); map["green"] = Rays::rgb8( 0, 228, 54); map["darkgreen"] = Rays::rgb8( 0, 135, 81); map["blue"] = Rays::rgb8( 41, 173, 255); map["darkblue"] = Rays::rgb8( 29, 43, 83); map["indigo"] = Rays::rgb8(131, 118, 156); map["pink"] = Rays::rgb8(255, 119, 168); map["peach"] = Rays::rgb8(255, 204, 170); map["darkpurple"] = Rays::rgb8(126, 37, 83); } return map; } static const Rays::String to_color_name (const char* name) { return Rays::String(name).downcase(); } static const Rays::Color& find_color (const char* name) { assert(name); const ColorMap& map = get_color_map(); ColorMap::const_iterator it = map.find(to_color_name(name)); if (it == map.end()) argument_error(__FILE__, __LINE__, "color '%s' is not found.", name); return it->second; } static VALUE hsv(VALUE self) { check_arg_count(__FILE__, __LINE__, "Color.hsv", argc, 3, 4); float h = to(argv[0]); float s = to(argv[1]); float v = to(argv[2]); if (argc >= 4) return value(Rays::hsv(h, s, v, to(argv[3]))); else return value(Rays::hsv(h, s, v)); } static VALUE set_palette_color(VALUE self) { check_arg_count(__FILE__, __LINE__, "Color.set_palette_color", argc, 2, 3, 4, 5); get_color_map()[to_color_name(argv[0].c_str())] = to(argc - 1, &argv[1]); } static Class cColor; void Init_rays_color () { Module mRays = rb_define_module("Rays"); cColor = rb_define_class_under(mRays, "Color", rb_cObject); rb_define_alloc_func(cColor, alloc); rb_define_private_method(cColor, "initialize", RUBY_METHOD_FUNC(initialize), -1); rb_define_private_method(cColor, "initialize_copy", RUBY_METHOD_FUNC(initialize_copy), 1); rb_define_method(cColor, "red=", RUBY_METHOD_FUNC(set_red), 1); rb_define_method(cColor, "red", RUBY_METHOD_FUNC(get_red), 0); rb_define_method(cColor, "green=", RUBY_METHOD_FUNC(set_green), 1); rb_define_method(cColor, "green", RUBY_METHOD_FUNC(get_green), 0); rb_define_method(cColor, "blue=", RUBY_METHOD_FUNC(set_blue), 1); rb_define_method(cColor, "blue", RUBY_METHOD_FUNC(get_blue), 0); rb_define_method(cColor, "alpha=", RUBY_METHOD_FUNC(set_alpha), 1); rb_define_method(cColor, "alpha", RUBY_METHOD_FUNC(get_alpha), 0); rb_define_method(cColor, "to_hsv", RUBY_METHOD_FUNC(to_hsv), 0); rb_define_module_function(cColor, "hsv", RUBY_METHOD_FUNC(hsv), -1); rb_define_module_function(cColor, "set_palette_color", RUBY_METHOD_FUNC(set_palette_color), -1); } namespace Rucy { static int char2hex (char c) { if ('0' <= c && c <= '9') return c - '0'; else if ('a' <= c && c <= 'f') return 10 + c - 'a'; else if ('A' <= c && c <= 'F') return 10 + c - 'A'; else return -1; } static bool parse_channel (float* channel, const char* str, size_t nchars, size_t index) { assert(channel && str && 1 <= nchars && nchars <= 2 && 0 <= index && index <= 3); const char* p = str + index * nchars; if (nchars == 1) { int c0 = char2hex(p[0]); if (c0 < 0) return false; assert(c0 < 16); *channel = c0 / 15.f; return true; } else { int c0 = char2hex(p[0]); int c1 = char2hex(p[1]); if (c0 < 0 || c1 < 0) return false; assert(c0 < 16 && c1 < 16); *channel = (c0 * 16 + c1) / 255.f; return true; } } static bool parse_string (Rays::Color* color, const char* str) { assert(color && str); if (*str != '#') return false; ++str; size_t len = strlen(str); switch (len) { case 3: case 6: { size_t nchars = len / 3; float r, g, b; if ( !parse_channel(&r, str, nchars, 0) || !parse_channel(&g, str, nchars, 1) || !parse_channel(&b, str, nchars, 2)) { return false; } color->reset(r, g, b); break; } case 4: case 8: { size_t nchars = len / 4; float r, g, b, a; if ( !parse_channel(&r, str, nchars, 0) || !parse_channel(&g, str, nchars, 1) || !parse_channel(&b, str, nchars, 2) || !parse_channel(&a, str, nchars, 3)) { return false; } color->reset(r, g, b, a); break; } default: return false; } return true; } static Rays::Color str2color (const char* str) { if (!str) argument_error(__FILE__, __LINE__); Rays::String str_ = str; str_ = str_.strip(); Rays::Color color; if (parse_string(&color, str_.c_str())) return color; else return find_color(str_.c_str()); } template <> Rays::Color value_to (int argc, const Value*argv, bool convert) { if (argc == 1 && argv->is_array()) { argc = argv->size(); argv = argv->as_array(); } assert(argc == 0 || (argc > 0 && argv)); if (convert) { if (argc == 0) return Rays::Color(); else if (argv->is_nil()) return str2color("none"); else if (argv->is_s() || argv->is_sym()) return str2color(argv[0].c_str()); else if (argv->is_num()) { switch (argc) { #define V(i) argv[i].as_f(true) case 1: return Rays::Color(V(0)); case 2: return Rays::Color(V(0), V(1)); case 3: return Rays::Color(V(0), V(1), V(2)); case 4: return Rays::Color(V(0), V(1), V(2), V(3)); #undef V default: argument_error(__FILE__, __LINE__, "invalid array size."); } } } if (argc != 1) argument_error(__FILE__, __LINE__); return value_to(*argv, convert); } }// Rucy namespace Rays { Class color_class () { return cColor; } }// Rays