yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
pixel_clipboard.cc
Go to the documentation of this file.
2
3#include <limits>
4#include <vector>
5
6#include "absl/strings/str_format.h"
7#include "absl/strings/str_join.h"
8#include "util/indexed_png.h"
9#include "util/macro.h"
10
11namespace yaze::editor {
12
13absl::StatusOr<std::vector<uint8_t>> EncodeClipboardPng(
14 const std::vector<uint8_t>& indices, int width, int height,
15 const SheetColors& colors) {
16 if (width <= 0 || height <= 0 ||
17 indices.size() != static_cast<size_t>(width) * height) {
18 return absl::InvalidArgumentError("Clipboard image has no pixels");
19 }
20 std::vector<std::array<uint8_t, 4>> palette;
21 for (size_t i = 0; i < colors.size(); ++i) {
22 palette.push_back({colors[i][0], colors[i][1], colors[i][2],
23 static_cast<uint8_t>(i == 0 ? 0 : 255)});
24 }
25 return util::EncodeIndexedPng(width, height, indices, palette);
26}
27
28absl::StatusOr<ClipboardPaste> MapClipboardPng(
29 const std::vector<uint8_t>& png_bytes, const SheetColors& colors,
30 int max_width, int max_height) {
31 ASSIGN_OR_RETURN(const util::PngImage image, util::DecodePng(png_bytes));
32 if (image.width > max_width || image.height > max_height) {
33 return absl::InvalidArgumentError(absl::StrFormat(
34 "The clipboard image is %dx%d; a sheet holds at most %dx%d",
35 image.width, image.height, max_width, max_height));
36 }
37 ClipboardPaste paste;
38 paste.width = image.width;
39 paste.height = image.height;
40 const size_t count = static_cast<size_t>(image.width) * image.height;
41 paste.indices.resize(count);
42
43 bool indices_fit = image.indexed;
44 if (image.indexed) {
45 for (uint8_t index : image.indices) {
46 if (index > 7) {
47 indices_fit = false;
48 ++paste.above_depth;
49 }
50 }
51 }
52 if (indices_fit) {
53 paste.indices = image.indices;
54 paste.kept = static_cast<int>(count);
55 return paste;
56 }
57
58 for (size_t i = 0; i < count; ++i) {
59 std::array<uint8_t, 4> rgba;
60 if (image.indexed) {
61 const uint8_t index = image.indices[i];
62 rgba = index < image.palette.size() ? image.palette[index]
63 : std::array<uint8_t, 4>{0, 0, 0, 0};
64 } else {
65 rgba = {image.rgba[i * 4], image.rgba[i * 4 + 1], image.rgba[i * 4 + 2],
66 image.rgba[i * 4 + 3]};
67 }
68 if (rgba[3] < 128) {
69 paste.indices[i] = 0;
70 ++paste.transparent;
71 continue;
72 }
73 int best = -1;
74 int best_distance = std::numeric_limits<int>::max();
75 for (int c = 0; c < 8; ++c) {
76 const int dr = rgba[0] - colors[c][0];
77 const int dg = rgba[1] - colors[c][1];
78 const int db = rgba[2] - colors[c][2];
79 const int distance = dr * dr + dg * dg + db * db;
80 if (distance == 0) {
81 best = c;
82 best_distance = 0;
83 break;
84 }
85 // Color 0 is transparent in game; only an exact match maps to it.
86 if (c != 0 && distance < best_distance) {
87 best = c;
88 best_distance = distance;
89 }
90 }
91 paste.indices[i] = static_cast<uint8_t>(best);
92 if (best_distance == 0) {
93 ++paste.exact;
94 } else {
95 ++paste.remapped;
96 }
97 }
98 return paste;
99}
100
101std::string DescribeClipboardPaste(const ClipboardPaste& paste) {
102 std::vector<std::string> parts;
103 if (paste.kept > 0) {
104 parts.push_back(absl::StrFormat("%d kept", paste.kept));
105 }
106 if (paste.exact > 0) {
107 parts.push_back(absl::StrFormat("%d exact", paste.exact));
108 }
109 if (paste.remapped > 0) {
110 parts.push_back(absl::StrFormat("%d remapped", paste.remapped));
111 }
112 if (paste.transparent > 0) {
113 parts.push_back(absl::StrFormat("%d transparent", paste.transparent));
114 }
115 if (paste.above_depth > 0) {
116 parts.push_back(absl::StrFormat("%d above color 7", paste.above_depth));
117 }
118 return absl::StrFormat("%dx%d: %s", paste.width, paste.height,
119 absl::StrJoin(parts, ", "));
120}
121
122} // namespace yaze::editor
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition macro.h:62
Editors are the view controllers for the application.
absl::StatusOr< ClipboardPaste > MapClipboardPng(const std::vector< uint8_t > &png_bytes, const SheetColors &colors, int max_width, int max_height)
std::array< std::array< uint8_t, 3 >, 8 > SheetColors
The 8 colors a 3bpp sheet shows (index 0 is transparent in game).
absl::StatusOr< std::vector< uint8_t > > EncodeClipboardPng(const std::vector< uint8_t > &indices, int width, int height, const SheetColors &colors)
std::string DescribeClipboardPaste(const ClipboardPaste &paste)
"16x16: 200 kept" / "16x8: 90 exact, 38 remapped, 4 above color 7".
absl::StatusOr< std::vector< uint8_t > > EncodeIndexedPng(int width, int height, const std::vector< uint8_t > &indices, const std::vector< std::array< uint8_t, 4 > > &palette)
absl::StatusOr< PngImage > DecodePng(const std::vector< uint8_t > &bytes)
A clipboard PNG mapped onto a sheet's colors.
std::vector< uint8_t > indices
A decoded PNG: palette indices for indexed files, RGBA otherwise.
Definition indexed_png.h:21
std::vector< std::array< uint8_t, 4 > > palette
Definition indexed_png.h:28
std::vector< uint8_t > indices
Definition indexed_png.h:26
std::vector< uint8_t > rgba
Definition indexed_png.h:30