Implement slicing input image

This commit is contained in:
ISSOtm
2022-05-21 19:15:11 +02:00
committed by Eldred Habert
parent 9646f15b59
commit 3f70372308
8 changed files with 91 additions and 46 deletions

View File

@@ -39,7 +39,12 @@ struct Options {
} palSpecType = NO_SPEC; // -c
std::vector<std::array<Rgba, 4>> palSpec{};
uint8_t bitDepth = 2; // -d
std::array<uint32_t, 4> inputSlice{0, 0, 0, 0}; // -L (margins in clockwise order, like CSS)
struct {
uint16_t left;
uint16_t top;
uint16_t width;
uint16_t height;
} inputSlice{0, 0, 0, 0}; // -L (margins in clockwise order, like CSS)
std::array<uint16_t, 2> maxNbTiles{UINT16_MAX, 0}; // -N
uint8_t nbPalettes = 8; // -n
std::string output{}; // -o

View File

@@ -157,8 +157,16 @@ Set the bit depth of the output tile data, in bits per pixel (bpp), either 1 or
This changes how tile data is output, and the maximum number of colors per palette (2 and 4 respectively).
.It Fl L Ar slice , Fl Fl slice Ar slice
Only process a given rectangle of the image.
.Sy TODO: arg format .
This is useful for example if the input image is a sheet of some sort, and you want to convert each item individually.
This is useful for example if the input image is a sheet of some sort, and you want to convert each cel individually.
The default is to process the whole image as-is.
.Pp
.Ar slice
must be two number pairs, separated by a colon.
The numbers must be separated by commas; space is allowed around all punctuation.
The first number pair specifies the X and Y coordinates of the top-left pixel that will be processed (anything above it or to its left will be ignored).
The second number pair specifies how many tiles to process horizontally and vertically, respectively.
.Pp
.Sy Fl L Sy is ignored in reverse mode , No no padding is inserted .
.It Fl m , Fl Fl mirror-tiles
Deduplicate tiles that are mirrors of each other.
Tiles are checked for horizontal, vertical, and horizontal-vertical mirroring.

View File

