DirectivesGroup prototype

This commit is contained in:
Kae 2023-06-24 19:41:52 +10:00
parent 51a9de3af3
commit 7bde128a87
8 changed files with 193 additions and 259 deletions

View File

@ -844,9 +844,14 @@ shared_ptr<Assets::AssetData> Assets::loadImage(AssetPath const& path) const {
as<ImageData>(loadAsset(AssetId{AssetType::Image, {path.basePath, path.subPath, {}}}));
if (!source)
return {};
List<ImageOperation> operations = path.directives.transformed(imageOperationFromString);
StringMap<ImageConstPtr> references;
for (auto const& ref : imageOperationReferences(operations)) {
StringList referencePaths;
path.directives.forEach([&](auto const& entry) {
addImageOperationReferences(entry.operation, referencePaths);
}); // TODO: This can definitely be better, was changed quickly to support the new Directives.
for (auto const& ref : referencePaths) {
auto components = AssetPath::split(ref);
validatePath(components, true, false);
auto refImage = as<ImageData>(loadAsset(AssetId{AssetType::Image, move(components)}));
@ -857,8 +862,11 @@ shared_ptr<Assets::AssetData> Assets::loadImage(AssetPath const& path) const {
return unlockDuring([&]() {
auto newData = make_shared<ImageData>();
newData->image = make_shared<Image>(processImageOperations(
operations, *source->image, [&](String const& ref) { return references.get(ref).get(); }));
Image newImage = *source->image;
path.directives.forEach([&](auto const& entry) {
processImageOperation(entry.operation, newImage, [&](String const& ref) { return references.get(ref).get(); });
});
newData->image = make_shared<Image>(move(newImage));
return newData;
});

View File

@ -151,6 +151,18 @@ AssetPath::AssetPath(String const& path) {
*this = move(AssetPath::split(path)); // split code should probably be in here, but whatever
}
AssetPath::AssetPath(String&& basePath, Maybe<String>&& subPath, DirectivesGroup&& directives) {
this->basePath = move(basePath);
this->subPath = move(subPath);
this->directives = move(directives);
}
AssetPath::AssetPath(String const& basePath, Maybe<String> const& subPath, DirectivesGroup const& directives) {
this->basePath = basePath;
this->subPath = subPath;
this->directives = directives;
}
std::ostream& operator<<(std::ostream& os, AssetPath const& rhs) {
os << rhs.basePath;
if (rhs.subPath) {
@ -158,9 +170,9 @@ std::ostream& operator<<(std::ostream& os, AssetPath const& rhs) {
os << *rhs.subPath;
}
rhs.directives.forEach([&](ImageOperation const& operation, String const& string) {
rhs.directives.forEach([&](auto const& entry) {
os << "?";
os << string;
os << entry.string;
});
return os;

View File

@ -53,10 +53,11 @@ struct AssetPath {
AssetPath() = default;
AssetPath(String const& path);
AssetPath(String&& basePath, Maybe<String>&& subPath, DirectivesGroup&& directives);
AssetPath(const String& basePath, const Maybe<String>& subPath, const DirectivesGroup& directives);
String basePath;
Maybe<String> subPath;
NestedDirectives directives;
DirectivesGroup directives;
bool operator==(AssetPath const& rhs) const;
};

View File

@ -2,207 +2,133 @@
#include "StarImageProcessing.hpp"
#include "StarDirectives.hpp"
#include "StarXXHash.hpp"
#include "StarHash.hpp"
namespace Star {
NestedDirectives::NestedDirectives() : m_root(nullptr) {}
NestedDirectives::NestedDirectives(String const& directives) {
parseDirectivesIntoLeaf(directives);
}
NestedDirectives::NestedDirectives(String&& directives) {
String mine = move(directives); // most useless move constructor in the world
parseDirectivesIntoLeaf(mine);
Directives::Directives() {}
Directives::Directives(String const& directives) {
parse(directives);
}
void NestedDirectives::parseDirectivesIntoLeaf(String const& directives) {
Leaf leaf;
for (String& op : directives.split('?')) {
if (!op.empty())
leaf.entries.emplace_back(imageOperationFromString(op), op);
Directives::Directives(String&& directives) {
String mine = move(directives);
parse(mine);
}
void Directives::parse(String const& directives) {
List<Entry> newList;
StringList split = directives.split('?');
newList.reserve(split.size());
for (String& str : split) {
if (!str.empty()) {
ImageOperation operation = imageOperationFromString(str);
newList.emplace_back(move(operation), move(str));
}
}
m_root = std::make_shared<Cell>(move(leaf));
entries = std::make_shared<List<Entry> const>(move(newList));
hash = XXH3_64bits(directives.utf8Ptr(), directives.utf8Size());
}
inline bool NestedDirectives::empty() const {
return (bool)m_root;
void Directives::buildString(String& out) const {
for (auto& entry : *entries) {
out += "?";
out += entry.string;
}
}
inline bool NestedDirectives::compare(NestedDirectives const& other) const {
if (m_root == other.m_root)
DirectivesGroup::DirectivesGroup() : m_count(0) {}
DirectivesGroup::DirectivesGroup(String const& directives) {
m_directives.emplace_back(directives);
m_count = m_directives.back().entries->size();
}
DirectivesGroup::DirectivesGroup(String&& directives) {
m_directives.emplace_back(move(directives));
m_count = m_directives.back().entries->size();
}
inline bool DirectivesGroup::empty() const {
return m_count == 0;
}
inline bool DirectivesGroup::compare(DirectivesGroup const& other) const {
if (m_count != other.m_count)
return false;
if (empty())
return true;
return false;
return hash() == other.hash();
}
void NestedDirectives::append(NestedDirectives const& other) {
convertToBranches().emplace_back(other.branch());
inline bool DirectivesGroup::operator==(DirectivesGroup const& other) const {
return compare(other);
}
NestedDirectives& NestedDirectives::operator+=(NestedDirectives const& other) {
inline bool DirectivesGroup::operator!=(DirectivesGroup const& other) const {
return !compare(other);
}
void DirectivesGroup::append(Directives const& directives) {
m_directives.push_back(directives);
m_count += m_directives.back().entries->size();
}
DirectivesGroup& DirectivesGroup::operator+=(Directives const& other) {
append(other);
return *this;
}
String NestedDirectives::toString() const {
inline String DirectivesGroup::toString() const {
String string;
addToString(string);
return string;
}
void NestedDirectives::addToString(String& string) const {
if (m_root)
m_root->buildString(string);
void DirectivesGroup::addToString(String& string) const {
for (auto& directives : m_directives)
directives.buildString(string);
}
void NestedDirectives::forEach(LeafCallback callback) const {
if (m_root)
m_root->forEach(callback);
}
void NestedDirectives::forEachPair(LeafPairCallback callback) const {
if (m_root) {
LeafCallback pairCallback = [&](Leaf const& leaf) {
for (auto& entry : leaf.entries)
callback(entry.operation, entry.string);
};
m_root->forEach(pairCallback);
void DirectivesGroup::forEach(DirectivesCallback callback) const {
for (auto& directives : m_directives) {
for (auto& entry : *directives.entries)
callback(entry);
}
}
bool NestedDirectives::forEachAbortable(AbortableLeafCallback callback) const {
if (!m_root)
return false;
else
return m_root->forEachAbortable(callback);
}
bool NestedDirectives::forEachPairAbortable(AbortableLeafPairCallback callback) const {
if (!m_root)
return false;
else {
AbortableLeafCallback pairCallback = [&](Leaf const& leaf) -> bool {
for (auto& entry : leaf.entries) {
if (!callback(entry.operation, entry.string))
return false;
}
return true;
};
return m_root->forEachAbortable(pairCallback);
}
}
Image NestedDirectives::apply(Image& image) const {
Image current = image;
forEachPair([&](ImageOperation const& operation, String const& string) {
processImageOperation(operation, current);
});
return current;
}
NestedDirectives::Branches& NestedDirectives::convertToBranches() {
if (!m_root) {
m_root = std::make_shared<Cell>(Branches());
}
else if (m_root->value.is<Branches>())
return;
Leaf& leaf = m_root->value.get<Leaf>();
Branches newBranches;
newBranches.emplace_back(std::make_shared<Cell const>(move(leaf)));
m_root->value = move(newBranches);
return m_root->value.get<Branches>();
}
bool NestedDirectives::Leaf::Entry::operator==(NestedDirectives::Leaf::Entry const& other) const {
return string == other.string;
}
bool NestedDirectives::Leaf::Entry::operator!=(NestedDirectives::Leaf::Entry const& other) const {
return string != other.string;
}
size_t NestedDirectives::Leaf::length() const {
return entries.size();
}
bool NestedDirectives::Leaf::operator==(NestedDirectives::Leaf const& other) const {
size_t len = length();
if (len != other.length())
return false;
for (size_t i = 0; i != len; ++i) {
if (entries[i] != other.entries[i])
return false;
}
return true;
}
bool NestedDirectives::Leaf::operator!=(NestedDirectives::Leaf const& other) const {
return !(*this == other);
}
NestedDirectives::Cell::Cell() : value(Leaf()) {};
NestedDirectives::Cell::Cell(Leaf&& leaf) : value(move(leaf)) {};
NestedDirectives::Cell::Cell(Branches&& branches) : value(move(branches)) {};
NestedDirectives::Cell::Cell(const Leaf& leaf) : value(leaf) {};
NestedDirectives::Cell::Cell(const Branches& branches) : value(branches) {};
/*
bool NestedDirectives::Cell::operator==(NestedDirectives::Cell const& other) const {
if (auto leaf = value.ptr<Leaf>()) {
if (auto otherLeaf = other.value.ptr<Leaf>())
return *leaf == *otherLeaf;
else {
}
}
else {
for (auto& branch : value.get<Branches>()) {
}
}
}
bool NestedDirectives::Cell::operator!=(NestedDirectives::Cell const& other) const {
return !(*this == other);
}
//*/
void NestedDirectives::Cell::buildString(String& string) const {
if (auto leaf = value.ptr<Leaf>())
for (auto& entry : leaf->entries) {
string += "?";
string += entry.string;
}
else {
for (auto& branch : value.get<Branches>())
branch->buildString(string);
}
}
void NestedDirectives::Cell::forEach(LeafCallback& callback) const {
if (auto leaf = value.ptr<Leaf>())
callback(*leaf);
else {
for (auto& branch : value.get<Branches>())
branch->forEach(callback);
}
}
bool NestedDirectives::Cell::forEachAbortable(AbortableLeafCallback& callback) const {
if (auto leaf = value.ptr<Leaf>()) {
if (!callback(*leaf))
return false;
} else {
for (auto& branch : value.get<Branches>())
if (!branch->forEachAbortable(callback))
bool DirectivesGroup::forEachAbortable(AbortableDirectivesCallback callback) const {
for (auto& directives : m_directives) {
for (auto& entry : *directives.entries) {
if (!callback(entry))
return false;
}
}
return true;
}
inline Image DirectivesGroup::applyNewImage(Image const& image) const {
Image result = image;
applyExistingImage(result);
return result;
}
void DirectivesGroup::applyExistingImage(Image& image) const {
forEach([&](auto const& entry) {
processImageOperation(entry.operation, image);
});
}
inline size_t DirectivesGroup::hash() const {
XXHash3 hasher;
for (auto& directives : m_directives)
hasher.push((const char*)&directives.hash, sizeof(directives.hash));
return hasher.digest();
}
inline size_t hash<DirectivesGroup>::operator()(DirectivesGroup const& s) const {
return s.hash();
}
}

View File

@ -2,89 +2,75 @@
#define STAR_DIRECTIVES_HPP
#include "StarImageProcessing.hpp"
#include "StarHash.hpp"
namespace Star {
STAR_CLASS(NestedDirectives);
STAR_CLASS(DirectivesGroup);
STAR_EXCEPTION(DirectivesException, StarException);
// Kae: My attempt at reducing memory allocation and per-frame string parsing for extremely long directives
class NestedDirectives {
struct Directives {
struct Entry {
ImageOperation operation;
String string;
Entry(ImageOperation&& operation, String&& string);
};
Directives();
Directives(String const& directives);
Directives(String&& directives);
void parse(String const& directives);
void buildString(String& out) const;
std::shared_ptr<List<Entry> const> entries;
size_t hash = 0;
};
class DirectivesGroup {
public:
struct Leaf {
struct Entry {
ImageOperation operation;
String string;
bool operator==(Entry const& other) const;
bool operator!=(Entry const& other) const;
Entry(ImageOperation&& operation, String&& string);
};
List<Entry> entries;
size_t length() const;
bool operator==(NestedDirectives::Leaf const& other) const;
bool operator!=(NestedDirectives::Leaf const& other) const;
};
typedef function<void(Leaf const&)> LeafCallback;
typedef function<void(ImageOperation const&, String const&)> LeafPairCallback;
typedef function<bool(Leaf const&)> AbortableLeafCallback;
typedef function<bool(ImageOperation const&, String const&)> AbortableLeafPairCallback;
struct Cell;
typedef std::shared_ptr<Cell> Branch;
typedef std::shared_ptr<Cell const> ConstBranch;
typedef List<ConstBranch> Branches;
struct Cell {
Variant<Leaf, Branches> value;
Cell();
Cell(Leaf&& leaf);
Cell(Branches&& branches);
Cell(const Leaf& leaf);
Cell(const Branches& branches);
void buildString(String& string) const;
void forEach(LeafCallback& callback) const;
bool forEachAbortable(AbortableLeafCallback& callback) const;
};
NestedDirectives();
NestedDirectives(String const& directives);
NestedDirectives(String&& directives);
DirectivesGroup();
DirectivesGroup(String const& directives);
DirectivesGroup(String&& directives);
void parseDirectivesIntoLeaf(String const& directives);
bool empty() const;
bool compare(NestedDirectives const& other) const;
void append(NestedDirectives const& other);
NestedDirectives& operator+=(NestedDirectives const& other);
bool operator==(NestedDirectives const& other) const;
bool operator!=(NestedDirectives const& other) const;
inline bool empty() const;
bool compare(DirectivesGroup const& other) const;
inline bool operator==(DirectivesGroup const& other) const;
inline bool operator!=(DirectivesGroup const& other) const;
void append(Directives const& other);
DirectivesGroup& operator+=(Directives const& other);
const ConstBranch& branch() const;
String toString() const;
inline String toString() const;
void addToString(String& string) const;
void forEach(LeafCallback callback) const;
void forEachPair(LeafPairCallback callback) const;
bool forEachAbortable(AbortableLeafCallback callback) const;
bool forEachPairAbortable(AbortableLeafPairCallback callback) const;
typedef function<void(Directives::Entry const&)> DirectivesCallback;
typedef function<bool(Directives::Entry const&)> AbortableDirectivesCallback;
Image apply(Image& image) const;
void forEach(DirectivesCallback callback) const;
bool forEachAbortable(AbortableDirectivesCallback callback) const;
inline Image applyNewImage(const Image& image) const;
void applyExistingImage(Image& image) const;
inline size_t hash() const;
private:
void buildString(String& string, const Cell& cell) const;
Branches& convertToBranches();
void buildString(String& string, const DirectivesGroup& directives) const;
Branch m_root;
List<Directives> m_directives;
size_t m_count;
};
typedef NestedDirectives ImageDirectives;
template <>
struct hash<DirectivesGroup> {
size_t operator()(DirectivesGroup const& s) const;
};
typedef DirectivesGroup ImageDirectives;
}

View File

@ -352,14 +352,17 @@ String printImageOperations(List<ImageOperation> const& list) {
return StringList(list.transformed(imageOperationToString)).join("?");
}
void addImageOperationReferences(ImageOperation const& operation, StringList& out) {
if (auto op = operation.ptr<AlphaMaskImageOperation>())
out.appendAll(op->maskImages);
else if (auto op = operation.ptr<BlendImageOperation>())
out.appendAll(op->blendImages);
}
StringList imageOperationReferences(List<ImageOperation> const& operations) {
StringList references;
for (auto const& operation : operations) {
if (auto op = operation.ptr<AlphaMaskImageOperation>())
references.appendAll(op->maskImages);
else if (auto op = operation.ptr<BlendImageOperation>())
references.appendAll(op->blendImages);
}
for (auto const& operation : operations)
addImageOperationReferences(operation, references);
return references;
}
@ -417,7 +420,7 @@ void processImageOperation(ImageOperation const& operation, Image& image, ImageR
} else if (auto op = operation.ptr<AlphaMaskImageOperation>()) {
if (op->maskImages.empty())
continue;
return;
if (!refCallback)
throw StarException("Missing image ref callback during AlphaMaskImageOperation in ImageProcessor::process");
@ -449,7 +452,7 @@ void processImageOperation(ImageOperation const& operation, Image& image, ImageR
} else if (auto op = operation.ptr<BlendImageOperation>()) {
if (op->blendImages.empty())
continue;
return;
if (!refCallback)
throw StarException("Missing image ref callback during BlendImageOperation in ImageProcessor::process");

View File

@ -145,6 +145,8 @@ List<ImageOperation> parseImageOperations(String const& params);
// Each operation separated by '?', returns string with leading '?'
String printImageOperations(List<ImageOperation> const& operations);
void addImageOperationReferences(ImageOperation const& operation, StringList& out);
StringList imageOperationReferences(List<ImageOperation> const& operations);
typedef function<Image const*(String const& refName)> ImageReferenceCallback;

View File

@ -122,7 +122,8 @@ String ImageMetadataDatabase::filterProcessing(String const& path) {
auto directives = move(components.directives);
String joined = AssetPath::join(components);
directives.forEachPair([&](ImageOperation const& operation, String const& string) {
directives.forEach([&](auto const& entry) {
ImageOperation const& operation = entry.operation;
if (!(operation.is<HueShiftImageOperation>() ||
operation.is<SaturationShiftImageOperation>() ||
operation.is<BrightnessMultiplyImageOperation>() ||
@ -130,7 +131,7 @@ String ImageMetadataDatabase::filterProcessing(String const& path) {
operation.is<ScanLinesImageOperation>() ||
operation.is<SetColorImageOperation>())) {
joined += "?";
joined += string;
joined += entry.string;
}
});
@ -229,14 +230,9 @@ Vec2U ImageMetadataDatabase::calculateImageSize(String const& path) const {
OperationSizeAdjust osa(imageSize);
bool complete = components.directives.forEachAbortable([&](auto const& leaf) -> bool {
for (const ImageOperation& operation : leaf.operations) {
operation.call(osa);
if (osa.hasError())
return false;
else
return true;
}
bool complete = components.directives.forEachAbortable([&](auto const& entry) -> bool {
entry.operation.call(osa);
return !osa.hasError;
});
if (!complete)