IPF: refactor to stop copying strings so much
We know the lifetime of the original strings for each of the ops. We don't need to keep copying strings left right front and center.
This commit is contained in:
parent
8a6de95d48
commit
d74ff6f31b
1 changed files with 86 additions and 91 deletions
|
@ -23,6 +23,7 @@
|
|||
#include "log.hpp"
|
||||
#include "serialization/string_utils.hpp"
|
||||
#include "team.hpp"
|
||||
#include "utils/from_chars.hpp"
|
||||
|
||||
#include "formula/formula.hpp"
|
||||
#include "formula/callable.hpp"
|
||||
|
@ -37,10 +38,7 @@ namespace image {
|
|||
/** Adds @a mod to the queue (unless mod is nullptr). */
|
||||
void modification_queue::push(std::unique_ptr<modification> mod)
|
||||
{
|
||||
// Null pointers do not get stored. (Shouldn't happen, but just in case.)
|
||||
if(mod != nullptr) {
|
||||
priorities_[mod->priority()].push_back(std::move(mod));
|
||||
}
|
||||
priorities_[mod->priority()].push_back(std::move(mod));
|
||||
}
|
||||
|
||||
/** Removes the top element from the queue */
|
||||
|
@ -78,14 +76,14 @@ modification * modification_queue::top() const
|
|||
namespace {
|
||||
|
||||
/** A function used to parse modification arguments */
|
||||
using mod_parser = std::function<std::unique_ptr<modification>(const std::string&)>;
|
||||
using mod_parser = std::function<std::unique_ptr<modification>(std::string_view)>;
|
||||
|
||||
/** A map of all registered mod parsers
|
||||
*
|
||||
* The mapping is between the modification name and the parser function pointer
|
||||
* An example of an entry would be "TC" -> &parse_TC_mod
|
||||
*/
|
||||
std::map<std::string, mod_parser> mod_parsers;
|
||||
std::map<std::string, mod_parser, std::less<>> mod_parsers;
|
||||
|
||||
/** Decodes a single modification using an appropriate mod_parser
|
||||
*
|
||||
|
@ -96,22 +94,22 @@ std::map<std::string, mod_parser> mod_parsers;
|
|||
*/
|
||||
std::unique_ptr<modification> decode_modification(const std::string& encoded_mod)
|
||||
{
|
||||
std::vector<std::string> split = utils::parenthetical_split(encoded_mod);
|
||||
const std::vector<std::string> split = utils::parenthetical_split(encoded_mod);
|
||||
|
||||
if(split.size() != 2) {
|
||||
ERR_DP << "error parsing image modifications: " << encoded_mod;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string mod_type = split[0];
|
||||
std::string args = split[1];
|
||||
const std::string& mod_type = split[0];
|
||||
const std::string& args = split[1];
|
||||
|
||||
if(mod_parsers.find(mod_type) == mod_parsers.end()) {
|
||||
if(const auto parser = mod_parsers.find(mod_type); parser != mod_parsers.end()) {
|
||||
return std::invoke(parser->second, args);
|
||||
} else {
|
||||
ERR_DP << "unknown image function in path: " << mod_type;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return mod_parsers[mod_type](args);
|
||||
}
|
||||
|
||||
} // end anon namespace
|
||||
|
@ -605,14 +603,14 @@ struct parse_mod_registration
|
|||
* @param args_var The name for the string argument provided
|
||||
*/
|
||||
#define REGISTER_MOD_PARSER(type, args_var) \
|
||||
static std::unique_ptr<modification> parse_##type##_mod(const std::string&); \
|
||||
static std::unique_ptr<modification> parse_##type##_mod(std::string_view); \
|
||||
static parse_mod_registration parse_##type##_mod_registration_aux(#type, &parse_##type##_mod); \
|
||||
static std::unique_ptr<modification> parse_##type##_mod(const std::string& args_var) \
|
||||
static std::unique_ptr<modification> parse_##type##_mod(std::string_view args_var) \
|
||||
|
||||
// Color-range-based recoloring
|
||||
REGISTER_MOD_PARSER(TC, args)
|
||||
{
|
||||
std::vector<std::string> params = utils::split(args,',');
|
||||
const auto params = utils::split_view(args,',');
|
||||
|
||||
if(params.size() < 2) {
|
||||
ERR_DP << "too few arguments passed to the ~TC() function";
|
||||
|
@ -655,7 +653,7 @@ REGISTER_MOD_PARSER(TC, args)
|
|||
// Team-color-based color range selection and recoloring
|
||||
REGISTER_MOD_PARSER(RC, args)
|
||||
{
|
||||
const std::vector<std::string> recolor_params = utils::split(args,'>');
|
||||
const auto recolor_params = utils::split_view(args,'>');
|
||||
|
||||
if(recolor_params.size() <= 1) {
|
||||
return nullptr;
|
||||
|
@ -684,7 +682,7 @@ REGISTER_MOD_PARSER(RC, args)
|
|||
// Palette switch
|
||||
REGISTER_MOD_PARSER(PAL, args)
|
||||
{
|
||||
const std::vector<std::string> remap_params = utils::split(args,'>');
|
||||
const auto remap_params = utils::split_view(args,'>');
|
||||
|
||||
if(remap_params.size() < 2) {
|
||||
ERR_DP << "not enough arguments passed to the ~PAL() function: " << args;
|
||||
|
@ -725,7 +723,7 @@ REGISTER_MOD_PARSER(FL, args)
|
|||
// Rotations
|
||||
REGISTER_MOD_PARSER(ROTATE, args)
|
||||
{
|
||||
const std::vector<std::string>& slice_params = utils::split(args, ',', utils::STRIP_SPACES);
|
||||
const auto slice_params = utils::split_view(args, ',', utils::STRIP_SPACES);
|
||||
const std::size_t s = slice_params.size();
|
||||
|
||||
switch(s) {
|
||||
|
@ -763,28 +761,35 @@ REGISTER_MOD_PARSER(CROP_TRANSPARENCY, )
|
|||
return std::make_unique<crop_transparency_modification>();
|
||||
}
|
||||
|
||||
// TODO: should this be made a more general util function?
|
||||
bool in_range(int val, int min, int max)
|
||||
{
|
||||
return min <= val && val <= max;
|
||||
}
|
||||
|
||||
// Black and white
|
||||
REGISTER_MOD_PARSER(BW, args)
|
||||
{
|
||||
const std::vector<std::string>& params = utils::split(args, ',');
|
||||
const auto params = utils::split_view(args, ',');
|
||||
|
||||
if(params.size() != 1) {
|
||||
ERR_DP << "~BW() requires exactly one argument";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
try {
|
||||
int threshold = std::stoi(params[0]);
|
||||
if(threshold < 0 || threshold > 255) {
|
||||
ERR_DP << "~BW() argument out of range 0 - 255";
|
||||
return nullptr;
|
||||
} else {
|
||||
return std::make_unique<bw_modification>(threshold);
|
||||
}
|
||||
} catch (const std::invalid_argument&) {
|
||||
// TODO: maybe get this directly as uint8_t?
|
||||
const auto threshold = utils::from_chars<int>(params[0]);
|
||||
if(!threshold) {
|
||||
ERR_DP << "unsupported argument in ~BW() function";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(!in_range(*threshold, 0, 255)) {
|
||||
ERR_DP << "~BW() argument out of range 0 - 255";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_unique<bw_modification>(*threshold);
|
||||
}
|
||||
|
||||
// Sepia
|
||||
|
@ -796,51 +801,42 @@ REGISTER_MOD_PARSER(SEPIA, )
|
|||
// Negative
|
||||
REGISTER_MOD_PARSER(NEG, args)
|
||||
{
|
||||
const std::vector<std::string>& params = utils::split(args, ',');
|
||||
const auto params = utils::split_view(args, ',');
|
||||
|
||||
switch (params.size()) {
|
||||
case 0:
|
||||
// apparently -1 may be a magic number
|
||||
// but this is the threshold value required
|
||||
// to fully invert a channel
|
||||
return std::make_unique<negative_modification>(-1, -1, -1);
|
||||
break;
|
||||
case 1:
|
||||
try {
|
||||
int threshold = std::stoi(params[0]);
|
||||
if(threshold < -1 || threshold > 255) {
|
||||
ERR_DP << "unsupported argument value in ~NEG() function";
|
||||
return nullptr;
|
||||
} else {
|
||||
return std::make_unique<negative_modification>(threshold, threshold, threshold);
|
||||
}
|
||||
} catch (const std::invalid_argument&) {
|
||||
ERR_DP << "unsupported argument value in ~NEG() function";
|
||||
return nullptr;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
try {
|
||||
int thresholdRed = std::stoi(params[0]);
|
||||
int thresholdGreen = std::stoi(params[1]);
|
||||
int thresholdBlue = std::stoi(params[2]);
|
||||
if(thresholdRed < -1 || thresholdRed > 255 || thresholdGreen < -1 || thresholdGreen > 255 || thresholdBlue < -1 || thresholdBlue > 255) {
|
||||
ERR_DP << "unsupported argument value in ~NEG() function";
|
||||
return nullptr;
|
||||
} else {
|
||||
return std::make_unique<negative_modification>(thresholdRed, thresholdGreen, thresholdBlue);
|
||||
}
|
||||
} catch (const std::invalid_argument&) {
|
||||
ERR_DP << "unsupported argument value in ~NEG() function";
|
||||
return nullptr;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ERR_DP << "~NEG() requires 0, 1 or 3 arguments";
|
||||
switch(params.size()) {
|
||||
case 0:
|
||||
// apparently -1 may be a magic number but this is the threshold
|
||||
// value required to fully invert a channel
|
||||
return std::make_unique<negative_modification>(-1, -1, -1);
|
||||
|
||||
case 1: {
|
||||
const auto threshold = utils::from_chars<int>(params[0]);
|
||||
|
||||
if(threshold && in_range(*threshold, -1, 255)) {
|
||||
return std::make_unique<negative_modification>(*threshold, *threshold, *threshold);
|
||||
} else {
|
||||
ERR_DP << "unsupported argument value in ~NEG() function";
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
case 3: {
|
||||
const auto thR = utils::from_chars<int>(params[0]);
|
||||
const auto thG = utils::from_chars<int>(params[1]);
|
||||
const auto thB = utils::from_chars<int>(params[2]);
|
||||
|
||||
if(thR && thG && thB && in_range(*thR, -1, 255) && in_range(*thG, -1, 255) && in_range(*thB, -1, 255)) {
|
||||
return std::make_unique<negative_modification>(*thR, *thG, *thB);
|
||||
} else {
|
||||
ERR_DP << "unsupported argument value in ~NEG() function";
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
ERR_DP << "~NEG() requires 0, 1 or 3 arguments";
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Plot Alpha
|
||||
|
@ -888,7 +884,7 @@ REGISTER_MOD_PARSER(CHAN, args)
|
|||
// Color-shift
|
||||
REGISTER_MOD_PARSER(CS, args)
|
||||
{
|
||||
std::vector<std::string> const factors = utils::split(args, ',');
|
||||
const auto factors = utils::split_view(args, ',');
|
||||
const std::size_t s = factors.size();
|
||||
|
||||
if(s == 0) {
|
||||
|
@ -913,7 +909,7 @@ REGISTER_MOD_PARSER(CS, args)
|
|||
// Color blending
|
||||
REGISTER_MOD_PARSER(BLEND, args)
|
||||
{
|
||||
const std::vector<std::string>& params = utils::split(args, ',');
|
||||
const auto params = utils::split_view(args, ',');
|
||||
|
||||
if(params.size() != 4) {
|
||||
ERR_DP << "~BLEND() requires exactly 4 arguments";
|
||||
|
@ -921,14 +917,14 @@ REGISTER_MOD_PARSER(BLEND, args)
|
|||
}
|
||||
|
||||
float opacity = 0.0f;
|
||||
const std::string& opacity_str = params[3];
|
||||
const std::string::size_type p100_pos = opacity_str.find('%');
|
||||
const std::string_view& opacity_str = params[3];
|
||||
const std::string_view::size_type p100_pos = opacity_str.find('%');
|
||||
|
||||
if(p100_pos == std::string::npos)
|
||||
opacity = lexical_cast_default<float>(opacity_str);
|
||||
else {
|
||||
// make multiplier
|
||||
const std::string& parsed_field = opacity_str.substr(0, p100_pos);
|
||||
const std::string_view parsed_field = opacity_str.substr(0, p100_pos);
|
||||
opacity = lexical_cast_default<float>(parsed_field);
|
||||
opacity /= 100.0f;
|
||||
}
|
||||
|
@ -943,7 +939,7 @@ REGISTER_MOD_PARSER(BLEND, args)
|
|||
// Crop/slice
|
||||
REGISTER_MOD_PARSER(CROP, args)
|
||||
{
|
||||
const std::vector<std::string>& slice_params = utils::split(args, ',', utils::STRIP_SPACES);
|
||||
const auto slice_params = utils::split_view(args, ',', utils::STRIP_SPACES);
|
||||
const std::size_t s = slice_params.size();
|
||||
|
||||
if(s == 0 || (s == 1 && slice_params[0].empty())) {
|
||||
|
@ -953,16 +949,16 @@ REGISTER_MOD_PARSER(CROP, args)
|
|||
|
||||
SDL_Rect slice_rect { 0, 0, 0, 0 };
|
||||
|
||||
slice_rect.x = lexical_cast_default<int16_t, const std::string&>(slice_params[0]);
|
||||
slice_rect.x = lexical_cast_default<int16_t, std::string_view>(slice_params[0]);
|
||||
|
||||
if(s > 1) {
|
||||
slice_rect.y = lexical_cast_default<int16_t, const std::string&>(slice_params[1]);
|
||||
slice_rect.y = lexical_cast_default<int16_t, std::string_view>(slice_params[1]);
|
||||
}
|
||||
if(s > 2) {
|
||||
slice_rect.w = lexical_cast_default<uint16_t, const std::string&>(slice_params[2]);
|
||||
slice_rect.w = lexical_cast_default<uint16_t, std::string_view>(slice_params[2]);
|
||||
}
|
||||
if(s > 3) {
|
||||
slice_rect.h = lexical_cast_default<uint16_t, const std::string&>(slice_params[3]);
|
||||
slice_rect.h = lexical_cast_default<uint16_t, std::string_view>(slice_params[3]);
|
||||
}
|
||||
|
||||
return std::make_unique<crop_modification>(slice_rect);
|
||||
|
@ -1050,17 +1046,16 @@ REGISTER_MOD_PARSER(L, args)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
surface surf = get_surface(args);
|
||||
|
||||
surface surf = get_surface(std::string{args}); // FIXME: string_view for image::locator::value
|
||||
return std::make_unique<light_modification>(surf);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
/** Common helper function to parse scaling IPF inputs. */
|
||||
std::optional<point> parse_scale_args(const std::string& args)
|
||||
std::optional<point> parse_scale_args(std::string_view args)
|
||||
{
|
||||
const std::vector<std::string>& scale_params = utils::split(args, ',', utils::STRIP_SPACES);
|
||||
const auto scale_params = utils::split_view(args, ',', utils::STRIP_SPACES);
|
||||
const std::size_t s = scale_params.size();
|
||||
|
||||
if(s == 0 || (s == 1 && scale_params[0].empty())) {
|
||||
|
@ -1068,10 +1063,10 @@ std::optional<point> parse_scale_args(const std::string& args)
|
|||
}
|
||||
|
||||
point size{0, 0};
|
||||
size.x = lexical_cast_default<int, const std::string&>(scale_params[0]);
|
||||
size.x = lexical_cast_default<int, std::string_view>(scale_params[0]);
|
||||
|
||||
if(s > 1) {
|
||||
size.y = lexical_cast_default<int, const std::string&>(scale_params[1]);
|
||||
size.y = lexical_cast_default<int, std::string_view>(scale_params[1]);
|
||||
}
|
||||
|
||||
return size;
|
||||
|
@ -1127,7 +1122,7 @@ REGISTER_MOD_PARSER(SCALE_INTO_SHARP, args)
|
|||
// xBRZ
|
||||
REGISTER_MOD_PARSER(XBRZ, args)
|
||||
{
|
||||
int z = lexical_cast_default<int, const std::string &>(args);
|
||||
int z = lexical_cast_default<int, std::string_view>(args);
|
||||
if(z < 1 || z > 5) {
|
||||
z = 5; //only values 2 - 5 are permitted for xbrz scaling factors.
|
||||
}
|
||||
|
@ -1150,11 +1145,11 @@ REGISTER_MOD_PARSER(O, args)
|
|||
const std::string::size_type p100_pos = args.find('%');
|
||||
float num = 0.0f;
|
||||
if(p100_pos == std::string::npos) {
|
||||
num = lexical_cast_default<float,const std::string&>(args);
|
||||
num = lexical_cast_default<float, std::string_view>(args);
|
||||
} else {
|
||||
// make multiplier
|
||||
const std::string parsed_field = args.substr(0, p100_pos);
|
||||
num = lexical_cast_default<float,const std::string&>(parsed_field);
|
||||
const std::string_view parsed_field = args.substr(0, p100_pos);
|
||||
num = lexical_cast_default<float, std::string_view>(parsed_field);
|
||||
num /= 100.0f;
|
||||
}
|
||||
|
||||
|
@ -1208,7 +1203,7 @@ REGISTER_MOD_PARSER(RIGHT, )
|
|||
REGISTER_MOD_PARSER(BG, args)
|
||||
{
|
||||
int c[4] { 0, 0, 0, SDL_ALPHA_OPAQUE };
|
||||
std::vector<std::string> factors = utils::split(args, ',');
|
||||
const auto factors = utils::split_view(args, ',');
|
||||
|
||||
for(int i = 0; i < std::min<int>(factors.size(), 4); ++i) {
|
||||
c[i] = lexical_cast_default<int>(factors[i]);
|
||||
|
@ -1220,7 +1215,7 @@ REGISTER_MOD_PARSER(BG, args)
|
|||
// Channel swap
|
||||
REGISTER_MOD_PARSER(SWAP, args)
|
||||
{
|
||||
std::vector<std::string> params = utils::split(args, ',', utils::STRIP_SPACES);
|
||||
const auto params = utils::split_view(args, ',', utils::STRIP_SPACES);
|
||||
|
||||
// accept 3 arguments (rgb) or 4 (rgba)
|
||||
if(params.size() != 3 && params.size() != 4) {
|
||||
|
|
Loading…
Add table
Reference in a new issue