diff --git a/include/gfx/main.hpp b/include/gfx/main.hpp index f5e16033..34270388 100644 --- a/include/gfx/main.hpp +++ b/include/gfx/main.hpp @@ -13,9 +13,6 @@ #include "gfx/rgba.hpp" struct Options { - uint16_t reversedWidth = 0; // -r, in tiles - bool reverse() const { return reversedWidth != 0; } - bool useColorCurve = false; // -C bool allowMirroring = false; // -m bool allowDedup = false; // -u @@ -42,6 +39,7 @@ struct Options { std::string output{}; // -o std::string palettes{}; // -p, -P std::string palmap{}; // -q, -Q + uint16_t reversedWidth = 0; // -r, in tiles uint8_t nbColorsPerPal = 0; // -s; 0 means "auto" = 1 << bitDepth; std::string tilemap{}; // -t, -T uint64_t trim = 0; // -x diff --git a/man/rgbgfx.1 b/man/rgbgfx.1 index bc1e301b..15b69d7a 100644 --- a/man/rgbgfx.1 +++ b/man/rgbgfx.1 @@ -22,7 +22,7 @@ .Op Fl o Ar out_file .Op Fl p Ar pal_file | Fl P .Op Fl q Ar pal_map | Fl Q -.Op Fl r Ar stride +.Op Fl r Ar width .Op Fl s Ar nb_colors .Op Fl t Ar tilemap | Fl T .Op Fl x Ar quantity @@ -246,6 +246,9 @@ below for details. .Pp .Ar width is the width of the image to generate, in tiles. +.Fl r 0 +chooses a width to make the image as square as possible. +This is useful if you do not know the original width. .It Fl s Ar nb_colors , Fl \-palette-size Ar nb_colors Specify how many colors each palette contains, including the transparent one if any. .Ar nb_colors diff --git a/src/gfx/main.cpp b/src/gfx/main.cpp index 385b08f2..a3793a8f 100644 --- a/src/gfx/main.cpp +++ b/src/gfx/main.cpp @@ -36,6 +36,7 @@ static struct LocalOptions { bool autoPalettes; bool autoPalmap; bool groupOutputs; + bool reverse; } localOptions; static uintmax_t nbErrors; @@ -535,13 +536,11 @@ static char *parseArgv(int argc, char *argv[]) { options.palmap = musl_optarg; break; case 'r': + localOptions.reverse = true; options.reversedWidth = parseNumber(arg, "Reversed image stride"); if (*arg != '\0') { error("Reversed image stride (-r) must be a valid number, not \"%s\"", musl_optarg); } - if (options.reversedWidth == 0) { - error("Reversed image stride (-r) may not be 0!"); - } break; case 's': options.nbColorsPerPal = parseNumber(arg, "Number of colors per palette", 4); @@ -830,13 +829,13 @@ int main(int argc, char *argv[]) { } if (!options.input.empty()) { - if (options.reverse()) { + if (localOptions.reverse) { reverse(); } else { process(); } } else if (!options.palettes.empty() && options.palSpecType == Options::EXPLICIT - && !options.reverse()) { + && !localOptions.reverse) { processPalettes(); } else { fputs("FATAL: No input image specified\n", stderr); diff --git a/src/gfx/reverse.cpp b/src/gfx/reverse.cpp index 398f0d83..e63b255d 100644 --- a/src/gfx/reverse.cpp +++ b/src/gfx/reverse.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -159,6 +160,17 @@ void reverse() { } size_t width = options.reversedWidth, height; // In tiles + if (width == 0) { + // Pick the smallest width that will result in a landscape-aspect rectangular image. + // Thus a prime number of tiles will result in a horizontal row. + // This avoids redundancy with `-r 1` which results in a vertical column. + width = (size_t)ceil(sqrt(nbTileInstances)); + for (; width < nbTileInstances; ++width) { + if (nbTileInstances % width == 0) + break; + } + options.verbosePrint(Options::VERB_INTERM, "Picked reversing width of %zu tiles\n", width); + } if (nbTileInstances % width != 0) { fatal( "Total number of tiles read (%zu) cannot be divided by image width (%zu tiles)", @@ -219,11 +231,9 @@ void reverse() { if (options.palSpecType == Options::EXPLICIT && palettes != options.palSpec) { warning("Colors in the palette file do not match those specified with `-c`!"); - // [#111111ff, #222222ff, #333333ff, #444444ff] [#111111ff, #222222ff, #333333ff, #444444ff] + // This spacing aligns "...versus with `-c`" above the column of `-c` palettes fputs("Colors specified in the palette file: ...versus with `-c`:\n", stderr); - for (size_t i = 0; - i < palettes.size() && i < options.palSpec.size(); - ++i) { + for (size_t i = 0; i < palettes.size() && i < options.palSpec.size(); ++i) { if (i < palettes.size()) { printPalette(palettes[i]); } else { @@ -336,7 +346,7 @@ void reverse() { png_set_IHDR( png, pngInfo, - options.reversedWidth * 8, + width * 8, height * 8, 8, PNG_COLOR_TYPE_RGB_ALPHA, @@ -353,8 +363,8 @@ void reverse() { sbitChunk.alpha = 1; 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 * 8 * SIZEOF_PIXEL; + constexpr uint8_t SIZEOF_TILE = 4 * 8; // 4 bytes/pixel (RGBA @ 8 bits/channel) * 8 pixels/tile + size_t const SIZEOF_ROW = width * SIZEOF_TILE; std::vector tileRow(8 * SIZEOF_ROW, 0xFF); // Data for 8 rows of pixels uint8_t * const rowPtrs[8] = { &tileRow.data()[0 * SIZEOF_ROW], @@ -415,7 +425,7 @@ void reverse() { bitplane0 = flipTable[bitplane0]; bitplane1 = flipTable[bitplane1]; } - uint8_t *ptr = &rowPtrs[y][tx * 8 * SIZEOF_PIXEL]; + uint8_t *ptr = &rowPtrs[y][tx * SIZEOF_TILE]; for (uint8_t x = 0; x < 8; ++x) { uint8_t bit0 = bitplane0 & 0x80, bit1 = bitplane1 & 0x80; Rgba const &pixel = *palette[bit0 >> 7 | bit1 >> 6];