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:
Charles Dang 2024-02-13 00:47:13 -05:00
parent 8a6de95d48
commit d74ff6f31b

View file

@ -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) {