Porytiles
Loading...
Searching...
No Matches
pixel_tile.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <array>
5#include <compare>
6#include <set>
7#include <string>
8
11
12namespace porytiles {
13
14namespace tile {
15
16inline constexpr std::size_t side_length_pix = 8;
17inline constexpr std::size_t size_pix = side_length_pix * side_length_pix;
18
27[[nodiscard]] constexpr std::pair<std::size_t, std::size_t> index_to_row_col(std::size_t index)
28{
29 return {index / side_length_pix, index % side_length_pix};
30}
31
41[[nodiscard]] constexpr std::size_t row_col_to_index(std::size_t row, std::size_t col)
42{
43 return row * side_length_pix + col;
44}
45
46} // namespace tile
47
71template <SupportsTransparency PixelType>
72class PixelTile {
73 public:
74 virtual ~PixelTile() = default;
75
76 PixelTile() : pix_{} {}
77
78 explicit PixelTile(std::array<PixelType, tile::size_pix> pix) : pix_{std::move(pix)} {}
79
83 explicit PixelTile(const PixelType &fill_value) : pix_{}
84 {
85 std::fill(pix_.begin(), pix_.end(), fill_value);
86 }
87
88 auto operator<=>(const PixelTile &) const = default;
89
97 [[nodiscard]] bool is_transparent() const
98 requires requires(const PixelType &p) { p.is_transparent(); }
99 {
100 return is_transparent_impl([](const PixelType &pixel) { return pixel.is_transparent(); });
101 }
102
112 [[nodiscard]] bool is_transparent(const PixelType &extrinsic) const
113 requires requires(const PixelType &p) { p.is_transparent(p); }
114 {
115 return is_transparent_impl([&extrinsic](const PixelType &pixel) { return pixel.is_transparent(extrinsic); });
116 }
117
137 [[nodiscard]] bool equals_ignoring_transparency(const PixelTile &other) const
138 requires requires(const PixelType &p) { p.is_transparent(); }
139 {
140 auto pred = [](const PixelType &pixel) { return pixel.is_transparent(); };
141 return equals_ignoring_transparency_impl(other, pred, pred);
142 }
143
164 [[nodiscard]] bool equals_ignoring_transparency(const PixelTile &other, const PixelType &extrinsic) const
165 requires requires(const PixelType &p) { p.is_transparent(p); }
166 {
167 auto pred = [&extrinsic](const PixelType &pixel) { return pixel.is_transparent(extrinsic); };
168 return equals_ignoring_transparency_impl(other, pred, pred);
169 }
170
193 [[nodiscard]] bool equals_ignoring_transparency( // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
194 const PixelTile &other, const PixelType &extrinsic, const PixelType &other_extrinsic) const
195 requires requires(const PixelType &p) { p.is_transparent(p); }
196 {
197 return equals_ignoring_transparency_impl(
198 other,
199 [&extrinsic](const PixelType &pixel) { return pixel.is_transparent(extrinsic); },
200 [&other_extrinsic](const PixelType &pixel) { return pixel.is_transparent(other_extrinsic); });
201 }
202
241 [[nodiscard]] static std::weak_ordering cross_et_compare( // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
242 const PixelTile &a, const PixelType &a_et, const PixelTile &b, const PixelType &b_et)
243 requires requires(const PixelType &p) { p.is_transparent(p); }
244 {
245 for (std::size_t i = 0; i < tile::size_pix; ++i) {
246 const auto &pa = a.pix_.at(i);
247 const auto &pb = b.pix_.at(i);
248 const bool ta = pa.is_transparent(a_et);
249 const bool tb = pb.is_transparent(b_et);
250 if (ta && tb) {
251 continue;
252 }
253 if (ta) {
254 return std::weak_ordering::less;
255 }
256 if (tb) {
257 return std::weak_ordering::greater;
258 }
259 if (const auto cmp = pa <=> pb; cmp != 0) {
260 return cmp;
261 }
262 }
263 return std::weak_ordering::equivalent;
264 }
265
266 [[nodiscard]] PixelType at(std::size_t i) const
267 {
268 if (i >= tile::size_pix) {
269 panic("index out of bounds: " + std::to_string(i));
270 }
271 return pix_[i];
272 }
273
274 [[nodiscard]] PixelType at(std::size_t row, std::size_t col) const
275 {
276 if (row >= tile::side_length_pix) {
277 panic("row index out of bounds: " + std::to_string(row));
278 }
279 if (col >= tile::side_length_pix) {
280 panic("col index out of bounds: " + std::to_string(col));
281 }
282 return pix_[row * tile::side_length_pix + col];
283 }
284
285 void set(std::size_t i, const PixelType &p)
286 {
287 if (i >= tile::size_pix) {
288 panic("index out of bounds: " + std::to_string(i));
289 }
290 pix_[i] = p;
291 }
292
293 void set(std::size_t row, std::size_t col, const PixelType &p)
294 {
295 if (row >= tile::side_length_pix) {
296 panic("row index out of bounds: " + std::to_string(row));
297 }
298 if (col >= tile::side_length_pix) {
299 panic("col index out of bounds: " + std::to_string(col));
300 }
301 pix_[row * tile::side_length_pix + col] = p;
302 }
303
313 [[nodiscard]] PixelTile flip(bool h_flip, bool v_flip) const
314 {
315 PixelTile flipped_tile{};
316 for (std::size_t row = 0; row < tile::side_length_pix; ++row) {
317 for (std::size_t col = 0; col < tile::side_length_pix; ++col) {
318 const std::size_t src_row = v_flip ? (tile::side_length_pix - 1 - row) : row;
319 const std::size_t src_col = h_flip ? (tile::side_length_pix - 1 - col) : col;
320 flipped_tile.set(row, col, at(src_row, src_col));
321 }
322 }
323 return flipped_tile;
324 }
325
338 [[nodiscard]] std::set<PixelType> unique_nontransparent_colors() const
339 requires requires(const PixelType &p) { p.is_transparent(); }
340 {
341 return unique_nontransparent_colors_impl([](const PixelType &pixel) { return pixel.is_transparent(); });
342 }
343
360 [[nodiscard]] std::set<PixelType> unique_nontransparent_colors(const PixelType &extrinsic) const
361 requires requires(const PixelType &p) { p.is_transparent(p); }
362 {
363 return unique_nontransparent_colors_impl(
364 [&extrinsic](const PixelType &pixel) { return pixel.is_transparent(extrinsic); });
365 }
366
367 [[nodiscard]] const std::array<PixelType, tile::size_pix> &pix() const
368 {
369 return pix_;
370 }
371
372 private:
383 template <typename TransparencyPredicate>
384 [[nodiscard]] bool is_transparent_impl(TransparencyPredicate is_transparent_pred) const
385 {
386 return std::ranges::all_of(pix(), is_transparent_pred);
387 }
388
404 template <typename SelfTransparencyPredicate, typename OtherTransparencyPredicate>
405 [[nodiscard]] bool equals_ignoring_transparency_impl(
406 const PixelTile &other,
407 SelfTransparencyPredicate self_is_transparent_pred,
408 OtherTransparencyPredicate other_is_transparent_pred) const
409 {
410 for (std::size_t i = 0; i < tile::size_pix; ++i) {
411 const auto &pixel1 = pix_.at(i);
412 const auto &pixel2 = other.pix_.at(i);
413
414 if (self_is_transparent_pred(pixel1) && other_is_transparent_pred(pixel2)) {
415 continue; // Both transparent, consider equal
416 }
417 if (pixel1 != pixel2) {
418 return false; // Different pixels
419 }
420 }
421 return true;
422 }
423
434 template <typename TransparencyPredicate>
435 [[nodiscard]] std::set<PixelType> unique_nontransparent_colors_impl(TransparencyPredicate is_transparent_pred) const
436 {
437 std::set<PixelType> colors;
438 for (const auto &pixel : pix_) {
439 if (!is_transparent_pred(pixel)) {
440 colors.insert(pixel);
441 }
442 }
443 return colors;
444 }
445
446 std::array<PixelType, tile::size_pix> pix_;
447};
448
449} // namespace porytiles
An 8x8 tile backed by literal-array-based per-pixel storage of an arbitrary pixel type.
std::set< PixelType > unique_nontransparent_colors() const
Returns the set of unique non-transparent colors present in this PixelTile (intrinsic transparency on...
auto operator<=>(const PixelTile &) const =default
virtual ~PixelTile()=default
bool equals_ignoring_transparency(const PixelTile &other, const PixelType &extrinsic, const PixelType &other_extrinsic) const
Compares this PixelTile with another, treating transparent pixels as equal, using independent extrins...
const std::array< PixelType, tile::size_pix > & pix() const
static std::weak_ordering cross_et_compare(const PixelTile &a, const PixelType &a_et, const PixelTile &b, const PixelType &b_et)
Strict weak ordering that treats transparent pixels under each side's own ET as equivalent.
bool equals_ignoring_transparency(const PixelTile &other) const
Compares this PixelTile with another, treating all transparent pixels as equal.
bool is_transparent() const
Checks if this entire PixelTile is transparent (intrinsic transparency only).
PixelTile(std::array< PixelType, tile::size_pix > pix)
PixelTile flip(bool h_flip, bool v_flip) const
Creates a flipped copy of this PixelTile.
PixelType at(std::size_t row, std::size_t col) const
bool is_transparent(const PixelType &extrinsic) const
Checks if this entire PixelTile is transparent.
PixelType at(std::size_t i) const
void set(std::size_t i, const PixelType &p)
bool equals_ignoring_transparency(const PixelTile &other, const PixelType &extrinsic) const
Compares this PixelTile with another, treating all transparent pixels as equal.
std::set< PixelType > unique_nontransparent_colors(const PixelType &extrinsic) const
Returns the set of unique non-transparent colors present in this PixelTile.
PixelTile(const PixelType &fill_value)
Constructs a PixelTile with all pixels set to a fill value.
void set(std::size_t row, std::size_t col, const PixelType &p)
constexpr std::size_t side_length_pix
constexpr std::pair< std::size_t, std::size_t > index_to_row_col(std::size_t index)
Converts a linear index to row and column coordinates.
constexpr std::size_t row_col_to_index(std::size_t row, std::size_t col)
Converts row and column coordinates to a linear index.
constexpr std::size_t size_pix
void panic(const StringViewSourceLoc &s)
Unconditionally terminates the program with a panic message.
Definition panic.cpp:43