@@ -11,6 +11,7 @@
#include <algorithm>
#include <assert.h>
#include <cinttypes>
#include <cstdint>
#include <ctype.h>
#include <fstream>
#include <ios>
@@ -394,7 +395,44 @@ static char *parseArgv(int argc, char **argv, bool &autoAttrmap, bool &autoTilem
}
break;
case 'L':
options.inputSlice = {0, 0, 0, 0}; // TODO
options.inputSlice.left = parseNumber(arg, "Input slice left coordinate");
if (options.inputSlice.left > INT16_MAX) {
error("Input slice left coordinate is out of range!");
break;
}
skipWhitespace(arg);
if (*arg != ',') {
error("Missing comma after left coordinate in \"%s\"", musl_optarg);
break;
}
++arg;
skipWhitespace(arg);
options.inputSlice.top = parseNumber(arg, "Input slice upper coordinate");
skipWhitespace(arg);
if (*arg != ':') {
error("Missing colon after upper coordinate in \"%s\"", musl_optarg);
break;
}
++arg;
skipWhitespace(arg);
options.inputSlice.width = parseNumber(arg, "Input slice width");
skipWhitespace(arg);
if (options.inputSlice.width == 0) {
error("Input slice width may not be 0!");
}
if (*arg != ',') {
error("Missing comma after width in \"%s\"", musl_optarg);
break;
}
++arg;
skipWhitespace(arg);
options.inputSlice.height = parseNumber(arg, "Input slice height");
if (options.inputSlice.height == 0) {
error("Input slice height may not be 0!");
}
if (*arg != '\0') {
error("Unexpected extra characters after slice spec in \"%s\"", musl_optarg);
}
break;
case 'm':
options.allowMirroring = true;
@@ -695,10 +733,10 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "\tDedup unit: %" PRIu16 "x%" PRIu16 " tiles\n", options.unitSize[0],
options.unitSize[1]);
fprintf(stderr,
"\tInput image slice: %" PRIu32 "x%" PRIu32 " pixels from (%" PRIu32 ", %" PRIu32
")\n",
options.inputSlice[2], options.inputSlice[3], options.inputSlice[0],
options.inputSlice[1]);
"\tInput image slice: %" PRIu32 "x%" PRIu32 " pixels starting at (%" PRIi32
", %" PRIi32 ")\n",
options.inputSlice.width, options.inputSlice.height, options.inputSlice.left,
options.inputSlice.top);
fprintf(stderr, "\tBase tile IDs: [%" PRIu8 ", %" PRIu8 "]\n", options.baseTileIDs[0],
options.baseTileIDs[1]);
fprintf(stderr, "\tMaximum %" PRIu16 " tiles in bank 0, %" PRIu16 " in bank 1\n",

View File

@@ -218,10 +218,10 @@ public:
png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, &interlaceType, nullptr,
nullptr);
if (width % 8 != 0) {
if (options.inputSlice.width == 0 && width % 8 != 0) {
fatal("Image width (%" PRIu32 " pixels) is not a multiple of 8!", width);
}
if (height % 8 != 0) {
if (options.inputSlice.height == 0 && height % 8 != 0) {
fatal("Image height (%" PRIu32 " pixels) is not a multiple of 8!", height);
}
@@ -424,8 +424,12 @@ public:
uint32_t const limit;
uint32_t x, y;
std::pair<uint32_t, uint32_t> coords() const { return {x, y}; }
Tile operator*() const { return {parent._png, x, y}; }
std::pair<uint32_t, uint32_t> coords() const {
return {x + options.inputSlice.left, y + options.inputSlice.top};
}
Tile operator*() const {
return {parent._png, x + options.inputSlice.left, y + options.inputSlice.top};
}
iterator &operator++() {
auto [major, minor] = parent._columnMajor ? std::tie(y, x) : std::tie(x, y);
@@ -455,7 +459,8 @@ public:
};
public:
TilesVisitor visitAsTiles(bool columnMajor) const {
return {*this, columnMajor, width, height};
return {*this, columnMajor, options.inputSlice.width ? options.inputSlice.width * 8 : width,
options.inputSlice.height ? options.inputSlice.height * 8 : height};
}
};
@@ -1010,7 +1015,8 @@ void process() {
attrs.protoPaletteID = protoPalettes.size();
if (protoPalettes.size() == AttrmapEntry::transparent) { // Check for overflow
fatal("Reached %zu proto-palettes... sorry, this image is too much for me to handle :(", AttrmapEntry::transparent);
fatal("Reached %zu proto-palettes... sorry, this image is too much for me to handle :(",
AttrmapEntry::transparent);
}
protoPalettes.push_back(tileColors);
contained:;

View File

@@ -91,6 +91,16 @@ void reverse() {
warning("The color curve is not yet supported in reverse mode...");
}
if (options.inputSlice.left != 0 || options.inputSlice.top != 0
|| options.inputSlice.height != 0) {
warning("\"Sliced-off\" pixels are ignored in reverse mode");
}
if (options.inputSlice.width != 0 && options.inputSlice.width != options.reversedWidth * 8) {
warning("Specified input slice width (%" PRIu16
") doesn't match provided reversing width (%" PRIu8 " * 8)",
options.inputSlice.width, options.reversedWidth);
}
options.verbosePrint(Options::VERB_LOG_ACT, "Reading tiles...\n");
auto const tiles = readInto(options.output);
uint8_t tileSize = 8 * options.bitDepth;
@@ -115,14 +125,7 @@ void reverse() {
options.maxNbTiles[0], options.maxNbTiles[1]);
}
size_t width, height;
size_t usefulWidth = options.reversedWidth - options.inputSlice[1] - options.inputSlice[3];
if (usefulWidth % 8 != 0) {
fatal(
"No input slice specified (`-L`), and specified image width (%zu) not a multiple of 8",
usefulWidth);
}
width = usefulWidth / 8;
size_t width = options.reversedWidth, height; // In tiles
if (nbTileInstances % width != 0) {
fatal("Total number of tiles read (%zu) cannot be divided by image width (%zu tiles)",
nbTileInstances, width);
@@ -207,10 +210,8 @@ void reverse() {
png_set_write_fn(png, &pngFile, writePng, flushPng);
// TODO: if `-f` is passed, write the image indexed instead of RGB
png_set_IHDR(png, pngInfo, options.reversedWidth,
height * 8 + options.inputSlice[0] + options.inputSlice[2], 8,
PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
png_set_IHDR(png, pngInfo, options.reversedWidth * 8, height * 8, 8, PNG_COLOR_TYPE_RGB_ALPHA,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_write_info(png, pngInfo);
png_color_8 sbitChunk;
@@ -221,26 +222,15 @@ void reverse() {
png_set_sBIT(png, pngInfo, &sbitChunk);
constexpr uint8_t SIZEOF_PIXEL = 4; // Each pixel is 4 bytes (RGBA @ 8 bits/component)
size_t const SIZEOF_ROW = options.reversedWidth * SIZEOF_PIXEL;
size_t const SIZEOF_ROW = options.reversedWidth * 8 * SIZEOF_PIXEL;
std::vector<uint8_t> tileRow(8 * SIZEOF_ROW, 0xFF); // Data for 8 rows of pixels
uint8_t * const rowPtrs[8] = {
&tileRow.data()[0 * SIZEOF_ROW + options.inputSlice[3]],
&tileRow.data()[1 * SIZEOF_ROW + options.inputSlice[3]],
&tileRow.data()[2 * SIZEOF_ROW + options.inputSlice[3]],
&tileRow.data()[3 * SIZEOF_ROW + options.inputSlice[3]],
&tileRow.data()[4 * SIZEOF_ROW + options.inputSlice[3]],
&tileRow.data()[5 * SIZEOF_ROW + options.inputSlice[3]],
&tileRow.data()[6 * SIZEOF_ROW + options.inputSlice[3]],
&tileRow.data()[7 * SIZEOF_ROW + options.inputSlice[3]],
&tileRow.data()[0 * SIZEOF_ROW], &tileRow.data()[1 * SIZEOF_ROW],
&tileRow.data()[2 * SIZEOF_ROW], &tileRow.data()[3 * SIZEOF_ROW],
&tileRow.data()[4 * SIZEOF_ROW], &tileRow.data()[5 * SIZEOF_ROW],
&tileRow.data()[6 * SIZEOF_ROW], &tileRow.data()[7 * SIZEOF_ROW],
};
auto const fillRows = [&png, &tileRow](size_t nbRows) {
for (size_t _ = 0; _ < nbRows; ++_) {
png_write_row(png, tileRow.data());
}
};
fillRows(options.inputSlice[0]);
for (size_t ty = 0; ty < height; ++ty) {
for (size_t tx = 0; tx < width; ++tx) {
size_t index = options.columnMajor ? ty + tx * width : ty * width + tx;
@@ -295,9 +285,6 @@ void reverse() {
// pointed-to data)
png_write_rows(png, const_cast<png_bytepp>(rowPtrs), 8);
}
// Clear the first row again for the function
std::fill(tileRow.begin(), tileRow.begin() + SIZEOF_ROW, 0xFF);
fillRows(options.inputSlice[2]);
// Finalize the write
png_write_end(png, pngInfo);

1
test/gfx/crop.flags Normal file
View File

@@ -0,0 +1 @@
-L 2,1:1,1

BIN
test/gfx/crop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 671 B

View File

@@ -404,7 +404,7 @@ int main(int argc, char **argv) {
char path[] = "../../rgbgfx", reverse_opt[] = "-r", out_opt[] = "-o",
out_file[] = "result.2bpp", pal_opt[] = "-p", pal_file[] = "result.pal",
attr_opt[] = "-a", attr_file[] = "result.attrmap", in_file[] = "result.png";
auto width_string = std::to_string(image0.getWidth());
auto width_string = std::to_string(image0.getWidth() / 8);
std::vector<char *> args = {
path, reverse_opt, width_string.data(), out_opt, out_file, pal_opt,
pal_file, attr_opt, attr_file, in_file};