ref: b4dadd35b6ea22192b3b68b042f439caa0dd0566
parent: d9b1402ef872edf36884670dbff30fdd2467efda
author: ISSOtm <[email protected]>
date: Sat May 21 06:41:56 EDT 2022
Use an iterator `zip` Simplifies iterating over tiles and attributes at the same time
--- a/include/defaultinitalloc.hpp
+++ b/include/defaultinitalloc.hpp
@@ -2,7 +2,8 @@
* Allocator adaptor that interposes construct() calls to convert value-initialization
* (which is what you get with e.g. `vector::resize`) into default-initialization (which does not
* zero out non-class types).
- * From https://stackoverflow.com/questions/21028299/is-this-behavior-of-vectorresizesize-type-n-under-c11-and-boost-container/21028912#21028912
+ * From
+ * https://stackoverflow.com/questions/21028299/is-this-behavior-of-vectorresizesize-type-n-under-c11-and-boost-container/21028912#21028912
*/
#ifndef DEFAULT_INIT_ALLOC_H
@@ -23,11 +24,11 @@
using A::A; // Inherit the allocator's constructors
template<typename U>
- void construct(U * ptr) noexcept(std::is_nothrow_default_constructible_v<U>) {
- ::new(static_cast<void *>(ptr)) U;
+ void construct(U *ptr) noexcept(std::is_nothrow_default_constructible_v<U>) {
+ ::new (static_cast<void *>(ptr)) U;
}
template<typename U, typename... Args>
- void construct(U * ptr, Args && ... args) {
+ void construct(U *ptr, Args &&...args) {
a_t::construct(static_cast<A &>(*this), ptr, std::forward<Args>(args)...);
}
};
--- /dev/null
+++ b/include/itertools.hpp
@@ -1,0 +1,90 @@
+/*
+ * This file is part of RGBDS.
+ *
+ * Copyright (c) 2022, Eldred Habert and RGBDS contributors.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef RGBDS_ITERTOOLS_HPP
+#define RGBDS_ITERTOOLS_HPP
+
+#include <tuple>
+#include <utility>
+
+template<typename... Ts>
+static inline void report() {
+ puts(__PRETTY_FUNCTION__);
+}
+
+// This is not a fully generic implementation; its current use cases only require for-loop behavior.
+// We also assume that all iterators have the same length.
+template<typename... Iters>
+class Zip {
+ std::tuple<Iters...> _iters;
+
+public:
+ explicit Zip(std::tuple<Iters...> &&iters) : _iters(iters) {}
+
+ Zip &operator++() {
+ std::apply([](auto &&...it) { (++it, ...); }, _iters);
+ return *this;
+ }
+
+ auto operator*() const {
+ return std::apply([](auto &&...it) { return std::tuple<decltype(*it)...>(*it...); },
+ _iters);
+ }
+
+ friend auto operator==(Zip const &lhs, Zip const &rhs) {
+ return std::get<0>(lhs._iters) == std::get<0>(rhs._iters);
+ }
+
+ friend auto operator!=(Zip const &lhs, Zip const &rhs) {
+ return std::get<0>(lhs._iters) != std::get<0>(rhs._iters);
+ }
+};
+
+namespace detail {
+template<typename... Containers>
+class ZipContainer {
+ std::tuple<Containers...> _containers;
+
+public:
+ ZipContainer(Containers &&...containers)
+ : _containers(std::forward<Containers>(containers)...) {}
+
+ auto begin() {
+ return Zip(std::apply(
+ [](auto &&...containers) {
+ using std::begin;
+ return std::make_tuple(begin(containers)...);
+ },
+ _containers));
+ }
+
+ auto end() {
+ return Zip(std::apply(
+ [](auto &&...containers) {
+ using std::end;
+ return std::make_tuple(end(containers)...);
+ },
+ _containers));
+ }
+};
+
+// Take ownership of objects and rvalue refs passed to us, but not lvalue refs
+template<typename T>
+using Holder = std::conditional_t<std::is_lvalue_reference_v<T>, T,
+ std::remove_cv_t<std::remove_reference_t<T>>>;
+}
+
+/**
+ * Does the same number of iterations as the first container's iterator!
+ */
+template<typename... Containers>
+static constexpr auto zip(Containers &&...cs) {
+ return detail::ZipContainer<detail::Holder<Containers>...>(std::forward<Containers>(cs)...);
+}
+
+#endif /* RGBDS_ITERTOOLS_HPP */
--- a/src/gfx/process.cpp
+++ b/src/gfx/process.cpp
@@ -27,6 +27,7 @@
#include "defaultinitalloc.hpp"
#include "helpers.h"
+#include "itertools.hpp"
#include "gfx/main.hpp"
#include "gfx/pal_packing.hpp"
@@ -435,9 +436,13 @@
return *this;
}
- bool operator!=(iterator const &rhs) const {
- return coords() != rhs.coords(); // Compare the returned coord pairs
+ friend bool operator==(iterator const &lhs, iterator const &rhs) {
+ return lhs.coords() == rhs.coords(); // Compare the returned coord pairs
}
+
+ friend bool operator!=(iterator const &lhs, iterator const &rhs) {
+ return lhs.coords() != rhs.coords(); // Compare the returned coord pairs
+ }
};
public:
@@ -565,12 +570,10 @@
// Convert the palette spec to actual palettes
std::vector<Palette> palettes(options.palSpec.size());
- auto palIter = palettes.begin(); // TODO: `zip`
- for (auto const &spec : options.palSpec) {
+ for (auto [spec, pal] : zip(options.palSpec, palettes)) {
for (size_t i = 0; i < options.nbColorsPerPal; ++i) {
- (*palIter)[i] = spec[i].cgbColor();
+ pal[i] = spec[i].cgbColor();
}
- ++palIter;
}
// Iterate through proto-palettes, and try mapping them to the specified palettes
@@ -728,10 +731,9 @@
}
remainingTiles -= options.trim;
- auto iter = attrmap.begin();
- for (auto tile : png.visitAsTiles(options.columnMajor)) {
+ for (auto [tile, attr] : zip(png.visitAsTiles(options.columnMajor), attrmap)) {
// If the tile is fully transparent, default to palette 0
- Palette const &palette = palettes[iter->getPalID(mappings)];
+ Palette const &palette = palettes[attr.getPalID(mappings)];
for (uint32_t y = 0; y < 8; ++y) {
uint16_t bitplanes = TileData::rowBitplanes(tile, palette, y);
output.sputc(bitplanes & 0xFF);
@@ -739,7 +741,6 @@
output.sputc(bitplanes >> 8);
}
}
- ++iter;
--remainingTiles;
if (remainingTiles == 0) {
@@ -747,10 +748,9 @@
}
}
assert(remainingTiles == 0);
- assert(iter + options.trim == attrmap.end());
}
-static void outputMaps(Png const &png, DefaultInitVec<AttrmapEntry> const &attrmap,
+static void outputMaps(DefaultInitVec<AttrmapEntry> const &attrmap,
DefaultInitVec<size_t> const &mappings) {
std::optional<std::filebuf> tilemapOutput, attrmapOutput, palmapOutput;
if (!options.tilemap.empty()) {
@@ -768,8 +768,7 @@
uint8_t tileID = 0;
uint8_t bank = 0;
- auto iter = attrmap.begin();
- for ([[maybe_unused]] auto tile : png.visitAsTiles(options.columnMajor)) {
+ for (auto attr : attrmap) {
if (tileID == options.maxNbTiles[bank]) {
assert(bank == 0);
bank = 1;
@@ -780,16 +779,14 @@
tilemapOutput->sputc(tileID + options.baseTileIDs[bank]);
}
if (attrmapOutput.has_value()) {
- uint8_t palID = iter->getPalID(mappings) & 7;
+ uint8_t palID = attr.getPalID(mappings) & 7;
attrmapOutput->sputc(palID | bank << 3); // The other flags are all 0
}
if (palmapOutput.has_value()) {
- palmapOutput->sputc(iter->getPalID(mappings));
+ palmapOutput->sputc(attr.getPalID(mappings));
}
++tileID;
- ++iter;
}
- assert(iter == attrmap.end());
}
} // namespace unoptimized
@@ -846,19 +843,15 @@
// by caching the full tile data anyway, so we might as well.)
UniqueTiles tiles;
- auto iter = attrmap.begin();
- for (auto tile : png.visitAsTiles(options.columnMajor)) {
- auto [tileID, matchType] = tiles.addTile(tile, palettes[mappings[iter->protoPaletteID]]);
+ for (auto [tile, attr] : zip(png.visitAsTiles(options.columnMajor), attrmap)) {
+ auto [tileID, matchType] = tiles.addTile(tile, palettes[mappings[attr.protoPaletteID]]);
- iter->xFlip = matchType == TileData::HFLIP || matchType == TileData::VHFLIP;
- iter->yFlip = matchType == TileData::VFLIP || matchType == TileData::VHFLIP;
- iter->bank = tileID >= options.maxNbTiles[0];
- iter->tileID = (iter->bank ? tileID - options.maxNbTiles[0] : tileID)
- + options.baseTileIDs[iter->bank];
-
- ++iter;
+ attr.xFlip = matchType == TileData::HFLIP || matchType == TileData::VHFLIP;
+ attr.yFlip = matchType == TileData::VFLIP || matchType == TileData::VHFLIP;
+ attr.bank = tileID >= options.maxNbTiles[0];
+ attr.tileID =
+ (attr.bank ? tileID - options.maxNbTiles[0] : tileID) + options.baseTileIDs[attr.bank];
}
- assert(iter == attrmap.end());
// Copy elision should prevent the contained `unordered_set` from being re-constructed
return tiles;
@@ -1057,7 +1050,7 @@
options.verbosePrint(
Options::VERB_LOG_ACT,
"Generating unoptimized tilemap and/or attrmap and/or palmap...\n");
- unoptimized::outputMaps(png, attrmap, mappings);
+ unoptimized::outputMaps(attrmap, mappings);
}
} else {
// All of these require the deduplication process to be performed to be output