00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "color_range.hpp"
00017 #include "config.hpp"
00018 #include "display.hpp"
00019 #include "foreach.hpp"
00020 #include "game_config.hpp"
00021 #include "image.hpp"
00022 #include "image_modifications.hpp"
00023 #include "log.hpp"
00024 #include "serialization/string_utils.hpp"
00025
00026 #include <map>
00027
00028 #define GETTEXT_DOMAIN "wesnoth-lib"
00029
00030 static lg::log_domain log_display("display");
00031 #define ERR_DP LOG_STREAM(err, log_display)
00032 #define LOG_DP LOG_STREAM(info, log_display)
00033
00034 namespace image {
00035
00036
00037 namespace {
00038
00039
00040 typedef modification* (*mod_parser)(const std::string&);
00041
00042
00043
00044
00045
00046
00047 std::map<std::string, mod_parser> mod_parsers;
00048
00049
00050
00051
00052
00053
00054
00055
00056 modification* decode_modification(const std::string& encoded_mod)
00057 {
00058 std::vector<std::string> split = utils::parenthetical_split(encoded_mod);
00059
00060 if(split.size() != 2) {
00061 ERR_DP << "error parsing image modifications: "
00062 << encoded_mod << "\n";
00063 return NULL;
00064 }
00065
00066 std::string mod_type = split[0];
00067 std::string args = split[1];
00068
00069 if(mod_parsers.find(mod_type) == mod_parsers.end()) {
00070 ERR_DP << "unknown image function in path: "
00071 << mod_type << '\n';
00072 return NULL;
00073 }
00074
00075 return (*mod_parsers[mod_type])(args);
00076 }
00077 }
00078
00079
00080 modification::texception::texception(const std::stringstream& message_stream)
00081 : message(message_stream.str())
00082 {
00083 }
00084
00085 modification::texception::texception(const std::string& message)
00086 : message(message)
00087 {
00088 }
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099 modification_queue modification::decode(const std::string& encoded_mods)
00100 {
00101 modification_queue mods;
00102
00103 foreach(const std::string& encoded_mod,
00104 utils::parenthetical_split(encoded_mods, '~')) {
00105 modification* mod = decode_modification(encoded_mod);
00106
00107 if(mod) {
00108 mods.push(mod);
00109 }
00110 }
00111
00112 return mods;
00113 }
00114
00115
00116 bool mod_ptr_comparator_::operator()(const modification* a,
00117 const modification* b) const
00118 {
00119 return a->priority() < b->priority();
00120 }
00121
00122 surface rc_modification::operator()(const surface& src) const
00123 {
00124
00125 return recolor_image(src, rc_map_);
00126 }
00127
00128 surface fl_modification::operator()(const surface& src) const
00129 {
00130 surface ret = src;
00131
00132 if ( horiz_ && vert_ ) {
00133
00134 ret = rotate_180_surface(ret);
00135 }
00136
00137 else if(horiz_) {
00138 ret = flip_surface(ret);
00139 }
00140
00141 else if(vert_) {
00142 ret = flop_surface(ret);
00143 }
00144
00145 return ret;
00146 }
00147
00148 surface rotate_modification::operator()(const surface& src) const
00149 {
00150
00151 const int normalized = degrees_ >= 0 ?
00152 degrees_ - 360*(degrees_/360) :
00153 degrees_ + 360*(1 + (-degrees_)/360);
00154
00155 switch ( normalized )
00156 {
00157 case 0: return src;
00158 case 90: return rotate_90_surface(src, true);
00159 case 180: return rotate_180_surface(src);
00160 case 270: return rotate_90_surface(src, false);
00161 case 360: return src;
00162 }
00163
00164
00165 return src;
00166 }
00167
00168 surface gs_modification::operator()(const surface& src) const
00169 {
00170 return greyscale_image(src);
00171 }
00172
00173 surface crop_modification::operator()(const surface& src) const
00174 {
00175 SDL_Rect area = slice_;
00176 if(area.w == 0) {
00177 area.w = src->w;
00178 }
00179 if(area.h == 0) {
00180 area.h = src->h;
00181 }
00182 if(area.x < 0) {
00183 ERR_DP << "start X coordinate of SECTION modification is negative - truncating to zero\n";
00184 area.x = 0;
00185 }
00186 if(area.y < 0) {
00187 ERR_DP << "start Y coordinate of SECTION modification is negative - truncating to zero\n";
00188 area.y = 0;
00189 }
00190 return cut_surface(src, area);
00191 }
00192
00193 const SDL_Rect& crop_modification::get_slice() const
00194 {
00195 return slice_;
00196 }
00197
00198 surface blit_modification::operator()(const surface& src) const
00199 {
00200 if(x_ >= src->w) {
00201 std::stringstream sstr;
00202 sstr << "~BLIT(): x-coordinate '"
00203 << x_ << "' larger than destination image's width '"
00204 << src->w << "' no blitting performed.\n";
00205
00206 throw texception(sstr);
00207 }
00208
00209 if(y_ >= src->h) {
00210 std::stringstream sstr;
00211 sstr << "~BLIT(): y-coordinate '"
00212 << y_ << "' larger than destination image's height '"
00213 << src->h << "' no blitting performed.\n";
00214
00215 throw texception(sstr);
00216 }
00217
00218 if(surf_->w + x_ > src->w) {
00219 std::stringstream sstr;
00220 sstr << "~BLIT(): offset and width '"
00221 << x_ + surf_->w << "' larger than destination image's width '"
00222 << src->w << "' no blitting performed.\n";
00223
00224 throw texception(sstr);
00225 }
00226
00227 if(surf_->h + y_ > src->h) {
00228 std::stringstream sstr;
00229 sstr << "~BLIT(): offset and height '"
00230 << y_ + surf_->h << "' larger than destination image's height '"
00231 << src->h << "' no blitting performed.\n";
00232
00233 throw texception(sstr);
00234 }
00235
00236
00237 surface nsrc = make_neutral_surface(src);
00238 surface nsurf = make_neutral_surface(surf_);
00239 SDL_Rect r = create_rect(x_, y_, 0, 0);
00240 blit_surface(nsurf, NULL, nsrc, &r);
00241 return nsrc;
00242 }
00243
00244 const surface& blit_modification::get_surface() const
00245 {
00246 return surf_;
00247 }
00248
00249 int blit_modification::get_x() const
00250 {
00251 return x_;
00252 }
00253
00254 int blit_modification::get_y() const
00255 {
00256 return y_;
00257 }
00258
00259 surface mask_modification::operator()(const surface& src) const
00260 {
00261 if(src->w == mask_->w && src->h == mask_->h && x_ == 0 && y_ == 0)
00262 return mask_surface(src, mask_);
00263 SDL_Rect r = create_rect(x_, y_, 0, 0);
00264 surface new_mask = create_neutral_surface(src->w, src->h);
00265 blit_surface(mask_, NULL, new_mask, &r);
00266 return mask_surface(src, new_mask);
00267 }
00268
00269 const surface& mask_modification::get_mask() const
00270 {
00271 return mask_;
00272 }
00273
00274 int mask_modification::get_x() const
00275 {
00276 return x_;
00277 }
00278
00279 int mask_modification::get_y() const
00280 {
00281 return y_;
00282 }
00283
00284 surface light_modification::operator()(const surface& src) const {
00285 if(src == NULL) { return NULL; }
00286
00287
00288 surface nsurf;
00289 if(surf_->w != src->w || surf_->h != src->h)
00290 nsurf = scale_surface(surf_, src->w, src->h, false);
00291 else
00292 nsurf = make_neutral_surface(surf_);
00293 return light_surface(src, nsurf);;
00294 }
00295
00296 const surface& light_modification::get_surface() const
00297 {
00298 return surf_;
00299 }
00300
00301 surface scale_modification::operator()(const surface& src) const
00302 {
00303 const int old_w = src->w;
00304 const int old_h = src->h;
00305 int w = w_;
00306 int h = h_;
00307
00308 if(w <= 0) {
00309 if(w < 0) {
00310 ERR_DP << "width of SCALE is negative - resetting to original width\n";
00311 }
00312 w = old_w;
00313 }
00314 if(h <= 0) {
00315 if(h < 0) {
00316 ERR_DP << "height of SCALE is negative - resetting to original height\n";
00317 }
00318 h = old_h;
00319 }
00320
00321 return scale_surface(src, w, h);
00322 }
00323
00324 int scale_modification::get_w() const
00325 {
00326 return w_;
00327 }
00328
00329 int scale_modification::get_h() const
00330 {
00331 return h_;
00332 }
00333
00334 surface o_modification::operator()(const surface& src) const
00335 {
00336 return adjust_surface_alpha(src, ftofxp(opacity_));
00337 }
00338
00339 float o_modification::get_opacity() const
00340 {
00341 return opacity_;
00342 }
00343
00344 surface cs_modification::operator()(const surface& src) const
00345 {
00346 return(
00347 (r_ != 0 || g_ != 0 || b_ != 0) ?
00348 adjust_surface_color(src, r_, g_, b_) :
00349 src
00350 );
00351 }
00352
00353 int cs_modification::get_r() const
00354 {
00355 return r_;
00356 }
00357
00358 int cs_modification::get_g() const
00359 {
00360 return g_;
00361 }
00362
00363 int cs_modification::get_b() const
00364 {
00365 return b_;
00366 }
00367
00368 surface blend_modification::operator()(const surface& src) const
00369 {
00370 return blend_surface(src, a_, display::rgb(r_, g_, b_));
00371
00372 }
00373
00374 int blend_modification::get_r() const
00375 {
00376 return r_;
00377 }
00378
00379 int blend_modification::get_g() const
00380 {
00381 return g_;
00382 }
00383
00384 int blend_modification::get_b() const
00385 {
00386 return b_;
00387 }
00388
00389 float blend_modification::get_a() const
00390 {
00391 return a_;
00392 }
00393
00394 surface bl_modification::operator()(const surface& src) const
00395 {
00396 return blur_alpha_surface(src, depth_);
00397 }
00398
00399 int bl_modification::get_depth() const
00400 {
00401 return depth_;
00402 }
00403
00404 surface brighten_modification::operator()(const surface &src) const
00405 {
00406 surface ret = make_neutral_surface(src);
00407 surface tod_bright(image::get_image(game_config::images::tod_bright));
00408 if (tod_bright)
00409 blit_surface(tod_bright, NULL, ret, NULL);
00410 return ret;
00411 }
00412
00413 surface darken_modification::operator()(const surface &src) const
00414 {
00415 surface ret = make_neutral_surface(src);
00416 surface tod_dark(image::get_image(game_config::images::tod_dark));
00417 if (tod_dark)
00418 blit_surface(tod_dark, NULL, ret, NULL);
00419 return ret;
00420 }
00421
00422 surface background_modification::operator()(const surface &src) const
00423 {
00424 surface ret = make_neutral_surface(src);
00425 SDL_FillRect(ret, NULL, SDL_MapRGBA(ret->format, color_.r, color_.g,
00426 color_.b, color_.unused));
00427 SDL_SetAlpha(src, SDL_SRCALPHA, SDL_ALPHA_OPAQUE);
00428 SDL_BlitSurface(src, NULL, ret, NULL);
00429 return ret;
00430 }
00431
00432 const SDL_Color& background_modification::get_color() const
00433 {
00434 return color_;
00435 }
00436
00437 namespace {
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448 #define REGISTER_MOD_PARSER(type, args_var) \
00449 modification* parse_##type##_mod(const std::string&); \
00450 struct parse_##type##_mod_registration \
00451 { \
00452 parse_##type##_mod_registration() \
00453 { \
00454 mod_parsers[#type] = &parse_##type##_mod; \
00455 } \
00456 } parse_##type##_mod_registration_aux; \
00457 modification* parse_##type##_mod(const std::string& args_var)
00458
00459
00460 REGISTER_MOD_PARSER(TC, args)
00461 {
00462 std::vector<std::string> params = utils::split(args,',');
00463
00464 if(params.size() < 2) {
00465 ERR_DP << "too few arguments passed to the ~TC() function\n";
00466
00467 return NULL;
00468 }
00469
00470 int side_n = lexical_cast_default<int>(params[0], -1);
00471 std::string team_color;
00472 if (side_n < 1) {
00473 ERR_DP << "invalid team (" << side_n
00474 << ") passed to the ~TC() function\n";
00475 return NULL;
00476 }
00477 else if (side_n < static_cast<int>(image::get_team_colors().size())) {
00478 team_color = image::get_team_colors()[side_n - 1];
00479 }
00480 else {
00481
00482 try {
00483 team_color = lexical_cast<std::string>(side_n);
00484 } catch(bad_lexical_cast const&) {
00485 ERR_DP << "bad things happen\n";
00486
00487 return NULL;
00488 }
00489 }
00490
00491
00492
00493
00494 if(!game_config::tc_info(params[1]).size()){
00495 ERR_DP << "could not load TC info for '" << params[1]
00496 << "' palette\n"
00497 << "bailing out from TC\n";
00498
00499 return NULL;
00500 }
00501
00502 std::map<Uint32, Uint32> rc_map;
00503 try {
00504 color_range const& new_color =
00505 game_config::color_info(team_color);
00506 std::vector<Uint32> const& old_color =
00507 game_config::tc_info(params[1]);
00508
00509 rc_map = recolor_range(new_color,old_color);
00510 }
00511 catch(config::error const& e) {
00512 ERR_DP << "caught config::error while processing TC: "
00513 << e.message
00514 << '\n'
00515 << "bailing out from TC\n";
00516
00517 return NULL;
00518 }
00519
00520 return new rc_modification(rc_map);
00521 }
00522
00523
00524 REGISTER_MOD_PARSER(RC, args)
00525 {
00526 const std::vector<std::string> recolor_params = utils::split(args,'>');
00527
00528 if(recolor_params.size()>1){
00529
00530
00531
00532 std::map<Uint32, Uint32> rc_map;
00533 try {
00534 color_range const& new_color =
00535 game_config::color_info(recolor_params[1]);
00536 std::vector<Uint32> const& old_color =
00537 game_config::tc_info(recolor_params[0]);
00538
00539 rc_map = recolor_range(new_color,old_color);
00540 }
00541 catch (config::error& e) {
00542 ERR_DP
00543 << "caught config::error while processing color-range RC: "
00544 << e.message
00545 << '\n';
00546 ERR_DP
00547 << "bailing out from RC\n";
00548 rc_map.clear();
00549 }
00550
00551 return new rc_modification(rc_map);
00552 }
00553 else {
00554
00555 if(args.find('=') != std::string::npos) {
00556 lg::wml_error << "the ~RC() image function cannot be used for palette switch (A=B) in 1.7.x; use ~PAL(A>B) instead\n";
00557 }
00558 }
00559
00560 return NULL;
00561 }
00562
00563
00564 REGISTER_MOD_PARSER(PAL, args)
00565 {
00566 const std::vector<std::string> remap_params = utils::split(args,'>');
00567
00568 if(remap_params.size() < 2) {
00569 ERR_DP << "not enough arguments passed to the ~PAL() function: "
00570 << args << "\n";
00571
00572 return NULL;
00573 }
00574
00575
00576 try {
00577 std::map<Uint32, Uint32> rc_map;
00578 std::vector<Uint32> const& old_palette =
00579 game_config::tc_info(remap_params[0]);
00580 std::vector<Uint32> const& new_palette =
00581 game_config::tc_info(remap_params[1]);
00582
00583 for(size_t i = 0; i < old_palette.size() && i < new_palette.size(); ++i) {
00584 rc_map[old_palette[i]] = new_palette[i];
00585 }
00586
00587 return new rc_modification(rc_map);
00588 }
00589 catch(config::error& e) {
00590 ERR_DP
00591 << "caught config::error while processing PAL function: "
00592 << e.message
00593 << '\n';
00594 ERR_DP
00595 << "bailing out from PAL\n";
00596
00597 return NULL;
00598 }
00599 }
00600
00601
00602 REGISTER_MOD_PARSER(FL, args)
00603 {
00604 bool horiz = (args.empty() || args.find("horiz") != std::string::npos);
00605 bool vert = (args.find("vert") != std::string::npos);
00606
00607 return new fl_modification(horiz, vert);
00608 }
00609
00610
00611 REGISTER_MOD_PARSER(ROTATE, args)
00612 {
00613 return new rotate_modification(lexical_cast_default<int>(args, 90));
00614 }
00615
00616
00617 REGISTER_MOD_PARSER(GS, )
00618 {
00619 return new gs_modification;
00620 }
00621
00622
00623 REGISTER_MOD_PARSER(CS, args)
00624 {
00625 std::vector<std::string> const factors = utils::split(args, ',');
00626 const size_t s = factors.size();
00627
00628 if(s == 0) {
00629 ERR_DP << "no arguments passed to the ~CS() function\n";
00630 return NULL;
00631 }
00632
00633 int r = 0, g = 0, b = 0;
00634
00635 r = lexical_cast_default<int>(factors[0]);
00636
00637 if( s > 1 ) {
00638 g = lexical_cast_default<int>(factors[1]);
00639 }
00640 if( s > 2 ) {
00641 b = lexical_cast_default<int>(factors[2]);
00642 }
00643
00644 return new cs_modification(r, g, b);
00645 }
00646
00647
00648 REGISTER_MOD_PARSER(BLEND, args)
00649 {
00650 const std::vector<std::string>& params = utils::split(args, ',');
00651
00652 if(params.size() != 4) {
00653 ERR_DP << "~BLEND() requires exactly 4 arguments\n";
00654 return NULL;
00655 }
00656
00657 float opacity = 0.0f;
00658 const std::string& opacity_str = params[3];
00659 const std::string::size_type p100_pos = opacity_str.find('%');
00660
00661 if(p100_pos == std::string::npos)
00662 opacity = lexical_cast_default<float>(opacity_str);
00663 else {
00664
00665 const std::string& parsed_field = opacity_str.substr(0, p100_pos);
00666 opacity = lexical_cast_default<float>(parsed_field);
00667 opacity /= 100.0f;
00668 }
00669
00670 return new blend_modification(
00671 lexical_cast_default<int>(params[0]),
00672 lexical_cast_default<int>(params[1]),
00673 lexical_cast_default<int>(params[2]),
00674 opacity);
00675 }
00676
00677
00678 REGISTER_MOD_PARSER(CROP, args)
00679 {
00680 std::vector<std::string> const& slice_params = utils::split(args, ',', utils::STRIP_SPACES);
00681 const size_t s = slice_params.size();
00682
00683 if(s == 0 || (s == 1 && slice_params[0].empty())) {
00684 ERR_DP << "no arguments passed to the ~CROP() function\n";
00685 return NULL;
00686 }
00687
00688 SDL_Rect slice_rect = { 0, 0, 0, 0 };
00689
00690 slice_rect.x = lexical_cast_default<Sint16, const std::string&>(slice_params[0]);
00691
00692 if(s > 1) {
00693 slice_rect.y = lexical_cast_default<Sint16, const std::string&>(slice_params[1]);
00694 }
00695 if(s > 2) {
00696 slice_rect.w = lexical_cast_default<Uint16, const std::string&>(slice_params[2]);
00697 }
00698 if(s > 3) {
00699 slice_rect.h = lexical_cast_default<Uint16, const std::string&>(slice_params[3]);
00700 }
00701
00702 return new crop_modification(slice_rect);
00703 }
00704
00705 static bool check_image(const image::locator& img, std::stringstream & message)
00706 {
00707 if(img.file_exists()) return true;
00708 message << " image not found: '" << img.get_filename() << "'\n";
00709 ERR_DP << message.str();
00710 return false;
00711 }
00712
00713
00714 REGISTER_MOD_PARSER(BLIT, args)
00715 {
00716 std::vector<std::string> param = utils::parenthetical_split(args, ',');
00717 const size_t s = param.size();
00718
00719 if(s == 0 || (s == 1 && param[0].empty())){
00720 ERR_DP << "no arguments passed to the ~BLIT() function\n";
00721 return NULL;
00722 }
00723
00724 int x = 0, y = 0;
00725
00726 if(s == 3) {
00727 x = lexical_cast_default<int>(param[1]);
00728 y = lexical_cast_default<int>(param[2]);
00729 }
00730
00731 if(x < 0 || y < 0) {
00732 ERR_DP << "negative position arguments in ~BLIT() function\n";
00733 return NULL;
00734 }
00735
00736 const image::locator img(param[0]);
00737 std::stringstream message;
00738 message << "~BLIT():";
00739 if(!check_image(img, message))
00740 return NULL;
00741 surface surf = get_image(img);
00742
00743 return new blit_modification(surf, x, y);
00744 }
00745
00746
00747 REGISTER_MOD_PARSER(MASK, args)
00748 {
00749 std::vector<std::string> param = utils::parenthetical_split(args, ',');
00750 const size_t s = param.size();
00751
00752 if(s == 0 || (s == 1 && param[0].empty())){
00753 ERR_DP << "no arguments passed to the ~MASK() function\n";
00754 return NULL;
00755 }
00756
00757 int x = 0, y = 0;
00758
00759 if(s == 3) {
00760 x = lexical_cast_default<int>(param[1]);
00761 y = lexical_cast_default<int>(param[2]);
00762 }
00763
00764 if(x < 0 || y < 0) {
00765 ERR_DP << "negative position arguments in ~MASK() function\n";
00766 return NULL;
00767 }
00768
00769 const image::locator img(param[0]);
00770 std::stringstream message;
00771 message << "~MASK():";
00772 if(!check_image(img, message))
00773 return NULL;
00774 surface surf = get_image(img);
00775
00776 return new mask_modification(surf, x, y);
00777 }
00778
00779
00780 REGISTER_MOD_PARSER(L, args)
00781 {
00782 if(args.empty()){
00783 ERR_DP << "no arguments passed to the ~L() function\n";
00784 return NULL;
00785 }
00786
00787 surface surf = get_image(args);
00788
00789 return new light_modification(surf);
00790 }
00791
00792
00793 REGISTER_MOD_PARSER(SCALE, args)
00794 {
00795 std::vector<std::string> const& scale_params = utils::split(args, ',', utils::STRIP_SPACES);
00796 const size_t s = scale_params.size();
00797
00798 if(s == 0 || (s == 1 && scale_params[0].empty())) {
00799 ERR_DP << "no arguments passed to the ~SCALE() function\n";
00800 return NULL;
00801 }
00802
00803 int w = 0, h = 0;
00804
00805 w = lexical_cast_default<int, const std::string&>(scale_params[0]);
00806
00807 if(s > 1) {
00808 h = lexical_cast_default<int, const std::string&>(scale_params[1]);
00809 }
00810
00811 return new scale_modification(w, h);
00812 }
00813
00814
00815 REGISTER_MOD_PARSER(BL, args)
00816 {
00817 const int depth = std::max<int>(0, lexical_cast_default<int>(args));
00818
00819 return new bl_modification(depth);
00820 }
00821
00822
00823 REGISTER_MOD_PARSER(O, args)
00824 {
00825 const std::string::size_type p100_pos = args.find('%');
00826 float num = 0.0f;
00827 if(p100_pos == std::string::npos)
00828 num = lexical_cast_default<float,const std::string&>(args);
00829 else {
00830
00831 const std::string parsed_field = args.substr(0, p100_pos);
00832 num = lexical_cast_default<float,const std::string&>(parsed_field);
00833 num /= 100.0f;
00834 }
00835
00836 return new o_modification(num);
00837 }
00838
00839
00840
00841
00842
00843
00844 REGISTER_MOD_PARSER(R, args)
00845 {
00846 const int r = lexical_cast_default<int>(args);
00847
00848 return new cs_modification(r,0,0);
00849 }
00850
00851
00852 REGISTER_MOD_PARSER(G, args)
00853 {
00854 const int g = lexical_cast_default<int>(args);
00855
00856 return new cs_modification(0,g,0);
00857 }
00858
00859
00860 REGISTER_MOD_PARSER(B, args)
00861 {
00862 const int b = lexical_cast_default<int>(args);
00863
00864 return new cs_modification(0,0,b);
00865 }
00866
00867 REGISTER_MOD_PARSER(NOP, )
00868 {
00869 return NULL;
00870 }
00871
00872
00873
00874 REGISTER_MOD_PARSER(RIGHT, )
00875 {
00876 return NULL;
00877 }
00878
00879
00880 REGISTER_MOD_PARSER(BRIGHTEN, )
00881 {
00882 return new brighten_modification;
00883 }
00884
00885
00886 REGISTER_MOD_PARSER(DARKEN, )
00887 {
00888 return new darken_modification;
00889 }
00890
00891
00892 REGISTER_MOD_PARSER(BG, args)
00893 {
00894 int c[4] = { 0, 0, 0, 255 };
00895 std::vector<std::string> factors = utils::split(args, ',');
00896
00897 for (int i = 0; i < std::min<int>(factors.size(), 4); ++i) {
00898 c[i] = lexical_cast_default<int>(factors[i]);
00899 }
00900
00901 return new background_modification(create_color(c[0], c[1], c[2], c[3]));
00902 }
00903
00904 }
00905
00906 }