forked from mia/Aegisub
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:
parent
ea5428b65f
commit
08983adc96
10 changed files with 185 additions and 108 deletions
|
@ -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"
|
||||||
>
|
>
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
108
aegisub/libaegisub/common/parser.cpp
Normal file
108
aegisub/libaegisub/common/parser.cpp
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
aegisub/libaegisub/common/parser.h
Normal file
41
aegisub/libaegisub/common/parser.h
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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));
|
||||||
|
|
Loading…
Reference in a new issue