42 Commits

Author SHA1 Message Date
Rupert
69f7e8baf3 do handle-conversion in bmp-common.c
(unearthed return-val bug in bmpread_load_palette)
reset magic on free
bump version to 1.7.3
2025-01-16 10:59:06 +01:00
Rupert
7b105681f4 logging: add log.panic instead of 'magic' size 2025-01-15 23:29:49 +01:00
Rupert
516944d992 embrace C99 bool for internal use (not in API).
ensure gen-*.c remain C89 compatible
2025-01-15 17:34:18 +01:00
Rupert
4aa6c34663 bitshifting cleanup
ensure we don't invoke undefined or implementation-defined behavior
2025-01-14 23:03:28 +01:00
Rupert
bec2e3ac00 formatting - big white space commit 2025-01-07 00:51:20 +01:00
Rupert
4cd1988d47 clang-format update
making it a little better
2025-01-07 00:50:45 +01:00
Rupert
3bebc4c182 improve OS/2 header detection 2025-01-06 21:17:23 +01:00
Rupert
f19502f8e9 improve/optimize reading headers 2025-01-06 21:17:23 +01:00
Rupert
d000a25250 more windows compiler 2025-01-06 00:30:00 +01:00
Rupert
b723c6f6ef allow shortened OS/2 headers
bump version to 1.7.2
2025-01-06 00:25:43 +01:00
Rupert
e8cd85b938 Make LCS_WINDOWS_COLOR_SPACE default for V4/V5 headers 2025-01-06 00:11:30 +01:00
Rupert
30495d92cf better fix for RLE error
limit the group size to 256 instead of looping through larger
repeat-groups.
Also, set 'small_number' for RLE24 to 2, yields slightly better
compression. (maybe even set to 1 in the future)
2024-11-17 08:54:58 +01:00
Rupert
7f125a3d81 fix error in writing long RLE sequences 2024-11-17 02:46:43 +01:00
Rupert
4626e2ac29 implement RLE24
* unify RLE4/RLE8/RLE24
* doc updates
* bimp version to 1.7.0
2024-11-16 23:17:07 +01:00
Rupert
ba2da3a6ff rename s_set_outpixel_rgb() to s_imgrgb_to_outbytes() 2024-11-16 13:32:41 +01:00
Rupert
052c5ade33 make it compile with MS Visual Studio
* add dll import/export declarations
* add casts to avoid compiler warnings
* fix long vs long long error, unearthed by compiling under Windows

Compiling with Visual Studio still needs some manual work that is
otherwise done by meson:
* create config.h
* compile and run gen-huffman.c and gen-reversebits.c to create the
  header files huffman-codes.h and reversebits.h

gitignore visual studio
2024-11-16 02:40:19 +01:00
Rupert
07497b7cea 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
2024-11-15 12:42:49 +01:00
Rupert
f6c7b97411 change s_read_whole_image() to call s_read_one_line()
instead of duplicating the calls to the low level line reading funcs
2024-11-15 12:37:48 +01:00
Rupert
4e5af5b693 Sublime Text white space settings
all didn't mean all; changed settings and removed all trailing
whitespace.
2024-11-14 23:18:52 +01:00
Rupert
11de4eada4 minor fixes
* reverse param order in cm_is_one_of
* logerr->logsyserr in bmpwrite_new()
* add orientation to compatibilty check
2024-11-14 22:51:15 +01:00
Rupert
5d6957bff1 huffman: improve eol detection
minor fixes
2024-11-11 23:20:23 +01:00
Rupert
4f9f82ca61 write huffman encoded bmps
first working draft
2024-11-06 15:24:36 +01:00
Rupert
05dea4441d auto-generation: small fixes
* found one last error in huffman makeup codes
* use smaller types (short/char) to reduce table size
* small fixes, formatting
2024-11-05 00:00:08 +01:00
Rupert
29854311ac auto-generate reversebits.h 2024-11-03 14:57:15 +01:00
Rupert
28e6a281ae huffman: auto-generate nodebuffer[]
generate the trees in nodebuffer at compile time from text tables in
huffman-gen-codes.h
2024-11-03 14:57:08 +01:00
Rupert
cdcd29026a huffman: got rid of callagain-afterthought 2024-11-03 10:42:37 +01:00
Rupert
c5384cdc86 minor fixes 2024-10-30 23:16:40 +01:00
Rupert
9add28e803 huffman: 2560 makeup can repeat for long sequences, cleanup 2024-10-28 12:46:19 +01:00
Rupert
093c984b84 update docs, bump version to 1.6.0 2024-10-28 01:52:21 +01:00
Rupert
d5338751d6 Huffman decoding: first implementation, successfully reads sample bmp
still pretty rough, needs some cleanup and optimization
2024-10-28 01:48:30 +01:00
Rupert
e6a84d2a34 bmpread_dimensions(): be more precise about when we consider
dimensions to be fetched; get rid of underscores in bitsperchannel
v1.5.0
2024-10-26 12:44:55 +02:00
Rupert
26edf63068 docs: quickstart update 2024-10-26 12:24:55 +02:00
Rupert
4c7f1881c8 minor corrections in API-quick-start.md 2024-10-26 10:13:13 +02:00
Rupert
0b12d525f9 README - add link to source in case readme is viewed on github "Pages" 2024-10-26 10:00:24 +02:00
Rupert
1dc99397fe add clang-format.
It's not exactly what I want, but it gets close.
Haven't actually used it to format the code, just adding it
as a reference.
2024-10-26 09:33:22 +02:00
Rupert
c490f79e76 google verification 2024-10-25 15:17:48 +02:00
Rupert
55c7e8d24a s2_13 conversion, avoid int overflow caused by rounding past limit 2024-10-25 08:59:43 +02:00
Rupert
e5c39138ae code format 2024-10-25 08:57:58 +02:00
Rupert
05e69a2f51 static asserts for type sizes 2024-10-24 19:12:37 +02:00
Rupert
8223b8038e if file- or imagesize is larger than uint32, write 0 instead of
bogus overflowed values to header.
make dimension check more precise.
2024-10-24 01:21:14 +02:00
Rupert
6db7f5ff5f small fixes for edge cases 2024-10-23 10:07:24 +02:00
Rupert
99598632f2 Quick Start Guide,
doc update to reflect latest changes and additions to API
2024-10-20 16:24:57 +02:00
24 changed files with 3007 additions and 1513 deletions

63
.clang-format Normal file
View File

@@ -0,0 +1,63 @@
# For more information, see:
#
# https://clang.llvm.org/docs/ClangFormat.html
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
#
---
BasedOnStyle: WebKit
SortIncludes: false
AlignAfterOpenBracket: Align
AlignArrayOfStructures: Right
AlignConsecutiveAssignments:
Enabled: true
AcrossEmptyLines: false
AcrossComments: true
AlignCompound: true
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveBitFields: AcrossComments
AlignConsecutiveDeclarations: AcrossComments
AlignConsecutiveMacros: AcrossComments
AlignConsecutiveShortCaseStatements:
Enabled: true
AcrossEmptyLines: true
AcrossComments: true
AlignCaseColons: true
AlignEscapedNewlines: LeftWithLastLine
AlignOperands: Align
AllowAllArgumentsOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: true
AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
BinPackArguments: true
BinPackParameters: true
BitFieldColonSpacing: After
BreakAfterReturnType: Automatic
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Linux
BreakBeforeTernaryOperators: false
BreakStringLiterals: false
IndentWidth: 8
PointerAlignment: Right
UseTab: ForIndentation
SpaceBeforeParens: ControlStatements
SpaceAfterLogicalNot: false
SpaceAfterCStyleCast: false
ColumnLimit: 80
PenaltyBreakAssignment: 60
PenaltyBreakBeforeFirstCallParameter: 100
PenaltyBreakOpenParenthesis: 40
PenaltyExcessCharacter: 1
PenaltyBreakString: 60
PenaltyReturnTypeOnItsOwnLine: 90

6
.gitignore vendored
View File

@@ -3,3 +3,9 @@ _debug
_release
*.sublime-workspace
*.patch
.vs
[Dd]ebug
[Rr]elease
bmplib
gen-huffman
gen-reversebits

View File

