huffman: small fixes

* write correct files/image sizes to header
* eol/rtc funcs in huffman.c
* align rtc on byte boundary
* doc update
* bump version to 1.6.2
This commit is contained in:
Rupert
2024-11-15 12:36:47 +01:00
parent f6c7b97411
commit 07497b7cea
6 changed files with 95 additions and 34 deletions

View File

@@ -413,20 +413,29 @@ BMP for 3- or 4-color images, call `bmpwrite_allow_2bit()` before calling
Indexed images may optionally be written as RLE4 or RLE8 compressed BMPs.
Images with 16 or fewer colors can be written as either RLE4 or RLE8
(default is RLE4), images with more than 16 colors only as RLE8.
(default is RLE4), images with more than 16 colors only as RLE8. Images with
only 2 colors can also be written with 1-D Huffman encoding, but only
after explicitly allowing it by calling `bmpwrite_allow_huffman()`.
To activate RLE compression, call
```
BMPRESULT bmpwrite_set_rle(BMPHANDLE h, BMPRLETYPE type)
BMPRESULT bmpwrite_allow_huffman(BMPHANDLE h)
```
with `type` set to one of the following values:
- `BMP_RLE_NONE` no RLE compression, same as not calling `bmpwrite_set_rle()`
at all
- `BMP_RLE_AUTO` choose RLE4 or RLE8 based on number of colors in palette
- `BMP_RLE_AUTO` choose RLE4, RLE8, or 1-D Huffman based on number of colors
in palette
- `BMP_RLE_RLE8` use RLE8, regardless of number of colors in palette
In order to write 1-D Huffman encoded bitmpas, the provided palette must have
2 colors, rle type must be set to `BMP_RLE_AUTO`, and `bmpwrite_allow_huffman
()` must be called. Be aware that *very* few programs will be able to read
Huffman encoded BMPs!
### top-down / bottom-up

View File

