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, {}}})); as<ImageData>(loadAsset(AssetId{AssetType::Image, {path.basePath, path.subPath, {}}}));
if (!source) if (!source)
return {}; return {};
List<ImageOperation> operations = path.directives.transformed(imageOperationFromString);
StringMap<ImageConstPtr> references; 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); auto components = AssetPath::split(ref);
validatePath(components, true, false); validatePath(components, true, false);
auto refImage = as<ImageData>(loadAsset(AssetId{AssetType::Image, move(components)})); 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([&]() { return unlockDuring([&]() {
auto newData = make_shared<ImageData>(); auto newData = make_shared<ImageData>();
newData->image = make_shared<Image>(processImageOperations( Image newImage = *source->image;
operations, *source->image, [&](String const& ref) { return references.get(ref).get(); })); 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; 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 *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) { std::ostream& operator<<(std::ostream& os, AssetPath const& rhs) {
os << rhs.basePath; os << rhs.basePath;
if (rhs.subPath) { if (rhs.subPath) {
@ -158,9 +170,9 @@ std::ostream& operator<<(std::ostream& os, AssetPath const& rhs) {
os << *rhs.subPath; os << *rhs.subPath;
} }
rhs.directives.forEach([&](ImageOperation const& operation, String const& string) { rhs.directives.forEach([&](auto const& entry) {
os << "?"; os << "?";
os << string; os << entry.string;
}); });
return os; return os;

View File

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

View File

@ -2,207 +2,133 @@
#include "StarImageProcessing.hpp" #include "StarImageProcessing.hpp"
#include "StarDirectives.hpp" #include "StarDirectives.hpp"
#include "StarXXHash.hpp" #include "StarXXHash.hpp"
#include "StarHash.hpp"
namespace Star { namespace Star {
NestedDirectives::NestedDirectives() : m_root(nullptr) {} Directives::Directives() {}
NestedDirectives::NestedDirectives(String const& directives) { Directives::Directives(String const& directives) {
parseDirectivesIntoLeaf(directives); parse(directives);
}
NestedDirectives::NestedDirectives(String&& directives) {
String mine = move(directives); // most useless move constructor in the world
parseDirectivesIntoLeaf(mine);
} }
void NestedDirectives::parseDirectivesIntoLeaf(String const& directives) { Directives::Directives(String&& directives) {
Leaf leaf; String mine = move(directives);
for (String& op : directives.split('?')) { parse(mine);
if (!op.empty()) }
leaf.entries.emplace_back(imageOperationFromString(op), op);
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 { void Directives::buildString(String& out) const {
return (bool)m_root; for (auto& entry : *entries) {
out += "?";
out += entry.string;
}
} }
inline bool NestedDirectives::compare(NestedDirectives const& other) const { DirectivesGroup::DirectivesGroup() : m_count(0) {}
if (m_root == other.m_root) 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 true;
return false; return hash() == other.hash();
} }
void NestedDirectives::append(NestedDirectives const& other) { inline bool DirectivesGroup::operator==(DirectivesGroup const& other) const {
convertToBranches().emplace_back(other.branch()); 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); append(other);
return *this; return *this;
} }
String NestedDirectives::toString() const { inline String DirectivesGroup::toString() const {
String string; String string;
addToString(string); addToString(string);
return string; return string;
} }
void NestedDirectives::addToString(String& string) const { void DirectivesGroup::addToString(String& string) const {
if (m_root) for (auto& directives : m_directives)
m_root->buildString(string); directives.buildString(string);
} }
void NestedDirectives::forEach(LeafCallback callback) const { void DirectivesGroup::forEach(DirectivesCallback callback) const {
if (m_root) for (auto& directives : m_directives) {
m_root->forEach(callback); for (auto& entry : *directives.entries)
} callback(entry);
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);
} }
} }
bool NestedDirectives::forEachAbortable(AbortableLeafCallback callback) const { bool DirectivesGroup::forEachAbortable(AbortableDirectivesCallback callback) const {
if (!m_root) for (auto& directives : m_directives) {
return false; for (auto& entry : *directives.entries) {
else if (!callback(entry))
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))
return false; return false;
}
} }
return true; 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 #define STAR_DIRECTIVES_HPP
#include "StarImageProcessing.hpp" #include "StarImageProcessing.hpp"
#include "StarHash.hpp"
namespace Star { namespace Star {
STAR_CLASS(NestedDirectives); STAR_CLASS(DirectivesGroup);
STAR_EXCEPTION(DirectivesException, StarException); STAR_EXCEPTION(DirectivesException, StarException);
// Kae: My attempt at reducing memory allocation and per-frame string parsing for extremely long directives // 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: public:
struct Leaf { DirectivesGroup();
struct Entry { DirectivesGroup(String const& directives);
ImageOperation operation; DirectivesGroup(String&& directives);
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);
void parseDirectivesIntoLeaf(String const& directives); void parseDirectivesIntoLeaf(String const& directives);
bool empty() const; inline bool empty() const;
bool compare(NestedDirectives const& other) const; bool compare(DirectivesGroup const& other) const;
void append(NestedDirectives const& other); inline bool operator==(DirectivesGroup const& other) const;
NestedDirectives& operator+=(NestedDirectives const& other); inline bool operator!=(DirectivesGroup const& other) const;
bool operator==(NestedDirectives const& other) const; void append(Directives const& other);
bool operator!=(NestedDirectives const& other) const; DirectivesGroup& operator+=(Directives const& other);
const ConstBranch& branch() const; inline String toString() const;
String toString() const;
void addToString(String& string) const; void addToString(String& string) const;
void forEach(LeafCallback callback) const; typedef function<void(Directives::Entry const&)> DirectivesCallback;
void forEachPair(LeafPairCallback callback) const; typedef function<bool(Directives::Entry const&)> AbortableDirectivesCallback;
bool forEachAbortable(AbortableLeafCallback callback) const;
bool forEachPairAbortable(AbortableLeafPairCallback callback) const;
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: private:
void buildString(String& string, const Cell& cell) const; void buildString(String& string, const DirectivesGroup& directives) const;
Branches& convertToBranches();
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("?"); 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 imageOperationReferences(List<ImageOperation> const& operations) {
StringList references; StringList references;
for (auto const& operation : operations) { for (auto const& operation : operations)
if (auto op = operation.ptr<AlphaMaskImageOperation>()) addImageOperationReferences(operation, references);
references.appendAll(op->maskImages);
else if (auto op = operation.ptr<BlendImageOperation>())
references.appendAll(op->blendImages);
}
return references; return references;
} }
@ -417,7 +420,7 @@ void processImageOperation(ImageOperation const& operation, Image& image, ImageR
} else if (auto op = operation.ptr<AlphaMaskImageOperation>()) { } else if (auto op = operation.ptr<AlphaMaskImageOperation>()) {
if (op->maskImages.empty()) if (op->maskImages.empty())
continue; return;
if (!refCallback) if (!refCallback)
throw StarException("Missing image ref callback during AlphaMaskImageOperation in ImageProcessor::process"); 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>()) { } else if (auto op = operation.ptr<BlendImageOperation>()) {
if (op->blendImages.empty()) if (op->blendImages.empty())
continue; return;
if (!refCallback) if (!refCallback)
throw StarException("Missing image ref callback during BlendImageOperation in ImageProcessor::process"); 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 '?' // Each operation separated by '?', returns string with leading '?'
String printImageOperations(List<ImageOperation> const& operations); String printImageOperations(List<ImageOperation> const& operations);
void addImageOperationReferences(ImageOperation const& operation, StringList& out);
StringList imageOperationReferences(List<ImageOperation> const& operations); StringList imageOperationReferences(List<ImageOperation> const& operations);
typedef function<Image const*(String const& refName)> ImageReferenceCallback; 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); auto directives = move(components.directives);
String joined = AssetPath::join(components); 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>() || if (!(operation.is<HueShiftImageOperation>() ||
operation.is<SaturationShiftImageOperation>() || operation.is<SaturationShiftImageOperation>() ||
operation.is<BrightnessMultiplyImageOperation>() || operation.is<BrightnessMultiplyImageOperation>() ||
@ -130,7 +131,7 @@ String ImageMetadataDatabase::filterProcessing(String const& path) {
operation.is<ScanLinesImageOperation>() || operation.is<ScanLinesImageOperation>() ||
operation.is<SetColorImageOperation>())) { operation.is<SetColorImageOperation>())) {
joined += "?"; joined += "?";
joined += string; joined += entry.string;
} }
}); });
@ -229,14 +230,9 @@ Vec2U ImageMetadataDatabase::calculateImageSize(String const& path) const {
OperationSizeAdjust osa(imageSize); OperationSizeAdjust osa(imageSize);
bool complete = components.directives.forEachAbortable([&](auto const& leaf) -> bool { bool complete = components.directives.forEachAbortable([&](auto const& entry) -> bool {
for (const ImageOperation& operation : leaf.operations) { entry.operation.call(osa);
operation.call(osa); return !osa.hasError;
if (osa.hasError())
return false;
else
return true;
}
}); });
if (!complete) if (!complete)