Skip to content

Commit 5d60ef8

Browse files
committed
Post-processing range and stripped image detection
1 parent d976cf8 commit 5d60ef8

File tree

4 files changed

+106
-36
lines changed

4 files changed

+106
-36
lines changed

changelog.MD

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
## Version history
22

33
* ??/??/2026 v5.0
4-
* `PNG2MSX` uses the most common color as preferred background color instead of 0
5-
* -o option removed
4+
* `PNG2MSX` compression optimization integrated by default (-o option removed)
65

76
* 09/12/2025 v4.0
87
* -o option in `PNG2MSX`

readme.MD

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,19 +88,46 @@ Mirroring:
8888
Error control:
8989

9090
* `-il` ignore line on color collision
91+
9192
Continue processing the file even if collisions are found. Offending lines will be have 0x00 both as pattern and color.
93+
9294
Can be useful in combination with `-d` to check all the collisions at once.
9395

96+
PNG2MSX has to choose, for each line, which color is foreground and which one is background. By default, it makes a best effort to get the most natural pattern (i.e.: the one that most likely an human will choose) and the combination of patterns/colors that will get the best compression ratio.
97+
98+
Pattern generation algorithm:
99+
100+
* `-sa` auto-detect stripped images (default)
101+
102+
* `-sy` force image to be detected as stripped
103+
104+
* `-sn` force image to be detected as non-stripped
105+
106+
Some particular images (e.g.: images with stripped background) get a better compression ratio using a simpler algorithm. These three options control which algorithm will be used.
107+
94108
Pattern generation:
95109

96110
* `-hl` force higher color to be foreground
111+
97112
* `-lh` force lower color to be foreground
113+
98114
* `-ld` force lighter foreground, darker background
115+
99116
* `-dl` force darker foreground, lighter background
117+
100118
* `-f<0..7>` force bit &lt;n&gt; to be foreground (set) on patterns
119+
101120
* `-b<0..7>` force bit &lt;n&gt; to be background (reset) on patterns
102-
These four options allow some control on how patterns are created, and which color is foreground and which one is background.
103-
Can be useful if colors are going to be set programatically (e.g.: fonts colored with FILVRM) or to improve compression results.
121+
122+
These six options allow some control on how patterns are created, and which color is foreground and which one is background.
123+
124+
Can be useful if colors are going to be set programatically (e.g.: fonts colored with FILVRM).
125+
126+
* `-pf<0000>` post-process only the specified address range (from, hexadecimal)
127+
128+
* `-pt<ffff>` post-process only the specified address range (to, hexadecimal)
129+
130+
Allows the specified post-processing pattern generator to be applied only to an specific address range (for example, the font part of a charset) without alterations of the rest of the image.
104131

105132
NAMTBL generation:
106133

src/charset.c

Lines changed: 60 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,11 @@ extern int veryVerbose;
3636
const unsigned int brightness[] = { 0, 1, 5, 10, 2, 7, 3, 11, 6, 9, 12, 13, 4, 8, 14, 15 };
3737

3838
int patternMode(int isForeground, char bit);
39+
void postProcessRange(struct stCharsetProcessor *instance, char *argstring);
3940

4041
void charsetProcessorInitForBitmap(struct stCharsetProcessor *instance, struct stBitmap *bitmap);
4142
int readLine(struct stCharsetProcessor *instance, struct stLine *line, struct stBitmap *bitmap, int x, int y, struct stLine *previousLine);
42-
void postProcessLine(struct stCharsetProcessor *instance, struct stLine *line, struct stLine *previousLine);
43+
void postProcessLine(struct stCharsetProcessor *instance, int index, struct stLine *line, struct stLine *previousLine);
4344
void debugPostProcessLine(struct stCharsetProcessor *instance, struct stLine *from, struct stLine *to, struct stLine *previousLine, char *message);
4445
void negateAndSwap(struct stLine *line);
4546
int isLineEquals(struct stLine *line, struct stLine *other);
@@ -49,28 +50,32 @@ byte colorAtBit(struct stLine *line, char bit);
4950
/* Function bodies --------------------------------------------------------- */
5051