@@ -8,7 +8,7 @@
Download [bmplib on github](https://github.com/rupertwh/bmplib).
## Current status (v1.6.1):
## Current status (v1.6.2):
### Reading BMP files:
- 16/24/32 bit RGB(A) with any bits/channel combination
(BI_RGB, BI_BITFIELDS, BI_ALPHABITFIELDS).
@@ -37,7 +37,7 @@ Download [bmplib on github](https://github.com/rupertwh/bmplib).
- RGB(A) 16/24/32 bit.
- 64-bit RGBA
- any bit-depth combination for the RGBA channels.
- Indexed 1/2/4/8 bit, optional RLE4 and RLE8 compression.
- Indexed 1/2/4/8 bit, optional RLE4, RLE8, and 1-D Huffman compression.
- write BI_RGB when possible, BI_BITFIELDS only when
necessary.
- optional line-by-line writing of BMPs.
@@ -86,7 +86,7 @@ Includes:
#include <bmplib.h>
```
see API.md for the API documentation
see API-quick-start.md and API-full.md for the API documentation
### 64bit BMPs

View File

@@ -244,7 +244,7 @@ API BMPRESULT bmpwrite_set_palette(BMPHANDLE h, int numcolors,
if (!s_is_setting_compatible(wp, "indexed"))
return BMP_RESULT_ERROR;
if (numcolors < 1 || numcolors > 256) {
if (numcolors < 2 || numcolors > 256) {
logerr(wp->log, "Invalid number of colors for palette (%d)",
numcolors);
return BMP_RESULT_ERROR;
@@ -607,8 +607,7 @@ static void s_decide_outformat(BMPWRITE_R wp)
wp->rle = 8;
wp->ih->compression = BI_RLE8;
wp->ih->bitcount = 8;
} else if (wp->palette->numcolors > 2 ||
!wp->allow_huffman) {
} else if (wp->palette->numcolors > 2 || !wp->allow_huffman) {
wp->rle = 4;
wp->ih->compression = BI_RLE4;
wp->ih->bitcount = 4;
@@ -727,9 +726,6 @@ API BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image)
wp->saveimage_done = TRUE;
wp->bytes_written_before_bitdata = wp->bytes_written;
if (wp->ih->compression == BI_OS2_HUFFMAN)
huff_encode(wp, -1, 0); /* leading eol */
linesize = (size_t) wp->width * wp->source_bytes_per_pixel;
for (y = 0; y < wp->height; y++) {
real_y = (wp->outorientation == BMP_ORIENT_TOPDOWN) ? y : wp->height - y - 1;
@@ -762,14 +758,11 @@ API BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image)
}
}
else {
huff_encode(wp, -1, 0);
huff_encode(wp, -1, 0);
huff_encode(wp, -1, 0);
huff_encode(wp, -1, 0);
huff_encode(wp, -1, 0);
huff_flush(wp);
if (!(huff_encode_rtc(wp) && huff_flush(wp))) {
logsyserr(wp->log, "Writing RTC end-of-file marker");
return BMP_RESULT_ERROR;
}
}
s_try_saving_image_size(wp);
}
@@ -799,8 +792,6 @@ API BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line)
goto abort;
wp->bytes_written_before_bitdata = wp->bytes_written;
wp->line_by_line = TRUE;
if (wp->ih->compression == BI_OS2_HUFFMAN)
huff_encode(wp, -1, 0); /* leading eol */
}
switch (wp->rle) {
@@ -830,12 +821,10 @@ API BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line)
goto abort;
}
} else {
huff_encode(wp, -1, 0);
huff_encode(wp, -1, 0);
huff_encode(wp, -1, 0);
huff_encode(wp, -1, 0);
huff_encode(wp, -1, 0);
huff_flush(wp);
if (!(huff_encode_rtc(wp) && huff_flush(wp))) {
logsyserr(wp->log, "Writing RTC end-of-file marker");
goto abort;
}
}
s_try_saving_image_size(wp);
}
@@ -1233,23 +1222,21 @@ abort:
static int s_save_line_huff(BMPWRITE_R wp, const unsigned char *line)
{
int x, len, total = 0;
int x = 0, len;
int black = FALSE;
x = 0;
if (!huff_encode_eol(wp)) /* each line starts with eol */
goto abort;
while (x < wp->width) {
len = 0;
while ((len < wp->width - x) && ((!!line[x + len]) == black))
len++;
if (!huff_encode(wp, len, black))
goto abort;
total += len;
black = !black;
x += len;
}
if (!huff_encode(wp, -1, 0)) /* eol */
goto abort;
return TRUE;
abort:
logsyserr(wp->log, "Writing 1-D Huffman data to BMP file");

View File

@@ -36,7 +36,7 @@
static int s_findnode(uint32_t bits, int nbits, int black, int *found);
static int s_zerofill(BMPWRITE_R wp);
/*****************************************************************************
@@ -116,6 +116,11 @@ void huff_fillbuf(BMPREAD_R rp)
}
/*****************************************************************************
* s_push()
****************************************************************************/
static int s_push(BMPWRITE_R wp, int bits, int nbits)
{
if (nbits > 32 - wp->hufbuf_len) {
@@ -130,6 +135,10 @@ static int s_push(BMPWRITE_R wp, int bits, int nbits)
/*****************************************************************************
* huff_encode()
****************************************************************************/
int huff_encode(BMPWRITE_R wp, int val, int black)
{
const struct Huffcode *makeup, *term;
@@ -159,6 +168,59 @@ int huff_encode(BMPWRITE_R wp, int val, int black)
return TRUE;
}
/*****************************************************************************
* huff_encode_eol()
****************************************************************************/
int huff_encode_eol(BMPWRITE_R wp)
{
return huff_encode(wp, -1, 0);
}
/*****************************************************************************
* huff_encode_rtc()
****************************************************************************/
int huff_encode_rtc(BMPWRITE_R wp)
{
if (!s_zerofill(wp))
return FALSE;
for (int i = 0; i < 6; i++) {
if (!huff_encode_eol(wp))
return FALSE;
}
return TRUE;
}
/*****************************************************************************
* s_zerofill()
*
* add fill 0s up to next byte boundary
****************************************************************************/
static int s_zerofill(BMPWRITE_R wp)
{
int n = 8 - wp->hufbuf_len % 8;
if (n < 8)
return s_push(wp, 0, n);
return TRUE;
}
/*****************************************************************************
* huff_flush()
****************************************************************************/
int huff_flush(BMPWRITE_R wp)
{
int byte;
@@ -169,6 +231,7 @@ int huff_flush(BMPWRITE_R wp)
logsyserr(wp->log, "writing Huffman bitmap");
return FALSE;
}
wp->bytes_written++;
wp->hufbuf_len -= 8;
wp->hufbuf &= (1UL << wp->hufbuf_len) - 1;
}

View File

@@ -22,4 +22,6 @@ int huff_decode(BMPREAD_R rp, int black);
void huff_fillbuf(BMPREAD_R rp);
int huff_encode(BMPWRITE_R wp, int val, int black);
int huff_encode_eol(BMPWRITE_R wp);
int huff_encode_rtc(BMPWRITE_R wp);
int huff_flush(BMPWRITE_R wp);

View File

@@ -1,4 +1,4 @@
project('bmplib', 'c', default_options: ['c_std=c11'], version: '1.6.1')
project('bmplib', 'c', default_options: ['c_std=c11'], version: '1.6.2')
cc = meson.get_compiler('c')