Move agi::Color parsing to its own file

Keeping all uses of boost.spirit in a single translation unit helps
avoid having it murder compile times, which requires that it be off by
itself.
This commit is contained in:
Thomas Goyne 2012-10-27 06:32:53 -07:00
parent ea5428b65f
commit 08983adc96
10 changed files with 185 additions and 108 deletions

View file

@ -323,6 +323,14 @@
RelativePath="..\..\libaegisub\common\option_visit.h" RelativePath="..\..\libaegisub\common\option_visit.h"
> >
</File> </File>
<File
RelativePath="..\..\libaegisub\common\parser.cpp"
>
</File>
<File
RelativePath="..\..\libaegisub\common\parser.h"
>
</File>
<File <File
RelativePath="..\..\libaegisub\common\thesaurus.cpp" RelativePath="..\..\libaegisub\common\thesaurus.cpp"
> >

View file

@ -27,10 +27,11 @@ SRC += \
common/hotkey.cpp \ common/hotkey.cpp \
common/io.cpp \ common/io.cpp \
common/json.cpp \ common/json.cpp \
common/keyframe.cpp \
common/mru.cpp \ common/mru.cpp \
common/option.cpp \ common/option.cpp \
common/option_visit.cpp \ common/option_visit.cpp \
common/keyframe.cpp \ common/parser.cpp \
common/util.cpp \ common/util.cpp \
common/log.cpp \ common/log.cpp \
common/thesaurus.cpp \ common/thesaurus.cpp \

View file

@ -16,83 +16,10 @@
#include "libaegisub/color.h" #include "libaegisub/color.h"
#include <boost/config/warning_disable.hpp> #include "parser.h"
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <boost/format.hpp> #include <boost/format.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
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<agi::Color> {
template<typename T> struct result { typedef agi::Color type; };
template<class T> 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<typename Iterator>
struct color_grammar : qi::grammar<Iterator, agi::Color()> {
qi::rule<Iterator, agi::Color()> color;
qi::rule<Iterator, agi::Color()> css_color;
qi::rule<Iterator, agi::Color()> ass_color;
qi::rule<Iterator, unsigned char()> rgb_component;
qi::rule<Iterator, unsigned char()> rgb_percent;
qi::rule<Iterator, unsigned char()> hex_byte;
qi::rule<Iterator, unsigned char()> hex_char;
qi::rule<Iterator> comma;
qi::rule<Iterator> blank;
#define HEX_PARSER(type, len) qi::uint_parser<unsigned type, 16, len, len>()
color_grammar() : color_grammar::base_type(color) {
color = css_color | ass_color;
boost::phoenix::function<unpack_colors> 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<unsigned char, 10, 1, 3>();
comma = *qi::blank >> "," >> *qi::blank;
blank = *qi::blank;
}
};
}
namespace agi { 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) Color::Color(std::string const& str)
: r(0), g(0), b(0), a(0) : r(0), g(0), b(0), a(0)
{ {
const char *begin = &str[0]; parser::parse(*this, str);
parse(begin, &str[str.size()], color_grammar<const char *>(), *this);
}
Color::Color(const char *str)
: r(0), g(0), b(0), a(0)
{
parse(str, str + strlen(str), color_grammar<const char *>(), *this);
} }
std::string Color::GetAssStyleFormatted() const { std::string Color::GetAssStyleFormatted() const {

View file

@ -0,0 +1,108 @@
// Copyright (c) 2012, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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 <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
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<agi::Color> {
template<typename T> struct result { typedef agi::Color type; };
template<class T> 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<typename Iterator>
struct color_grammar : qi::grammar<Iterator, agi::Color()> {
qi::rule<Iterator, agi::Color()> color;
qi::rule<Iterator, agi::Color()> css_color;
qi::rule<Iterator, agi::Color()> ass_color;
qi::rule<Iterator, unsigned char()> rgb_component;
qi::rule<Iterator, unsigned char()> rgb_percent;
qi::rule<Iterator, unsigned char()> hex_byte;
qi::rule<Iterator, unsigned char()> hex_char;
qi::rule<Iterator> comma;
qi::rule<Iterator> blank;
#define HEX_PARSER(type, len) qi::uint_parser<unsigned type, 16, len, len>()
color_grammar() : color_grammar::base_type(color) {
color = css_color | ass_color;
boost::phoenix::function<unpack_colors> 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<unsigned char, 10, 1, 3>();
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<std::string::const_iterator>(), dst);
return parsed && begin == str.end();
}
}
}

View file

@ -0,0 +1,41 @@
// Copyright (c) 2012, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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 <string>
#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);
}
}

View file