5152
// Supported arguments:
52-
// -il ignore line on color collision
53-
// -sa auto-detect stripped images (default)
54-
// -sy force image to be detected as stripped (simplified algorithm)
55-
// -sn force image to be detected as non-stripped (default algorithm)
56-
// -hl force higher color to be foreground
57-
// -lh force lower color to be foreground
58-
// -ld force lighter foreground, darker background
59-
// -dl force darker foreground, lighter background
60-
// -f<0..7> force bit <n> to be foreground (set) on patterns
61-
// -b<0..7> force bit <n> to be background (reset) on patterns
53+
// -il ignore line on color collision
54+
// -sa auto-detect stripped images (default)
55+
// -sy force image to be detected as stripped (simplified algorithm)
56+
// -sn force image to be detected as non-stripped (default algorithm)
57+
// -hl force higher color to be foreground
58+
// -lh force lower color to be foreground
59+
// -ld force lighter foreground, darker background
60+
// -dl force darker foreground, lighter background
61+
// -f<0..7> force bit <n> to be foreground (set) on patterns
62+
// -b<0..7> force bit <n> to be background (reset) on patterns
63+
// -pf<0000> post-process only the specified address range (from)
64+
// -pt<ffff> post-process only the specified address range (to)
6265
void charsetProcessorOptions() {
6366

6467
printf("\t-il\tignore line on color collision\n");
6568
printf("\t-sa\tauto-detect stripped images (default)\n");
66-
printf("\t-sy\tforce image to be detected as stripped (simplified algorithm)\n");
67-
printf("\t-sn\tforce image to be detected as non-stripped (default algorithm)\n");
69+
printf("\t-sy\tforce stripped image (forces using simplified algorithm)\n");
70+
printf("\t-sn\tforce non-stripped image\n");
6871
printf("\t-hl\tforce higher color to be foreground\n");
6972
printf("\t-lh\tforce lower color to be foreground\n");
7073
printf("\t-ld\tforce lighter foreground, darker background\n");
7174
printf("\t-dl\tforce darker foreground, lighter background\n");
7275
printf("\t-f<0..7>\tforce bit <n> to be foreground (set) on patterns\n");
7376
printf("\t-b<0..7>\tforce bit <n> to be background (reset) on patterns\n");
77+
printf("\t-pf<0000>\tpost-process only the specified address range (from)\n");
78+
printf("\t-pt<ffff>\tpost-process only the specified address range (to)\n");
7479
}
7580