@@ -1,25 +1,8 @@
# API -- Rupert's bmplib
# Rupert's bmplib -- Full API Description (v1.7.1)
Refer to the *Quick Start Guide* (API-quick-start.md) for a quick intro to bmplib which describes only the minimal set of functions needed to read/write BMP files.
The API has grown quite a bit more than I had originally anticipated. But most
of the provided functions are optional. So, to not scare you off right away,
here is a short overview of the basic functions needed, which will be
sufficient for many/most use cases (see also the two examples at the end of
this document):
### Reading BMPs:
```
bmpread_new()
bmpread_dimensions()
bmpread_load_image()
bmp_free()
```
### Writing BMPs:
```
bmpwrite_new()
bmpwrite_set_dimensions()
bmpwrite_save_image()
bmp_free()
```
## 1. Functions for reading BMP files
### Get a handle
@@ -34,6 +17,7 @@ handle.
The handle cannot be reused to read multiple files.
### Read the file header
```
BMPRESULT bmpread_load_info(BMPHANDLE h)
@@ -41,11 +25,13 @@ BMPRESULT bmpread_load_info(BMPHANDLE h)
bmplib reads the file header and checks validity. Possible return values are:
- `BMP_RESULT_OK`: All is good, you can proceed to read the file.
- `BMP_INSANE`: The file is valid, but huge. The default limit is 500MB. If
you want to read the file anyway, use `bmpread_set_insanity_limit()` to
increase the allowed file size. Otherwise, `bmpread_load_image()` will
refuse to load the image.(You can build the library with a different
default limit, using the meson option '-Dinsanity_limit_mb=nnn')
- `BMP_INSANE`: The file is valid, but huge. The default limit is 500MB
(relevant is the required buffer size to hold the complete image, not the
file size. If you want to read the file anyway, use
`bmpread_set_insanity_limit()` to increase the allowed file size.
Otherwise, `bmpread_load_image()` will refuse to load the image. (You can
build the library with a different default limit, using the meson
option '-Dinsanity_limit_mb=nnn')
- `BMP_RESULT_PNG` / `BMP_RESULT_JPEG`: It's not really a BMP file, but a
wrapped PNG or JPEG. The file pointer is left in the correct state to be
passed on to e.g. libpng or libjpeg.
@@ -79,14 +65,14 @@ at a time, each returned as an `int`. Getting the horizontal and vertical
resolutions in DPI is only available via these single functions.
Note, in order to use these functions, -- unlike with `bmpread_dimensions
()` -- you must first(successfully) call `bmpread_load_info()`, otherwise
()` -- you must first (successfully) call `bmpread_load_info()`, otherwise
they will all return 0!
```
int bmpread_width(BMPHANDLE h)
int bmpread_height(BMPHANDLE h)
int bmpread_channels(BMPHANDLE h)
int bmpread_bits_per_channel(BMPHANDLE h)
int bmpread_bitsperchannel(BMPHANDLE h)
BMPORIENT bmpread_orientation(BMPHANDLE h)
int bmpread_resolution_xdpi(BMPHANDLE h)
@@ -95,16 +81,16 @@ int bmpread_resolution_ydpi(BMPHANDLE h)
```
#### top-down / bottom-up
`orientation` is one of:
- `BMPORIENT_BOTTOMUP`
- `BMPORIENT_TOPDOWN`
`*orientation` is one of:
- `BMP_ORIENT_BOTTOMUP`
- `BMP_ORIENT_TOPDOWN`
`bmpread_orientation()` or the orientation value returned by
`bmpread_orientation()` or the `orientation` value returned by
`bmpread_dimensions()` **is only relevant if you load the BMP file
line-by-line**. In line-by-line mode (using `bmpread_load_line()`), the
image data is always delivered in the order it is in the BMP file. The
`orientation` value will tell you if it's top-down or bottom-up. On the
other hand, when the whole image is loaded at once(using `bmpread_load_image
other hand, when the whole image is loaded at once (using `bmpread_load_image
()`), bmplib will **always** return the image top-down, regardless of how
the BMP file is oriented. The `orientation` value will still indicate the
orientation of the original BMP.
@@ -119,7 +105,7 @@ Returns the buffer size you have to allocate for the whole image.
### Indexed BMPs
By default, bmplib will interpret indexed (color palette) BMPs and return the
image as 24-bit RGB data.
image as 24-bit RGB data, same as non-indexed (RGB) BMPs.
If instead you want to keep the image as indexed, you have the option do so
with these two functions:
@@ -144,7 +130,7 @@ let bmplib allocate it for you (and then `free()` it, once you are done):
```
unsigned char *palette;
int numcolors;
int numcolors;
numcolors = bmpread_num_palette_colors(h);
@@ -189,8 +175,8 @@ void bmpread_set_undefined(BMPHANDLE h, BMPUNDEFINED mode)
```
`mode` can be one of:
- `BMPUNDEFINED_LEAVE`
- `BMPUNDEFINED_TO_ALPHA` (default)
- `BMP_UNDEFINED_LEAVE`
- `BMP_UNDEFINED_TO_ALPHA` (default)
Note: If you use `bmpread_load_palette()` to switch to loading the index data
instead of RGB data, this setting will have no effect and undefined pixels
@@ -205,23 +191,33 @@ BMPRESULT bmpread_set_64bit_conv(BMPHANDLE h, BMPCONV64 conv)
```
If you don't do anything, 64bit BMPs will be read like any other BMP and the
data will be returned as 16bit/channel sRGB RGBA. (Hopefully, see README.md)
data will be returned as 16bit/channel sRGB RGBA.
But if you want to access the original s2.13 fixed-point components, or you
don't want the linear-to-sRGB conversion, you can call `bmpread_is_64bit
()` to inquire if a BMP file is 64bit and subsequently call
`bmpread_set_64bit_conv()` with `conv` set to one of the following values:
don't want the linear-to-sRGB conversion, you can use `bmpread_set_64bit_conv
()` and `bmp_set_number_format()` to control how the image is returned:
- `BMP_CONV64_NONE`: no conversion is done, image data is returned as is
(probably 16 bit per channel RGBA in s2.13 fixed-point)
- `BMP_CONV64_16BIT`: image is returned as normal 16bit per channel, without
converting from linear to sRGB-gamma.
- `BMP_CONV64_16BIT_SRGB`: the default, original data is assumed to be s2.13
fixed-point linear and converted to normal 16bit per channel with
sRGB-gamma.
Options for `bmpread_set_64bit()` are:
- `BMP_CONV64_SRGB`: the default, original data is assumed to be s2.13
fixed-point linear and converted to sRGB-gamma.
- `BMP_CONV64_LINEAR`: no gamma-conversion is applied to the image data.
- `BMP_CONV64_NONE`: this option is just a shorthand for setting
BMP_CONV64_LINEAR *and* BMP_FORMAT_S2_13. Image values are returned exactly
as they are in the BMP file, without any conversion or attempt at
interpretation.
### Floating point
### Setting a number format
By default, bmplib will always return the image data as 8-,16-, or 32-bit integer values. You can instead set the number format to floating point or fixed using:
```
BMPRESULT bmp_set_number_format(BMPHANDLE h, BMPFORMAT format)
```
(see below, *3. General functions for both reading/writing BMPs*)
### Huge files: bmpread_set_insanity_limit()
@@ -290,12 +286,12 @@ are done with it.
To determine the required buffer size, either divide the value from
`bmpread_buffersize()` by the number of scanlines (= `bmpread_height()`), or
calculate from the image dimensions returned by bmplib as width * channels *
bits_per_channel / 8.
bitsperchannel / 8.
```
single_line_buffersize = bmpread_buffersize(h) / bmpread_height(h);
/* or */
single_line_buffersize = bmpread_width(h) * bmpread_channels(h) * bmpread_bits_per_channel(h) / 8;
single_line_buffersize = bmpread_width(h) * bmpread_channels(h) * bmpread_bitsperchannel(h) / 8;
```
Repeated calls to `bmpread_load_line()` will return each scan line, one after
@@ -313,7 +309,8 @@ bottom-up. Almost all BMPs will be bottom-up. (see above)
Invalid pixels may occur in indexed BMPs, both RLE and non-RLE. Invalid pixels
either point beyond the given color palette, or they try to set pixels
outside the image dimensions. Pixels containing an invalid color value will
be set to zero, and attempts to point outside the image will be ignored.
be set to the maximum allowed value, and attempts to point outside the image
will be ignored.
In both cases, `bmpread_load_image()` and `bmpread_load_line()` will return
BMP_RESULT_INVALID, unless the image is also truncated, then
@@ -337,6 +334,20 @@ BMPRESULT bmpread_info_channel_bits(BMPHANDLE h, int *r, int *g, int *b, int *
```
### Release the handle
```
void bmp_free(BMPHANDLE h)
```
Frees all resources associated with the handle `h`. **Image data is not
affected**, so you can call bmp_free() immediately after `bmpread_load_image
()` and still use the returned image data.
Note: Any error message strings returned by `bmp_errmsg()` are invalidated by
`bmp_free()` and must not be used anymore!
## 2. Functions for writing BMP files
@@ -352,7 +363,7 @@ BMPRESULT bmpwrite_set_dimensions(BMPHANDLE h,
unsigned width,
unsigned height,
unsigned channels,
unsigned bits_per_channel)
unsigned bitsperchannel)
BMPRESULT bmpwrite_set_resolution(BMPHANDLE h, int xdpi, int ydpi)
@@ -360,8 +371,8 @@ BMPRESULT bmpwrite_set_resolution(BMPHANDLE h, int xdpi, int ydpi)
Note: the dimensions set with `bmpwrite_set_dimensions()` describe the source
data that you pass to bmplib, *not* the output BMP format. Use
`bmpwrite_set_output_bits()` and `bmpwrite_set_palette()` to modify the
format written to the BMP file.
`bmpwrite_set_output_bits()`, `bmpwrite_set_palette()`, and
`bmpwrite_set_64bit()` to modify the format written to the BMP file.
### Set the output format
@@ -401,22 +412,43 @@ BMP for 3- or 4-color images, call `bmpwrite_allow_2bit()` before calling
#### RLE
Indexed images may optionally be written as RLE4 or RLE8 compressed BMPs.
```
BMPRESULT bmpwrite_set_rle(BMPHANDLE h, BMPRLETYPE type)
BMPRESULT bmpwrite_allow_huffman(BMPHANDLE h)
```
Indexed images may optionally be written run-lenght-encoded (RLE) bitmaps.
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.
To activate RLE compression, call
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()`
(very few programs will be able to read Huffman encoded BMPs).
```
BMPRESULT bmpwrite_set_rle(BMPHANDLE h, BMPRLETYPE type)
```
with `type` set to one of the following values:
To activate RLE compression, call `bmpwrite_set_rle()` 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!
#### RLE24
RLE24 is an old OS/2 compression method. As the name suggests, it's 24-bit RLE
for non-indexed images. Like Huffman encoding, it's very uncommon and only
very few programs will be able to read these BMPs. Writing RLE24 bitmaps must
be explicitly allowed by first calling `bmpwrite_allow_rle24()`.
In order to save an image as RLE24, the data must be provided as 8 bits per
channel RGB (no alpha channel). Call `bmpwrite_set_rle()` with type set to
`BMP_RLE_AUTO` and also call `bmpwrite_allow_rle24()` (in any order).
### top-down / bottom-up
@@ -443,6 +475,26 @@ provide the image lines in the order according to the orientation you have
chosen for the BMP file.
### 64-bit RGBA BMPs
By default, bmplib will not write 64-bit BMPs because they are rather exotic and hardly any
software can open them.
If you do want to write 64-bit BMPs, call
```
BMPRESULT bmpwrite_set_64bit(BMPHANDLE h)
```
In order to make use of the extended range available in 64-bit BMPs (-4.0 to +3.999...), you will probably want to provide the image buffer either as 32-bit float or as 16-bit s2.13 (and call `bmp_set_number_format()` accordingly).
Note: 64-bit BMPs store pixel values in *linear light*. Unlike when *reading* 64-bit BMPs, bmplib will not make any gamma/linear conversion while writing BMPs. You have to provide the proper linear values in the image buffer.
### Write the image
```
@@ -475,7 +527,7 @@ set with `bmpwrite_set_orientation()` (see above).
### bmp_free()
```
void bmp_free(BMPHANDLE h)
void bmp_free(BMPHANDLE h)
```
Frees all resources associated with the handle `h`. Image data is not
@@ -496,6 +548,21 @@ description(s). The returned string is safe to use until any other
bmplib-function is called with the same handle.
### bmp_set_number_format()
```
BMPRESULT bmp_set_number_format(BMPHANDLE h, BMPFORMAT format)
```
sets the number format of the image buffer received from / passed to bmplib. `format` can be one of
- `BMP_FORMAT_INT` image buffer values are expected/returned as 8-, 16-, or 32-bit integers. (this is the default)
- `BMP_FORMAT_FLOAT` image buffer values are expected/returned as 32-bit floating point numbers (C `float`).
- `BMP_FORMAT_S2_13` image buffer values are expected/returned as s2.13 fixed point numbers. s2.13 is a 16-bit format with one sign bit, 2 integer bits, and 13 bits for the fractional part. Range is from -4.0 to +3.999...
For indexed images, `BMP_FORMAT_INT` is the only valid format.
### bmp_version()
```
@@ -576,13 +643,22 @@ Can safely be cast from/to int.
#### `BMPCONV64`
Used in `bmpread_set_64bit_conv()`. Possible value are:
- `BMP_CONV64_16BIT_SRGB` (default)
- `BMP_CONV64_16BIT`
Used in `bmpread_set_64bit_conv()`. Possible values are:
- `BMP_CONV64_SRGB` (default)
- `BMP_CONV64_LINEAR`
- `BMP_CONV64_NONE`
Can safely be cast from/to int.
#### `BMPFORMAT`
Used in `bmp_set_number_format()`. Possible values are:
- `BMP_FORMAT_INT` (default)
- `BMP_FORMAT_FLOAT` 32-bit floating point
- `BMP_FORMAT_S2_13` s2.13 fixed point
## 5. Sample code
### Reading BMPs
@@ -596,7 +672,7 @@ Can safely be cast from/to int.
uint8_t *image_buffer;
/* open a file and call bmpread_new() to get a BMPHANDLE,
/* Open a file and call bmpread_new() to get a BMPHANDLE,
* which will be used on all subsequent calls.
*/
@@ -604,8 +680,8 @@ Can safely be cast from/to int.
h = bmpread_new(file);
/* get image dimensions
* the color information (channels/bits) describes the data
/* Get image dimensions
* The color information (channels/bits) describes the data
* that bmplib will return, NOT necessarily the BMP file.
*/
@@ -614,24 +690,24 @@ Can safely be cast from/to int.
width = bmpread_width(h);
height = bmpread_height(h);
channels = bmpread_channels(h);
bitsperchannel = bmpread_bits_per_channel(h);
bitsperchannel = bmpread_bitsperchannel(h);
/* get required size for memory buffer and allocate memory */
/* Get required size for memory buffer and allocate memory */
image_buffer = malloc(bmpread_buffersize(h));
/* load the image and clean up: */
/* Load the image and clean up: */
bmpread_load_image(h, &image_buffer);
bmp_free(h);
fclose(file);
/* ready to use the image written to image_buffer */
/* Ready to use the image written to image_buffer */
/* image data is always returned in host byte order as
/* Image data is always returned in host byte order as
* 8, 16, or 32 bits per channel RGB or RGBA data.
* No padding.
*/
@@ -654,13 +730,13 @@ Can safely be cast from/to int.
*/
/* open a file for writing and get a BMPHANDLE */
/* Open a file for writing and get a BMPHANDLE */
file = fopen("image.bmp", "wb");
h = bmpwrite_new(file);
/* inform bmplib of the image dimensions.
/* Inform bmplib of the image dimensions.
* The color information (channels, bits) refer to the format
* your image buffer is in, not the format the BMP file should
* be written in.
@@ -669,7 +745,7 @@ Can safely be cast from/to int.
bmpwrite_set_dimensions(h, width, height, channels, bitsperchannel);
/* Optional: choose bit-depths (independantly for each channel,
/* Optional: choose bit-depths (independently for each channel,
* in the order R,G,B,A) for the BMP file. bmplib will choose
* an appropriate BMP file format to accomodate those bitdepths.
*/
@@ -677,7 +753,7 @@ Can safely be cast from/to int.
bmpwrite_set_output_bits(h, 5, 6, 5, 0);
/* save data to file */
/* Save data to file */
bmpwrite_save_image(h, image_buffer);

282
API-quick-start.md Normal file
View File

@@ -0,0 +1,282 @@
# Rupert's bmplib -- Quick Start Guide
The API has grown quite a bit more than I had originally anticipated. But most
of the provided functions are optional.
So, to not scare you off right away, I wrote this quick start guide version of
the API description which only covers the basic functions. These basic
functions will be sufficient for many/most use cases. (see also the two
examples at the end of this document):
For the complete API, refer to the *Full API Description* (API-full.md).
## 1. Reading BMP files:
```
bmpread_new()
bmpread_dimensions()
bmpread_load_image()
bmp_free()
```
### Get a handle
```
BMPHANDLE bmpread_new(FILE *file)
```
`bmpread_new()` returns a handle which is used for all subsequent calls to
bmplib. When you are done with the file, call `bmp_free()` to release this
handle.
The handle cannot be reused to read multiple files.
### Get image dimensions
```
BMPRESULT bmpread_dimensions(BMPHANDLE h,
int *width,
int *height,
int *channels,
int *bitsperchannel,
BMPORIENT *orientation)
```
Use `bmpread_dimensions()` to get all dimensions with one call. The return
value will be the same as for `bmpread_load_info()` (see "3. Result codes"
below and *Full API Description*).
The dimensions describe the image returned by bmplib, *not* necessarily the
original BMP file.
`orientation` can be ignored, it is only relevant when loading the image
line-by-line. Can be set to NULL. (see *Full API Description*)
### Load the image
```
BMPRESULT bmpread_load_image(BMPHANDLE h, unsigned char **pbuffer)
```
Loads the complete image from the BMP file into the buffer pointed to by
`pbuffer`. You can either allocate a buffer yourself or let `pbuffer` point
to a NULL-pointer in which case bmplib will allocate an appropriate buffer.
In the latter case, you will have to `free()` the buffer, once you are done
with it.
If you allocate the buffer yourself, the buffer must be at least as large as
the size returned by `bmpread_buffersize()` (see *Full API description*).
```
unsigned char *buffer;
/* either: */
buffer = NULL;
bmpread_load_image(h, &buffer); /* bmplib will allocate the buffer */
/* or: */
buffer = malloc(bmpread_buffersize(h));
bmpread_load_image(h, &buffer); /* bmplib will use the provided buffer */
```
The image data is written to the buffer according to the returned dimensions
(see above). 16-bit and 32-bit values are always written in host byte order,
in the order R-G-B or R-G-B-A. The returned image is always top-down, i.e.
data starts in the top left corner. Unlike BMPs which are (almost always)
bottom-up.
If `bmpread_load_image()` returns BMP_RESULT_TRUNCATED or BMP_RESULT_INVALID,
the file may have been damaged or simply contains invalid image data. Image
data is loaded anyway as far as possible and may be partially usable.
### Release the handle
```
void bmp_free(BMPHANDLE h)
```
Frees all resources associated with the handle `h`. **Image data is not
affected**, so you can call bmp_free() immediately after `bmpread_load_image
()` and still use the returned image data.
Note: Any error message strings returned by `bmp_errmsg()` are invalidated by
`bmp_free()` and must not be used anymore!
## 2. Writing BMP files:
```
bmpwrite_new()
bmpwrite_set_dimensions()
bmpwrite_save_image()
bmp_free()
```
### Get a handle
```
BMPHANDLE bmpwrite_new(FILE *file)
```
### Set image dimensions
```
BMPRESULT bmpwrite_set_dimensions(BMPHANDLE h,
unsigned width,
unsigned height,
unsigned channels,
unsigned bitsperchannel)
```
Note: the dimensions set with `bmpwrite_set_dimensions()` describe the source
data that you pass to bmplib, *not* the output BMP format. Use
`bmpwrite_set_output_bits()`, `bmpwrite_set_palette()`, and
`bmpwrite_set_64bit()` to modify the format written to the BMP file. (see *Full
API description*)
### Write the image
```
BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image)
```
Write the whole image at once with `bmpwrite_save_image()`.
The image data pointed to by `image` must be in the format described
by `bmpwrite_set_dimensions()`. Multi-byte values (16 or 32 bit) are expected
in host byte order, the channels in the order R-G-B-(A).
Important: Image data must be provided top-down. (Even though the created BMP
file will be bottom-up.)
### bmp_free()
```
void bmp_free(BMPHANDLE h)
```
Frees all resources associated with the handle `h`.
Note: Any error messages returned by `bmp_errmsg()` are invalidated by
`bmp_free()` and cannot be used anymore.
## 3. Result codes
#### `BMPRESULT`
Many bmplib functions return the success/failure of an operation as a
`BMPRESULT`. It can have one of the following values:
- `BMP_RESULT_OK`
- `BMP_RESULT_INVALID`
- `BMP_RESULT_TRUNCATED`
- `BMP_RESULT_INSANE`
- `BMP_RESULT_PNG`
- `BMP_RESULT_JPEG`
- `BMP_RESULT_ERROR`
Can safely be cast from/to int. BMP_RESULT_OK is guaranteed to have the value 0.
## 5. Sample code
### Reading BMPs
```
/* (all error checking left out for clarity) */
BMPHANDLE h;
FILE *file;
int width, height, channels, bitsperchannel;
unsigned char *image_buffer;
/* Open a file and call bmpread_new() to get a BMPHANDLE,
* which will be used on all subsequent calls.
*/
file = fopen("someimage.bmp", "rb");
h = bmpread_new(file);
/* Get image dimensions
* The color information (channels/bits) describes the data
* that bmplib will return, NOT necessarily the BMP file.
* Setting orientation to NULL, image is always returned top-down.
*/
bmpread_dimensions(h, &width, &height, &channels, &bitsperchannel, NULL);
/* Load the image and clean up: */
image_buffer = NULL;
bmpread_load_image(h, &image_buffer);
bmp_free(h);
fclose(file);
/* Ready to use the image written to image_buffer */
/* Image data is always returned in host byte order as
* 8, 16, or 32 bits per channel RGB or RGBA data.
* No padding.
*/
```
### Writing BMPs
```
/* (all error checking left out for clarity) */
BMPHANDLE h;
FILE *file;
unsigned char *image_buffer;
int width, height, channels, bitsperchannel;
/* 'image_buffer' contains the image to be saved as either
* 8, 16, or 32 bits per channel RGB or RGBA data in
* host byte order without any padding
*/
/* Open a file for writing and get a BMPHANDLE */
file = fopen("image.bmp", "wb");
h = bmpwrite_new(file);
/* Inform bmplib of the image dimensions.
* The color information (channels, bits) refer to the format
* your image buffer is in, not the format the BMP file should
* be written in.
*/
bmpwrite_set_dimensions(h, width, height, channels, bitsperchannel);
/* Optional: choose bit-depths (independently for each channel,
* in the order R,G,B,A) for the BMP file. bmplib will choose
* an appropriate BMP file format to accomodate those bitdepths.
*/
bmpwrite_set_output_bits(h, 5, 6, 5, 0);
/* Save data to file */
bmpwrite_save_image(h, image_buffer);
bmp_free(h);
fclose(file);
```

View File

@@ -6,15 +6,18 @@
- Write any sensible BMP
- Robustness! Don't let malformed BMPs bother us
## Current status (v1.4.6):
Download [bmplib on github](https://github.com/rupertwh/bmplib).
## Current status (v1.7.1):
### Reading BMP files:
- 16/24/32 bit RGB(A) with any bits/channel combination
(BI_RGB, BI_BITFIELDS, BI_ALPHABITFIELDS).
- 64 bit RGBA (caveat see below)
- 1/2/4/8 bit indexed (palette), including RLE4 and RLE8 compressed.
- RLE24 compressed (OS/2).
- optional line-by-line reading of BMPs, even RLE.
- optionally return image data as float or s2.13
- Huffman encoded (OS/2).
- optional line-by-line reading of BMPs.
- optionally return image data as float or s2.13 fixed point.
successful results from reading sample images from Jason Summers'
fantastic [BMP Suite](https://entropymine.com/jason/bmpsuite/):
@@ -27,7 +30,6 @@
BMP_RESULT_PNG and leave the file pointer in the correct state to be
passed on to either libpng or libjpeg. Works as designed. Don't want to
create dependency on those libs.
- Huffman-encoded OS/2 BMPs: see TODO.
- We currently ignore icc-profiles and chromaticity/gamma values. See TODO.
@@ -35,10 +37,12 @@
- 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.
- RLE24 compression.
- write BI_RGB when possible, BI_BITFIELDS only when
necessary.
- optional line-by-line writing of BMPs.
- optionally supply image data as float or s2.13 fixed point.
## Installation
@@ -46,7 +50,7 @@
### Download and compile bmplib library
To install the latest development version of the library under the default
`/usr/local` prefix:
`/usr/local` prefix on debian-like Linux:
```
sudo apt install build-essential git meson pkg-config
@@ -83,51 +87,55 @@ 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
64bit BMPs are a special breed. First of all, there is very little information
about the format out there, let alone an 'official' spec from MS. It seems
that the RGBA components are (always?) stored as s2.13 fixed-point numbers.
And according to Jason Summers' BMP Suite the RGB components are encoded in
that the RGBA components are stored as s2.13 fixed-point numbers. And
according to Jason Summers' BMP Suite the RGB components are encoded in
linear light. As that's the only sample of a 64-bit BMP I have, that's what I
am going with for now. But that also means that there is no one obvious
format in which to return the data.
Possible choices are:
1. return the values untouched, which means the caller has to
be aware of the s2.13 format. (BMP_CONV64_NONE)
2. return the values as normal 16bit values, left in linear
light (BMP_CONV64_16BIT)
3. return the values as normal 16bit values, converted to sRGB
gamma. (BMP_CONV64_16BIT_SRGB)
be aware of the s2.13 format and linear gamma. (BMP_CONV64_NONE)
2. return the values converted to 16-bit integers (or other selected
number format), left in linear light (BMP_CONV64_LINEAR)
3. return the values converted to 16-bit integers (or other selected
numer format), converted to sRGB gamma. (BMP_CONV64_SRGB)
Choice 3 (16bit sRGB gamma) seems to be the most sensible default(and I made
Choice 3 (16bit sRGB gamma) seems to be the most sensible default (and I made
it the default), as it will work well for all callers which are not
aware/don't care about 64bit BMPs and just want to use/diplay them like any
other BMP. (Even though this goes against my original intent to not have
bmplib do any color conversions.)
Note: the s2.13 format allows for negative values and values greater than 1!
When converting to normal 16bit (BMP_CONV64_16BIT and BMP_CONV64_16BIT_SRGB),
these values will be clipped to 0...1.
When converting to 16bit integers, these values will be clipped to 0...1. In
order to preserve the full possible range of 64bit BMP pixel values, the
number format should be set to either BMP_FORMAT_FLOAT or BMP_FORMAT_S2_13.
In case the default (BMP_CONV64_16BIT_SRGB) doesn't work for you, bmplib now
provides these two functions to check if a BMP is 64bit and to set the
bmplib provides these functions to check if a BMP is 64bit and to set the
conversion:
- `bmpread_is_64bit()`
- `bmpread_set_64bit_conv()`
- `bmp_set_number_format()`
As to writing BMPs, by default bmplib will not write 64bit BMPs, as they are so exotic that only few applications will read them (other than native Microsoft tools, the new GIMP 3.0 is the only one I am aware of).
Use `bmpwrite_set_64bit()` in order to write 64bit BMPs.
## TODOs:
### Definitely:
- [x] write indexed images.
- [x] write RLE-compressed images (RLE4/RLE8 only. No OS/2 v2 BMPs).
- [x] write RLE-compressed images ~~(RLE4/RLE8 only. No OS/2 v2 BMPs)~~.
- [x] read RLE24-encoded BMPs.
- [ ] read Huffman-encoded BMPs.
- [x] read Huffman-encoded BMPs. (Still haven't found any real-life examples)
- [x] line-by-line reading/writing. ~~Right now, the image can only be
passed as a whole to/from bmplib.~~
- [ ] read/write icc-profile and chromaticity/gamma values
@@ -147,7 +155,7 @@ conversion:
(which is the 'official' default) or let user pick one/multiple/all to
be read in sequence.
- [ ] Add a 'not-a-BMP-file' return type instead of just returning error.
- [ ] icon- and pointer-files ("CI", "CP", "IC", "PT").
- ~~[ ] icon- and pointer-files ("CI", "CP", "IC", "PT").~~
- [x] 64-bits BMPs. (I changed my mind)
### Unclear:
@@ -159,7 +167,7 @@ conversion:
### Non-feature (internal):
- [x] complete API description (see API.md)
- [x] complete API description (see API-full.md and API-quick-start.md)
- [x] bmp-read.c is getting too big, split into several files

View File

@@ -3,18 +3,18 @@
* Copyright (c) 2024, Rupert Weber.
*
* This file is part of bmplib.
* bmplib is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* bmplib is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
* License along with this library.
* If not, see <https://www.gnu.org/licenses/>
*/
@@ -23,12 +23,16 @@
#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#include <stdbool.h>
#define BMPLIB_LIB
#include "config.h"
#include "bmplib.h"
#include "logging.h"
#include "bmp-common.h"
#include "bmp-read.h"
#include "huffman.h"
#include "bmp-write.h"
@@ -60,7 +64,7 @@ API const char* bmp_errmsg(BMPHANDLE h)
{
if (!(h && (h->magic == HMAGIC_READ || h->magic == HMAGIC_WRITE)))
return "BMPHANDLE is NULL or invalid";
return logmsg(h->log);
}
@@ -115,7 +119,7 @@ API void bmp_free(BMPHANDLE h)
#ifdef DEBUG
printf("bmp_free() called with invalid handle (0x%04x)\n",
(unsigned int) h->magic);
#endif
#endif
break;
}
}
@@ -123,30 +127,32 @@ API void bmp_free(BMPHANDLE h)
/********************************************************
* cm_check_is_read_handle
* cm_read_handle
*******************************************************/
int cm_check_is_read_handle(BMPHANDLE h)
BMPREAD cm_read_handle(BMPHANDLE h)
{
BMPREAD rp = (BMPREAD)(void*)h;
if (rp && rp->magic == HMAGIC_READ)
return TRUE;
return FALSE;
return rp;
return NULL;
}
/********************************************************
* bm_check_is_write_handle
* cm_write_handle
*******************************************************/
int cm_check_is_write_handle(BMPHANDLE h)
BMPWRITE cm_write_handle(BMPHANDLE h)
{
BMPWRITE wp = (BMPWRITE)(void*)h;
if (wp && wp->magic == HMAGIC_WRITE)
return TRUE;
return FALSE;
return wp;
return NULL;
}
@@ -154,7 +160,7 @@ int cm_check_is_write_handle(BMPHANDLE h)
* cm_gobble_up
*******************************************************/
int cm_gobble_up(BMPREAD_R rp, int count)
bool cm_gobble_up(BMPREAD_R rp, int count)
{
int i;
@@ -163,15 +169,14 @@ int cm_gobble_up(BMPREAD_R rp, int count)
if (feof(rp->file)) {
rp->lasterr = BMP_ERR_TRUNCATED;
logerr(rp->log, "unexpected end of file");
}
else {
} else {
rp->lasterr = BMP_ERR_FILEIO;
logsyserr(rp->log, "error reading from file");
}
return FALSE;
return false;
}
}
return TRUE;
return true;
}
@@ -187,9 +192,6 @@ int cm_count_bits(unsigned long v)
{
int bits = 0;
if (v < 0)
v = -v;
while (v) {
bits++;
v >>= 1;
@@ -221,18 +223,19 @@ const char* cm_format_name(enum BmpFormat format)
int cm_all_lessoreq_int(int limit, int n, ...)
bool cm_all_lessoreq_int(int limit, int n, ...)
{
va_list ap;
int i, ret = TRUE;
int i;
bool ret = true;
if (n < 1)
return TRUE;
return true;
va_start(ap, n);
for (i = 0; i < n; i++) {
if (va_arg(ap, int) > limit) {
ret = FALSE;
ret = false;
break;
}
}
@@ -242,19 +245,20 @@ int cm_all_lessoreq_int(int limit, int n, ...)
}
int cm_all_equal_int(int n, ...)
bool cm_all_equal_int(int n, ...)
{
va_list ap;
int first, i, ret = TRUE;
int first, i;
bool ret = true;
if (n < 2)
return TRUE;
return true;
va_start(ap, n);
first = va_arg(ap, int);
for (i = 1; i < n; i++) {
if (va_arg(ap, int) != first) {
ret = FALSE;
ret = false;
break;
}
}
@@ -264,18 +268,19 @@ int cm_all_equal_int(int n, ...)
}
int cm_all_positive_int(int n, ...)
bool cm_all_positive_int(int n, ...)
{
va_list ap;
int i, ret = TRUE;
int i;
bool ret = true;
if (n < 1)
return TRUE;
return true;
va_start(ap, n);
for (i = 0; i < n; i++) {
if (va_arg(ap, int) < 0) {
ret = FALSE;
ret = false;
break;
}
}
@@ -285,18 +290,19 @@ int cm_all_positive_int(int n, ...)
}
int cm_is_one_of(int candidate, int n, ...)
bool cm_is_one_of(int n, int candidate, ...)
{
va_list ap;
int i, ret = FALSE;
int i;
bool ret = false;
if (n < 1)
return TRUE;
return true;
va_start(ap, n);
va_start(ap, candidate);
for (i = 0; i < n; i++) {
if (va_arg(ap, int) == candidate) {
ret = TRUE;
ret = true;
break;
}
}
@@ -307,14 +313,14 @@ int cm_is_one_of(int candidate, int n, ...)
int cm_align4padding(unsigned long a)
int cm_align4padding(unsigned long long a)
{
return cm_align4size(a) - a;
return (int) (cm_align4size(a) - a);
}
int cm_align2padding(unsigned long a)
int cm_align2padding(unsigned long long a)
{
return cm_align2size(a) - a;
return (int) (cm_align2size(a) - a);
}
@@ -324,91 +330,106 @@ int cm_align2padding(unsigned long a)
* from little-endian files
*********************************************************/
int write_u16_le(FILE *file, uint16_t val)
bool write_u16_le(FILE *file, uint16_t val)
{
return (EOF != fputc(val & 0xff, file) &&
EOF != fputc((val >> 8) & 0xff, file));
}
int write_u32_le(FILE *file, uint32_t val)
bool write_u32_le(FILE *file, uint32_t val)
{
int i;
for (i = 0; i < 4; i++) {
if (EOF == fputc((val >> (i*8)) & 0xff, file))
return 0;
return false;
}
return 1;
return true;
}
int read_u16_le(FILE *file, uint16_t *val)
bool read_u16_le(FILE *file, uint16_t *val)
{
unsigned char buf[2];
if (2 != fread(buf, 1, 2, file))
return 0;
return false;
*val = (buf[1] << 8) | buf[0];
*val = ((unsigned)buf[1] << 8) | (unsigned)buf[0];
return 1;
return true;
}
int read_u32_le(FILE *file, uint32_t *val)
bool read_u32_le(FILE *file, uint32_t *val)
{
unsigned char buf[4];
if (4 != fread(buf, 1, 4, file))
return 0;
return false;
*val = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
*val = ((uint32_t)buf[3] << 24) | ((uint32_t)buf[2] << 16) |
((uint32_t)buf[1] << 8) | (uint32_t)buf[0];
return 1;
return true;
}
int write_s16_le(FILE *file, int16_t val)
bool write_s16_le(FILE *file, int16_t val)
{
return (EOF != fputc(val & 0xff, file) &&
EOF != fputc((val >> 8) & 0xff, file));
return write_u16_le(file, (uint16_t)val);
}
int read_s16_le(FILE *file, int16_t *val)
bool write_s32_le(FILE *file, int32_t val)
{
unsigned char buf[2];
if (2 != fread(buf, 1, 2, file))
return 0;
*val = (((int16_t)(signed char)buf[1]) << 8) | (int16_t) buf[0];
return 1;
return write_u32_le(file, (uint32_t)val);
}
int read_s32_le(FILE *file, int32_t *val)
bool read_s16_le(FILE *file, int16_t *val)
{
unsigned char buf[4];
uint16_t u16;
if (4 != fread(buf, 1, 4, file))
return 0;
if (!read_u16_le(file, &u16))
return false;
*val = (((int32_t)(signed char)buf[3]) << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
*val = (int16_t)u16;
return 1;
return true;
}
int write_s32_le(FILE *file, int32_t val)
bool read_s32_le(FILE *file, int32_t *val)
{
int i;
uint32_t u32;
for (i = 0; i < 4; i++) {
if (EOF == fputc((val >> (i*8)) & 0xff, file))
return 0;
}
return 1;
if (!read_u32_le(file, &u32))
return false;
*val = (int32_t)u32;
return true;
}
uint32_t u32_from_le(const unsigned char *buf)
{
return (uint32_t)buf[3] << 24 | (uint32_t)buf[2] << 16 |
(uint32_t)buf[1] << 8 | (uint32_t)buf[0];
}
int32_t s32_from_le(const unsigned char *buf)
{
return (int32_t)u32_from_le(buf);
}
uint16_t u16_from_le(const unsigned char *buf)
{
return (uint16_t)buf[1] << 8 | (uint16_t)buf[0];
}
int16_t s16_from_le(const unsigned char *buf)
{
return (int16_t)u16_from_le(buf);
}

View File

@@ -3,27 +3,21 @@
* Copyright (c) 2024, Rupert Weber.
*
* This file is part of bmplib.
* bmplib is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* bmplib is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
* License along with this library.
* If not, see <https://www.gnu.org/licenses/>
*/
#undef TRUE
#define TRUE (1)
#undef FALSE
#define FALSE (0)
#undef MAX
#undef MIN
#define MAX(a,b) ((a)>(b)?(a):(b))
@@ -32,10 +26,13 @@
#if defined(__GNUC__)
#define ATTR_CONST __attribute__((const))
#define API __attribute__ ((visibility ("default")))
#else
#define ATTR_CONST
#define API
#endif
union Pixel {
unsigned int value[4];
struct {
@@ -48,7 +45,7 @@ union Pixel {
struct Colormask {
union {
unsigned long value[4];
unsigned long long value[4];
struct {
unsigned long long red;
unsigned long long green;
@@ -57,21 +54,21 @@ struct Colormask {
};
} mask;
union {
unsigned long value[4];
int value[4];
struct {
unsigned long red;
unsigned long green;
unsigned long blue;
unsigned long alpha;
int red;
int green;
int blue;
int alpha;
};
} shift;
union {
int value[4];
struct {
int red;
int green;
int blue;
int alpha;
int red;
int green;
int blue;
int alpha;
};
} bits;
union {
@@ -87,8 +84,8 @@ struct Colormask {
typedef struct Bmpread *BMPREAD;
typedef struct Bmpwrite *BMPWRITE;
typedef struct Bmpread * restrict BMPREAD_R;
typedef struct Bmpwrite * restrict BMPWRITE_R;
typedef struct Bmpread *restrict BMPREAD_R;
typedef struct Bmpwrite *restrict BMPWRITE_R;
struct Palette {
int numcolors;
@@ -106,50 +103,52 @@ struct Bmpread {
struct Bmpinfo *ih;
unsigned int insanity_limit;
int width;
int height;
unsigned height;
enum BmpOrient orientation;
int has_alpha; /* original BMP has alpha channel */
bool has_alpha; /* original BMP has alpha channel */
enum BmpUndefined undefined_mode;
int we_allocated_buffer;
int line_by_line;
bool we_allocated_buffer;
bool line_by_line;
struct Palette *palette;
struct Colormask cmask;
/* result image dimensions */
enum Bmpconv64 conv64;
int conv64_explicit;
bool conv64_explicit;
int result_channels;
int result_indexed;
bool result_indexed;
int result_bits_per_pixel;
int result_bytes_per_pixel;
int result_bits_per_channel;
int result_bitsperchannel;
enum BmpFormat result_format;
int result_format_explicit;
bool result_format_explicit;
size_t result_size;
/* state */
unsigned long lasterr;
int getinfo_called;
bool getinfo_called;
int getinfo_return;
int jpeg;
int png;
int dimensions_queried;
int dim_queried_width;
int dim_queried_height;
int dim_queried_channels;
int dim_queried_bits_per_channel;
int image_loaded;
int rle;
int rle_eol;
int rle_eof;
bool jpeg;
bool png;
bool dimensions_queried;
bool dim_queried_width;
bool dim_queried_height;
bool dim_queried_channels;
bool dim_queried_bitsperchannel;
bool image_loaded;
bool rle;
bool rle_eol;
bool rle_eof;
int lbl_x; /* remember where we are in the image */
int lbl_y; /* for line by line reading */
int lbl_file_y; /* RLE files may be ahead of the image y */
int truncated;
int invalid_index;
int invalid_delta;
int invalid_overrun;
int file_err;
int file_eof;
int panic;
uint32_t hufbuf;
int hufbuf_len;
bool truncated;
bool invalid_index;
bool invalid_delta;
bool invalid_overrun;
bool file_err;
bool file_eof;
bool panic;
};
@@ -174,72 +173,80 @@ struct Bmpwrite {
/* output */
size_t bytes_written;
size_t bytes_written_before_bitdata;
int has_alpha;
bool has_alpha;
enum BmpOrient outorientation;
struct Colormask cmask;
int rle_requested;
int rle;
int allow_2bit; /* Windows CE, but many will not read it */
int out64bit;
enum BmpRLEtype rle_requested;
int rle; /* 1, 4, or 8 */
bool allow_2bit; /* Windows CE, but many will not read it */
bool allow_huffman;
bool allow_rle24;
bool out64bit;
int outbytes_per_pixel;
int padding;
int *group;
int group_count;
/* state */
int outbits_set;
int dimensions_set;
int saveimage_done;
int line_by_line;
bool outbits_set;
bool dimensions_set;
bool saveimage_done;
bool line_by_line;
int lbl_y;
uint32_t hufbuf;
int hufbuf_len;
};
int cm_all_lessoreq_int(int limit, int n, ...);
int cm_all_equal_int(int n, ...);
int cm_all_positive_int(int n, ...);
int cm_is_one_of(int candidate, int n, ...);
bool cm_all_lessoreq_int(int limit, int n, ...);
bool cm_all_equal_int(int n, ...);
bool cm_all_positive_int(int n, ...);
bool cm_is_one_of(int n, int candidate, ...);
#define cm_align4size(a) ((((a) + 3) >> 2) << 2)
#define cm_align2size(a) ((((a) + 1) >> 1) << 1)
int cm_align4padding(unsigned long a);
int cm_align2padding(unsigned long a);
int cm_align4padding(unsigned long long a);
int cm_align2padding(unsigned long long a);
int cm_count_bits(unsigned long v);
int cm_gobble_up(BMPREAD_R rp, int count);
int cm_check_is_read_handle(BMPHANDLE h);
int cm_check_is_write_handle(BMPHANDLE h);
bool cm_gobble_up(BMPREAD_R rp, int count);
BMPREAD cm_read_handle(BMPHANDLE h);
BMPWRITE cm_write_handle(BMPHANDLE h);
const char* cm_conv64_name(enum Bmpconv64 conv);
const char* cm_format_name(enum BmpFormat format);
int write_u16_le(FILE *file, uint16_t val);
int write_u32_le(FILE *file, uint32_t val);
int read_u16_le(FILE *file, uint16_t *val);
int read_u32_le(FILE *file, uint32_t *val);
bool write_u16_le(FILE *file, uint16_t val);
bool write_u32_le(FILE *file, uint32_t val);
bool read_u16_le(FILE *file, uint16_t *val);
bool read_u32_le(FILE *file, uint32_t *val);
int write_s16_le(FILE *file, int16_t val);
int write_s32_le(FILE *file, int32_t val);
int read_s16_le(FILE *file, int16_t *val);
int read_s32_le(FILE *file, int32_t *val);
bool write_s16_le(FILE *file, int16_t val);
bool write_s32_le(FILE *file, int32_t val);
bool read_s16_le(FILE *file, int16_t *val);
bool read_s32_le(FILE *file, int32_t *val);
#define API __attribute__ ((visibility ("default")))
uint32_t u32_from_le(const unsigned char *buf);
int32_t s32_from_le(const unsigned char *buf);
uint16_t u16_from_le(const unsigned char *buf);
int16_t s16_from_le(const unsigned char *buf);
#define HMAGIC_READ (0x44414552UL)
#define HMAGIC_WRITE (0x54495257UL)
#define HMAGIC_READ 0x44414552UL
#define HMAGIC_WRITE 0x54495257UL
#define BMPFILE_BM (0x4d42)
#define BMPFILE_BA (0x4142)
#define BMPFILE_CI (0x4943)
#define BMPFILE_CP (0x5043)
#define BMPFILE_IC (0x4349)
#define BMPFILE_PT (0x5450)
#define BMPFILE_BM 0x4d42
#define BMPFILE_BA 0x4142
#define BMPFILE_CI 0x4943
#define BMPFILE_CP 0x5043
#define BMPFILE_IC 0x4349
#define BMPFILE_PT 0x5450
#define BMPFHSIZE (14)
#define BMPIHSIZE_V3 (40)
#define BMPIHSIZE_V4 (108)
#define BMPFHSIZE 14
#define BMPIHSIZE_V3 40
#define BMPIHSIZE_V4 108
#define BMPIHSIZE_OS22 64
typedef uint16_t WORD;
typedef uint32_t DWORD;
@@ -324,3 +331,10 @@ struct Bmpinfo {
/* we set our own unique values: */
#define BI_OS2_HUFFMAN 1001
#define BI_OS2_RLE24 1002
#define LCS_CALIBRATED_RGB 0
#define LCS_sRGB 0x73524742 /* 'sRGB' */
#define LCS_WINDOWS_COLOR_SPACE 0x57696e20 /* 'Win ' */
#define PROFILE_LINKED 0x4c494e4b /* 'LINK' */
#define PROFILE_EMBEDDED 0x4d424544 /* 'MBED' */

View File

@@ -3,18 +3,18 @@
* Copyright (c) 2024, Rupert Weber.
*
* This file is part of bmplib.
* bmplib is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* bmplib is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
* License along with this library.
* If not, see <https://www.gnu.org/licenses/>
*/
@@ -22,13 +22,18 @@
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <math.h>
#define BMPLIB_LIB
#include "config.h"
#include "bmplib.h"
#include "logging.h"
#include "bmp-common.h"
#include "huffman.h"
#include "bmp-read.h"
#include "reversebits.h"
/*
@@ -40,31 +45,37 @@
* \ /
* common prep work, \ /
* buffer allocation, s_load_image_or_line()
* sanity checks, etc. / \
* / \
* / \
* 'supervision' s_read_whole_image() s_read_one_line()
* \ /
* \ /
* \ /
* sanity checks, etc. | |
* | |
* | |
* s_read_whole_image() |
* \ |
* \ |
* s_read_one_line()
* |
* s_read_rgb_line()
* 'grunt work' s_read_indexed_line()
* s_read_rle_line()
* s_read_huffman_line()
*/
static inline unsigned long s_scaleint(unsigned long val, int frombits, int tobits) ATTR_CONST;
static void s_set_file_error(BMPREAD_R rp);
static void s_log_error_from_state(BMPREAD_R rp);
static int s_cont_error(BMPREAD_R rp);
static int s_stopping_error(BMPREAD_R rp);
static bool s_cont_error(BMPREAD_R rp);
static bool s_stopping_error(BMPREAD_R rp);
static inline int s_read_one_byte(BMPREAD_R rp);
static inline void s_int_to_result_format(BMPREAD_R rp, int frombits, unsigned char *restrict px);
static BMPRESULT s_load_image_or_line(BMPREAD_R rp, unsigned char **restrict buffer, int line_by_line);
static int s_read_rgb_line(BMPREAD_R rp, unsigned char *restrict line);
static BMPRESULT s_load_image_or_line(BMPREAD_R rp, unsigned char **restrict buffer, bool line_by_line);
static void s_read_rgb_line(BMPREAD_R rp, unsigned char *restrict line);
static void s_read_indexed_line(BMPREAD_R rp, unsigned char *restrict line);
static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
int *restrict x, int *restrict yoff);
static void s_read_huffman_line(BMPREAD_R rp, unsigned char *restrict line);
_Static_assert(sizeof(float) == 4, "sizeof(float) must be 4. Cannot build bmplib.");
_Static_assert(sizeof(int) >= 4, "int must be at least 32bit. Cannot build bmplib.");
/********************************************************
@@ -75,12 +86,10 @@ API BMPRESULT bmpread_load_image(BMPHANDLE h, unsigned char **restrict buffer)
{
BMPREAD rp;
if (!(h && cm_check_is_read_handle(h)))
if (!(rp = cm_read_handle(h)))
return BMP_RESULT_ERROR;
rp = (BMPREAD)(void*)h;
return s_load_image_or_line(rp, buffer, FALSE);
return s_load_image_or_line(rp, buffer, false);
}
@@ -93,14 +102,13 @@ API BMPRESULT bmpread_load_line(BMPHANDLE h, unsigned char **restrict buffer)
{
BMPREAD rp;
if (!(h && cm_check_is_read_handle(h)))
if (!(rp = cm_read_handle(h)))
return BMP_RESULT_ERROR;
rp = (BMPREAD)(void*)h;
logreset(rp->log); /* otherwise we might accumulate thousands */
/* of log entries with large corrupt images */
return s_load_image_or_line(rp, buffer, TRUE);
return s_load_image_or_line(rp, buffer, true);
}
@@ -112,7 +120,7 @@ API BMPRESULT bmpread_load_line(BMPHANDLE h, unsigned char **restrict buffer)
static void s_read_whole_image(BMPREAD_R rp, unsigned char *restrict image);
static void s_read_one_line(BMPREAD_R rp, unsigned char *restrict image);
static BMPRESULT s_load_image_or_line(BMPREAD_R rp, unsigned char **restrict buffer, int line_by_line)
static BMPRESULT s_load_image_or_line(BMPREAD_R rp, unsigned char **restrict buffer, bool line_by_line)
{
size_t buffer_size;
@@ -147,7 +155,7 @@ static BMPRESULT s_load_image_or_line(BMPREAD_R rp, unsigned char **restrict buf
}
if (line_by_line)
buffer_size = rp->width * rp->result_bytes_per_pixel;
buffer_size = (size_t) rp->width * rp->result_bytes_per_pixel;
else
buffer_size = rp->result_size;
if (!*buffer) { /* no buffer supplied, we will allocate one */
@@ -155,17 +163,16 @@ static BMPRESULT s_load_image_or_line(BMPREAD_R rp, unsigned char **restrict buf
logsyserr(rp->log, "allocating result buffer");
return BMP_RESULT_ERROR;
}
rp->we_allocated_buffer = TRUE;
}
else {
rp->we_allocated_buffer = FALSE;
rp->we_allocated_buffer = true;
} else {
rp->we_allocated_buffer = false;
}
if (rp->we_allocated_buffer || (rp->rle && (rp->undefined_mode == BMP_UNDEFINED_TO_ALPHA)))
memset(*buffer, 0, buffer_size);
if (!line_by_line)
rp->image_loaded = TRUE; /* point of no return */
rp->image_loaded = true; /* point of no return */
if (!rp->line_by_line) { /* either whole image or first line */
if (rp->bytes_read > rp->fh->offbits) {
@@ -181,21 +188,19 @@ static BMPRESULT s_load_image_or_line(BMPREAD_R rp, unsigned char **restrict buf
}
if (line_by_line) {
rp->line_by_line = TRUE; /* don't set this earlier, or we won't */
rp->line_by_line = true; /* don't set this earlier, or we won't */
/* be able to identify first line */
s_read_one_line(rp, *buffer);
}
else {
} else {
s_read_whole_image(rp, *buffer);
}
s_log_error_from_state(rp);
if (s_stopping_error(rp)) {
rp->truncated = TRUE;
rp->image_loaded = TRUE;
rp->truncated = true;
rp->image_loaded = true;
return BMP_RESULT_TRUNCATED;
}
else if (s_cont_error(rp))
} else if (s_cont_error(rp))
return BMP_RESULT_INVALID;
return BMP_RESULT_OK;
@@ -205,7 +210,7 @@ abort:
free(*buffer);
*buffer = NULL;
}
rp->image_loaded = TRUE;
rp->image_loaded = true;
return BMP_RESULT_ERROR;
}
@@ -217,31 +222,17 @@ abort:
static void s_read_whole_image(BMPREAD_R rp, unsigned char *restrict image)
{
int x = 0, y, yoff = 1;
int y, yoff = 1;
size_t linesize, real_y;
linesize = (size_t) rp->width * (size_t) rp->result_bytes_per_pixel;
linesize = (size_t) rp->width * rp->result_bytes_per_pixel;
for (y = 0; y < rp->height; y += yoff) {
for (y = 0; y < (int) rp->height; y += yoff) {
real_y = (rp->orientation == BMP_ORIENT_TOPDOWN) ? y : rp->height-1-y;
if (rp->rle) {
s_read_rle_line(rp, image + real_y * linesize, &x, &yoff);
if (x >= rp->width)
x = 0;
}
else if (rp->ih->bitcount <= 8) {
s_read_indexed_line(rp, image + real_y * linesize);
}
else {
s_read_rgb_line(rp, image + real_y * linesize);
}
s_read_one_line(rp, image + real_y * linesize);
if (rp->rle_eof || s_stopping_error(rp))
break;
}
if (y > rp->height) {
logerr(rp->log, "RLE delta beyond image dimensions");
rp->invalid_delta = TRUE;
}
}
@@ -261,33 +252,34 @@ static void s_read_one_line(BMPREAD_R rp, unsigned char *restrict line)
if (rp->lbl_file_y > rp->lbl_y) {
; /* nothing to do, RLE skipped line */
}
else {
} else {
if (rp->rle) {
s_read_rle_line(rp, line, &rp->lbl_x, &yoff);
}
else {
s_read_indexed_line(rp, line);
} else {
if (rp->ih->compression == BI_OS2_HUFFMAN) {
s_read_huffman_line(rp, line);
} else {
s_read_indexed_line(rp, line);
}
}
if (!(rp->rle_eof || s_stopping_error(rp))) {
if (yoff > (int) rp->height - rp->lbl_file_y) {
rp->invalid_delta = true;
}
rp->lbl_file_y += yoff;
if (rp->lbl_file_y > rp->height) {
rp->invalid_delta = TRUE;
}
}
if (rp->rle_eof)
rp->lbl_file_y = rp->height;
}
}
else {
} else {
s_read_rgb_line(rp, line);
}
rp->lbl_y++;
if (rp->lbl_y >= rp->height) {
rp->image_loaded = TRUE;
if (rp->lbl_y >= (int) rp->height) {
rp->image_loaded = true;
}
}
@@ -297,7 +289,7 @@ static void s_read_one_line(BMPREAD_R rp, unsigned char *restrict line)
* s_read_rgb_line
*******************************************************/
static inline int s_read_rgb_pixel(BMPREAD_R rp, union Pixel *restrict px);
static inline bool s_read_rgb_pixel(BMPREAD_R rp, union Pixel *restrict px);
static inline double s_s2_13_to_float(uint16_t s2_13);
static inline double s_int_to_float(unsigned long ul, int bits);
static inline void s_convert64(uint16_t *val64);
@@ -305,12 +297,12 @@ static inline void s_convert64srgb(uint16_t *val64);
static inline double s_srgb_gamma_float(double d);
static inline uint16_t s_srgb_gamma_s2_13(uint16_t s2_13);
static int s_read_rgb_line(BMPREAD_R rp, unsigned char *restrict line)
static void s_read_rgb_line(BMPREAD_R rp, unsigned char *restrict line)
{
int i, x, padding;
union Pixel px;
size_t offs;
int bits = rp->result_bits_per_channel;
int bits = rp->result_bitsperchannel;
uint32_t pxval;
double d;
uint16_t s2_13;
@@ -318,7 +310,7 @@ static int s_read_rgb_line(BMPREAD_R rp, unsigned char *restrict line)
for (x = 0; x < rp->width; x++) {
if (!s_read_rgb_pixel(rp, &px)) {
return FALSE;
return;
}
offs = x * rp->result_channels;
@@ -339,8 +331,8 @@ static int s_read_rgb_line(BMPREAD_R rp, unsigned char *restrict line)
break;
default:
logerr(rp->log, "Waaaaaaaaaaaaaah!");
rp->panic = TRUE;
return FALSE;
rp->panic = true;
return;
}
}
if (rp->ih->bitcount == 64) {
@@ -363,13 +355,12 @@ static int s_read_rgb_line(BMPREAD_R rp, unsigned char *restrict line)
d = s_s2_13_to_float(px.value[i]);
if (i < 3 && rp->conv64 == BMP_CONV64_SRGB)
d = s_srgb_gamma_float(d);
((float*)line)[offs+i] = d;
((float*)line)[offs+i] = (float) d;
}
}
else {
} else {
for (i = 0; i < rp->result_channels; i++) {
d = s_int_to_float(px.value[i], rp->cmask.bits.value[i]);
((float*)line)[offs + i] = d;
((float*)line)[offs + i] = (float) d;
}
}
break;
@@ -382,8 +373,7 @@ static int s_read_rgb_line(BMPREAD_R rp, unsigned char *restrict line)
s2_13 = s_srgb_gamma_s2_13(s2_13);
((uint16_t*)line)[offs+i] = s2_13;
}
}
else {
} else {
for (i = 0; i < rp->result_channels; i++) {
d = s_int_to_float(px.value[i], rp->cmask.bits.value[i]);
d = d * 8192.0 + 0.5;
@@ -394,70 +384,60 @@ static int s_read_rgb_line(BMPREAD_R rp, unsigned char *restrict line)
default:
logerr(rp->log, "Unknown format");
rp->panic = TRUE;
return FALSE;
rp->panic = true;
return;
}
}
padding = cm_align4padding((rp->width * rp->ih->bitcount + 7) / 8);
padding = cm_align4padding(((uint64_t)rp->width * rp->ih->bitcount + 7) / 8);
if (!cm_gobble_up(rp, padding)) {
s_set_file_error(rp);
return FALSE;
return;
}
rp->bytes_read += padding;
return TRUE;
}
static inline double s_s2_13_to_float(uint16_t s2_13)
{
return ((double)((int16_t)s2_13)) / 8192.0;
return (int16_t)s2_13 / 8192.0;
}
static inline double s_int_to_float(unsigned long ul, int bits)
{
return (double) ul / (double) ((1ULL<<bits)-1);
return (double) ul / ((1ULL<<bits)-1);
}
static inline void s_convert64(uint16_t *val64)
{
int i;
int32_t s;
double d;
for (i = 0; i < 4; i++) {
s = val64[i];
s = s << 16 >> 16; /* propagate sign bit */
s *= 0xffff;
s >>= 13;
s = MAX(0, s);
s = MIN(s, 0xffff);
val64[i] = s;
for (int i = 0; i < 4; i++) {
d = (double) (int16_t) val64[i];
d /= 8192.0;
d = MAX(0.0, d);
d = MIN(d, 1.0);
val64[i] = (uint16_t) (d * 0xffff + 0.5);
}
}
static inline void s_convert64srgb(uint16_t *val64)
{
int i;
int32_t s;
double v;
double d;
for (i = 0; i < 4; i++) {
s = val64[i];
s = s << 16 >> 16; /* propagate sign bit */
if (i < 3) {
v = (double) s / (1<<13);
v = s_srgb_gamma_float(v);
s = (int32_t) (v * (double) 0xffff);
}
else { /* don't apply gamma to alpha channel */
s *= 0xffff;
s >>= 13;
}
s = MAX(0, s);
s = MIN(s, 0xffff);
val64[i] = s;
for (int i = 0; i < 4; i++) {
d = (double) (int16_t) val64[i];
d /= 8192.0;
d = MAX(0.0, d);
d = MIN(d, 1.0);
/* apply gamma to RGB channels, but not to alpha channel */
if (i < 3)
d = s_srgb_gamma_float(d);
val64[i] = (uint16_t) (d * 0xffff + 0.5);
}
}
@@ -477,7 +457,7 @@ static inline uint16_t s_srgb_gamma_s2_13(uint16_t s2_13)
{
double d;
d = (double) ((int16_t)s2_13) / 8192.0;
d = (int16_t)s2_13 / 8192.0;
d = s_srgb_gamma_float(d);
return (uint16_t) (((int)(d * 8192.0 + 0.5)) & 0xffff);
}
@@ -488,7 +468,7 @@ static inline uint16_t s_srgb_gamma_s2_13(uint16_t s2_13)
* s_read_rgb_pixel
*******************************************************/
static inline int s_read_rgb_pixel(BMPREAD_R rp, union Pixel *restrict px)
static inline bool s_read_rgb_pixel(BMPREAD_R rp, union Pixel *restrict px)
{
unsigned long long v;
int i, byte;
@@ -497,20 +477,20 @@ static inline int s_read_rgb_pixel(BMPREAD_R rp, union Pixel *restrict px)
for (i = 0; i < rp->ih->bitcount; i+=8 ) {
if (EOF == (byte = s_read_one_byte(rp))) {
s_set_file_error(rp);
return FALSE;
return false;
}
v |= ((unsigned long long)byte) << i;
}
px->red = (v & rp->cmask.mask.red) >> rp->cmask.shift.red;
px->green = (v & rp->cmask.mask.green) >> rp->cmask.shift.green;
px->blue = (v & rp->cmask.mask.blue) >> rp->cmask.shift.blue;
px->red = (unsigned int) ((v & rp->cmask.mask.red) >> rp->cmask.shift.red);
px->green = (unsigned int) ((v & rp->cmask.mask.green) >> rp->cmask.shift.green);
px->blue = (unsigned int) ((v & rp->cmask.mask.blue) >> rp->cmask.shift.blue);
if (rp->has_alpha)
px->alpha = (v & rp->cmask.mask.alpha) >> rp->cmask.shift.alpha;
px->alpha = (unsigned int) ((v & rp->cmask.mask.alpha) >> rp->cmask.shift.alpha);
else
px->alpha = (1<<rp->result_bits_per_channel) - 1;
px->alpha = (1ULL<<rp->result_bitsperchannel) - 1;
return TRUE;
return true;
}
@@ -519,14 +499,14 @@ static inline int s_read_rgb_pixel(BMPREAD_R rp, union Pixel *restrict px)
* s_read_indexed_line
* - 1/2/4/8 bits non-RLE indexed
*******************************************************/
static inline int s_read_n_bytes(BMPREAD_R rp, int n, unsigned long *restrict buff);
static inline bool s_read_n_bytes(BMPREAD_R rp, int n, unsigned long *restrict buff);
static inline unsigned long s_bits_from_buffer(unsigned long buf, int size,
int nbits, int used_bits);
static void s_read_indexed_line(BMPREAD_R rp, unsigned char *restrict line)
{
int bits_used, buffer_size, x = 0, v;
int done = FALSE;
bool done = false;
unsigned long buffer;
size_t offs;
@@ -545,21 +525,20 @@ static void s_read_indexed_line(BMPREAD_R rp, unsigned char *restrict line)
if (v >= rp->palette->numcolors) {
v = rp->palette->numcolors - 1;
rp->invalid_index = TRUE;
rp->invalid_index = true;
}
offs = (size_t) x * (size_t) rp->result_bytes_per_pixel;
offs = (size_t) x * rp->result_bytes_per_pixel;
if (rp->result_indexed) {
line[offs] = v;
}
else {
} else {
line[offs] = rp->palette->color[v].red;
line[offs+1] = rp->palette->color[v].green;
line[offs+2] = rp->palette->color[v].blue;
s_int_to_result_format(rp, 8, line + offs);
}
if (++x == rp->width) {
done = TRUE;
done = true;
break; /* discarding rest of buffer == padding */
}
}
@@ -572,7 +551,7 @@ static void s_read_indexed_line(BMPREAD_R rp, unsigned char *restrict line)
* s_read_n_bytes
*******************************************************/
static inline int s_read_n_bytes(BMPREAD_R rp, int n, unsigned long *restrict buff)
static inline bool s_read_n_bytes(BMPREAD_R rp, int n, unsigned long *restrict buff)
{
int byte;
@@ -580,12 +559,12 @@ static inline int s_read_n_bytes(BMPREAD_R rp, int n, unsigned long *restrict bu
while (n--) {
if (EOF == (byte = s_read_one_byte(rp))) {
s_set_file_error(rp);
return FALSE;
return false;
}
*buff <<= 8;
*buff |= byte;
}
return TRUE;
return true;
}
@@ -621,14 +600,15 @@ static inline unsigned long s_bits_from_buffer(unsigned long buf, int size,
static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
int *restrict x, int *restrict yoff)
{
int repeat = FALSE, left_in_run = 0;
int left_in_run = 0;
bool repeat = false, padding = false, odd = false;
int right, up;
int padding = FALSE, odd = FALSE, v, r = 0, g = 0, b = 0;
int v, r = 0, g = 0, b = 0;
size_t offs;
int bits = rp->ih->bitcount;
if (!(bits == 4 || bits == 8 || bits == 24)) {
rp->panic = TRUE;
rp->panic = true;
return;
}
@@ -656,7 +636,7 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
}
}
offs = (size_t) *x * (size_t) rp->result_bytes_per_pixel;
offs = (size_t) *x * rp->result_bytes_per_pixel;
if ((rp->undefined_mode == BMP_UNDEFINED_TO_ALPHA) && !rp->result_indexed)
line[offs+3] = 0xff; /* set alpha to 1.0 for defined pixels */
switch (bits) {
@@ -676,25 +656,24 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
}
if (v >= rp->palette->numcolors) {
v = rp->palette->numcolors - 1;
rp->invalid_index = TRUE;
rp->invalid_index = true;
}
if (rp->result_indexed) {
line[offs] = v;
}
else {
} else {
line[offs] = rp->palette->color[v].red;
line[offs+1] = rp->palette->color[v].green;
line[offs+2] = rp->palette->color[v].blue;
s_int_to_result_format(rp, 8, line+offs);
s_int_to_result_format(rp, 8, line + offs);
}
break;
}
*x += 1;
if (*x >= rp->width) {
rp->rle_eol = FALSE; /* EOL detected by width, not by RLE-code */
rp->rle_eol = false; /* EOL detected by width, not by RLE-code */
if (left_in_run) {
rp->invalid_overrun = TRUE;
rp->invalid_overrun = true;
}
break;
}
@@ -715,10 +694,10 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
s_set_file_error(rp);
break;
}
padding = FALSE;
odd = FALSE;
padding = false;
odd = false;
left_in_run = v;
repeat = TRUE;
repeat = true;
continue;
}
@@ -731,21 +710,21 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
/* start literal run */
if (v > 2) {
left_in_run = v;
repeat = FALSE;
repeat = false;
switch (bits) {
case 8:
case 24:
padding = v & 0x01 ? TRUE : FALSE;
padding = v & 0x01 ? true : false;
break;
case 4:
if ((v+1)%4 >= 2)
padding = TRUE;
padding = true;
else
padding = FALSE;
padding = false;
break;
}
odd = FALSE;
odd = false;
continue;
}
@@ -753,7 +732,7 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
if (v == 0) {
if (*x != 0 || rp->rle_eol) {
*x = rp->width;
rp->rle_eol = TRUE;
rp->rle_eol = true;
break;
}
continue;
@@ -761,7 +740,7 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
/* end of bitmap */
if (v == 1) {
rp->rle_eof = TRUE;
rp->rle_eof = true;
break;
}
@@ -772,7 +751,7 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
break;
}
if (right >= rp->width - *x) {
rp->invalid_delta = TRUE;
rp->invalid_delta = true;
break;
}
*x += right;
@@ -784,12 +763,115 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
}
logerr(rp->log, "Should never get here! (x=%d, byte=%d)", (int) *x, (int) v);
rp->panic = TRUE;
rp->panic = true;
break;
}
}
/********************************************************
* s_read_huffman_line
*******************************************************/
static bool s_huff_skip_eol(BMPREAD_R rp);
static bool s_huff_find_eol(BMPREAD_R rp);
static void s_read_huffman_line(BMPREAD_R rp, unsigned char *restrict line)
{
size_t offs;
int x = 0, runlen;
bool black = false;
while (x < rp->width) {
huff_fillbuf(rp);
if (rp->hufbuf_len == 0)
break;
if ((rp->hufbuf & 0x00ff) == 0) {
if (!s_huff_skip_eol(rp)) {
rp->truncated = true;
break;
}
if (x == 0) /* ignore eol at start of line */
continue;
break;
}
runlen = huff_decode(rp, black);
if (runlen == -1) {
/* code was invalid, look for next eol */
rp->lasterr |= BMP_ERR_PIXEL;
if (!s_huff_find_eol(rp))
rp->truncated = true;
break;
}
if (runlen > rp->width - x) {
rp->lasterr |= BMP_ERR_PIXEL;
runlen = rp->width - x;
}
for (int i = 0; i < runlen; i++, x++) {
offs = (size_t) x * rp->result_bytes_per_pixel;
if (rp->result_indexed) {
line[offs] = black;
} else {
line[offs] = rp->palette->color[black].red;
line[offs+1] = rp->palette->color[black].green;
line[offs+2] = rp->palette->color[black].blue;
s_int_to_result_format(rp, 8, line + offs);
}
}
black = !black;
}
}
static bool s_huff_skip_eol(BMPREAD_R rp)
{
huff_fillbuf(rp);
while (rp->hufbuf_len > 0) {
if (rp->hufbuf == 0) {
rp->hufbuf_len = 0;
huff_fillbuf(rp);
continue;
}
while ((rp->hufbuf & 0x0001) == 0) {
rp->hufbuf >>= 1;
rp->hufbuf_len--;
}
rp->hufbuf >>= 1;
rp->hufbuf_len--;
return true;
}
return false;
}
static bool s_huff_find_eol(BMPREAD_R rp)
{
/* look for the next full 12-bit eol sequence,
* discard anything else
*/
huff_fillbuf (rp);
while (rp->hufbuf_len > 11)
{
if ((rp->hufbuf & 0x07ff) == 0) {
rp->hufbuf >>= 11;
rp->hufbuf_len -= 11;
return s_huff_skip_eol (rp);
}
rp->hufbuf >>= 1;
rp->hufbuf_len -= 1;
if (rp->hufbuf_len < 12)
huff_fillbuf (rp);
}
return false;
}
/********************************************************
* s_int_to_result_format
@@ -805,12 +887,15 @@ static inline void s_int_to_result_format(BMPREAD_R rp, int frombits, unsigned c
if (rp->result_format == BMP_FORMAT_INT)
return;
#ifdef DEBUG
if (frombits > rp->result_bits_per_channel) {
printf("This is bad, frombits must be <= bits_per_channel");
if (frombits > rp->result_bitsperchannel) {
printf("This is bad, frombits must be <= result_bitsperchannel");
exit(1);
}
#endif
for (c = rp->result_channels - 1; c >= 0; c--) {
/* going backwards because we are converting in place and
* always growing, never shrinking
*/
switch (frombits) {
case 8:
v = px[c];
@@ -830,7 +915,7 @@ static inline void s_int_to_result_format(BMPREAD_R rp, int frombits, unsigned c
}
switch (rp->result_format) {
case BMP_FORMAT_FLOAT:
((float*)px)[c] = (double) v / ((1ULL<<frombits)-1);
((float*)px)[c] = (float) ((double) v / ((1ULL<<frombits)-1));
break;
case BMP_FORMAT_S2_13:
((uint16_t*)px)[c] = (uint16_t) ((double) v / ((1ULL<<frombits)-1) * 8192.0 + 0.5);
@@ -854,9 +939,9 @@ static inline void s_int_to_result_format(BMPREAD_R rp, int frombits, unsigned c
static void s_set_file_error(BMPREAD_R rp)
{
if (feof(rp->file))
rp->file_eof = TRUE;
rp->file_eof = true;
else
rp->file_err = TRUE;
rp->file_err = true;
}
@@ -889,13 +974,12 @@ static void s_log_error_from_state(BMPREAD_R rp)
* s_cont_error
*******************************************************/
static int s_cont_error(BMPREAD_R rp)
static bool s_cont_error(BMPREAD_R rp)
{
if (rp->invalid_index ||
rp->invalid_overrun) {
return TRUE;
if (rp->invalid_index || rp->invalid_overrun) {
return true;
}
return FALSE;
return false;
}
@@ -904,16 +988,16 @@ static int s_cont_error(BMPREAD_R rp)
* s_stopping_error
*******************************************************/
static int s_stopping_error(BMPREAD_R rp)
static bool s_stopping_error(BMPREAD_R rp)
{
if (rp->truncated ||
if (rp->truncated ||
rp->invalid_delta ||
rp->file_err ||
rp->file_eof ||
rp->file_err ||
rp->file_eof ||
rp->panic) {
return TRUE;
return true;
}
return FALSE;
return false;
}
@@ -938,31 +1022,5 @@ static inline int s_read_one_byte(BMPREAD_R rp)
static inline unsigned long s_scaleint(unsigned long val, int frombits, int tobits)
{
return (unsigned long) ((double) val * ((1<<tobits)-1) / ((1<<frombits)-1) + 0.5);
#ifdef NEVER
/* nice, but has some slight off-by-one rounding errors */
unsigned long result;
int spaceleft;
/* scaling down, easy */
if (frombits >= tobits)
return val >> (frombits - tobits);
if (frombits < 1)
return 0UL;
/* scaling up */
result = val << (tobits - frombits);
spaceleft = tobits - frombits;
while (spaceleft > 0) {
if (spaceleft >= frombits)
result |= val << (spaceleft - frombits);
else
result |= val >> (frombits - spaceleft);
spaceleft -= frombits;
}
return result;
#endif
return (unsigned long) ((double) val * ((1ULL<<tobits)-1) / ((1ULL<<frombits)-1) + 0.5);
}

View File

@@ -22,6 +22,9 @@
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#define BMPLIB_LIB
#include "config.h"
#include "bmplib.h"
@@ -48,9 +51,8 @@ API int bmpread_num_palette_colors(BMPHANDLE h)
{
BMPREAD rp;
if (!(h && cm_check_is_read_handle(h)))
if (!(rp = cm_read_handle(h)))
return 0;
rp = (BMPREAD)(void*)h;
if (rp->palette)
return rp->palette->numcolors;
@@ -72,10 +74,8 @@ API BMPRESULT bmpread_load_palette(BMPHANDLE h, unsigned char **palette)
int i,c;
size_t memsize;
if (!(h && cm_check_is_read_handle(h)))
return 0;
rp = (BMPREAD)(void*)h;
if (!(rp = cm_read_handle(h)))
return BMP_RESULT_ERROR;
if (!rp->getinfo_called) {
logerr(rp->log, "Must call bmpread_load_info() before loading palette");
@@ -102,9 +102,9 @@ API BMPRESULT bmpread_load_palette(BMPHANDLE h, unsigned char **palette)
/* irreversible. image will be returned as indexed pixels */
if (!rp->result_indexed) {
rp->result_indexed = TRUE;
rp->dimensions_queried = FALSE;
rp->dim_queried_channels = FALSE;
rp->result_indexed = true;
rp->dimensions_queried = false;
rp->dim_queried_channels = false;
rp->result_channels = 1;
if (!br_set_resultbits(rp))
return BMP_RESULT_ERROR;

File diff suppressed because it is too large Load Diff

View File

@@ -3,21 +3,21 @@
* Copyright (c) 2024, Rupert Weber.
*
* This file is part of bmplib.
* bmplib is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* bmplib is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
* License along with this library.
* If not, see <https://www.gnu.org/licenses/>
*/
void br_free(BMPREAD rp);
int br_set_resultbits(BMPREAD_R rp);
bool br_set_resultbits(BMPREAD_R rp);
BMPRESULT br_set_number_format(BMPREAD_R rp, enum BmpFormat format);

File diff suppressed because it is too large Load Diff

View File

@@ -3,18 +3,18 @@
* Copyright (c) 2024, Rupert Weber.
*
* This file is part of bmplib.
* bmplib is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* bmplib is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
* License along with this library.
* If not, see <https://www.gnu.org/licenses/>
*/

198
bmplib.h
View File

@@ -3,18 +3,18 @@
* Copyright (c) 2024, Rupert Weber.
*
* This file is part of bmplib.
* bmplib is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* bmplib is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
* License along with this library.
* If not, see <https://www.gnu.org/licenses/>
*/
@@ -22,15 +22,26 @@
#ifndef BMPLIB_H
#define BMPLIB_H
#ifdef __cplusplus
extern "C" {
extern "C" {
#endif
#if defined(__GNUC__)
#define DEPR(m) __attribute__ ((deprecated(m)))
#define DEPR(m) __attribute__ ((deprecated(m)))
#else
#define DEPR(m)
#define DEPR(m)
#endif
#if defined (WIN32) || defined (_WIN32)
#ifdef BMPLIB_LIB
#define APIDECL __declspec(dllexport)
#else
#define APIDECL __declspec(dllimport)
#endif
#else
#define APIDECL
#endif
typedef struct Bmphandle *BMPHANDLE;
@@ -75,13 +86,13 @@ typedef struct Bmphandle *BMPHANDLE;
* sufficiently high limit.
*/
enum Bmpresult {
BMP_RESULT_OK = 0,
BMP_RESULT_INVALID,
BMP_RESULT_TRUNCATED,
BMP_RESULT_INSANE,
BMP_RESULT_PNG,
BMP_RESULT_JPEG,
BMP_RESULT_ERROR,
BMP_RESULT_OK = 0,
BMP_RESULT_INVALID,
BMP_RESULT_TRUNCATED,
BMP_RESULT_INSANE,
BMP_RESULT_PNG,
BMP_RESULT_JPEG,
BMP_RESULT_ERROR,
};
typedef enum Bmpresult BMPRESULT;
@@ -101,11 +112,11 @@ typedef enum Bmpresult BMPRESULT;
* - bmp_set_number_format(BMP_FORMAT_S2_13).
*/
enum Bmpconv64 {
BMP_CONV64_SRGB = 0, /* default */
BMP_CONV64_LINEAR = 1,
BMP_CONV64_16BIT_SRGB DEPR("use BMP_CONV64_SRGB instead") = 0,
BMP_CONV64_16BIT DEPR("use BMP_CONV64_LINEAR instead") = 1,
BMP_CONV64_NONE
BMP_CONV64_SRGB = 0, /* default */
BMP_CONV64_LINEAR = 1,
BMP_CONV64_16BIT_SRGB DEPR("use BMP_CONV64_SRGB instead") = 0,
BMP_CONV64_16BIT DEPR("use BMP_CONV64_LINEAR instead") = 1,
BMP_CONV64_NONE
};
typedef enum Bmpconv64 BMPCONV64;
@@ -121,14 +132,14 @@ typedef enum Bmpconv64 BMPCONV64;
* I am going with BITMAPINFOHEADER = v3
*/
enum BmpInfoVer {
BMPINFO_CORE_OS21 = 1, /* 12 bytes */
BMPINFO_OS22, /* 16 / 40(!) / 64 bytes */
BMPINFO_V3, /* 40 bytes */
BMPINFO_V3_ADOBE1, /* 52 bytes, unofficial */
BMPINFO_V3_ADOBE2, /* 56 bytes, unofficial */
BMPINFO_V4, /* 108 bytes */
BMPINFO_V5, /* 124 bytes */
BMPINFO_FUTURE /* future versions, larger than 124 bytes */
BMPINFO_CORE_OS21 = 1, /* 12 bytes */
BMPINFO_OS22, /* 16 / 40(!) / 64 bytes */
BMPINFO_V3, /* 40 bytes */
BMPINFO_V3_ADOBE1, /* 52 bytes, unofficial */
BMPINFO_V3_ADOBE2, /* 56 bytes, unofficial */
BMPINFO_V4, /* 108 bytes */
BMPINFO_V5, /* 124 bytes */
BMPINFO_FUTURE /* future versions, larger than 124 bytes */
};
typedef enum BmpInfoVer BMPINFOVER;
@@ -145,9 +156,9 @@ typedef enum BmpInfoVer BMPINFOVER;
* table size.
*/
enum BmpRLEtype {
BMP_RLE_NONE,
BMP_RLE_AUTO,
BMP_RLE_RLE8
BMP_RLE_NONE,
BMP_RLE_AUTO,
BMP_RLE_RLE8
};
typedef enum BmpRLEtype BMPRLETYPE;
@@ -164,9 +175,9 @@ typedef enum BmpRLEtype BMPRLETYPE;
*
*/
enum BmpUndefined {
BMP_UNDEFINED_LEAVE,
BMP_UNDEFINED_TO_ZERO DEPR("use BMP_UNDEFINED_LEAVE instead") = 0,
BMP_UNDEFINED_TO_ALPHA /* default */
BMP_UNDEFINED_LEAVE,
BMP_UNDEFINED_TO_ZERO DEPR("use BMP_UNDEFINED_LEAVE instead") = 0,
BMP_UNDEFINED_TO_ALPHA /* default */
};
typedef enum BmpUndefined BMPUNDEFINED;
@@ -180,95 +191,100 @@ typedef enum BmpUndefined BMPUNDEFINED;
* still gives the orientation of the BMP file.
*/
enum BmpOrient {
BMP_ORIENT_BOTTOMUP,
BMP_ORIENT_TOPDOWN
BMP_ORIENT_BOTTOMUP,
BMP_ORIENT_TOPDOWN
};
typedef enum BmpOrient BMPORIENT;
enum BmpFormat {
BMP_FORMAT_INT,
BMP_FORMAT_FLOAT,
BMP_FORMAT_S2_13
BMP_FORMAT_INT,
BMP_FORMAT_FLOAT,
BMP_FORMAT_S2_13
};
typedef enum BmpFormat BMPFORMAT;
BMPHANDLE bmpread_new(FILE *file);
APIDECL BMPHANDLE bmpread_new(FILE *file);
BMPRESULT bmpread_load_info(BMPHANDLE h);
APIDECL BMPRESULT bmpread_load_info(BMPHANDLE h);
BMPRESULT bmpread_dimensions(BMPHANDLE h,
int *width,
int *height,
int *channels,
int *bitsperchannel,
BMPORIENT *orientation);
APIDECL BMPRESULT bmpread_dimensions(BMPHANDLE h,
int *width,
int *height,
int *channels,
int *bitsperchannel,
BMPORIENT *orientation);
int bmpread_width(BMPHANDLE h);
int bmpread_height(BMPHANDLE h);
int bmpread_channels(BMPHANDLE h);
int bmpread_bits_per_channel(BMPHANDLE h);
BMPORIENT bmpread_orientation(BMPHANDLE h);
APIDECL int bmpread_width(BMPHANDLE h);
APIDECL int bmpread_height(BMPHANDLE h);
APIDECL int bmpread_channels(BMPHANDLE h);
APIDECL int bmpread_bitsperchannel(BMPHANDLE h);
APIDECL BMPORIENT bmpread_orientation(BMPHANDLE h);
int bmpread_resolution_xdpi(BMPHANDLE h);
int bmpread_resolution_ydpi(BMPHANDLE h);
APIDECL int bmpread_resolution_xdpi(BMPHANDLE h);
APIDECL int bmpread_resolution_ydpi(BMPHANDLE h);
size_t bmpread_buffersize(BMPHANDLE h);
APIDECL size_t bmpread_buffersize(BMPHANDLE h);
BMPRESULT bmpread_load_image(BMPHANDLE h, unsigned char **buffer);
BMPRESULT bmpread_load_line(BMPHANDLE h, unsigned char **buffer);
APIDECL BMPRESULT bmpread_load_image(BMPHANDLE h, unsigned char **buffer);
APIDECL BMPRESULT bmpread_load_line(BMPHANDLE h, unsigned char **buffer);
int bmpread_num_palette_colors(BMPHANDLE h);
BMPRESULT bmpread_load_palette(BMPHANDLE h, unsigned char **palette);
APIDECL int bmpread_num_palette_colors(BMPHANDLE h);
APIDECL BMPRESULT bmpread_load_palette(BMPHANDLE h, unsigned char **palette);
void bmpread_set_undefined(BMPHANDLE h, BMPUNDEFINED mode);
void bmpread_set_insanity_limit(BMPHANDLE h, size_t limit);
APIDECL void bmpread_set_undefined(BMPHANDLE h, BMPUNDEFINED mode);
APIDECL void bmpread_set_insanity_limit(BMPHANDLE h, size_t limit);
int bmpread_is_64bit(BMPHANDLE h);
BMPRESULT bmpread_set_64bit_conv(BMPHANDLE h, BMPCONV64 conv);
APIDECL int bmpread_is_64bit(BMPHANDLE h);
APIDECL BMPRESULT bmpread_set_64bit_conv(BMPHANDLE h, BMPCONV64 conv);
BMPINFOVER bmpread_info_header_version(BMPHANDLE h);
int bmpread_info_header_size(BMPHANDLE h);
int bmpread_info_compression(BMPHANDLE h);
int bmpread_info_bitcount(BMPHANDLE h);
const char* bmpread_info_header_name(BMPHANDLE h);
const char* bmpread_info_compression_name(BMPHANDLE h);
BMPRESULT bmpread_info_channel_bits(BMPHANDLE h, int *r, int *g, int *b, int *a);
APIDECL BMPINFOVER bmpread_info_header_version(BMPHANDLE h);
APIDECL const char* bmpread_info_header_name(BMPHANDLE h);
APIDECL int bmpread_info_header_size(BMPHANDLE h);
APIDECL int bmpread_info_compression(BMPHANDLE h);
APIDECL const char* bmpread_info_compression_name(BMPHANDLE h);
APIDECL int bmpread_info_bitcount(BMPHANDLE h);
APIDECL BMPRESULT bmpread_info_channel_bits(BMPHANDLE h, int *r, int *g, int *b, int *a);
BMPHANDLE bmpwrite_new(FILE *file);
APIDECL BMPHANDLE bmpwrite_new(FILE *file);
BMPRESULT bmpwrite_set_dimensions(BMPHANDLE h,
unsigned width,
unsigned height,
unsigned channels,
unsigned bits_per_channel);
BMPRESULT bmpwrite_set_resolution(BMPHANDLE h, int xdpi, int ydpi);
BMPRESULT bmpwrite_set_output_bits(BMPHANDLE h, int red, int green, int blue, int alpha);
BMPRESULT bmpwrite_set_palette(BMPHANDLE h, int numcolors, const unsigned char *palette);
BMPRESULT bmpwrite_allow_2bit(BMPHANDLE h);
BMPRESULT bmpwrite_set_rle(BMPHANDLE h, BMPRLETYPE type);
BMPRESULT bmpwrite_set_orientation(BMPHANDLE h, BMPORIENT orientation);
BMPRESULT bmpwrite_set_64bit(BMPHANDLE h);
APIDECL BMPRESULT bmpwrite_set_dimensions(BMPHANDLE h,
unsigned width,
unsigned height,
unsigned channels,
unsigned bitsperchannel);
APIDECL BMPRESULT bmpwrite_set_resolution(BMPHANDLE h, int xdpi, int ydpi);
APIDECL BMPRESULT bmpwrite_set_output_bits(BMPHANDLE h, int red, int green, int blue, int alpha);
APIDECL BMPRESULT bmpwrite_set_palette(BMPHANDLE h, int numcolors, const unsigned char *palette);
APIDECL BMPRESULT bmpwrite_allow_2bit(BMPHANDLE h);
APIDECL BMPRESULT bmpwrite_allow_huffman(BMPHANDLE h);
APIDECL BMPRESULT bmpwrite_allow_rle24(BMPHANDLE h);
APIDECL BMPRESULT bmpwrite_set_rle(BMPHANDLE h, BMPRLETYPE type);
APIDECL BMPRESULT bmpwrite_set_orientation(BMPHANDLE h, BMPORIENT orientation);
APIDECL BMPRESULT bmpwrite_set_64bit(BMPHANDLE h);
BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image);
BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line);
APIDECL BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image);
APIDECL BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line);
BMPRESULT bmp_set_number_format(BMPHANDLE h, BMPFORMAT format);
APIDECL BMPRESULT bmp_set_number_format(BMPHANDLE h, BMPFORMAT format);
void bmp_free(BMPHANDLE h);
APIDECL void bmp_free(BMPHANDLE h);
const char* bmp_errmsg(BMPHANDLE h);
APIDECL const char* bmp_errmsg(BMPHANDLE h);
const char* bmp_version(void);
APIDECL const char* bmp_version(void);
/* these errorcodes aren't part of the API yet, but will be.
* currently only used internally by bmplib.
*/
#define BMP_ERRTYPE_HARD 0x0000000f
#define BMP_ERR_FILEIO 0x00000001
@@ -300,8 +316,10 @@ const char* bmp_version(void);
* removed from future versions:
*/
int DEPR("use bmpread_orientation() instead") bmpread_topdown(BMPHANDLE h);
void DEPR("use bmpread_set_undefined() instead") bmpread_set_undefined_to_alpha(BMPHANDLE h, int mode);
APIDECL int DEPR("use bmpread_orientation() instead") bmpread_topdown(BMPHANDLE h);
APIDECL void DEPR("use bmpread_set_undefined() instead") bmpread_set_undefined_to_alpha(BMPHANDLE h, int mode);
APIDECL int DEPR("use bmpread_bitsperchannel() instead") bmpread_bits_per_channel(BMPHANDLE h);
#ifdef __cplusplus
}

View File

@@ -12,5 +12,7 @@
"tab_size": 8,
"translate_tabs_to_spaces": false,
"trim_trailing_white_space_on_save": "all",
"trim_only_modified_white_space": false,
"ensure_newline_at_eof_on_save": true,
},
}

245
gen-huffman-codes.h Normal file
View File

@@ -0,0 +1,245 @@
/* bmplib - gen-huffman-codes.h
*
* Copyright (c) 2024, Rupert Weber.
*
* This file is part of bmplib.
* bmplib is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
* If not, see <https://www.gnu.org/licenses/>
*/
struct Huffcodetxt {
int number;
const char *bits;
};
static struct Huffcodetxt huff_term_white[] = {
{ 0, "00110101" },
{ 1, "000111" },
{ 2, "0111" },
{ 3, "1000" },
{ 4, "1011" },
{ 5, "1100" },
{ 6, "1110" },
{ 7, "1111" },
{ 8, "10011" },
{ 9, "10100" },
{ 10, "00111" },
{ 11, "01000" },
{ 12, "001000" },
{ 13, "000011" },
{ 14, "110100" },
{ 15, "110101" },
{ 16, "101010" },
{ 17, "101011" },
{ 18, "0100111" },
{ 19, "0001100" },
{ 20, "0001000" },
{ 21, "0010111" },
{ 22, "0000011" },
{ 23, "0000100" },
{ 24, "0101000" },
{ 25, "0101011" },
{ 26, "0010011" },
{ 27, "0100100" },
{ 28, "0011000" },
{ 29, "00000010" },
{ 30, "00000011" },
{ 31, "00011010" },
{ 32, "00011011" },
{ 33, "00010010" },
{ 34, "00010011" },
{ 35, "00010100" },
{ 36, "00010101" },
{ 37, "00010110" },
{ 38, "00010111" },
{ 39, "00101000" },
{ 40, "00101001" },
{ 41, "00101010" },
{ 42, "00101011" },
{ 43, "00101100" },
{ 44, "00101101" },
{ 45, "00000100" },
{ 46, "00000101" },
{ 47, "00001010" },
{ 48, "00001011" },
{ 49, "01010010" },
{ 50, "01010011" },
{ 51, "01010100" },
{ 52, "01010101" },
{ 53, "00100100" },
{ 54, "00100101" },
{ 55, "01011000" },
{ 56, "01011001" },
{ 57, "01011010" },
{ 58, "01011011" },
{ 59, "01001010" },
{ 60, "01001011" },
{ 61, "00110010" },
{ 62, "00110011" },
{ 63, "00110100" },
};
static struct Huffcodetxt huff_term_black[] = {
{ 0, "0000110111" },
{ 1, "010" },
{ 2, "11" },
{ 3, "10" },
{ 4, "011" },
{ 5, "0011" },
{ 6, "0010" },
{ 7, "00011" },
{ 8, "000101" },
{ 9, "000100" },
{ 10, "0000100" },
{ 11, "0000101" },
{ 12, "0000111" },
{ 13, "00000100" },
{ 14, "00000111" },
{ 15, "000011000" },
{ 16, "0000010111" },
{ 17, "0000011000" },
{ 18, "0000001000" },
{ 19, "00001100111" },
{ 20, "00001101000" },
{ 21, "00001101100" },
{ 22, "00000110111" },
{ 23, "00000101000" },
{ 24, "00000010111" },
{ 25, "00000011000" },
{ 26, "000011001010" },
{ 27, "000011001011" },
{ 28, "000011001100" },
{ 29, "000011001101" },
{ 30, "000001101000" },
{ 31, "000001101001" },
{ 32, "000001101010" },
{ 33, "000001101011" },
{ 34, "000011010010" },
{ 35, "000011010011" },
{ 36, "000011010100" },
{ 37, "000011010101" },
{ 38, "000011010110" },
{ 39, "000011010111" },
{ 40, "000001101100" },
{ 41, "000001101101" },
{ 42, "000011011010" },
{ 43, "000011011011" },
{ 44, "000001010100" },
{ 45, "000001010101" },
{ 46, "000001010110" },
{ 47, "000001010111" },
{ 48, "000001100100" },
{ 49, "000001100101" },
{ 50, "000001010010" },
{ 51, "000001010011" },
{ 52, "000000100100" },
{ 53, "000000110111" },
{ 54, "000000111000" },
{ 55, "000000100111" },
{ 56, "000000101000" },
{ 57, "000001011000" },
{ 58, "000001011001" },
{ 59, "000000101011" },
{ 60, "000000101100" },
{ 61, "000001011010" },
{ 62, "000001100110" },
{ 63, "000001100111" },
};
static struct Huffcodetxt huff_makeup_white[] = {
{ 64, "11011" },
{ 128, "10010" },
{ 192, "010111" },
{ 256, "0110111" },
{ 320, "00110110" },
{ 384, "00110111" },
{ 448, "01100100" },
{ 512, "01100101" },
{ 576, "01101000" },
{ 640, "01100111" },
{ 704, "011001100" },
{ 768, "011001101" },
{ 832, "011010010" },
{ 896, "011010011" },
{ 960, "011010100" },
{ 1024, "011010101" },
{ 1088, "011010110" },
{ 1152, "011010111" },
{ 1216, "011011000" },
{ 1280, "011011001" },
{ 1344, "011011010" },
{ 1408, "011011011" },
{ 1472, "010011000" },
{ 1536, "010011001" },
{ 1600, "010011010" },
{ 1664, "011000" },
{ 1728, "010011011" },
{ 1792, "00000001000" },
{ 1856, "00000001100" },
{ 1920, "00000001101" },
{ 1984, "000000010010" },
{ 2048, "000000010011" },
{ 2112, "000000010100" },
{ 2176, "000000010101" },
{ 2240, "000000010110" },
{ 2304, "000000010111" },
{ 2368, "000000011100" },
{ 2432, "000000011101" },
{ 2496, "000000011110" },
{ 2560, "000000011111" },
};
static struct Huffcodetxt huff_makeup_black[] = {
{ 64, "0000001111" },
{ 128, "000011001000" },
{ 192, "000011001001" },
{ 256, "000001011011" },
{ 320, "000000110011" },
{ 384, "000000110100" },
{ 448, "000000110101" },
{ 512, "0000001101100" },
{ 576, "0000001101101" },
{ 640, "0000001001010" },
{ 704, "0000001001011" },
{ 768, "0000001001100" },
{ 832, "0000001001101" },
{ 896, "0000001110010" },
{ 960, "0000001110011" },
{ 1024, "0000001110100" },
{ 1088, "0000001110101" },
{ 1152, "0000001110110" },
{ 1216, "0000001110111" },
{ 1280, "0000001010010" },
{ 1344, "0000001010011" },
{ 1408, "0000001010100" },
{ 1472, "0000001010101" },
{ 1536, "0000001011010" },
{ 1600, "0000001011011" },
{ 1664, "0000001100100" },
{ 1728, "0000001100101" },
{ 1792, "00000001000" },
{ 1856, "00000001100" },
{ 1920, "00000001101" },
{ 1984, "000000010010" },
{ 2048, "000000010011" },
{ 2112, "000000010100" },
{ 2176, "000000010101" },
{ 2240, "000000010110" },
{ 2304, "000000010111" },
{ 2368, "000000011100" },
{ 2432, "000000011101" },
{ 2496, "000000011110" },
{ 2560, "000000011111" },
};

253
gen-huffman.c Normal file
View File

@@ -0,0 +1,253 @@
/* bmplib - gen-huffman.c
*
* Copyright (c) 2024, Rupert Weber.
*
* This file is part of bmplib.
* bmplib is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
* If not, see <https://www.gnu.org/licenses/>
*/
/* This program generates the header file "huffman-codes.h" */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#if (! __bool_true_false_are_defined)
typedef int bool;
#define true 1
#define false 0
#endif
#include "gen-huffman-codes.h"
#define ARR_SIZE(a) (sizeof a / sizeof a[0])
struct Node {
int l;
int r;
int value;
bool terminal;
bool makeup;
};
static int nnodes = 0;
static struct Node nodebuffer[416];
static int black_tree = -1;
static int white_tree = -1;
static void s_buildtree(void);
static void add_node(int *nodeidx, const char *bits, int value, bool makeup);
static unsigned short str2bits(const char *str);
int main(int argc, char *argv[])
{
int i;
struct Node *n = nodebuffer;
FILE *file;
const char *src_name = "huffman-codes.h";
const char *this_name = "gen-huffman.c";
if (argc == 2) {
if (!(file = fopen(argv[1], "w"))) {
perror(argv[1]);
return 1;
}
} else {
file = stdout;
}
s_buildtree();
fprintf(file, "/* bmplib - %s\n", src_name);
fprintf(file, " *\n"
" * Copyright (c) 2024, Rupert Weber.\n"
" *\n"
" * This file is part of bmplib.\n"
" * bmplib is free software: you can redistribute it and/or modify\n"
" * it under the terms of the GNU Lesser General Public License as\n"
" * published by the Free Software Foundation, either version 3 of\n"
" * the License, or (at your option) any later version.\n"
" *\n");
fprintf(file, " * This program is distributed in the hope that it will be useful,\n"
" * but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
" * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
" * GNU Lesser General Public License for more details.\n"
" *\n"
" * You should have received a copy of the GNU Lesser General Public\n"
" * License along with this library.\n"
" * If not, see <https://www.gnu.org/licenses/>\n"
" */\n\n");
fprintf(file, "/* This file is auto-generated by %s */\n\n\n", this_name);
fputs("struct Node {\n"
"\tsigned short l;\n"
"\tsigned short r;\n"
"\tshort value;\n"
"\tchar terminal;\n"
"\tchar makeup;\n"
"};\n\n",
file);
fputs("struct Huffcode {\n"
"\tunsigned char bits;\n"
"\tunsigned char nbits;\n"
"};\n\n",
file);
fprintf(file, "static const int blackroot = %d;\n", black_tree);
fprintf(file, "static const int whiteroot = %d;\n\n\n", white_tree);
fprintf(file, "static const struct Node nodebuffer[] = {\n");
for (i = 0; i < ARR_SIZE(nodebuffer); i++) {
fprintf(file, "\t{ %3d, %3d, %4d, %d, %d },\n",
n[i].l, n[i].r, n[i].value, n[i].terminal, n[i].makeup);
}
fputs("};\n\n", file);
fputs("static const struct Huffcode huff_term_black[] = {\n\t", file);
for (i = 0; i < ARR_SIZE(huff_term_black); i++) {
fprintf(file, "{ 0x%02hx, %2d },",
str2bits(huff_term_black[i].bits),
(int) strlen(huff_term_black[i].bits));
if ((i+1) % 4 == 0 && i != ARR_SIZE(huff_term_black) - 1)
fputs("\n\t", file);
else
fputs(" ", file);
}
fputs("\n};\n\n", file);
fputs("static const struct Huffcode huff_term_white[] = {\n\t", file);
for (i = 0; i < ARR_SIZE(huff_term_white); i++) {
fprintf(file, "{ 0x%02hx, %2d },",
str2bits(huff_term_white[i].bits),
(int) strlen(huff_term_white[i].bits));
if ((i+1) % 4 == 0 && i != ARR_SIZE(huff_term_white) - 1)
fputs("\n\t", file);
else
fputs(" ", file);
}
fputs("\n};\n\n", file);
fputs("static const struct Huffcode huff_makeup_black[] = {\n\t", file);
for (i = 0; i < ARR_SIZE(huff_makeup_black); i++) {
fprintf(file, "{ 0x%02hx, %2d },",
str2bits(huff_makeup_black[i].bits),
(int) strlen(huff_makeup_black[i].bits));
if ((i+1) % 4 == 0 && i != ARR_SIZE(huff_makeup_black) - 1)
fputs("\n\t", file);
else
fputs(" ", file);
}
fputs("\n};\n\n", file);
fputs("static const struct Huffcode huff_makeup_white[] = {\n\t", file);
for (i = 0; i < ARR_SIZE(huff_makeup_white); i++) {
fprintf(file, "{ 0x%02hx, %2d },",
str2bits(huff_makeup_white[i].bits),
(int) strlen(huff_makeup_white[i].bits));
if ((i+1) % 4 == 0 && i != ARR_SIZE(huff_makeup_white) - 1)
fputs("\n\t", file);
else
fputs(" ", file);
}
fputs("\n};\n\n", file);
return 0;
}
static unsigned short str2bits(const char *str)
{
unsigned short value = 0;
while (*str) {
value <<= 1;
value |= *str - '0';
str++;
}
return value;
}
/*****************************************************************************
* s_buildtree()
****************************************************************************/
static void s_buildtree(void)
{
int i;
memset(nodebuffer, 0, sizeof nodebuffer);
for (i = 0; i < ARR_SIZE(huff_term_black); i++) {
add_node(&black_tree, huff_term_black[i].bits,
huff_term_black[i].number, false);
}
for (i = 0; i < ARR_SIZE(huff_makeup_black); i++) {
add_node(&black_tree, huff_makeup_black[i].bits,
huff_makeup_black[i].number, true);
}
for (i = 0; i < ARR_SIZE(huff_term_white); i++) {
add_node(&white_tree, huff_term_white[i].bits,
huff_term_white[i].number, false);
}
for (i = 0; i < ARR_SIZE(huff_makeup_white); i++) {
add_node(&white_tree, huff_makeup_white[i].bits,
huff_makeup_white[i].number, true);
}
}
/*****************************************************************************
* add_node()
****************************************************************************/
static void add_node(int *nodeidx, const char *bits, int value, bool makeup)
{
if (*nodeidx == -1) {
*nodeidx = nnodes++;
nodebuffer[*nodeidx].l = -1;
nodebuffer[*nodeidx].r = -1;
}
if (nnodes > ARR_SIZE(nodebuffer)) {
printf("too many nodes (have %d, max is %d)\n",
nnodes, (int) ARR_SIZE(nodebuffer));
exit(1);
}
if (!*bits) {
/* we are on the final bit of the sequence */
nodebuffer[*nodeidx].value = value;
nodebuffer[*nodeidx].terminal = true;
nodebuffer[*nodeidx].makeup = makeup;
} else {
switch (*bits) {
case '0':
add_node(&nodebuffer[*nodeidx].l, bits+1, value, makeup);
break;
case '1':
add_node(&nodebuffer[*nodeidx].r, bits+1, value, makeup);
break;
}
}
}

82
gen-reversebits.c Normal file
View File

@@ -0,0 +1,82 @@
/* bmplib - gen-reversebits.c
*
* Copyright (c) 2024, Rupert Weber.
*
* This file is part of bmplib.
* bmplib is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
* If not, see <https://www.gnu.org/licenses/>
*/
#include <stdio.h>
static int reverse(int val, int bits)
{
int mask;
bits /= 2;
if (bits == 0)
return val;
mask = (1 << bits) - 1;
return (reverse(val & mask, bits) << bits) | reverse(val >> bits, bits);
}
int main(int argc, char *argv[])
{
int reversed, i;
FILE *file;
const char *src_name = "reversebits.h";
const char *this_name = "gen-reversebits.c";
if (argc == 2) {
if (!(file = fopen(argv[1], "w"))) {
perror(argv[1]);
return 1;
}
} else {
file = stdout;
}
fprintf(file, "/* bmplib - %s\n", src_name);
fprintf(file, " *\n"
" * Copyright (c) 2024, Rupert Weber.\n"
" *\n"
" * This file is part of bmplib.\n"
" * bmplib is free software: you can redistribute it and/or modify\n"
" * it under the terms of the GNU Lesser General Public License as\n"
" * published by the Free Software Foundation, either version 3 of\n"
" * the License, or (at your option) any later version.\n"
" *\n");
fprintf(file, " * This program is distributed in the hope that it will be useful,\n"
" * but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
" * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
" * GNU Lesser General Public License for more details.\n"
" *\n"
" * You should have received a copy of the GNU Lesser General Public\n"
" * License along with this library.\n"
" * If not, see <https://www.gnu.org/licenses/>\n"
" */\n\n");
fprintf(file, "/* This file is auto-generated by %s */\n\n\n", this_name);
fprintf(file, "static const unsigned char reversebits[] = {\n\t");
for (i = 0; i < 256; i++) {
reversed = reverse(i, 8);
fprintf(file, "0x%02x, ", reversed);
if ((i + 1) % 8 == 0 && i < 255)
fprintf(file, "\n\t");
}
fprintf(file, "\n};\n");
return 0;
}

View File

@@ -0,0 +1 @@
google-site-verification: google0f997b088a966545.html

240
huffman.c Normal file
View File

@@ -0,0 +1,240 @@
/* bmplib - huffman.c
*
* Copyright (c) 2024, Rupert Weber.
*
* This file is part of bmplib.
* bmplib is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
* If not, see <https://www.gnu.org/licenses/>
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <limits.h>
#include <stdbool.h>
#include "config.h"
#include "bmplib.h"
#include "logging.h"
#include "bmp-common.h"
#include "reversebits.h"
#include "huffman.h"
#include "huffman-codes.h"
static int s_findnode(uint32_t bits, int nbits, bool black, int *found);
static bool s_zerofill(BMPWRITE_R wp);
/*****************************************************************************
* huff_decode()
*
* Decodes the rp->hufbuf_len long bit sequence given in rp->hufbuf.
* Direction is from lowest to highest bit.
* Returns -1 if no valid terminating code is found.
* EOL is _not_ handled, must be done by caller.
****************************************************************************/
int huff_decode(BMPREAD_R rp, int black)
{
int idx;
int bits_used = 0;
int result = 0;
do {
huff_fillbuf(rp);
bits_used = s_findnode(rp->hufbuf, rp->hufbuf_len, black, &idx);
if (idx == -1) {
/* invalid code */
return -1;
}
result += nodebuffer[idx].value;
rp->hufbuf >>= bits_used;
rp->hufbuf_len -= bits_used;
} while (nodebuffer[idx].makeup && result < INT_MAX - 2560);
return nodebuffer[idx].makeup ? -1 : result;
}
/*****************************************************************************
* s_findnode()
****************************************************************************/
static int s_findnode(uint32_t bits, int nbits, bool black, int *found)
{
int idx;
int bits_used = 0;
idx = black ? blackroot : whiteroot;
while (idx != -1 && !nodebuffer[idx].terminal && bits_used < nbits) {
if (bits & 1)
idx = nodebuffer[idx].r;
else
idx = nodebuffer[idx].l;
bits_used++;
bits >>= 1;
}
*found = idx;
return idx != -1 ? bits_used : 0;
}
/*****************************************************************************
* huff_fillbuf()
****************************************************************************/
void huff_fillbuf(BMPREAD_R rp)
{
int byte;
while (rp->hufbuf_len <= 24) {
if (EOF == (byte = getc(rp->file)))
break;
rp->bytes_read++;
byte = reversebits[byte];
rp->hufbuf |= ((uint32_t)byte) << rp->hufbuf_len;
rp->hufbuf_len += 8;
}
}
/*****************************************************************************
* s_push()
****************************************************************************/
static bool s_push(BMPWRITE_R wp, int bits, int nbits)
{
if (nbits > 32 - wp->hufbuf_len) {
if (!huff_flush(wp))
return false;
}
wp->hufbuf <<= nbits;
wp->hufbuf |= bits;
wp->hufbuf_len += nbits;
return true;
}
/*****************************************************************************
* huff_encode()
****************************************************************************/
bool huff_encode(BMPWRITE_R wp, int val, bool black)
{
const struct Huffcode *makeup, *term;
if (val == -1) {
/* eol */
return s_push(wp, 1, 12);
}
if (black) {
makeup = huff_makeup_black;
term = huff_term_black;
} else {
makeup = huff_makeup_white;
term = huff_term_white;
}
while (val > 63) {
int n = MIN(2560 / 64, val / 64);
if (!s_push(wp, makeup[n - 1].bits, makeup[n - 1].nbits))
return false;
val -= n * 64;
}
if (!s_push(wp, term[val].bits, term[val].nbits))
return false;
return true;
}
/*****************************************************************************
* huff_encode_eol()
****************************************************************************/
bool huff_encode_eol(BMPWRITE_R wp)
{
return huff_encode(wp, -1, 0);
}
/*****************************************************************************
* huff_encode_rtc()
****************************************************************************/
bool 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 bool 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()
****************************************************************************/
bool huff_flush(BMPWRITE_R wp)
{
int byte;
while (wp->hufbuf_len >= 8) {
byte = 0x00ff & (wp->hufbuf >> (wp->hufbuf_len - 8));
if (EOF == putc(byte, wp->file)) {
logsyserr(wp->log, "writing Huffman bitmap");
return false;
}
wp->bytes_written++;
wp->hufbuf_len -= 8;
wp->hufbuf &= (1UL << wp->hufbuf_len) - 1;
}
return true;
}

27
huffman.h Normal file
View File

@@ -0,0 +1,27 @@
/* bmplib - huffman.h
*
* Copyright (c) 2024, Rupert Weber.
*
* This file is part of bmplib.
* bmplib is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
* If not, see <https://www.gnu.org/licenses/>
*/
int huff_decode(BMPREAD_R rp, int black);
void huff_fillbuf(BMPREAD_R rp);
bool huff_encode(BMPWRITE_R wp, int val, bool black);
bool huff_encode_eol(BMPWRITE_R wp);
bool huff_encode_rtc(BMPWRITE_R wp);
bool huff_flush(BMPWRITE_R wp);

104
logging.c
View File

@@ -3,18 +3,18 @@
* Copyright (c) 2024, Rupert Weber.
*
* This file is part of bmplib.
* bmplib is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* bmplib is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
* License along with this library.
* If not, see <https://www.gnu.org/licenses/>
*/
@@ -22,6 +22,8 @@
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <limits.h>
#include <stdbool.h>
#include <errno.h>
#include "config.h"
@@ -29,28 +31,30 @@
struct Log {
size_t size;
char *buffer;
int size;
char *buffer;
bool panic;
};
/* logerr(log, fmt, ...) and logsyserr(log, fmt, ...) are
* printf-style logging functions.
*
* Use logsyserr() where perror() would be used, logerr()
* otherwise.
*
* 'separator' and 'inter' can have any length
* 'air' is just there so we don't need to realloc every
* single time.
*/
static const char separator[]="\n"; /* separator between log entries */
static const char inter[]=": "; /* between own message and sys err text */
static const int air = 80; /* how much more than required we allocate */
static const int air = 80; /* how much more than required we allocate */
static int s_allocate(LOG log, size_t add_chars);
static bool s_allocate(LOG log, size_t add_chars);
static void s_log(LOG log, const char *file, int line, const char *function,
const char *etxt, const char *fmt, va_list args);
const char *etxt, const char *fmt, va_list args);
static void panic(LOG log);
#ifdef DEBUG
static int s_add_file_etc(LOG log, const char *file, int line, const char *function);
@@ -64,7 +68,7 @@ static int s_add_file_etc(LOG log, const char *file, int line, const char *funct
LOG logcreate(void)
{
LOG log;
LOG log;
if (!(log = malloc(sizeof *log)))
return NULL;
@@ -75,7 +79,7 @@ LOG logcreate(void)
void logfree(LOG log)
{
if (log) {
if (log->size != (size_t)-1 && log->buffer)
if (log->size > 0 && log->buffer)
free(log->buffer);
free(log);
}
@@ -95,10 +99,16 @@ void logreset(LOG log)
const char* logmsg(LOG log)
{
if (log && log->buffer)
return log->buffer;
else
return "";
if (log) {
if (log->panic) {
return "PANIC! bmplib encountered an error while "
"trying to set an error message";
}
else if (log->buffer) {
return log->buffer;
}
}
return "";
}
@@ -113,17 +123,17 @@ void logerr_(LOG log, const char *file, int line, const char *function, const ch
void logerr(LOG log, const char *fmt, ...)
#endif
{
va_list args;
va_list args;
va_start(args, fmt);
va_start(args, fmt);
#ifdef DEBUG
s_log(log, file, line, function, NULL, fmt, args);
s_log(log, file, line, function, NULL, fmt, args);
#else
s_log(log, NULL, 0, NULL, NULL, fmt, args);
s_log(log, NULL, 0, NULL, NULL, fmt, args);
#endif
va_end(args);
va_end(args);
}
@@ -139,18 +149,18 @@ void logsyserr(LOG log, const char *fmt, ...)
#endif
{
va_list args;
const char *etxt;
const char *etxt;
va_start(args, fmt);
etxt = strerror(errno);
va_start(args, fmt);
etxt = strerror(errno);
#ifdef DEBUG
s_log(log, file, line, function, etxt, fmt, args);
s_log(log, file, line, function, etxt, fmt, args);
#else
s_log(log, NULL, 0, NULL, etxt, fmt, args);
s_log(log, NULL, 0, NULL, etxt, fmt, args);
#endif
va_end(args);
va_end(args);
}
@@ -160,13 +170,13 @@ void logsyserr(LOG log, const char *fmt, ...)
*********************************************************/
static void s_log(LOG log, const char *file, int line, const char *function,
const char *etxt, const char *fmt, va_list args)
const char *etxt, const char *fmt, va_list args)
{
va_list argsdup;
int len = 0,addl_len, required_len;
va_list argsdup;
int len = 0,addl_len, required_len;
if (log->size == (size_t)-1)
return; /* log is set to a string literal (panic) */
if (log->panic)
return;
#ifdef DEBUG
if (!s_add_file_etc(log, file, line, function))
@@ -174,7 +184,7 @@ static void s_log(LOG log, const char *file, int line, const char *function,
#endif
if (log->buffer)
len = strlen(log->buffer);
len = strlen(log->buffer);
va_copy(argsdup, args);
addl_len = vsnprintf(NULL, 0, fmt, argsdup);
@@ -197,7 +207,7 @@ static void s_log(LOG log, const char *file, int line, const char *function,
#endif
if (log->size - len <= vsnprintf(log->buffer + len, log->size - len,
fmt, args)) {
fmt, args)) {
panic(log);
return;
}
@@ -214,28 +224,34 @@ static void s_log(LOG log, const char *file, int line, const char *function,
* s_allocate()
*********************************************************/
static int s_allocate(LOG log, size_t add_chars)
static bool s_allocate(LOG log, size_t add_chars)
{
char *tmp;
size_t newsize;
if (log->size == (size_t)-1)
return 0; /* log is set to a string literal (panic) */
if (log->panic)
return false;
add_chars += air;
newsize = log->size + add_chars;
newsize = (size_t) log->size + add_chars;
if (newsize > INT_MAX) {
panic(log);
return false;
}
tmp = realloc(log->buffer, newsize);
if (tmp) {
log->buffer = tmp;
if (log->size == 0)
log->buffer[0] = 0;
log->size = newsize;
} else {
panic(log);
return false;
}
else
return 0;
return 1;
return true;
}
@@ -246,9 +262,7 @@ static int s_allocate(LOG log, size_t add_chars)
static void panic(LOG log)
{
log->size = (size_t) -1;
log->buffer = "PANIC! bmplib encountered an error while trying to set "
"an error message";
log->panic = true;
}
@@ -282,7 +296,7 @@ static int s_add_file_etc(LOG log, const char *file, int line, const char *funct
}
if (log->size - len <= snprintf(log->buffer + len, log->size - len,
"[%s, line %d, %s()] ", file, line, function)) {
"[%s, line %d, %s()] ", file, line, function)) {
panic(log);
return 0;
}

View File

@@ -3,18 +3,18 @@
* Copyright (c) 2024, Rupert Weber.
*
* This file is part of bmplib.
* bmplib is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* bmplib is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
* License along with this library.
* If not, see <https://www.gnu.org/licenses/>
*/
@@ -25,23 +25,23 @@ typedef struct Log *LOG;
#if defined(__GNUC__)
#define PRINTF(a,b) __attribute__((format(printf, a, b)))
#define PRINTF(a,b) __attribute__((format(printf, a, b)))
#else
#define PRINTF(a,b)
#define PRINTF(a,b)
#endif
#ifdef DEBUG
#define logerr(log, ...) logerr_(log, __FILE__, __LINE__, __func__, __VA_ARGS__)
void PRINTF(5,6) logerr_(LOG log, const char *file, int line,
const char *function, const char *fmt, ...);
#define logerr(log, ...) logerr_(log, __FILE__, __LINE__, __func__, __VA_ARGS__)
void PRINTF(5,6) logerr_(LOG log, const char *file, int line,
const char *function, const char *fmt, ...);
#define logsyserr(log, ...) logsyserr_(log, __FILE__, __LINE__, __func__, __VA_ARGS__)
void PRINTF(5,6) logsyserr_(LOG log, const char *file, int line,
const char *function, const char *fmt, ...);
#define logsyserr(log, ...) logsyserr_(log, __FILE__, __LINE__, __func__, __VA_ARGS__)
void PRINTF(5,6) logsyserr_(LOG log, const char *file, int line,
const char *function, const char *fmt, ...);
#else
void PRINTF(2,3) logerr(LOG log, const char *fmt, ...);
void PRINTF(2,3) logsyserr(LOG log, const char *fmt, ...);
void PRINTF(2,3) logerr(LOG log, const char *fmt, ...);
void PRINTF(2,3) logsyserr(LOG log, const char *fmt, ...);
#endif

View File

@@ -1,4 +1,4 @@
project('bmplib', 'c', default_options: ['c_std=c11'], version: '1.4.7')
project('bmplib', 'c', default_options: ['c_std=c11'], version: '1.7.3')
cc = meson.get_compiler('c')
@@ -27,6 +27,7 @@ bmplib_sources = ['bmp-read.c',
'bmp-read-loadimage.c',
'bmp-read-loadindexed.c',
'bmp-common.c',
'huffman.c',
'logging.c']
@@ -37,15 +38,31 @@ elif cc.sizeof('int') < 4
endif
gen_huffman = executable('gen-huffman', 'gen-huffman.c')
huff_codes = custom_target('huffman-codes.h',
output: 'huffman-codes.h',
command: [gen_huffman, '@OUTPUT@'],
)
gen_reversebits = executable('gen-reversebits', 'gen-reversebits.c')
reversebits = custom_target('reversebits.h',
output: 'reversebits.h',
command: [gen_reversebits, '@OUTPUT@'],
)
bmplib = shared_library('bmp',
bmplib_sources,
[bmplib_sources, huff_codes[0], reversebits[0]],
version: meson.project_version(),
install: true,
dependencies: m_dep)
dependencies: m_dep,
)
pkg_mod = import('pkgconfig')
pkg_mod.generate(libraries: bmplib,
version: meson.project_version(),
name: 'libbmp',
filebase: 'libbmp',
description: 'Library for reading/writing Windows BMP files.')
description: 'Library for reading/writing Windows BMP files.',
)