Skip to content

Commit

Permalink
huffman: small fixes
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Rupert committed Nov 15, 2024
1 parent f6c7b97 commit 4394bbb
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 33 deletions.
13 changes: 11 additions & 2 deletions API-full.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -38,6 +38,7 @@ Download [bmplib on github](https://github.com/rupertwh/bmplib).
- 64-bit RGBA
- any bit-depth combination for the RGBA channels.
- Indexed 1/2/4/8 bit, optional RLE4 and RLE8 compression.
- HUffman encoded (OS/2).
- write BI_RGB when possible, BI_BITFIELDS only when
necessary.
- optional line-by-line writing of BMPs.
Expand Down Expand Up @@ -86,7 +87,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
Expand Down
41 changes: 14 additions & 27 deletions bmp-write.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -1233,23 +1222,21 @@ static int s_save_line_rle4(BMPWRITE_R wp, const unsigned char *line)

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");
Expand Down
65 changes: 64 additions & 1 deletion huffman.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@


static int s_findnode(uint32_t bits, int nbits, int black, int *found);

static int s_zerofill(BMPWRITE_R wp);


/*****************************************************************************
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
Expand Down
2 changes: 2 additions & 0 deletions huffman.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
2 changes: 1 addition & 1 deletion meson.build
Original file line number Diff line number Diff line change
@@ -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')

Expand Down

0 comments on commit 4394bbb

Please sign in to comment.