7681
void charsetProcessorInit(struct stCharsetProcessor *this, int argc, char **argv) {
@@ -83,7 +88,7 @@ void charsetProcessorInit(struct stCharsetProcessor *this, int argc, char **argv
8388
// Read arguments
8489
int i;
8590
this->ignoreCollision = (argEquals(argc, argv, "-il") != -1);
86-
this->strippedMode =
91+
this->forceStrippedImage =
8792
(argEquals(argc, argv, "-sy") != -1) ? 1
8893
: (argEquals(argc, argv, "-sn") != -1) ? 0
8994
: -1;
@@ -95,6 +100,12 @@ void charsetProcessorInit(struct stCharsetProcessor *this, int argc, char **argv
95100
: (argEquals(argc, argv, "-ld") != -1) ? PATTERN_MODE_LIGHT_DARK
96101
: (argEquals(argc, argv, "-dl") != -1) ? PATTERN_MODE_DARK_LIGHT
97102
: PATTERN_MODE_UNSET;
103+
this->postProcessRangeFrom =
104+
((i = argStartsWith(argc, argv, "-pf", 4)) != -1) ? hexadecimalInt(&(argv[i][3]))
105+
: -1;
106+
this->postProcessRangeTo =
107+
((i = argStartsWith(argc, argv, "-pt", 4)) != -1) ? hexadecimalInt(&(argv[i][3]))
108+
: -1;
98109
}
99110

100111
int charsetProcessorRead(struct stCharsetProcessor *this, struct stCharset *charset, struct stBitmap *bitmap) {
@@ -104,9 +115,9 @@ int charsetProcessorRead(struct stCharsetProcessor *this, struct stCharset *char
104115
if (verbose)
105116
printf("Detected preferred background color = %1x, image is %s\n",
106117
this->preferredBackground,
107-
this->stripped
108-
? (this->strippedMode == 1 ? "stripped (forced)" : "stripped")
109-
: (this->strippedMode == 0 ? "not stripped (forced)" : "not stripped"));
118+
this->isStrippedImage
119+
? (this->forceStrippedImage == 1 ? "stripped (forced)" : "stripped")
120+
: (this->forceStrippedImage == 0 ? "not stripped (forced)" : "not stripped"));
110121

111122
// Allocate space for the blocks
112123
charset->blockCount = ((int) (bitmap->width / TILE_WIDTH)) * ((int) (bitmap->height / TILE_HEIGHT));
@@ -146,7 +157,7 @@ void charsetProcessorPostProcess(struct stCharsetProcessor *this, struct stChars
146157
int j;
147158
struct stLine *itLine;
148159
for (j = 0, itLine = itBlock->line; j < TILE_HEIGHT; j++, previousLine = itLine, itLine++) {
149-
postProcessLine(this, itLine, previousLine);
160+
postProcessLine(this, i * TILE_HEIGHT + j, itLine, previousLine);
150161
}
151162
}
152163
}
@@ -262,9 +273,28 @@ void charsetProcessorInitForBitmap(struct stCharsetProcessor *this, struct stBit
262273
}
263274

264275
this->preferredBackground = mostFrequentColor;
265-
this->stripped =
266-
this->strippedMode == -1 ? ((mostFrequentColor == mostFrequentEvenColor) != (mostFrequentColor == mostFrequentOddColor))
267-
: this->strippedMode;
276+
277+
// Stripped image detection -----------------------------------------------
278+
279+
// Forced to yes/no?
280+
if (this->forceStrippedImage != -1) {
281+
this->isStrippedImage = this->forceStrippedImage;
282+
return;
283+
}
284+
285+
// Even/odd background is reference background and the other one is not?
286+
if ((mostFrequentColor == mostFrequentEvenColor) == (mostFrequentColor == mostFrequentOddColor)) {
287+
this->isStrippedImage = 0;
288+
return;
289+
}
290+
291+
// Enough excess of reference background representation in even/odd background (>= 5%)?
292+
unsigned int excess = (mostFrequentColor == mostFrequentEvenColor)
293+
? abs((int) maxEvenCount * 2 - (int) maxCount) / 2
294+
: abs((int) maxOddCount * 2 - (int) maxCount) / 2;
295+
unsigned int threshold = 5 * bitmap->width * bitmap->height / 100;
296+
this->isStrippedImage = excess >= threshold;
297+
return;
268298
}
269299

270300
int readLine(struct stCharsetProcessor *this, struct stLine *line, struct stBitmap *bitmap, int x, int y, __attribute__((unused)) struct stLine *previousLine) {
@@ -321,12 +351,18 @@ int readLine(struct stCharsetProcessor *this, struct stLine *line, struct stBitm
321351
return 1;
322352
}
323353

324-
void postProcessLine(struct stCharsetProcessor *this, struct stLine *line, struct stLine *previousLine) {
354+
void postProcessLine(struct stCharsetProcessor *this, int index, struct stLine *line, struct stLine *previousLine) {
325355

326356
struct stLine copyOfLine = { line->pattern, line->color };
327357

358+
int patternMode =
359+
((this->postProcessRangeFrom == -1) || (this->postProcessRangeFrom <= index))
360+
&& ((this->postProcessRangeTo == -1) || (this->postProcessRangeTo >= index))
361+
? this->patternMode
362+
: PATTERN_MODE_UNSET;
363+
328364
// Apply current pattern mode
329-
switch (this->patternMode & PATTERN_MODE_MASK) {
365+
switch (patternMode & PATTERN_MODE_MASK) {
330366
case PATTERN_MODE_FOREGROUND:
331367
// Force foreground bit
332368
if (!(line->pattern & (1 << (this->patternMode & 0x07)))) {
@@ -410,7 +446,7 @@ void postProcessLine(struct stCharsetProcessor *this, struct stLine *line, struc
410446
const int singleColor = isLineSingleColor(line);
411447
if (singleColor != -1) {
412448

413-
if (this->stripped) {
449+
if (this->isStrippedImage) {
414450
// This seems to yield better compression ratios than more complex algorithms
415451
// for stripped images that have rapidly changing either CHRLTBL or CLRTBL bytes
416452
// (this should be no-op)

src/charset.h

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,23 +39,31 @@ struct stCharset {
3939
struct stCharsetProcessor {
4040
// Arguments
4141
int ignoreCollision;
42+
int forceStrippedImage;
4243
int patternMode;
43-
int strippedMode;
44+
int postProcessRangeFrom;
45+
int postProcessRangeTo;
4446

4547
// State
4648
byte preferredBackground;
47-
int stripped;
49+
int isStrippedImage;
4850
};
4951

5052
/* Function prototypes ----------------------------------------------------- */
5153

5254
// Supported arguments:
53-
// -il ignore line on color collision
54-
// -hl force higher color to be foreground
55-
// -lh force lower color to be foreground
56-
// -f<0..7> force bit <n> to be foreground (set) on patterns
57-
// -b<0..7> force bit <n> to be background (reset) on patterns
58-
// -o attempt to optimize CLRTBL for compression
55+
// -il ignore line on color collision
56+
// -sa auto-detect stripped images (default)
57+
// -sy force image to be detected as stripped (simplified algorithm)
58+
// -sn force image to be detected as non-stripped (default algorithm)
59+
// -hl force higher color to be foreground
60+
// -lh force lower color to be foreground
61+
// -ld force lighter foreground, darker background
62+
// -dl force darker foreground, lighter background
63+
// -f<0..7> force bit <n> to be foreground (set) on patterns
64+
// -b<0..7> force bit <n> to be background (reset) on patterns
65+
// -pf<0000> post-process only the specified address range (from)
66+
// -pt<ffff> post-process only the specified address range (to)
5967
void charsetProcessorOptions();
6068

6169
void charsetProcessorInit(struct stCharsetProcessor *instance, int argc, char **argv);

0 commit comments

Comments
 (0)