@ -28,7 +28,6 @@ namespace agi {
Color(); Color();
Color(unsigned char r, unsigned char g, unsigned char b, unsigned char a = 0); Color(unsigned char r, unsigned char g, unsigned char b, unsigned char a = 0);
Color(std::string const& str); Color(std::string const& str);
Color(const char *str);
bool operator==(Color const& col) const; bool operator==(Color const& col) const;
bool operator!=(Color const& col) const; bool operator!=(Color const& col) const;

View file

@ -413,14 +413,14 @@
"Colour Picker" : { "Colour Picker" : {
"Mode" : 4, "Mode" : 4,
"Recent Colours" : [ "Recent Colours" : [
"&H000000&", {"color" : "&H000000&"},
"&H0000FF&", {"color" : "&H0000FF&"},
"&H00FFFF&", {"color" : "&H00FFFF&"},
"&H00FF00&", {"color" : "&H00FF00&"},
"&HFFFF00&", {"color" : "&HFFFF00&"},
"&HFF0000&", {"color" : "&HFF0000&"},
"&HFF00FF&", {"color" : "&HFF00FF&"},
"&HFFFFFF&" {"color" : "&HFFFFFF&"}
], ],
"Last" : { "Last" : {
"X" : -1, "X" : -1,

View file

@ -413,14 +413,14 @@
"Colour Picker" : { "Colour Picker" : {
"Mode" : 4, "Mode" : 4,
"Recent Colours" : [ "Recent Colours" : [
"&H000000&", {"color" : "&H000000&"},
"&H0000FF&", {"color" : "&H0000FF&"},
"&H00FFFF&", {"color" : "&H00FFFF&"},
"&H00FF00&", {"color" : "&H00FF00&"},
"&HFFFF00&", {"color" : "&HFFFF00&"},
"&HFF0000&", {"color" : "&HFF0000&"},
"&HFF00FF&", {"color" : "&HFF00FF&"},
"&HFFFFFF&" {"color" : "&HFFFFFF&"}
], ],
"Last" : { "Last" : {
"X" : -1, "X" : -1,

View file

@ -44,11 +44,11 @@ TEST(lagi_color, hex) {
} }
TEST(lagi_color, rgb) { TEST(lagi_color, rgb) {
EXPECT_EQ(agi::Color(0, 0, 0), "rgb(0, 0, 0)"); EXPECT_EQ(agi::Color(0, 0, 0), agi::Color("rgb(0, 0, 0)"));
EXPECT_EQ(agi::Color(255, 255, 255), "rgb(255, 255, 255)"); EXPECT_EQ(agi::Color(255, 255, 255), agi::Color("rgb(255, 255, 255)"));
EXPECT_EQ(agi::Color(255, 255, 255), "rgb(255,255,255)"); EXPECT_EQ(agi::Color(255, 255, 255), agi::Color("rgb(255,255,255)"));
EXPECT_EQ(agi::Color(255, 0, 127), "rgb(255, 0, 127)"); EXPECT_EQ(agi::Color(255, 0, 127), agi::Color("rgb(255, 0, 127)"));
EXPECT_EQ(agi::Color(16, 32, 48), "rgb( 16 , 32 , 48 )"); 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(0, 0, 0)", agi::Color(0, 0, 0).GetRgbFormatted());
EXPECT_EQ("rgb(255, 255, 255)", agi::Color(255, 255, 255).GetRgbFormatted()); EXPECT_EQ("rgb(255, 255, 255)", agi::Color(255, 255, 255).GetRgbFormatted());

View file

@ -132,7 +132,7 @@ TEST_F(lagi_option, flush_roundtrip) {
EXPECT_NO_THROW(opt.Get("Integer")->SetInt(1)); EXPECT_NO_THROW(opt.Get("Integer")->SetInt(1));
EXPECT_NO_THROW(opt.Get("Double")->SetDouble(1.1)); EXPECT_NO_THROW(opt.Get("Double")->SetDouble(1.1));
EXPECT_NO_THROW(opt.Get("String")->SetString("hello")); 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)); EXPECT_NO_THROW(opt.Get("Boolean")->SetBool(true));
std::vector<int64_t> int_arr; int_arr.push_back(1); std::vector<int64_t> 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)); EXPECT_NO_THROW(opt.Get("Array/Double")->SetListDouble(double_arr));
std::vector<std::string> str_arr; str_arr.push_back("hello"); std::vector<std::string> str_arr; str_arr.push_back("hello");
EXPECT_NO_THROW(opt.Get("Array/String")->SetListString(str_arr)); EXPECT_NO_THROW(opt.Get("Array/String")->SetListString(str_arr));
std::vector<agi::Color> clr_arr; clr_arr.push_back("rgb(255,255,255)"); std::vector<agi::Color> clr_arr; clr_arr.push_back(agi::Color("rgb(255,255,255)"));
EXPECT_NO_THROW(opt.Get("Array/Color")->SetListColor(clr_arr)); EXPECT_NO_THROW(opt.Get("Array/Color")->SetListColor(clr_arr));
std::vector<bool> bool_arr; bool_arr.push_back(true); std::vector<bool> bool_arr; bool_arr.push_back(true);
EXPECT_NO_THROW(opt.Get("Array/Boolean")->SetListBool(bool_arr)); EXPECT_NO_THROW(opt.Get("Array/Boolean")->SetListBool(bool_arr));