DirectivesGroup prototype
This commit is contained in:
parent
51a9de3af3
commit
7bde128a87
@ -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;
|
||||
});
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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))
|
||||
bool DirectivesGroup::forEachAbortable(AbortableDirectivesCallback callback) const {
|
||||
for (auto& directives : m_directives) {
|
||||
for (auto& entry : *directives.entries) {
|
||||
if (!callback(entry))
|
||||
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);
|
||||
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);
|
||||
});
|
||||
return current;
|
||||
}
|
||||
|
||||
NestedDirectives::Branches& NestedDirectives::convertToBranches() {
|
||||
if (!m_root) {
|
||||
m_root = std::make_shared<Cell>(Branches());
|
||||
}
|
||||
else if (m_root->value.is<Branches>())
|
||||
return;
|
||||
inline size_t DirectivesGroup::hash() const {
|
||||
XXHash3 hasher;
|
||||
for (auto& directives : m_directives)
|
||||
hasher.push((const char*)&directives.hash, sizeof(directives.hash));
|
||||
|
||||
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>();
|
||||
return hasher.digest();
|
||||
}
|
||||
|
||||
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 true;
|
||||
inline size_t hash<DirectivesGroup>::operator()(DirectivesGroup const& s) const {
|
||||
return s.hash();
|
||||
}
|
||||
|
||||
}
|
@ -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 {
|
||||
public:
|
||||
struct Leaf {
|
||||
struct Directives {
|
||||
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;
|
||||
};
|
||||
Directives();
|
||||
Directives(String const& directives);
|
||||
Directives(String&& directives);
|
||||
|
||||
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;
|
||||
void parse(String const& directives);
|
||||
|
||||
struct Cell;
|
||||
typedef std::shared_ptr<Cell> Branch;
|
||||
typedef std::shared_ptr<Cell const> ConstBranch;
|
||||
typedef List<ConstBranch> Branches;
|
||||
void buildString(String& out) const;
|
||||
|
||||
std::shared_ptr<List<Entry> const> entries;
|
||||
size_t hash = 0;
|
||||
};
|
||||
|
||||
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);
|
||||
class DirectivesGroup {
|
||||
public:
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user