37 if (!SupportedDepth(permutation.
bpp)) {
38 return absl::InvalidArgumentError(
39 "Color-ID permutations support 2, 3 or 4 bpp");
41 if (permutation.
ids[0] != 0) {
42 return absl::InvalidArgumentError(
43 "Color-ID swaps keep ID 0 transparent; replacing transparency is a "
46 const int colors = permutation.
colors();
47 std::array<bool, 16> seen{};
48 for (
int i = 0; i < 16; ++i) {
49 const uint8_t value = permutation.
ids[i];
52 return absl::InvalidArgumentError(
53 absl::StrFormat(
"Color-ID permutation names ID %d outside %dbpp", i,
58 if (value >= colors) {
59 return absl::InvalidArgumentError(
60 absl::StrFormat(
"Color ID %d does not fit %dbpp (IDs 0-%d)", value,
61 permutation.
bpp, colors - 1));
64 return absl::InvalidArgumentError(
65 "Color-ID permutation must map each ID exactly once");
69 return absl::OkStatus();
86 if (!SupportedDepth(bpp)) {
87 return absl::InvalidArgumentError(
88 "Color-ID permutations support 2, 3 or 4 bpp");
90 const int colors = 1 << bpp;
91 if (from == 0 || to == 0) {
92 return absl::InvalidArgumentError(
93 "Color-ID swaps keep ID 0 transparent; replacing transparency is a "
96 if (from < 0 || to < 0 || from >= colors || to >= colors) {
97 return absl::InvalidArgumentError(
98 absl::StrFormat(
"Swap IDs must be 1-%d for %dbpp", colors - 1, bpp));
101 std::swap(permutation.ids[from], permutation.ids[to]);
107 for (
int y = 0; y < 8; ++y) {
108 for (
int x = 0; x < 8; ++x) {
109 if ((displayed_mask & (uint64_t{1} << (y * 8 + x))) == 0) {
112 const int raw_x = hflip ? 7 - x : x;
113 const int raw_y = vflip ? 7 - y : y;
114 mask |= uint64_t{1} << (raw_y * 8 + raw_x);
121 const std::vector<ColorIdBacking>& backings,
122 const std::vector<ColorIdTarget>& targets,
127 std::map<uint32_t, const ColorIdBacking*> by_range;
128 for (
const auto& backing : backings) {
129 if (!by_range.emplace(backing.range, &backing).second) {
130 return absl::InvalidArgumentError(
"Duplicate color-ID backing range");
133 if (targets.empty()) {
134 return absl::InvalidArgumentError(
"Select pixels to remap");
136 std::map<std::pair<uint32_t, uint16_t>, uint64_t> masks;
137 for (
const auto& target : targets) {
138 const auto backing = by_range.find(target.range);
139 if (backing == by_range.end()) {
140 return absl::NotFoundError(
"Selected color-ID range is unavailable");
142 if (target.tile_index >= kTilesPerPage) {
143 return absl::OutOfRangeError(
"Selected tile is outside its page");
145 const uint64_t mask =
148 masks[{target.range, target.tile_index}] |= mask;
152 return absl::InvalidArgumentError(
"Select at least one pixel");
155 std::set<uint32_t> used_ranges;
156 for (
const auto& [key, mask] : masks) {
157 used_ranges.insert(key.first);
159 for (
const auto range : used_ranges) {
160 const auto& backing = *by_range.at(range);
166 const int colors = 1 << backing.bpp;
167 if (backing.pixels.size() != kPagePixels ||
168 std::any_of(backing.pixels.begin(), backing.pixels.end(),
169 [colors](uint8_t pixel) { return pixel >= colors; })) {
170 return absl::FailedPreconditionError(
171 absl::StrFormat(
"Backing range %u is not a 128x32 page of %dbpp IDs",
172 range, backing.bpp));
178 std::map<uint32_t, ColorIdRangeEdit> edits;
179 for (
const auto& [key, mask] : masks) {
180 const auto [range, tile] = key;
181 const auto& pixels = by_range.at(range)->pixels;
182 plan.
targets.push_back({range, tile, mask});
183 const size_t start = TileOffset(tile);
184 for (
int y = 0; y < 8; ++y) {
185 for (
int x = 0; x < 8; ++x) {
186 if ((mask & (uint64_t{1} << (y * 8 + x))) == 0) {
190 const size_t offset = start + y * kPageWidth + x;
191 const uint8_t before = pixels[offset];
192 const uint8_t after = permutation.
ids[before];
193 if (before == after) {
196 auto& edit = edits[range];
198 edit.offsets.push_back(
static_cast<uint16_t
>(offset));
199 edit.before.push_back(before);
200 edit.after.push_back(after);
205 for (
auto& [range, edit] : edits) {
206 plan.
ranges.push_back(std::move(edit));
212 bool forward, std::vector<uint8_t>& pixels) {
213 if (!SupportedDepth(bpp) || pixels.size() != kPagePixels ||
216 return absl::InvalidArgumentError(
"Invalid color-ID range edit");
218 const int colors = 1 << bpp;
220 std::set<uint16_t> seen;
221 for (
size_t i = 0; i < edit.
offsets.size(); ++i) {
222 const auto offset = edit.
offsets[i];
223 if (offset >= next.size() || !seen.insert(offset).second ||
224 edit.
before[i] >= colors || edit.
after[i] >= colors ||
226 edit.
after[i] == 0) {
227 return absl::InvalidArgumentError(
"Invalid color-ID pixel edit");
229 const uint8_t expected = forward ? edit.
before[i] : edit.
after[i];
230 if (next[offset] != expected) {
231 return absl::FailedPreconditionError(
232 "Pixels changed since this color-ID edit");
234 next[offset] = forward ? edit.
after[i] : edit.
before[i];
236 pixels = std::move(next);
237 return absl::OkStatus();