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
29[[nodiscard]] constexpr std::pair<std::size_t, std::size_t> index_to_row_col(std::size_t index)
30{
31 return {index / side_length_pix, index % side_length_pix};
32}
33
45[[nodiscard]] constexpr std::size_t row_col_to_index(std::size_t row, std::size_t col)
46{
47 return row * side_length_pix + col;
48}
49
50} // namespace tile
51
77template <SupportsTransparency PixelType>
78class PixelTile {
79 public:
80 virtual ~PixelTile() = default;
81
82 PixelTile() : pix_{} {}
83
84 explicit PixelTile(std::array<PixelType, tile::size_pix> pix) : pix_{std::move(pix)} {}
85
91 explicit PixelTile(const PixelType &fill_value) : pix_{}
92 {
93 std::fill(pix_.begin(), pix_.end(), fill_value);
94 }
95
96 auto operator<=>(const PixelTile &) const = default;
97
107 [[nodiscard]] bool is_transparent() const
108 requires requires(const PixelType &p) { p.is_transparent(); }
109 {
110 return is_transparent_impl([](const PixelType &pixel) { return pixel.is_transparent(); });
111 }
112
124 [[nodiscard]] bool is_transparent(const PixelType &extrinsic) const
125 requires requires(const PixelType &p) { p.is_transparent(p); }
126 {
127 return is_transparent_impl([&extrinsic](const PixelType &pixel) { return pixel.is_transparent(extrinsic); });
128 }
129
151 [[nodiscard]] bool equals_ignoring_transparency(const PixelTile &other) const
152 requires requires(const PixelType &p) { p.is_transparent(); }
153 {
154 auto pred = [](const PixelType &pixel) { return pixel.is_transparent(); };
155 return equals_ignoring_transparency_impl(other, pred, pred);
156 }
157
180 [[nodiscard]] bool equals_ignoring_transparency(const PixelTile &other, const PixelType &extrinsic) const
181 requires requires(const PixelType &p) { p.is_transparent(p); }
182 {
183 auto pred = [&extrinsic](const PixelType &pixel) { return pixel.is_transparent(extrinsic); };
184 return equals_ignoring_transparency_impl(other, pred, pred);
185 }
186
211 [[nodiscard]] bool equals_ignoring_transparency( // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
212 const PixelTile &other, const PixelType &extrinsic, const PixelType &other_extrinsic) const
213 requires requires(const PixelType &p) { p.is_transparent(p); }
214 {
215 return equals_ignoring_transparency_impl(
216 other,
217 [&extrinsic](const PixelType &pixel) { return pixel.is_transparent(extrinsic); },
218 [&other_extrinsic](const PixelType &pixel) { return pixel.is_transparent(other_extrinsic); });
219 }
220
261 [[nodiscard]] static std::weak_ordering cross_et_compare( // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
262 const PixelTile &a, const PixelType &a_et, const PixelTile &b, const PixelType &b_et)
263 requires requires(const PixelType &p) { p.is_transparent(p); }
264 {
265 for (std::size_t i = 0; i < tile::size_pix; ++i) {
266 const auto &pa = a.pix_.at(i);
267 const auto &pb = b.pix_.at(i);
268 const bool ta = pa.is_transparent(a_et);
269 const bool tb = pb.is_transparent(b_et);
270 if (ta && tb) {
271 continue;
272 }
273 if (ta) {
274 return std::weak_ordering::less;
275 }
276 if (tb) {
277 return std::weak_ordering::greater;
278 }
279 if (const auto cmp = pa <=> pb; cmp != 0) {
280 return cmp;
281 }
282 }
283 return std::weak_ordering::equivalent;
284 }
285
286 [[nodiscard]] PixelType at(std::size_t i) const
287 {
288 if (i >= tile::size_pix) {
289 panic("index out of bounds: " + std::to_string(i));
290 }
291 return pix_[i];
292 }
293
294 [[nodiscard]] PixelType at(std::size_t row, std::size_t col) const
295 {
296 if (row >= tile::side_length_pix) {
297 panic("row index out of bounds: " + std::to_string(row));
298 }
299 if (col >= tile::side_length_pix) {
300 panic("col index out of bounds: " + std::to_string(col));
301 }
302 return pix_[row * tile::side_length_pix + col];
303 }
304
305 void set(std::size_t i, const PixelType &p)
306 {
307 if (i >= tile::size_pix) {
308 panic("index out of bounds: " + std::to_string(i));
309 }
310 pix_[i] = p;
311 }
312
313 void set(std::size_t row, std::size_t col, const PixelType &p)
314 {
315 if (row >= tile::side_length_pix) {
316 panic("row index out of bounds: " + std::to_string(row));
317 }
318 if (col >= tile::side_length_pix) {
319 panic("col index out of bounds: " + std::to_string(col));
320 }
321 pix_[row * tile::side_length_pix + col] = p;
322 }
323
335 [[nodiscard]] PixelTile flip(bool h_flip, bool v_flip) const
336 {
337 PixelTile flipped_tile{};
338 for (std::size_t row = 0; row < tile::side_length_pix; ++row) {
339 for (std::size_t col = 0; col < tile::side_length_pix; ++col) {
340 const std::size_t src_row = v_flip ? (tile::side_length_pix - 1 - row) : row;
341 const std::size_t src_col = h_flip ? (tile::side_length_pix - 1 - col) : col;
342 flipped_tile.set(row, col, at(src_row, src_col));
343 }
344 }
345 return flipped_tile;
346 }
347
362 [[nodiscard]] std::set<PixelType> unique_nontransparent_colors() const
363 requires requires(const PixelType &p) { p.is_transparent(); }
364 {
365 return unique_nontransparent_colors_impl([](const PixelType &pixel) { return pixel.is_transparent(); });
366 }
367
386 [[nodiscard]] std::set<PixelType> unique_nontransparent_colors(const PixelType &extrinsic) const
387 requires requires(const PixelType &p) { p.is_transparent(p); }
388 {
389 return unique_nontransparent_colors_impl(
390 [&extrinsic](const PixelType &pixel) { return pixel.is_transparent(extrinsic); });
391 }
392
393 [[nodiscard]] const std::array<PixelType, tile::size_pix> &pix() const
394 {
395 return pix_;
396 }
397
398 private:
411 template <typename TransparencyPredicate>
412 [[nodiscard]] bool is_transparent_impl(TransparencyPredicate is_transparent_pred) const
413 {
414 return std::ranges::all_of(pix(), is_transparent_pred);
415 }
416
434 template <typename SelfTransparencyPredicate, typename OtherTransparencyPredicate>
435 [[nodiscard]] bool equals_ignoring_transparency_impl(
436 const PixelTile &other,
437 SelfTransparencyPredicate self_is_transparent_pred,
438 OtherTransparencyPredicate other_is_transparent_pred) const
439 {
440 for (std::size_t i = 0; i < tile::size_pix; ++i) {
441 const auto &pixel1 = pix_.at(i);
442 const auto &pixel2 = other.pix_.at(i);
443
444 if (self_is_transparent_pred(pixel1) && other_is_transparent_pred(pixel2)) {
445 continue; // Both transparent, consider equal
446 }
447 if (pixel1 != pixel2) {
448 return false; // Different pixels
449 }
450 }
451 return true;
452 }
453
466 template <typename TransparencyPredicate>
467 [[nodiscard]] std::set<PixelType> unique_nontransparent_colors_impl(TransparencyPredicate is_transparent_pred) const
468 {
469 std::set<PixelType> colors;
470 for (const auto &pixel : pix_) {
471 if (!is_transparent_pred(pixel)) {
472 colors.insert(pixel);
473 }
474 }
475 return colors;
476 }
477
478 std::array<PixelType, tile::size_pix> pix_;
479};
480
481} // 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