diff --git a/aegisub/build/libaegisub_vs2008/libaegisub_vs2008.vcproj b/aegisub/build/libaegisub_vs2008/libaegisub_vs2008.vcproj index 9d9299f5d..08b5568c1 100644 --- a/aegisub/build/libaegisub_vs2008/libaegisub_vs2008.vcproj +++ b/aegisub/build/libaegisub_vs2008/libaegisub_vs2008.vcproj @@ -323,6 +323,14 @@ RelativePath="..\..\libaegisub\common\option_visit.h" > + + + + diff --git a/aegisub/libaegisub/Makefile b/aegisub/libaegisub/Makefile index 368d293d0..8b57339ae 100644 --- a/aegisub/libaegisub/Makefile +++ b/aegisub/libaegisub/Makefile @@ -27,10 +27,11 @@ SRC += \ common/hotkey.cpp \ common/io.cpp \ common/json.cpp \ + common/keyframe.cpp \ common/mru.cpp \ common/option.cpp \ common/option_visit.cpp \ - common/keyframe.cpp \ + common/parser.cpp \ common/util.cpp \ common/log.cpp \ common/thesaurus.cpp \ diff --git a/aegisub/libaegisub/common/color.cpp b/aegisub/libaegisub/common/color.cpp index abc260e47..730e49adc 100644 --- a/aegisub/libaegisub/common/color.cpp +++ b/aegisub/libaegisub/common/color.cpp @@ -16,83 +16,10 @@ #include "libaegisub/color.h" -#include +#include "parser.h" + #include #include -#include -#include -#include -#include -#include -#include -#include - -BOOST_FUSION_ADAPT_STRUCT( - agi::Color, - (unsigned char, r) - (unsigned char, g) - (unsigned char, b) - (unsigned char, a) -) - -namespace { -using namespace boost::spirit; - -struct unpack_colors : public boost::static_visitor { - template struct result { typedef agi::Color type; }; - - template agi::Color operator()(T arg) const { - return boost::apply_visitor(*this, arg); - } - - agi::Color operator()(int abgr) const { return (*this)((unsigned)abgr); } - agi::Color operator()(unsigned int abgr) const { - return agi::Color(abgr & 0xFF, (abgr >> 8) & 0xFF, (abgr >> 16) & 0xFF, (abgr >> 24) & 0xFF); - } -}; - -template -struct color_grammar : qi::grammar { - qi::rule color; - - qi::rule css_color; - qi::rule ass_color; - - qi::rule rgb_component; - qi::rule rgb_percent; - qi::rule hex_byte; - qi::rule hex_char; - - qi::rule comma; - qi::rule blank; - -#define HEX_PARSER(type, len) qi::uint_parser() - - color_grammar() : color_grammar::base_type(color) { - color = css_color | ass_color; - - boost::phoenix::function unpack; - ass_color = ( - int_ - | -lit('&') >> -(lit('H') | lit('h')) >> (HEX_PARSER(int, 8) | HEX_PARSER(int, 6)) >> -lit('&') - )[_val = unpack(_1)] >> blank >> qi::eoi; - - css_color - = "rgb(" >> blank >> rgb_component >> comma >> rgb_component >> comma >> rgb_component >> blank >> ')' - | '#' >> hex_byte >> hex_byte >> hex_byte - | '#' >> hex_char >> hex_char >> hex_char - ; - - hex_char = HEX_PARSER(int, 1)[_val = _1 * 16 + _1]; - hex_byte = HEX_PARSER(int, 2); - rgb_component = qi::uint_parser(); - - comma = *qi::blank >> "," >> *qi::blank; - blank = *qi::blank; - } -}; - -} namespace agi { @@ -105,14 +32,7 @@ Color::Color(unsigned char r, unsigned char g, unsigned char b, unsigned char a) Color::Color(std::string const& str) : r(0), g(0), b(0), a(0) { - const char *begin = &str[0]; - parse(begin, &str[str.size()], color_grammar(), *this); -} - -Color::Color(const char *str) -: r(0), g(0), b(0), a(0) -{ - parse(str, str + strlen(str), color_grammar(), *this); + parser::parse(*this, str); } std::string Color::GetAssStyleFormatted() const { diff --git a/aegisub/libaegisub/common/parser.cpp b/aegisub/libaegisub/common/parser.cpp new file mode 100644 index 000000000..9d9359cf0 --- /dev/null +++ b/aegisub/libaegisub/common/parser.cpp @@ -0,0 +1,108 @@ +// Copyright (c) 2012, Thomas Goyne +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +#include "../config.h" + +#include "parser.h" + +#include "libaegisub/color.h" + +#include +#include +#include +#include +#include + +BOOST_FUSION_ADAPT_STRUCT( + agi::Color, + (unsigned char, r) + (unsigned char, g) + (unsigned char, b) + (unsigned char, a) +) + +namespace { +using namespace boost::spirit; + +/// Convert a abgr value in an int or unsigned int to an agi::Color +struct unpack_colors : public boost::static_visitor { + template struct result { typedef agi::Color type; }; + + template agi::Color operator()(T arg) const { + return boost::apply_visitor(*this, arg); + } + + agi::Color operator()(int abgr) const { return (*this)((unsigned)abgr); } + agi::Color operator()(unsigned int abgr) const { + return agi::Color(abgr & 0xFF, (abgr >> 8) & 0xFF, (abgr >> 16) & 0xFF, (abgr >> 24) & 0xFF); + } +}; + +template +struct color_grammar : qi::grammar { + qi::rule color; + + qi::rule css_color; + qi::rule ass_color; + + qi::rule rgb_component; + qi::rule rgb_percent; + qi::rule hex_byte; + qi::rule hex_char; + + qi::rule comma; + qi::rule blank; + +#define HEX_PARSER(type, len) qi::uint_parser() + + color_grammar() : color_grammar::base_type(color) { + color = css_color | ass_color; + + boost::phoenix::function unpack; + + // Order is important here; int_ (for SSA) needs to come before the ASS + // option as decimal numbers of the appropriate length are also valid + // hex numbers + // ASS with alpha needs to come before ASS without alpha for the same + // reason + ass_color = ( + int_ + | -lit('&') >> -(lit('H') | lit('h')) >> (HEX_PARSER(int, 8) | HEX_PARSER(int, 6)) >> -lit('&') + )[_val = unpack(_1)] >> blank >> qi::eoi; + + css_color + = "rgb(" >> blank >> rgb_component >> comma >> rgb_component >> comma >> rgb_component >> blank >> ')' + | '#' >> hex_byte >> hex_byte >> hex_byte + | '#' >> hex_char >> hex_char >> hex_char + ; + + hex_char = HEX_PARSER(int, 1)[_val = _1 * 16 + _1]; + hex_byte = HEX_PARSER(int, 2); + rgb_component = qi::uint_parser(); + + comma = *qi::blank >> "," >> *qi::blank; + blank = *qi::blank; + } +}; + +} + +namespace agi { namespace parser { + bool parse(Color &dst, std::string const& str) { + std::string::const_iterator begin = str.begin(); + bool parsed = parse(begin, str.end(), color_grammar(), dst); + return parsed && begin == str.end(); + } +} +} diff --git a/aegisub/libaegisub/common/parser.h b/aegisub/libaegisub/common/parser.h new file mode 100644 index 000000000..f5e07f855 --- /dev/null +++ b/aegisub/libaegisub/common/parser.h @@ -0,0 +1,41 @@ +// Copyright (c) 2012, Thomas Goyne +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +#ifndef LAGI_PRE +#include +#endif + +namespace agi { + struct Color; + + namespace parser { + /// Try to parse a string as a color + /// @param[out] dst Color struct to populate with the parsed result + /// @param str String to parse + /// @return Was the string successfully parsed as a color? + /// + /// If this function returns false, the contents of dst is undefined. It + /// may contain partially-parsed garbage. + /// + /// This function supports the following formats: + /// * SSA colors (i.e. a decimal number representing a bgr value) + /// * ASS override and style formats (a (a)bgr hex number possibly with + /// some ampersands and an H somewhere) + /// * CSS-style #rrggbb and #rgb + /// * CSS-style rgb(r,g,b) + /// + /// CSS's rgb(r%,g%,b%) format is not currently supported. + bool parse(Color &dst, std::string const& str); + } +} diff --git a/aegisub/libaegisub/include/libaegisub/color.h b/aegisub/libaegisub/include/libaegisub/color.h index 0f40bc8c5..c6abe6ab0 100644 --- a/aegisub/libaegisub/include/libaegisub/color.h +++ b/aegisub/libaegisub/include/libaegisub/color.h @@ -28,7 +28,6 @@ namespace agi { Color(); Color(unsigned char r, unsigned char g, unsigned char b, unsigned char a = 0); Color(std::string const& str); - Color(const char *str); bool operator==(Color const& col) const; bool operator!=(Color const& col) const; diff --git a/aegisub/src/libresrc/default_config.json b/aegisub/src/libresrc/default_config.json index fb0ba34c4..f0238777b 100644 --- a/aegisub/src/libresrc/default_config.json +++ b/aegisub/src/libresrc/default_config.json @@ -413,14 +413,14 @@ "Colour Picker" : { "Mode" : 4, "Recent Colours" : [ - "&H000000&", - "&H0000FF&", - "&H00FFFF&", - "&H00FF00&", - "&HFFFF00&", - "&HFF0000&", - "&HFF00FF&", - "&HFFFFFF&" + {"color" : "&H000000&"}, + {"color" : "&H0000FF&"}, + {"color" : "&H00FFFF&"}, + {"color" : "&H00FF00&"}, + {"color" : "&HFFFF00&"}, + {"color" : "&HFF0000&"}, + {"color" : "&HFF00FF&"}, + {"color" : "&HFFFFFF&"} ], "Last" : { "X" : -1, diff --git a/aegisub/src/libresrc/osx/default_config.json b/aegisub/src/libresrc/osx/default_config.json index c4c334ce4..70d2bf383 100644 --- a/aegisub/src/libresrc/osx/default_config.json +++ b/aegisub/src/libresrc/osx/default_config.json @@ -413,14 +413,14 @@ "Colour Picker" : { "Mode" : 4, "Recent Colours" : [ - "&H000000&", - "&H0000FF&", - "&H00FFFF&", - "&H00FF00&", - "&HFFFF00&", - "&HFF0000&", - "&HFF00FF&", - "&HFFFFFF&" + {"color" : "&H000000&"}, + {"color" : "&H0000FF&"}, + {"color" : "&H00FFFF&"}, + {"color" : "&H00FF00&"}, + {"color" : "&HFFFF00&"}, + {"color" : "&HFF0000&"}, + {"color" : "&HFF00FF&"}, + {"color" : "&HFFFFFF&"} ], "Last" : { "X" : -1, diff --git a/aegisub/tests/libaegisub_color.cpp b/aegisub/tests/libaegisub_color.cpp index f657c5472..71ed86bb1 100644 --- a/aegisub/tests/libaegisub_color.cpp +++ b/aegisub/tests/libaegisub_color.cpp @@ -44,11 +44,11 @@ TEST(lagi_color, hex) { } TEST(lagi_color, rgb) { - EXPECT_EQ(agi::Color(0, 0, 0), "rgb(0, 0, 0)"); - EXPECT_EQ(agi::Color(255, 255, 255), "rgb(255, 255, 255)"); - EXPECT_EQ(agi::Color(255, 255, 255), "rgb(255,255,255)"); - EXPECT_EQ(agi::Color(255, 0, 127), "rgb(255, 0, 127)"); - EXPECT_EQ(agi::Color(16, 32, 48), "rgb( 16 , 32 , 48 )"); + EXPECT_EQ(agi::Color(0, 0, 0), agi::Color("rgb(0, 0, 0)")); + EXPECT_EQ(agi::Color(255, 255, 255), agi::Color("rgb(255, 255, 255)")); + EXPECT_EQ(agi::Color(255, 255, 255), agi::Color("rgb(255,255,255)")); + EXPECT_EQ(agi::Color(255, 0, 127), agi::Color("rgb(255, 0, 127)")); + EXPECT_EQ(agi::Color(16, 32, 48), agi::Color("rgb( 16 , 32 , 48 )")); EXPECT_EQ("rgb(0, 0, 0)", agi::Color(0, 0, 0).GetRgbFormatted()); EXPECT_EQ("rgb(255, 255, 255)", agi::Color(255, 255, 255).GetRgbFormatted()); diff --git a/aegisub/tests/libaegisub_option.cpp b/aegisub/tests/libaegisub_option.cpp index 1832e1d63..2a22646d4 100644 --- a/aegisub/tests/libaegisub_option.cpp +++ b/aegisub/tests/libaegisub_option.cpp @@ -132,7 +132,7 @@ TEST_F(lagi_option, flush_roundtrip) { EXPECT_NO_THROW(opt.Get("Integer")->SetInt(1)); EXPECT_NO_THROW(opt.Get("Double")->SetDouble(1.1)); EXPECT_NO_THROW(opt.Get("String")->SetString("hello")); - EXPECT_NO_THROW(opt.Get("Color")->SetColor("rgb(255,255,255)")); + EXPECT_NO_THROW(opt.Get("Color")->SetColor(agi::Color("rgb(255,255,255)"))); EXPECT_NO_THROW(opt.Get("Boolean")->SetBool(true)); std::vector int_arr; int_arr.push_back(1); @@ -141,7 +141,7 @@ TEST_F(lagi_option, flush_roundtrip) { EXPECT_NO_THROW(opt.Get("Array/Double")->SetListDouble(double_arr)); std::vector str_arr; str_arr.push_back("hello"); EXPECT_NO_THROW(opt.Get("Array/String")->SetListString(str_arr)); - std::vector clr_arr; clr_arr.push_back("rgb(255,255,255)"); + std::vector clr_arr; clr_arr.push_back(agi::Color("rgb(255,255,255)")); EXPECT_NO_THROW(opt.Get("Array/Color")->SetListColor(clr_arr)); std::vector bool_arr; bool_arr.push_back(true); EXPECT_NO_THROW(opt.Get("Array/Boolean")->SetListBool(bool_arr));