mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Implement grayscale DMG palette specs (#1709)
This commit is contained in:
@@ -29,8 +29,10 @@ struct Options {
|
|||||||
NO_SPEC,
|
NO_SPEC,
|
||||||
EXPLICIT,
|
EXPLICIT,
|
||||||
EMBEDDED,
|
EMBEDDED,
|
||||||
|
DMG,
|
||||||
} palSpecType = NO_SPEC; // -c
|
} palSpecType = NO_SPEC; // -c
|
||||||
std::vector<std::array<std::optional<Rgba>, 4>> palSpec{};
|
std::vector<std::array<std::optional<Rgba>, 4>> palSpec{};
|
||||||
|
uint8_t palSpecDmg = 0;
|
||||||
uint8_t bitDepth = 2; // -d
|
uint8_t bitDepth = 2; // -d
|
||||||
std::string inputTileset{}; // -i
|
std::string inputTileset{}; // -i
|
||||||
struct {
|
struct {
|
||||||
@@ -65,6 +67,12 @@ struct Options {
|
|||||||
|
|
||||||
mutable bool hasTransparentPixels = false;
|
mutable bool hasTransparentPixels = false;
|
||||||
uint8_t maxOpaqueColors() const { return nbColorsPerPal - hasTransparentPixels; }
|
uint8_t maxOpaqueColors() const { return nbColorsPerPal - hasTransparentPixels; }
|
||||||
|
|
||||||
|
uint8_t dmgColors[4] = {};
|
||||||
|
uint8_t dmgValue(uint8_t i) const {
|
||||||
|
assume(i < 4);
|
||||||
|
return (palSpecDmg >> (2 * i)) & 0b11;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Options options;
|
extern Options options;
|
||||||
@@ -119,27 +127,4 @@ static constexpr auto flipTable = ([]() constexpr {
|
|||||||
return table;
|
return table;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// Parsing helpers.
|
|
||||||
|
|
||||||
static constexpr uint8_t nibble(char c) {
|
|
||||||
if (c >= 'a') {
|
|
||||||
assume(c <= 'f');
|
|
||||||
return c - 'a' + 10;
|
|
||||||
} else if (c >= 'A') {
|
|
||||||
assume(c <= 'F');
|
|
||||||
return c - 'A' + 10;
|
|
||||||
} else {
|
|
||||||
assume(c >= '0' && c <= '9');
|
|
||||||
return c - '0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr uint8_t toHex(char c1, char c2) {
|
|
||||||
return nibble(c1) * 16 + nibble(c2);
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr uint8_t singleToHex(char c) {
|
|
||||||
return toHex(c, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // RGBDS_GFX_MAIN_HPP
|
#endif // RGBDS_GFX_MAIN_HPP
|
||||||
|
|||||||
@@ -3,7 +3,10 @@
|
|||||||
#ifndef RGBDS_GFX_PAL_SPEC_HPP
|
#ifndef RGBDS_GFX_PAL_SPEC_HPP
|
||||||
#define RGBDS_GFX_PAL_SPEC_HPP
|
#define RGBDS_GFX_PAL_SPEC_HPP
|
||||||
|
|
||||||
void parseInlinePalSpec(char const * const arg);
|
void parseInlinePalSpec(char const * const rawArg);
|
||||||
void parseExternalPalSpec(char const *arg);
|
void parseExternalPalSpec(char const *arg);
|
||||||
|
void parseDmgPalSpec(char const * const rawArg);
|
||||||
|
|
||||||
|
void parseBackgroundPalSpec(char const *arg);
|
||||||
|
|
||||||
#endif // RGBDS_GFX_PAL_SPEC_HPP
|
#endif // RGBDS_GFX_PAL_SPEC_HPP
|
||||||
|
|||||||
20
man/rgbgfx.1
20
man/rgbgfx.1
@@ -159,6 +159,24 @@ is the case-insensitive word
|
|||||||
then the first four colors of the input PNG's embedded palette are used.
|
then the first four colors of the input PNG's embedded palette are used.
|
||||||
It is an error if the PNG is not indexed, or if colors other than these 4 are used.
|
It is an error if the PNG is not indexed, or if colors other than these 4 are used.
|
||||||
.Pq This is different from the default behavior of indexed PNGs, as then unused entries in the embedded palette are ignored, whereas they are not with Fl c Cm embedded .
|
.Pq This is different from the default behavior of indexed PNGs, as then unused entries in the embedded palette are ignored, whereas they are not with Fl c Cm embedded .
|
||||||
|
.It Sy DMG palette spec
|
||||||
|
If
|
||||||
|
.Ar pal_spec
|
||||||
|
starts with case-insensitive
|
||||||
|
.Cm dmg= ,
|
||||||
|
then the following two-digit hexadecimal number specifies four grayscale DMG color indexes.
|
||||||
|
The number functions like the DMG's $FF47
|
||||||
|
.Sy BGP
|
||||||
|
register
|
||||||
|
(see
|
||||||
|
.Lk https://gbdev.io/pandocs/Palettes.html Pan Docs
|
||||||
|
for more information):
|
||||||
|
the low two bits 0-1 specify which gray shade goes in color index 0,
|
||||||
|
the next two bits 2-3 specify which gray shade goes in color index 1,
|
||||||
|
and so on.
|
||||||
|
Gray shade 0 is the lightest (white), 3 is the darkest (black).
|
||||||
|
The same gray shade cannot go in two color indexes.
|
||||||
|
To specify a DMG palette, the input PNG must have all its colors in shades of gray, without any transparent colors.
|
||||||
.It Sy external palette spec
|
.It Sy external palette spec
|
||||||
Otherwise,
|
Otherwise,
|
||||||
.Ar pal_spec
|
.Ar pal_spec
|
||||||
@@ -528,6 +546,8 @@ Otherwise, if the PNG only contains shades of gray, they will be categorized int
|
|||||||
.Dq bins
|
.Dq bins
|
||||||
as there are colors per palette, and the palette is set to these bins.
|
as there are colors per palette, and the palette is set to these bins.
|
||||||
The darkest gray will end up in bin #0, and so on; note that this is the opposite of the RGB method below.
|
The darkest gray will end up in bin #0, and so on; note that this is the opposite of the RGB method below.
|
||||||
|
This is equivalent to having specified a DMG palette of
|
||||||
|
.Fl c Cm dmg=E4 .
|
||||||
If two distinct grays end up in the same bin, the RGB method is used instead.
|
If two distinct grays end up in the same bin, the RGB method is used instead.
|
||||||
.Pp
|
.Pp
|
||||||
Be careful that
|
Be careful that
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
#include "extern/getopt.hpp"
|
#include "extern/getopt.hpp"
|
||||||
#include "file.hpp"
|
#include "file.hpp"
|
||||||
#include "helpers.hpp" // assume
|
|
||||||
#include "platform.hpp"
|
#include "platform.hpp"
|
||||||
#include "version.hpp"
|
#include "version.hpp"
|
||||||
|
|
||||||
@@ -354,7 +353,6 @@ static char *parseArgv(int argc, char *argv[]) {
|
|||||||
for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, nullptr)) != -1;) {
|
for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, nullptr)) != -1;) {
|
||||||
char *arg = musl_optarg; // Make a copy for scanning
|
char *arg = musl_optarg; // Make a copy for scanning
|
||||||
uint16_t number;
|
uint16_t number;
|
||||||
size_t size;
|
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
case 'A':
|
case 'A':
|
||||||
localOptions.autoAttrmap = true;
|
localOptions.autoAttrmap = true;
|
||||||
@@ -367,41 +365,7 @@ static char *parseArgv(int argc, char *argv[]) {
|
|||||||
options.attrmap = musl_optarg;
|
options.attrmap = musl_optarg;
|
||||||
break;
|
break;
|
||||||
case 'B':
|
case 'B':
|
||||||
if (strcasecmp(musl_optarg, "transparent") == 0) {
|
parseBackgroundPalSpec(musl_optarg);
|
||||||
options.bgColor = Rgba(0x00, 0x00, 0x00, 0x00);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (musl_optarg[0] != '#') {
|
|
||||||
error("Background color specification must be `#rgb`, `#rrggbb`, or `transparent`");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
size = strspn(&musl_optarg[1], "0123456789ABCDEFabcdef");
|
|
||||||
switch (size) {
|
|
||||||
case 3:
|
|
||||||
options.bgColor = Rgba(
|
|
||||||
singleToHex(musl_optarg[1]),
|
|
||||||
singleToHex(musl_optarg[2]),
|
|
||||||
singleToHex(musl_optarg[3]),
|
|
||||||
0xFF
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
options.bgColor = Rgba(
|
|
||||||
toHex(musl_optarg[1], musl_optarg[2]),
|
|
||||||
toHex(musl_optarg[3], musl_optarg[4]),
|
|
||||||
toHex(musl_optarg[5], musl_optarg[6]),
|
|
||||||
0xFF
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
error("Unknown background color specification \"%s\"", musl_optarg);
|
|
||||||
}
|
|
||||||
if (musl_optarg[size + 1] != '\0') {
|
|
||||||
error(
|
|
||||||
"Unexpected text \"%s\" after background color specification",
|
|
||||||
&musl_optarg[size + 1]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 'b':
|
case 'b':
|
||||||
number = parseNumber(arg, "Bank 0 base tile ID", 0);
|
number = parseNumber(arg, "Bank 0 base tile ID", 0);
|
||||||
@@ -442,18 +406,18 @@ static char *parseArgv(int argc, char *argv[]) {
|
|||||||
options.useColorCurve = true;
|
options.useColorCurve = true;
|
||||||
break;
|
break;
|
||||||
case 'c':
|
case 'c':
|
||||||
|
localOptions.externalPalSpec = nullptr; // Allow overriding a previous pal spec
|
||||||
if (musl_optarg[0] == '#') {
|
if (musl_optarg[0] == '#') {
|
||||||
options.palSpecType = Options::EXPLICIT;
|
options.palSpecType = Options::EXPLICIT;
|
||||||
parseInlinePalSpec(musl_optarg);
|
parseInlinePalSpec(musl_optarg);
|
||||||
} else if (strcasecmp(musl_optarg, "embedded") == 0) {
|
} else if (strcasecmp(musl_optarg, "embedded") == 0) {
|
||||||
// Use PLTE, error out if missing
|
// Use PLTE, error out if missing
|
||||||
options.palSpecType = Options::EMBEDDED;
|
options.palSpecType = Options::EMBEDDED;
|
||||||
|
} else if (strncasecmp(musl_optarg, "dmg=", literal_strlen("dmg=")) == 0) {
|
||||||
|
options.palSpecType = Options::DMG;
|
||||||
|
parseDmgPalSpec(&musl_optarg[literal_strlen("dmg=")]);
|
||||||
} else {
|
} else {
|
||||||
options.palSpecType = Options::EXPLICIT;
|
options.palSpecType = Options::EXPLICIT;
|
||||||
// Can't parse the file yet, as "flat" color collections need to know the palette
|
|
||||||
// size to be split; thus, we defer that.
|
|
||||||
// If a following `-c` overrides a previous one, the `fmt` part of an overridden
|
|
||||||
// external palette spec will not be validated, but I guess that's okay.
|
|
||||||
localOptions.externalPalSpec = musl_optarg;
|
localOptions.externalPalSpec = musl_optarg;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -877,6 +841,8 @@ int main(int argc, char *argv[]) {
|
|||||||
return "Explicit";
|
return "Explicit";
|
||||||
case Options::EMBEDDED:
|
case Options::EMBEDDED:
|
||||||
return "Embedded";
|
return "Embedded";
|
||||||
|
case Options::DMG:
|
||||||
|
return "DMG";
|
||||||
}
|
}
|
||||||
return "???";
|
return "???";
|
||||||
}());
|
}());
|
||||||
|
|||||||
@@ -299,7 +299,7 @@ static void decant(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
auto attrs = from.begin();
|
auto attrs = from.begin();
|
||||||
std::advance(attrs, (iter - processed.begin()));
|
std::advance(attrs, iter - processed.begin());
|
||||||
|
|
||||||
// Build up the "component"...
|
// Build up the "component"...
|
||||||
colors.clear();
|
colors.clear();
|
||||||
|
|||||||
@@ -28,6 +28,27 @@ static void skipWhitespace(Str const &str, size_t &pos) {
|
|||||||
pos = std::min(str.find_first_not_of(" \t"sv, pos), str.length());
|
pos = std::min(str.find_first_not_of(" \t"sv, pos), str.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr uint8_t nibble(char c) {
|
||||||
|
if (c >= 'a') {
|
||||||
|
assume(c <= 'f');
|
||||||
|
return c - 'a' + 10;
|
||||||
|
} else if (c >= 'A') {
|
||||||
|
assume(c <= 'F');
|
||||||
|
return c - 'A' + 10;
|
||||||
|
} else {
|
||||||
|
assume(c >= '0' && c <= '9');
|
||||||
|
return c - '0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr uint8_t toHex(char c1, char c2) {
|
||||||
|
return nibble(c1) * 16 + nibble(c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr uint8_t singleToHex(char c) {
|
||||||
|
return toHex(c, c);
|
||||||
|
}
|
||||||
|
|
||||||
void parseInlinePalSpec(char const * const rawArg) {
|
void parseInlinePalSpec(char const * const rawArg) {
|
||||||
// List of #rrggbb/#rgb colors (or #none); comma-separated.
|
// List of #rrggbb/#rgb colors (or #none); comma-separated.
|
||||||
// Palettes are separated by colons.
|
// Palettes are separated by colons.
|
||||||
@@ -595,3 +616,61 @@ void parseExternalPalSpec(char const *arg) {
|
|||||||
|
|
||||||
std::get<1> (*iter)(file);
|
std::get<1> (*iter)(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void parseDmgPalSpec(char const * const rawArg) {
|
||||||
|
// Two hex digit DMG palette spec
|
||||||
|
|
||||||
|
std::string_view arg(rawArg);
|
||||||
|
|
||||||
|
if (arg.length() != 2
|
||||||
|
|| arg.find_first_not_of("0123456789ABCDEFabcdef"sv) != std::string_view::npos) {
|
||||||
|
error("Unknown DMG palette specification \"%s\"", rawArg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
options.palSpecDmg = toHex(arg[0], arg[1]);
|
||||||
|
|
||||||
|
// Map gray shades to their DMG color indexes for fast lookup by `Rgba::grayIndex`
|
||||||
|
for (uint8_t i = 0; i < 4; ++i) {
|
||||||
|
options.dmgColors[options.dmgValue(i)] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate that DMG palette spec does not have conflicting colors
|
||||||
|
for (uint8_t i = 0; i < 3; ++i) {
|
||||||
|
for (uint8_t j = i + 1; j < 4; ++j) {
|
||||||
|
if (options.dmgValue(i) == options.dmgValue(j)) {
|
||||||
|
error("DMG palette specification maps two gray shades to the same color index");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseBackgroundPalSpec(char const *arg) {
|
||||||
|
if (strcasecmp(arg, "transparent") == 0) {
|
||||||
|
options.bgColor = Rgba(0x00, 0x00, 0x00, 0x00);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg[0] != '#') {
|
||||||
|
error("Background color specification must be `#rgb`, `#rrggbb`, or `transparent`");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size = strspn(&arg[1], "0123456789ABCDEFabcdef");
|
||||||
|
switch (size) {
|
||||||
|
case 3:
|
||||||
|
options.bgColor = Rgba(singleToHex(arg[1]), singleToHex(arg[2]), singleToHex(arg[3]), 0xFF);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
options.bgColor =
|
||||||
|
Rgba(toHex(arg[1], arg[2]), toHex(arg[3], arg[4]), toHex(arg[5], arg[6]), 0xFF);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error("Unknown background color specification \"%s\"", arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg[size + 1] != '\0') {
|
||||||
|
error("Unexpected text \"%s\" after background color specification", &arg[size + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -586,8 +586,10 @@ static std::tuple<DefaultInitVec<size_t>, std::vector<Palette>>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// "Sort" colors in the generated palettes, see the man page for the flowchart
|
// "Sort" colors in the generated palettes, see the man page for the flowchart
|
||||||
auto [embPalSize, embPalRGB, embPalAlphaSize, embPalAlpha] = png.getEmbeddedPal();
|
if (options.palSpecType == Options::DMG) {
|
||||||
if (embPalRGB != nullptr) {
|
sortGrayscale(palettes, png.getColors().raw());
|
||||||
|
} else if (auto [embPalSize, embPalRGB, embPalAlphaSize, embPalAlpha] = png.getEmbeddedPal();
|
||||||
|
embPalRGB != nullptr) {
|
||||||
sortIndexed(palettes, embPalSize, embPalRGB, embPalAlphaSize, embPalAlpha);
|
sortIndexed(palettes, embPalSize, embPalRGB, embPalAlphaSize, embPalAlpha);
|
||||||
} else if (png.isSuitableForGrayscale()) {
|
} else if (png.isSuitableForGrayscale()) {
|
||||||
sortGrayscale(palettes, png.getColors().raw());
|
sortGrayscale(palettes, png.getColors().raw());
|
||||||
@@ -1139,6 +1141,18 @@ void process() {
|
|||||||
}
|
}
|
||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
|
|
||||||
|
if (options.palSpecType == Options::DMG) {
|
||||||
|
if (options.hasTransparentPixels) {
|
||||||
|
fatal(
|
||||||
|
"Image contains transparent pixels, not compatible with a DMG palette specification"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!png.isSuitableForGrayscale()) {
|
||||||
|
fatal("Image contains too many or non-gray colors, not compatible with a DMG palette "
|
||||||
|
"specification");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Now, iterate through the tiles, generating proto-palettes as we go
|
// Now, iterate through the tiles, generating proto-palettes as we go
|
||||||
// We do this unconditionally because this performs the image validation (which we want to
|
// We do this unconditionally because this performs the image validation (which we want to
|
||||||
// perform even if no output is requested), and because it's necessary to generate any
|
// perform even if no output is requested), and because it's necessary to generate any
|
||||||
@@ -1248,7 +1262,8 @@ continue_visiting_tiles:;
|
|||||||
if (options.palSpecType == Options::EMBEDDED) {
|
if (options.palSpecType == Options::EMBEDDED) {
|
||||||
generatePalSpec(png);
|
generatePalSpec(png);
|
||||||
}
|
}
|
||||||
auto [mappings, palettes] = options.palSpecType == Options::NO_SPEC
|
auto [mappings, palettes] =
|
||||||
|
options.palSpecType == Options::NO_SPEC || options.palSpecType == Options::DMG
|
||||||
? generatePalettes(protoPalettes, png)
|
? generatePalettes(protoPalettes, png)
|
||||||
: makePalsAsSpecified(protoPalettes);
|
: makePalsAsSpecified(protoPalettes);
|
||||||
outputPalettes(palettes);
|
outputPalettes(palettes);
|
||||||
|
|||||||
@@ -195,10 +195,13 @@ void reverse() {
|
|||||||
Options::VERB_INTERM, "Reversed image dimensions: %zux%zu tiles\n", width, height
|
Options::VERB_INTERM, "Reversed image dimensions: %zux%zu tiles\n", width, height
|
||||||
);
|
);
|
||||||
|
|
||||||
std::vector<std::array<std::optional<Rgba>, 4>> palettes{
|
Rgba const grayColors[4] = {
|
||||||
{Rgba(0xFFFFFFFF), Rgba(0xAAAAAAFF), Rgba(0x555555FF), Rgba(0x000000FF)}
|
Rgba(0xFFFFFFFF), Rgba(0xAAAAAAFF), Rgba(0x555555FF), Rgba(0x000000FF)
|
||||||
};
|
};
|
||||||
// If a palette file is used as input, it overrides the default colors.
|
std::vector<std::array<std::optional<Rgba>, 4>> palettes{
|
||||||
|
{grayColors[0], grayColors[1], grayColors[2], grayColors[3]}
|
||||||
|
};
|
||||||
|
// If a palette file or palette spec is used as input, it overrides the default colors.
|
||||||
if (!options.palettes.empty()) {
|
if (!options.palettes.empty()) {
|
||||||
File file;
|
File file;
|
||||||
if (!file.open(options.palettes, std::ios::in | std::ios::binary)) {
|
if (!file.open(options.palettes, std::ios::in | std::ios::binary)) {
|
||||||
@@ -255,6 +258,10 @@ void reverse() {
|
|||||||
putc('\n', stderr);
|
putc('\n', stderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (options.palSpecType == Options::DMG) {
|
||||||
|
for (size_t i = 0; i < palettes[0].size(); ++i) {
|
||||||
|
palettes[0][i] = grayColors[options.dmgValue(i)];
|
||||||
|
}
|
||||||
} else if (options.palSpecType == Options::EMBEDDED) {
|
} else if (options.palSpecType == Options::EMBEDDED) {
|
||||||
warning("An embedded palette was requested, but no palette file was specified; ignoring "
|
warning("An embedded palette was requested, but no palette file was specified; ignoring "
|
||||||
"request.");
|
"request.");
|
||||||
|
|||||||
@@ -58,7 +58,15 @@ uint16_t Rgba::cgbColor() const {
|
|||||||
|
|
||||||
uint8_t Rgba::grayIndex() const {
|
uint8_t Rgba::grayIndex() const {
|
||||||
assume(isGray());
|
assume(isGray());
|
||||||
// Convert from 0..<256 to hasTransparentPixels..<nbColorsPerPal
|
// 2bpp shades are inverted from RGB PNG; %00 = white, %11 = black
|
||||||
// Note that `maxOpaqueColors()` already takes `hasTransparentPixels` into account
|
uint8_t gray = 255 - red;
|
||||||
return (255 - red) * options.maxOpaqueColors() / 256 + options.hasTransparentPixels;
|
if (options.palSpecType == Options::DMG) {
|
||||||
|
assume(!options.hasTransparentPixels);
|
||||||
|
// Reduce gray shade from 0..<256 to 0..<4, then map to color index,
|
||||||
|
// then reduce to 0..<nbColorsPerPal
|
||||||
|
return options.dmgColors[gray * 4 / 256] * options.nbColorsPerPal / 4;
|
||||||
|
}
|
||||||
|
// Reduce gray shade from 0..<256 to hasTransparentPixels..<nbColorsPerPal
|
||||||
|
// Note that `maxOpaqueColors()` already takes `hasTransparentPixels` into account
|
||||||
|
return gray * options.maxOpaqueColors() / 256 + options.hasTransparentPixels;
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
test/gfx/dmg_1bit_round_trip.1bpp
Normal file
BIN
test/gfx/dmg_1bit_round_trip.1bpp
Normal file
Binary file not shown.
2
test/gfx/dmg_1bit_round_trip.flags
Normal file
2
test/gfx/dmg_1bit_round_trip.flags
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
-d 1
|
||||||
|
-c dmg=1b
|
||||||
2
test/gfx/dmg_2bit.flags
Normal file
2
test/gfx/dmg_2bit.flags
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
-c nonexistent.gpl
|
||||||
|
-c DMG=E4
|
||||||
BIN
test/gfx/dmg_2bit.out.2bpp
Normal file
BIN
test/gfx/dmg_2bit.out.2bpp
Normal file
Binary file not shown.
BIN
test/gfx/dmg_2bit.out.pal
Normal file
BIN
test/gfx/dmg_2bit.out.pal
Normal file
Binary file not shown.
BIN
test/gfx/dmg_2bit.png
Normal file
BIN
test/gfx/dmg_2bit.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 79 B |
2
test/gfx/dmg_plte.flags
Normal file
2
test/gfx/dmg_plte.flags
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
-c embedded
|
||||||
|
-c DMG=E4
|
||||||
BIN
test/gfx/dmg_plte.out.2bpp
Normal file
BIN
test/gfx/dmg_plte.out.2bpp
Normal file
Binary file not shown.
BIN
test/gfx/dmg_plte.out.pal
Normal file
BIN
test/gfx/dmg_plte.out.pal
Normal file
Binary file not shown.
BIN
test/gfx/dmg_plte.png
Normal file
BIN
test/gfx/dmg_plte.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 118 B |
BIN
test/gfx/dmg_round_trip.2bpp
Normal file
BIN
test/gfx/dmg_round_trip.2bpp
Normal file
Binary file not shown.
1
test/gfx/dmg_round_trip.flags
Normal file
1
test/gfx/dmg_round_trip.flags
Normal file
@@ -0,0 +1 @@
|
|||||||
|
-c dmg=72
|
||||||
Reference in New Issue
Block a user