54 Commits

Author SHA1 Message Date
Rupert
b43fd940e6 bump version to 1.7.7 2025-04-18 15:53:08 +02:00
Rupert
61a4caf6ef fix line-by-line writing bug, refactor write state
bug introduced by 142427e17: line-by-line writing of RLE files resulted
in corrupted files.
Found while refactoring write state
2025-04-18 15:51:09 +02:00
Rupert
4942795e14 don't require explicitly calling load_info() before reading ICC profile 2025-04-17 23:48:19 +02:00
Rupert
52801cd5d5 cleanup s_decide_outformat() 2025-04-17 23:47:24 +02:00
Rupert
0e4e611955 add semicolons in doc for consistent syntax highlighter 2025-04-16 23:50:00 +02:00
Rupert
58ebfc8dc7 doc update, bump version to v1.7.6 2025-04-16 23:43:45 +02:00
Rupert
1e3f4abe16 add support for setting rendering intent
new function: bmpwrite_set_rendering_intent()
2025-04-16 23:08:14 +02:00
Rupert
be9882e04f documentation update
- add documentation for ICC profile functions
- add 'c' to code snippets to enable syntax highlighting
- minor corrections
2025-04-16 13:11:37 +02:00
Rupert
3218cd6e65 small fix in RLE24 preconditions 2025-04-15 22:13:26 +02:00
Rupert
142427e179 add ICC profile writing support
new function bmpwrite_set_iccprofile()
2025-04-15 22:13:26 +02:00
Rupert
0580565bf3 add ICC profile reading support
- new functions:
 - bmpread_iccprofile_size()
 - bmpread_load_iccprofile()
- only pass on profile as is, profile is not interpreted or applied
2025-04-15 22:10:13 +02:00
Rupert
7523138fbb bump version to 1.7.5 2025-04-10 18:29:17 +02:00
Rupert
592605e06f add sanitize meson option 2025-04-10 18:29:17 +02:00
Rupert
eeae2205c5 eliminate all -Wextra warnings
revert height back to int and make INT_MIN an invalid height. Having
a possible height value that's only valid for top-down but not bottom-up
BMPs just creates headaches down the way.
2025-04-10 18:29:17 +02:00
Rupert
2d7763de7f refactor huffman bit-flipping for readability 2025-04-10 11:57:12 +02:00
Rupert
7513352a7b add bmp_set_huffman_t4black_value()
ITU-T T.4 defines 'black' and 'white' (referring to fore- and back-
ground, respectively) pixel sequences, but it doesn't prescribe which
of those is represented by 0 or 1. That would have to be defined by the
BMP specification, but documentation on Huffman BMPs is close to
non-existent.

Current consensus seems to be that 'black' is 1, i.e. indexing the
second color in the palette, and 'white' is 0, i.e. indexing the first
color.

In case that's wrong (in fact it's not even clear if there is a right
and a wrong), bmp_set_huffman_t4black_value() can be used to set the
pixel value of 'black' to either 0 or 1 (and white to the respective
opposite).

Can be used both for reading and writing BMPs.

Changing this value will invert the image colors.
2025-04-09 23:11:58 +02:00
Rupert
ea7b93ce64 read/write handles: move shared portion into struct Bmpcommon
make the generic handle a union of common/read/write
2025-04-09 18:40:06 +02:00
Rupert
1b3c8a1081 add bmpwrite_set_huffman_img_fg_idx()
add ability to specify which palette index corresponds to foreground,
essential to optimize Huffman compression.
2025-04-09 12:21:26 +02:00
Rupert
1ede644b71 s_read_indexed_line(): refactor of 32-bit buffer 2025-04-03 16:01:10 +02:00
Rupert
1b9c8af659 typo NODEBUG -> NDEBUG 2025-04-03 15:15:53 +02:00
Rupert
a4800cb684 minor changes
- add an assert
- comment typo / formatting
2025-04-02 19:30:38 +02:00
Rupert
ed07c4f618 eliminate MS typedefs 2025-04-02 18:31:33 +02:00
Rupert
9297d88483 meson.build update
huffstats.c shouldn't have been in there. Was for testing, only
2025-03-02 17:28:48 +01:00
Rupert
c0c2513c7d remove reversebits table
change huffman bit buffer orientation, so we don't need the
reversebits lookup anymore.
2025-03-02 00:25:36 +01:00
Rupert
352a231632 minor corrections in s2.13 conversion 2025-01-23 12:36:01 +01:00
Rupert
72f8085c41 s_check_dimensions() needs only be called by s_set_resultbits() 2025-01-16 22:35:08 +01:00
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
25 changed files with 3291 additions and 2341 deletions

View File

@@ -7,6 +7,8 @@
BasedOnStyle: WebKit
SortIncludes: false
AlignAfterOpenBracket: Align
AlignArrayOfStructures: Right
AlignConsecutiveAssignments:
@@ -15,34 +17,47 @@ AlignConsecutiveAssignments:
AcrossComments: true
AlignCompound: true
AlignFunctionPointers: false
PadOperators: true
AlignConsecutiveDeclarations: Consecutive
AlignConsecutiveMacros: Consecutive
AlignEscapedNewlines: Left
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
AllowAllParametersOfDeclarationOnNextLine: true
BreakAfterReturnType: Automatic
BinPackArguments: true
BinPackParameters: true
BitFieldColonSpacing: After
BreakAfterReturnType: Automatic
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Linux
BreakBeforeTernaryOperators: false
BreakStringLiterals: false
IndentWidth: 8
PointerAlignment: Right
UseTab: ForIndentation
SpaceBeforeParens: ControlStatementsExceptControlMacros
SpaceBeforeParens: ControlStatements
SpaceAfterLogicalNot: false
SpaceAfterCStyleCast: false
ColumnLimit: 80
PenaltyBreakAssignment: 60
PenaltyBreakBeforeFirstCallParameter: 100
PenaltyBreakOpenParenthesis: 40
PenaltyExcessCharacter: 1
# Strings are more often longer by usage, so let's give these slightly
# more space to breath.
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,12 +1,12 @@
# Rupert's bmplib -- Full API Description
# Rupert's bmplib -- Full API Description (v1.7.7)
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.
## 1. Functions for reading BMP files
### Get a handle
```
```c
BMPHANDLE bmpread_new(FILE *file)
```
@@ -16,14 +16,14 @@ handle.
The handle cannot be reused to read multiple files.
### Read the file header
```
```c
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
(relevant is the required buffer size to hold the complete image, not the
@@ -31,7 +31,7 @@ bmplib reads the file header and checks validity. Possible return values are:
`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')
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.
@@ -42,15 +42,15 @@ bmplib reads the file header and checks validity. Possible return values are:
Calling `bmpread_load_info()` is optional when you use `bmpread_dimensions()`
(see below).
### Get image dimensions
```
```c
BMPRESULT bmpread_dimensions(BMPHANDLE h,
int *width,
int *height,
int *channels,
int *bitsperchannel,
BMPORIENT *orientation)
BMPORIENT *orientation);
```
Use `bmpread_dimensions()` to get all dimensions with one call. It is not
@@ -68,39 +68,41 @@ Note, in order to use these functions, -- unlike with `bmpread_dimensions
()` -- 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_bitsperchannel(BMPHANDLE h)
BMPORIENT bmpread_orientation(BMPHANDLE h)
int bmpread_resolution_xdpi(BMPHANDLE h)
int bmpread_resolution_ydpi(BMPHANDLE h)
```c
int bmpread_width(BMPHANDLE h);
int bmpread_height(BMPHANDLE h);
int bmpread_channels(BMPHANDLE h);
int bmpread_bitsperchannel(BMPHANDLE h);
BMPORIENT bmpread_orientation(BMPHANDLE h);
int bmpread_resolution_xdpi(BMPHANDLE h);
int bmpread_resolution_ydpi(BMPHANDLE h);
```
#### top-down / bottom-up
`orientation` is one of:
- `BMPORIENT_BOTTOMUP`
- `BMPORIENT_TOPDOWN`
`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
()`), 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.
`*orientation` is one of:
- `BMP_ORIENT_BOTTOMUP`
- `BMP_ORIENT_TOPDOWN`
`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
()`), 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.
#### Required size for buffer to receive image
```
size_t bmpread_buffersize(BMPHANDLE h)
```
Returns the buffer size you have to allocate for the whole image.
```c
size_t bmpread_buffersize(BMPHANDLE h);
```
Returns the buffer size you have to allocate for the whole image.
### Indexed BMPs
@@ -110,9 +112,9 @@ 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:
```
int bmpread_num_palette_colors(BMPHANDLE h)
BMPRESULT bmpread_load_palette(BMPHANDLE h, unsigned char **palette)
```c
int bmpread_num_palette_colors(BMPHANDLE h);
BMPRESULT bmpread_load_palette(BMPHANDLE h, unsigned char **palette);
```
`bmpread_num_palette_colors()` will return 0 for non-indexed images, otherwise
@@ -128,7 +130,7 @@ the palette buffer will contain "rgb0rgb0rgb0...".
As with the main image buffer, you can either provide one for the palette or
let bmplib allocate it for you (and then `free()` it, once you are done):
```
```c
unsigned char *palette;
int numcolors;
@@ -141,7 +143,6 @@ bmpread_load_palette(h, &palette); /* bmplib will allocate the palette
/* or: */
palette = malloc(4 * numcolors);
bmpread_load_palette(h, &palette); /* bmplib will use the provided buffer */
```
Note: Once you have called `bmpread_load_palette()`, both `bmpread_load_image
@@ -170,24 +171,50 @@ untouched by undefined pixels. (Note: if you let bmplib allocate the image
buffer, it will always be initialized to zero before loading the image). This
function has no effect on non-RLE BMPs.
```
void bmpread_set_undefined(BMPHANDLE h, BMPUNDEFINED mode)
```c
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
will always be left alone! (see above)
### ICC color profiles
```c
size_t bmpread_iccprofile_size(BMPHANDLE h);
BMPRESULT bmpread_load_iccprofile(BMPHANDLE h, unsigned char **pprofile);
```
Use `bmpread_iccprofile_size()` to query the size (or existence) of an
embedded color profile. If the BMP file doesn't contain a profile, the return
value is 0.
bmplib does not interpret or apply embedded ICC color profiles. The profile is
simply returned 'as is', image data is not afected in any way.
`bmpread_load_iccprofile()` loads the profile into the buffer pointed to by
`*pprofile`. As with loading image and palette data, you can either allocate
the buffer yourself or pass a pointer to a NULL-pointer and let bmplib
allocate an appropriate buffer, e.g.:
```c
unsigned char *profile = NULL;
if (bmpread_iccprofile_size(h) > 0)
bmpread_load_iccprofile(h, &profile);
```
### Optional settings for 64bit BMPs
```
int bmpread_is_64bit(BMPHANDLE h)
BMPRESULT bmpread_set_64bit_conv(BMPHANDLE h, BMPCONV64 conv)
```c
int bmpread_is_64bit(BMPHANDLE h);
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
@@ -195,7 +222,11 @@ 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 use `bmpread_set_64bit_conv
()` and `bmp_set_number_format()` to control how the image is returned:
()` and `bmp_set_number_format()` to control how the image is returned.
64bit BMP pixel values are in the [-4...4) range, beyond the usual
[0...1]. Unless you load the image as `BMP_FORMAT_S2_13` or `BMP_FORMAT_FLOAT`,
the values will be clipped to [0...1].
Options for `bmpread_set_64bit()` are:
@@ -203,41 +234,39 @@ Options for `bmpread_set_64bit()` are:
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.
`BMP_CONV64_LINEAR` *and also* calling `bmp_set_number_format()` with
`BMP_FORMAT_S2_13`. Image values are returned exactly as they are in the BMP
file, without any conversion or attempt at interpretation.
### 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:
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)
```c
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()
bmplib will refuse to load images beyond a certain size (default 500MB) and
instead return BMP_RESULT_INSANE. If you want to load the image anyway, call
`bmpread_set_insanity_limit()` at any time before calling `bmpread_load_image
()`. `limit` is the new allowed size in bytes. (not MB!)
instead return `BMP_RESULT_INSANE`. If you want to load the image anyway, call
`bmpread_set_insanity_limit()` at any time before calling `bmpread_load_image()`.
`limit` is the new allowed size in bytes (not MB!).
```c
void bmpread_set_insanity_limit(BMPHANDLE h, size_t limit);
```
void
bmpread_set_insanity_limit(BMPHANDLE h, size_t limit)
```
### Load the image
#### bmpread_load_image()
```
BMPRESULT bmpread_load_image(BMPHANDLE h, unsigned char **pbuffer)
```c
BMPRESULT bmpread_load_image(BMPHANDLE h, unsigned char **pbuffer);
```
Loads the complete image from the BMP file into the buffer pointed to by
@@ -249,7 +278,7 @@ with it.
If you allocate the buffer yourself, the buffer must be at least as large as
the size returned by `bmpread_buffersize()`.
```
```c
unsigned char *buffer;
/* either: */
@@ -259,7 +288,6 @@ 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
@@ -268,13 +296,14 @@ 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. (See above, "Getting information...")
If `bmpread_load_image()` returns BMP_RESULT_TRUNCATED or BMP_RESULT_INVALID,
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.
#### bmpread_load_line()
```
BMPRESULT bmpread_load_line(BMPHANDLE h, unsigned char **pbuffer)
```c
BMPRESULT bmpread_load_line(BMPHANDLE h, unsigned char **pbuffer);
```
Loads a single scan line from the BMP file into the buffer pointed to by
@@ -288,7 +317,7 @@ To determine the required buffer size, either divide the value from
calculate from the image dimensions returned by bmplib as width * channels *
bitsperchannel / 8.
```
```c
single_line_buffersize = bmpread_buffersize(h) / bmpread_height(h);
/* or */
single_line_buffersize = bmpread_width(h) * bmpread_channels(h) * bmpread_bitsperchannel(h) / 8;
@@ -303,18 +332,17 @@ returned in whichever order they are stored in the BMP. Use the value
returned by `bmpread_orientation()` to determine if it is top-down or
bottom-up. Almost all BMPs will be bottom-up. (see above)
### Invalid pixels
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
BMP_RESULT_TRUNCATED is returned.
`BMP_RESULT_INVALID`, unless the image is also truncated, then
`BMP_RESULT_TRUNCATED` is returned.
### Query info about the BMP file
@@ -322,50 +350,47 @@ Note: these functions return information about the original BMP file being
read. They do *not* describe the format of the returned image data, which may
be different!
```c
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);
```
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)
```
### Release the handle
```
void bmp_free(BMPHANDLE h)
```c
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.
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
### Get a handle
```
BMPHANDLE bmpwrite_new(FILE *file)
```c
BMPHANDLE bmpwrite_new(FILE *file);
```
### Set image dimensions
```
```c
BMPRESULT bmpwrite_set_dimensions(BMPHANDLE h,
unsigned width,
unsigned height,
unsigned channels,
unsigned bitsperchannel)
BMPRESULT bmpwrite_set_resolution(BMPHANDLE h, int xdpi, int ydpi)
unsigned bitsperchannel);
BMPRESULT bmpwrite_set_resolution(BMPHANDLE h, int xdpi, int ydpi);
```
Note: the dimensions set with `bmpwrite_set_dimensions()` describe the source
@@ -373,7 +398,6 @@ 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.
### Set the output format
Optional: set the bit-depth for each output channel. bmplib will otherwise
@@ -381,15 +405,15 @@ choose appropriate bit-depths for your image. The bit-depth per channel can
be anywhere between 0 and 32, inclusive. In sum, the bits must be at least 1
and must not exceed 32.
```
BMPRESULT bmpwrite_set_output_bits(BMPHANDLE h, int red, int green, int blue, int alpha)
```c
BMPRESULT bmpwrite_set_output_bits(BMPHANDLE h, int red, int green, int blue, int alpha);
```
### Indexed images
```
BMPRESULT bmpwrite_set_palette(BMPHANDLE h, int numcolors, unsigned char *palette)
BMPRESULT bmpwrite_allow_2bit(BMPHANDLE h)
```c
BMPRESULT bmpwrite_set_palette(BMPHANDLE h, int numcolors, unsigned char *palette);
BMPRESULT bmpwrite_allow_2bit(BMPHANDLE h);
```
You can write 1/2/4/8-bit indexed images by providing a color palette with
@@ -411,23 +435,58 @@ 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.
```c
BMPRESULT bmpwrite_set_rle(BMPHANDLE h, BMPRLETYPE type);
BMPRESULT bmpwrite_allow_huffman(BMPHANDLE h);
BMPRESULT bmpwrite_set_huffman_img_fg_idx(BMPHANDLE h, int idx);
```
Indexed images may optionally be written as run-length-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)
```
To activate RLE compression, call `bmpwrite_set_rle()` with `type` set to one
of the following values:
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
#### 1-D Huffman encoding
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!
In order to get the best compression result, you should also call
`bmpwrite_set_huffman_img_fg_idx()` to specify which color index (0 or 1) in
the image corresponds to the foreground color. Huffman compression is
optimized for scanned text, meaning short runs of foreground color and long(er)
runs of background color. This will not change the appearance of the
image, but setting it correctly will result in better compression.
#### 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
By default, bmplib will write BMP files bottom-up, which is how BMP files are
@@ -436,13 +495,14 @@ usually orientated.
For non-RLE files, you have the option to change the orientation to top-down.
(RLE files always have to be written in the default bottom-up orientation.)
```
BMPRESULT bmpwrite_set_orientation(BMPHANDLE h, BMPORIENT orientation)
```c
BMPRESULT bmpwrite_set_orientation(BMPHANDLE h, BMPORIENT orientation);
```
with `orientation` set to one of the following values:
- `BMPORIENT_BOTTOMUP`
- `BMPORIENT_TOPDOWN`
- `BMPORIENT_BOTTOMUP`
- `BMPORIENT_TOPDOWN`
Note: When writing the whole image at once using `bmpwrite_save_image()`, the
image buffer you provide must **always** be in top-down orientation,
@@ -453,31 +513,57 @@ provide the image lines in the order according to the orientation you have
chosen for the BMP file.
### ICC color profiles
```c
BMPRESULT bmpwrite_set_iccprofile(BMPHANDLE h, size_t size,
const unsigned char *iccprofile);
BMPRESULT bmpwrite_set_rendering_intent(BMPHANDLE h, BMPINTENT intent);
```
Use `bmpwrite_set_iccprofile()` to write an embedded ICC color profile to the
BMP file.
bmplib will not interpret or validate the supplied profile in any way.
Setting a color profile or rendering intent will disable Huffman and RLE24 encodings.
(color profiles require a BITMAPV5HEADER, but those encodings would require
an older OS/2 info header, instead.)
You can optionally specify a rendering intent with `bmpwrite_set_rendering_intent()`,
where `intent` is one of:
- `BMP_INTENT_NONE`
- `BMP_INTENT_BUSINESS` (= saturation)
- `BMP_INTENT_GRAPHICS` (= relative colorimetric)
- `BMP_INTENT_IMAGES` (= perceptive)
- `BMP_INTENT_ABS_COLORIMETRIC` (= absolute colorimetric)
### 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.
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)
```c
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.
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
```
BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image)
BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line)
```c
BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image);
BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line);
```
Write either the whole image at once with `bmpwrite_save_image()` or one line
@@ -489,76 +575,104 @@ in host byte order, the channels in the order R-G-B-(A). Indexed data must be
supplied as 8 bit per pixel, even when writing lower bit (1/2/4) BMPs
(see above).
Important: When writing the whole image at once using `bmpwrite_save_image
()`, the image data must be provided top-down (same as is returned by
Important: When writing the whole image at once using `bmpwrite_save_image()`,
the image data must be provided top-down (same as is returned by
`bmpread_load_image()`). When using `bmpwrite_save_line()` to write the image
line-by-line, the image data must be provided according to the orientation
set with `bmpwrite_set_orientation()` (see above).
## 3. General functions for both reading/writing BMPs
### bmp_free()
```
void bmp_free(BMPHANDLE h)
```c
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 messages returned
affected, so you can call `bmp_free()` immediately after `bmpread_load_image()`
and still use the returned image data. Note: Any error messages returned
by `bmp_errmsg()` are invalidated by `bmp_free()` and cannot be used
anymore.
anymore!
### bmp_errmsg()
```
const char* bmp_errmsg(BMPHANDLE h)
```c
const char* bmp_errmsg(BMPHANDLE h);
```
Returns a zero-terminated character string containing the last error
description(s). The returned string is safe to use until any other
bmplib-function is called with the same handle.
bmplib-function is called with the same handle or the handle is freed with
`bmp_free()`.
### bmp_set_number_format()
```
BMPRESULT bmp_set_number_format(BMPHANDLE h, BMPFORMAT format)
```c
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_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...
- `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()
```
const char* bmp_version(void)
```c
const char* bmp_version(void);
```
Returns a zero-terminated character string containing the version of bmplib.
### bmp_set_huffman_t4black_value()
```c
BMPRESULT bmp_set_huffman_t4black_value(BMPHANDLE h, int blackidx);
```
(not to be confused with `bmpwrite_set_huffman_img_fg_idx()`, which serves an
entirely different purpose, see above.)
ITU-T T.4 defines 'black' and 'white' pixel sequences (referring to fore- and
background, respectively), but it doesn't prescribe which of those is
represented by 0 or 1. That would have to be defined by a BMP specification,
but documentation on Huffman BMPs is close to non-existent.
Current consensus seems to be that 'black' is 1, i.e. indexing the second
color in the palette, and 'white' is 0, i.e. indexing the first color. This
is the default for bmplib.
In case that's wrong (in fact it's not even clear if there is a right and a
wrong), `bmp_set_huffman_t4black_value()` can be used to set the pixel value
of 'black' to either 0 or 1 (and white to the respective opposite).
Can be used both for reading and writing BMPs.
Changing this value will invert the image colors!
Reasons to use this function:
- You know that bmplib's default assumption of 'black'=1 is wrong, and you
want to set it to 0. (In that case, please also drop a note on github.)
- You don't care either way, but you want to be sure to get consistent
behaviour, in case bmplib's default is ever changed in light of new
information/documentation.
- You need to interface with other software that you know assumes 'black'=0.
## 4. Data types and constants
#### `BMPHANDLE`
Returned by `bmpread_new()` and `bmpwrite_new()`.
Identifies the current operation for all subsequent
calls to bmplib-functions.
Returned by `bmpread_new()` and `bmpwrite_new()`. Identifies the current
operation for all subsequent calls to bmplib-functions.
#### `BMPRESULT`
@@ -586,12 +700,12 @@ else {
}
```
#### `BMPINFOVER`
Returned by `bmpread_info_header_version()`. Possible values are:
- `BMPINFO_CORE_OS21` BITMAPCOREHEADER aka OS21XBITMAPHEADER (12 bytes)
- `BMPINFO_OS22` OS22XBITMAPHEADER (16/40/64 bytes)
- `BMPINFO_OS22` OS22XBITMAPHEADER (16-64 bytes)
- `BMPINFO_V3` BITMAPINFOHEADER (40 bytes)
- `BMPINFO_V3_ADOBE1` BITMAPINFOHEADER with additional RGB masks (52 bytes)
- `BMPINFO_V3_ADOBE2` BITMAPINFOHEADER with additional RGBA masks (56 bytes)
@@ -605,6 +719,7 @@ from `BMPINFO_CORE_OS21` to `BMPINFO_FUTURE`.
#### `BMPRLETYPE`
Used in `bmpwrite_set_rle()`. Possible values are:
- `BMP_RLE_NONE` No RLE
- `BMP_RLE_AUTO` RLE4 or RLE8, chosen based on number of colors in palette
- `BMP_RLE_RLE8` Use RLE8 for any number of colors in palette
@@ -614,14 +729,16 @@ Can safely be cast from/to int.
#### `BMPUNDEFINED`
Used in `bmpread_set_undefined()`. Possible values are:
- `BMP_UNDEFINED_TO_ALPHA` (default)
- `BMP_UNDEFINED_TO_ZERO`
- `BMP_UNDEFINED_LEAVE`
Can safely be cast from/to int.
#### `BMPCONV64`
Used in `bmpread_set_64bit_conv()`. Possible values are:
- `BMP_CONV64_SRGB` (default)
- `BMP_CONV64_LINEAR`
- `BMP_CONV64_NONE`
@@ -631,17 +748,16 @@ 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
```
```c
/* (all error checking left out for clarity) */
BMPHANDLE h;
@@ -650,7 +766,7 @@ Used in `bmp_set_number_format()`. Possible values are:
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.
*/
@@ -658,8 +774,8 @@ Used in `bmp_set_number_format()`. Possible values are:
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.
*/
@@ -671,30 +787,29 @@ Used in `bmp_set_number_format()`. Possible values are:
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.
*/
```
### Writing BMPs
```
```c
/* (all error checking left out for clarity) */
BMPHANDLE h;
@@ -708,13 +823,13 @@ Used in `bmp_set_number_format()`. Possible values are:
*/
/* 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.
@@ -723,7 +838,7 @@ Used in `bmp_set_number_format()`. Possible values are:
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.
*/
@@ -731,7 +846,7 @@ Used in `bmp_set_number_format()`. Possible values are:
bmpwrite_set_output_bits(h, 5, 6, 5, 0);
/* save data to file */
/* Save data to file */
bmpwrite_save_image(h, image_buffer);

View File

@@ -12,15 +12,16 @@ 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()
```c
bmpread_new();
bmpread_dimensions();
bmpread_load_image();
bmp_free();
```
### Get a handle
```
```c
BMPHANDLE bmpread_new(FILE *file)
```
@@ -32,13 +33,13 @@ The handle cannot be reused to read multiple files.
### Get image dimensions
```
```c
BMPRESULT bmpread_dimensions(BMPHANDLE h,
int *width,
int *height,
int *channels,
int *bitsperchannel,
BMPORIENT *orientation)
BMPORIENT *orientation);
```
Use `bmpread_dimensions()` to get all dimensions with one call. The return
@@ -49,13 +50,13 @@ 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 (see *Full API Description*)
line-by-line. Can be set to NULL. (see *Full API Description*)
### Load the image
```
BMPRESULT bmpread_load_image(BMPHANDLE h, unsigned char **pbuffer)
```c
BMPRESULT bmpread_load_image(BMPHANDLE h, unsigned char **pbuffer);
```
Loads the complete image from the BMP file into the buffer pointed to by
@@ -67,7 +68,7 @@ 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*).
```
```c
unsigned char *buffer;
/* either: */
@@ -86,7 +87,7 @@ 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,
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.
@@ -94,13 +95,13 @@ data is loaded anyway as far as possible and may be partially usable.
### Release the handle
```
void bmp_free(BMPHANDLE h)
```c
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.
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!
@@ -109,25 +110,27 @@ Note: Any error message strings returned by `bmp_errmsg()` are invalidated by
## 2. Writing BMP files:
```
bmpwrite_new()
bmpwrite_set_dimensions()
bmpwrite_save_image()
bmp_free()
```c
bmpwrite_new();
bmpwrite_set_dimensions();
bmpwrite_save_image();
bmp_free();
```
### Get a handle
```
BMPHANDLE bmpwrite_new(FILE *file)
```c
BMPHANDLE bmpwrite_new(FILE *file);
```
### Set image dimensions
```
```c
BMPRESULT bmpwrite_set_dimensions(BMPHANDLE h,
unsigned width,
unsigned height,
unsigned channels,
unsigned bitsperchannel)
unsigned bitsperchannel);
```
Note: the dimensions set with `bmpwrite_set_dimensions()` describe the source
@@ -140,8 +143,8 @@ API description*)
### Write the image
```
BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image)
```c
BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image);
```
Write the whole image at once with `bmpwrite_save_image()`.
@@ -155,8 +158,8 @@ file will be bottom-up.)
### bmp_free()
```
void bmp_free(BMPHANDLE h)
```c
void bmp_free(BMPHANDLE h);
```
Frees all resources associated with the handle `h`.
@@ -182,7 +185,7 @@ Many bmplib functions return the success/failure of an operation as a
- `BMP_RESULT_JPEG`
- `BMP_RESULT_ERROR`
Can safely be cast from/to int. BMP_RESULT_OK is guaranteed to have the value 0.
Can safely be cast from/to int. `BMP_RESULT_OK` is guaranteed to have the value 0.
@@ -190,16 +193,16 @@ Can safely be cast from/to int. BMP_RESULT_OK is guaranteed to have the value 0.
### Reading BMPs
```
```c
/* (all error checking left out for clarity) */
BMPHANDLE h;
FILE *file;
int width, height, channels, bitsperchannel, orientation;
int width, height, channels, bitsperchannel;
unsigned char *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.
*/
@@ -207,15 +210,16 @@ Can safely be cast from/to int. BMP_RESULT_OK is guaranteed to have the value 0.
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.
* Setting orientation to NULL, image is always returned top-down.
*/
bmpread_dimensions(h, &width, &height, &channels, &bitsperchannel, &orientation);
bmpread_dimensions(h, &width, &height, &channels, &bitsperchannel, NULL);
/* load the image and clean up: */
/* Load the image and clean up: */
image_buffer = NULL;
bmpread_load_image(h, &image_buffer);
@@ -224,9 +228,9 @@ Can safely be cast from/to int. BMP_RESULT_OK is guaranteed to have the value 0.
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.
*/
@@ -235,7 +239,7 @@ Can safely be cast from/to int. BMP_RESULT_OK is guaranteed to have the value 0.
### Writing BMPs
```
```c
/* (all error checking left out for clarity) */
BMPHANDLE h;
@@ -249,13 +253,13 @@ Can safely be cast from/to int. BMP_RESULT_OK is guaranteed to have the value 0.
*/
/* 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);
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.
@@ -264,7 +268,7 @@ Can safely be cast from/to int. BMP_RESULT_OK is guaranteed to have the value 0.
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.
*/
@@ -272,7 +276,7 @@ Can safely be cast from/to int. BMP_RESULT_OK is guaranteed to have the value 0.
bmpwrite_set_output_bits(h, 5, 6, 5, 0);
/* save data to file */
/* Save data to file */
bmpwrite_save_image(h, image_buffer);

View File

@@ -8,7 +8,7 @@
Download [bmplib on github](https://github.com/rupertwh/bmplib).
## Current status (v1.6.0):
## Current status (v1.7.7):
### Reading BMP files:
- 16/24/32 bit RGB(A) with any bits/channel combination
(BI_RGB, BI_BITFIELDS, BI_ALPHABITFIELDS).
@@ -25,7 +25,7 @@ Download [bmplib on github](https://github.com/rupertwh/bmplib).
- most 'questionable' files (see below)
- some 'bad' files
Questionable files that failed:
Questionable files that fail:
- embedded JPEG and PNG. Not really a fail. We return BMP_RESULT_JPEG or
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
@@ -37,7 +37,8 @@ Download [bmplib on github](https://github.com/rupertwh/bmplib).
- RGB(A) 16/24/32 bit.
- 64-bit RGBA
- any bit-depth combination for the RGBA channels.
- Indexed 1/2/4/8 bit, optional RLE4 and RLE8 compression.
- Indexed 1/2/4/8 bit, optional RLE4, RLE8, and 1-D Huffman compression.
- RLE24 compression.
- write BI_RGB when possible, BI_BITFIELDS only when
necessary.
- optional line-by-line writing of BMPs.
@@ -86,7 +87,7 @@ Includes:
#include <bmplib.h>
```
see API.md for the API documentation
see API-quick-start.md and API-full.md for the API documentation
### 64bit BMPs
@@ -124,8 +125,10 @@ conversion:
- `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.
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:
@@ -137,7 +140,7 @@ Use `bmpwrite_set_64bit()` in order to write 64bit 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
- [x] read/write icc-profile and chromaticity/gamma values
- [x] sanity checks for size of of image / palette. Require confirmation
above a certain size (~ 500MB?)
- [x] store undefined pixels (RLE delta and early EOL/EOF) as alpha

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,6 +23,9 @@
#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#include <stdbool.h>
#define BMPLIB_LIB
#include "config.h"
#include "bmplib.h"
@@ -33,13 +36,6 @@
#include "bmp-write.h"
struct Bmphandle {
struct {
uint32_t magic;
LOG log;
};
};
/********************************************************
@@ -59,10 +55,10 @@ API const char* bmp_version(void)
API const char* bmp_errmsg(BMPHANDLE h)
{
if (!(h && (h->magic == HMAGIC_READ || h->magic == HMAGIC_WRITE)))
if (!(h && (h->common.magic == HMAGIC_READ || h->common.magic == HMAGIC_WRITE)))
return "BMPHANDLE is NULL or invalid";
return logmsg(h->log);
return logmsg(h->common.log);
}
@@ -76,17 +72,17 @@ API BMPRESULT bmp_set_number_format(BMPHANDLE h, enum BmpFormat format)
if (!h)
return BMP_RESULT_ERROR;
switch (h->magic) {
switch (h->common.magic) {
case HMAGIC_READ:
return br_set_number_format((BMPREAD)(void*)h, format);
return br_set_number_format(&h->read, format);
case HMAGIC_WRITE:
return bw_set_number_format((BMPWRITE)(void*)h, format);
return bw_set_number_format(&h->write, format);
default:
#ifdef DEBUG
printf("bmp_set_number_format() called with invalid handle (0x%04x)\n",
(unsigned int) h->magic);
(unsigned int) h->common.magic);
#endif
break;
}
@@ -95,6 +91,25 @@ API BMPRESULT bmp_set_number_format(BMPHANDLE h, enum BmpFormat format)
/********************************************************
* bmp_set_huffman_t4black_value
*******************************************************/
API BMPRESULT bmp_set_huffman_t4black_value(BMPHANDLE h, int blackidx)
{
if (!h)
return BMP_RESULT_ERROR;
if (!(h->common.magic == HMAGIC_READ || h->common.magic == HMAGIC_WRITE))
return BMP_RESULT_ERROR;
h->common.huffman_black_is_zero = !blackidx;
return BMP_RESULT_OK;
}
/********************************************************
* bmp_free
*******************************************************/
@@ -104,19 +119,19 @@ API void bmp_free(BMPHANDLE h)
if (!h)
return;
switch (h->magic) {
switch (h->common.magic) {
case HMAGIC_READ:
br_free((BMPREAD)(void*)h);
br_free(&h->read);
break;
case HMAGIC_WRITE:
bw_free((BMPWRITE)(void*)h);
bw_free(&h->write);
break;
default:
#ifdef DEBUG
printf("bmp_free() called with invalid handle (0x%04x)\n",
(unsigned int) h->magic);
#endif
(unsigned int) h->common.magic);
#endif
break;
}
}
@@ -124,30 +139,28 @@ 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 (h && h->common.magic == HMAGIC_READ)
return &h->read;
if (rp && rp->magic == HMAGIC_READ)
return TRUE;
return FALSE;
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 (h && h->common.magic == HMAGIC_WRITE)
return &h->write;
if (wp && wp->magic == HMAGIC_WRITE)
return TRUE;
return FALSE;
return NULL;
}
@@ -155,7 +168,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 +176,15 @@ int cm_gobble_up(BMPREAD_R rp, int count)
if (EOF == getc(rp->file)) {
if (feof(rp->file)) {
rp->lasterr = BMP_ERR_TRUNCATED;
logerr(rp->log, "unexpected end of file");
logerr(rp->c.log, "unexpected end of file");
} else {
rp->lasterr = BMP_ERR_FILEIO;
logsyserr(rp->log, "error reading from file");
logsyserr(rp->c.log, "error reading from file");
}
return FALSE;
return false;
}
}
return TRUE;
return true;
}
@@ -187,9 +200,6 @@ int cm_count_bits(unsigned long v)
{
int bits = 0;
if (v < 0)
v = -v;
while (v) {
bits++;
v >>= 1;
@@ -221,18 +231,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 +253,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 +276,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 +298,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;
}
}
@@ -309,12 +323,12 @@ int cm_is_one_of(int candidate, int n, ...)
int cm_align4padding(unsigned long long a)
{
return cm_align4size(a) - a;
return (int) (cm_align4size(a) - a);
}
int cm_align2padding(unsigned long long a)
{
return cm_align2size(a) - a;
return (int) (cm_align2size(a) - a);
}
@@ -324,91 +338,130 @@ int cm_align2padding(unsigned long 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];
return write_u32_le(file, (uint32_t)val);
}
if (2 != fread(buf, 1, 2, file))
return 0;
bool read_s16_le(FILE *file, int16_t *val)
{
uint16_t u16;
*val = (((int16_t)(signed char)buf[1]) << 8) | (int16_t) buf[0];
if (!read_u16_le(file, &u16))
return false;
return 1;
*val = (int16_t)u16;
return true;
}
bool read_s32_le(FILE *file, int32_t *val)
{
uint32_t u32;
if (!read_u32_le(file, &u32))
return false;
*val = (int32_t)u32;
return true;
}
int read_s32_le(FILE *file, int32_t *val)
uint32_t u32_from_le(const unsigned char *buf)
{
unsigned char buf[4];
return (uint32_t)buf[3] << 24 | (uint32_t)buf[2] << 16 |
(uint32_t)buf[1] << 8 | (uint32_t)buf[0];
}
if (4 != fread(buf, 1, 4, file))
return 0;
int32_t s32_from_le(const unsigned char *buf)
{
return (int32_t)u32_from_le(buf);
}
*val = (((int32_t)(signed char)buf[3]) << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
uint16_t u16_from_le(const unsigned char *buf)
{
return (uint16_t)buf[1] << 8 | (uint16_t)buf[0];
}
return 1;
int16_t s16_from_le(const unsigned char *buf)
{
return (int16_t)u16_from_le(buf);
}
int write_s32_le(FILE *file, int32_t val)
{
int i;
for (i = 0; i < 4; i++) {
if (EOF == fputc((val >> (i*8)) & 0xff, file))
return 0;
/*****************************************************************************
* cm_infoheader_name
*****************************************************************************/
const char* cm_infoheader_name(enum BmpInfoVer infoversion)
{
switch (infoversion) {
case BMPINFO_CORE_OS21 : return "OS21XBITMAPHEADER";
case BMPINFO_OS22 : return "OS22XBITMAPHEADER";
case BMPINFO_V3 : return "BITMAPINFOHEADER";
case BMPINFO_V3_ADOBE1 : return "BITMAPINFOHEADER + RGB mask";
case BMPINFO_V3_ADOBE2 : return "BITMAPINFOHEADER + RGBA mask";
case BMPINFO_V4 : return "BITMAPV4HEADER";
case BMPINFO_V5 : return "BITMAPV5HEADER";
case BMPINFO_FUTURE : return "unknown future version";
default:
return "invalid infoheader version";
}
return 1;
}

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 {
@@ -85,82 +82,88 @@ struct Colormask {
} maxval;
};
typedef struct Bmpread *BMPREAD;
typedef struct Bmpwrite *BMPWRITE;
typedef struct Bmpread * restrict BMPREAD_R;
typedef struct Bmpwrite * restrict BMPWRITE_R;
struct Palette {
int numcolors;
union Pixel color[1];
};
struct Bmpcommon {
uint32_t magic;
LOG log;
bool huffman_black_is_zero; /* defaults to false */
};
struct Bmpread {
struct {
uint32_t magic;
LOG log;
};
struct Bmpcommon c;
FILE *file;
size_t bytes_read; /* number of bytes we have read from the file */
struct Bmpfile *fh;
struct Bmpinfo *ih;
unsigned int insanity_limit;
int width;
unsigned height;
int 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_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_bitsperchannel;
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 */
uint32_t hufbuf;
int hufbuf_len;
int truncated;
int invalid_index;
int invalid_delta;
int invalid_overrun;
int file_err;
int file_eof;
int panic;
bool truncated;
bool invalid_index;
bool invalid_delta;
bool invalid_overrun;
bool file_err;
bool file_eof;
bool panic;
};
enum WriteState {
WS_INIT,
WS_DIMENSIONS_SET,
WS_SAVE_STARTED,
WS_SAVE_DONE,
WS_FATAL,
};
struct Bmpwrite {
struct {
uint32_t magic;
LOG log;
};
struct Bmpcommon c;
FILE *file;
struct Bmpfile *fh;
struct Bmpinfo *ih;
@@ -170,37 +173,54 @@ struct Bmpwrite {
int source_channels;
int source_bitsperchannel;
int source_bytes_per_pixel;
int source_format;
enum BmpFormat source_format;
bool source_has_alpha;
struct Palette *palette;
int palette_size; /* sizeof palette in bytes */
unsigned char *iccprofile;
int iccprofile_size;
/* output */
size_t bytes_written;
uint64_t bytes_written;
size_t bytes_written_before_bitdata;
int has_alpha;
enum BmpOrient outorientation;
bool huffman_fg_idx;
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, 8, or 24 */
bool allow_2bit; /* Windows CE */
bool allow_huffman; /* OS/2 */
bool allow_rle24; /* OS/2 */
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;
enum WriteState write_state;
bool outbits_set;
int lbl_y;
uint32_t hufbuf;
int hufbuf_len;
};
union Bmphandle {
struct Bmpcommon common;
struct Bmpread read;
struct Bmpwrite write;
};
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, ...);
typedef struct Bmpread *BMPREAD;
typedef struct Bmpwrite *BMPWRITE;
typedef struct Bmpread *restrict BMPREAD_R;
typedef struct Bmpwrite *restrict BMPWRITE_R;
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)
@@ -208,105 +228,109 @@ 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);
const char* cm_infoheader_name(enum BmpInfoVer infoversion);
#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)
typedef uint16_t WORD;
typedef uint32_t DWORD;
typedef int32_t LONG;
typedef uint8_t BYTE;
#define BMPFHSIZE 14
#define BMPIHSIZE_V3 40
#define BMPIHSIZE_V4 108
#define BMPIHSIZE_OS22 64
#define BMPIHSIZE_V5 124
struct Bmpfile {
WORD type; /* "BM" */
DWORD size; /* bytes in file */
WORD reserved1;
WORD reserved2;
DWORD offbits;
uint16_t type; /* "BM" */
uint32_t size; /* bytes in file */
uint16_t reserved1;
uint16_t reserved2;
uint32_t offbits;
};
struct Bmpinfo {
/* BITMAPINFOHEADER (40 bytes) */
DWORD size; /* sizof struct */
LONG width;
LONG height;
WORD planes;
WORD bitcount;
DWORD compression;
DWORD sizeimage; /* 0 ok for uncompressed */
LONG xpelspermeter;
LONG ypelspermeter;
DWORD clrused;
DWORD clrimportant;
uint32_t size; /* sizof struct */
int32_t width;
int32_t height;
uint16_t planes;
uint16_t bitcount;
uint32_t compression;
uint32_t sizeimage; /* 0 ok for uncompressed */
int32_t xpelspermeter;
int32_t ypelspermeter;
uint32_t clrused;
uint32_t clrimportant;
/* BITMAPV4INFOHEADER (108 bytes) */
DWORD redmask;
DWORD greenmask;
DWORD bluemask;
DWORD alphamask;
DWORD cstype;
LONG redX;
LONG redY;
LONG redZ;
LONG greenX;
LONG greenY;
LONG greenZ;
LONG blueX;
LONG blueY;
LONG blueZ;
DWORD gammared;
DWORD gammagreen;
DWORD gammablue;
uint32_t redmask;
uint32_t greenmask;
uint32_t bluemask;
uint32_t alphamask;
uint32_t cstype;
int32_t redX;
int32_t redY;
int32_t redZ;
int32_t greenX;
int32_t greenY;
int32_t greenZ;
int32_t blueX;
int32_t blueY;
int32_t blueZ;
uint32_t gammared;
uint32_t gammagreen;
uint32_t gammablue;
/* BITMAPV5INFOHEADER (124 bytes) */
DWORD intent;
DWORD profiledata;
DWORD profilesize;
DWORD reserved;
uint32_t intent;
uint32_t profiledata;
uint32_t profilesize;
uint32_t reserved;
/* OS22XBITMAPHEADER */
WORD resolution; /* = 0 */
WORD orientation; /* = 0 */
WORD halftone_alg;
DWORD halftone_parm1;
DWORD halftone_parm2;
DWORD color_encoding; /* = 0 (RGB) */
DWORD app_id;
uint16_t resolution; /* = 0 */
uint16_t orientation; /* = 0 */
uint16_t halftone_alg;
uint32_t halftone_parm1;
uint32_t halftone_parm2;
uint32_t color_encoding; /* = 0 (RGB) */
uint32_t app_id;
/* internal only, not from file: */
enum BmpInfoVer version;
/* internal only, not from file: */
enum BmpInfoVer version;
};
#define IH_PROFILEDATA_OFFSET (14L + 112L)
#define MAX_ICCPROFILE_SIZE (1UL << 20)
#define BI_RGB 0
@@ -326,3 +350,16 @@ 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' */
#define LCS_GM_BUSINESS 1
#define LCS_GM_GRAPHICS 2
#define LCS_GM_IMAGES 4
#define LCS_GM_ABS_COLORIMETRIC 8

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,18 @@
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <assert.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"
/*
@@ -42,28 +45,30 @@
* \ /
* 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);
@@ -81,11 +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);
}
@@ -98,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 */
logreset(rp->c.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);
}
@@ -117,37 +120,37 @@ 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;
if (!(rp->getinfo_called && (rp->getinfo_return == BMP_RESULT_OK))) {
if (rp->getinfo_return == BMP_RESULT_INSANE) {
logerr(rp->log, "trying to load insanley large image");
logerr(rp->c.log, "trying to load insanley large image");
return BMP_RESULT_INSANE;
}
logerr(rp->log, "getinfo had failed, cannot load image");
logerr(rp->c.log, "getinfo had failed, cannot load image");
return BMP_RESULT_ERROR;
}
if (rp->image_loaded) {
logerr(rp->log, "Cannot load image more than once!");
logerr(rp->c.log, "Cannot load image more than once!");
return BMP_RESULT_ERROR;
}
if (rp->line_by_line && !line_by_line) {
logerr(rp->log, "Image is being loaded line-by-line. "
logerr(rp->c.log, "Image is being loaded line-by-line. "
"Cannot switch to full image.");
return BMP_RESULT_ERROR;
}
if (!rp->dimensions_queried) {
logerr(rp->log, "must query dimensions before loading image");
logerr(rp->c.log, "must query dimensions before loading image");
return BMP_RESULT_ERROR;
}
if (!buffer) {
logerr(rp->log, "buffer pointer is NULL");
logerr(rp->c.log, "buffer pointer is NULL");
return BMP_RESULT_ERROR;
}
@@ -157,35 +160,35 @@ static BMPRESULT s_load_image_or_line(BMPREAD_R rp, unsigned char **restrict buf
buffer_size = rp->result_size;
if (!*buffer) { /* no buffer supplied, we will allocate one */
if (!(*buffer = malloc(buffer_size))) {
logsyserr(rp->log, "allocating result buffer");
logsyserr(rp->c.log, "allocating result buffer");
return BMP_RESULT_ERROR;
}
rp->we_allocated_buffer = TRUE;
rp->we_allocated_buffer = true;
} else {
rp->we_allocated_buffer = FALSE;
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) {
logerr(rp->log, "Corrupt file");
logerr(rp->c.log, "Corrupt file");
goto abort;
}
/* skip to actual bitmap data: */
if (!cm_gobble_up(rp, rp->fh->offbits - rp->bytes_read)) {
logerr(rp->log, "while seeking start of bitmap data");
logerr(rp->c.log, "while seeking start of bitmap data");
goto abort;
}
rp->bytes_read += rp->fh->offbits - rp->bytes_read;
}
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 {
@@ -194,8 +197,8 @@ static BMPRESULT s_load_image_or_line(BMPREAD_R rp, unsigned char **restrict buf
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))
return BMP_RESULT_INVALID;
@@ -207,7 +210,7 @@ abort:
free(*buffer);
*buffer = NULL;
}
rp->image_loaded = TRUE;
rp->image_loaded = true;
return BMP_RESULT_ERROR;
}
@@ -219,31 +222,16 @@ 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 * rp->result_bytes_per_pixel;
for (y = 0; y < 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) {
if (rp->ih->compression == BI_OS2_HUFFMAN)
s_read_huffman_line(rp, image + real_y * linesize);
else
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 (yoff > rp->height - y) {
logerr(rp->log, "RLE delta beyond image dimensions");
rp->invalid_delta = TRUE;
}
}
}
@@ -277,7 +265,7 @@ static void s_read_one_line(BMPREAD_R rp, unsigned char *restrict line)
if (!(rp->rle_eof || s_stopping_error(rp))) {
if (yoff > rp->height - rp->lbl_file_y) {
rp->invalid_delta = TRUE;
rp->invalid_delta = true;
}
rp->lbl_file_y += yoff;
@@ -291,7 +279,7 @@ static void s_read_one_line(BMPREAD_R rp, unsigned char *restrict line)
rp->lbl_y++;
if (rp->lbl_y >= rp->height) {
rp->image_loaded = TRUE;
rp->image_loaded = true;
}
}
@@ -301,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);
@@ -309,7 +297,7 @@ 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;
@@ -322,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;
@@ -342,9 +330,9 @@ static int s_read_rgb_line(BMPREAD_R rp, unsigned char *restrict line)
((uint32_t*)line)[offs + i] = pxval;
break;
default:
logerr(rp->log, "Waaaaaaaaaaaaaah!");
rp->panic = TRUE;
return FALSE;
logerr(rp->c.log, "Waaaaaaaaaaaaaah!");
rp->panic = true;
return;
}
}
if (rp->ih->bitcount == 64) {
@@ -367,12 +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 {
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;
@@ -395,70 +383,61 @@ static int s_read_rgb_line(BMPREAD_R rp, unsigned char *restrict line)
break;
default:
logerr(rp->log, "Unknown format");
rp->panic = TRUE;
return FALSE;
logerr(rp->c.log, "Unknown format");
rp->panic = true;
return;
}
}
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);
}
}
@@ -478,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);
}
@@ -489,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;
@@ -498,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_bitsperchannel) - 1;
px->alpha = (1ULL<<rp->result_bitsperchannel) - 1;
return TRUE;
return true;
}
@@ -520,33 +499,62 @@ 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 unsigned long s_bits_from_buffer(unsigned long buf, int size,
int nbits, int used_bits);
struct Buffer32 {
uint32_t buffer;
int n;
};
static inline bool s_buffer32_fill(BMPREAD_R rp, struct Buffer32 *restrict buf)
{
int byte;
memset(buf, 0, sizeof *buf);
for (int i = 0; i < 4; i++) {
if (EOF == (byte = s_read_one_byte(rp))) {
s_set_file_error(rp);
return false;
}
buf->buffer <<= 8;
buf->buffer |= byte;
}
buf->n = 32;
return true;
}
static inline uint32_t s_buffer32_bits(struct Buffer32 *restrict buf, int nbits)
{
uint32_t result;
assert(nbits < 32);
result = buf->buffer >> (32 - nbits);
buf->buffer = (buf->buffer << nbits) & 0xffffffffUL;
buf->n -= nbits;
return result;
}
static void s_read_indexed_line(BMPREAD_R rp, unsigned char *restrict line)
{
int bits_used, buffer_size, x = 0, v;
int done = FALSE;
unsigned long buffer;
size_t offs;
int x = 0, v;
bool done = false;
struct Buffer32 buffer;
size_t offs;
/* setting the buffer size to the alignment takes care of padding bytes */
buffer_size = 32;
/* the buffer size of 32 bits takes care of padding bytes */
while (!done && s_read_n_bytes(rp, buffer_size / 8, &buffer)) {
while (!done && s_buffer32_fill(rp, &buffer)) {
bits_used = 0;
while (bits_used < buffer_size) {
while (buffer.n >= rp->ih->bitcount) {
/* mask out the relevant bits for current pixel */
v = (int) s_bits_from_buffer(buffer, buffer_size,
rp->ih->bitcount, bits_used);
bits_used += rp->ih->bitcount;
v = (int) s_buffer32_bits(&buffer, rp->ih->bitcount);
if (v >= rp->palette->numcolors) {
v = rp->palette->numcolors - 1;
rp->invalid_index = TRUE;
rp->invalid_index = true;
}
offs = (size_t) x * rp->result_bytes_per_pixel;
@@ -558,8 +566,9 @@ static void s_read_indexed_line(BMPREAD_R rp, unsigned char *restrict line)
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 */
}
}
@@ -568,51 +577,6 @@ 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)
{
int byte;
*buff = 0;
while (n--) {
if (EOF == (byte = s_read_one_byte(rp))) {
s_set_file_error(rp);
return FALSE;
}
*buff <<= 8;
*buff |= byte;
}
return TRUE;
}
/********************************************************
* s_bits_from_buffer
*******************************************************/
static inline unsigned long s_bits_from_buffer(unsigned long buf, int size,
int nbits, int used_bits)
{
unsigned long mask;
int shift;
shift = size - (nbits + used_bits);
mask = (1U << nbits) - 1;
mask <<= shift;
buf &= mask;
buf >>= shift;
return buf;
}
/********************************************************
* s_read_rle_line
* - 4/8/24 bit RLE
@@ -621,14 +585,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;
}
@@ -676,7 +641,7 @@ 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;
@@ -691,9 +656,9 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
*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;
}
@@ -714,10 +679,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;
}
@@ -730,21 +695,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;
}
@@ -752,7 +717,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;
@@ -760,7 +725,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;
}
@@ -771,7 +736,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;
@@ -782,8 +747,8 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
continue;
}
logerr(rp->log, "Should never get here! (x=%d, byte=%d)", (int) *x, (int) v);
rp->panic = TRUE;
logerr(rp->c.log, "Should never get here! (x=%d, byte=%d)", (int) *x, (int) v);
rp->panic = true;
break;
}
}
@@ -792,72 +757,106 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
/********************************************************
* 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)
{
int count;
size_t x = 0, offs;
int byte, eol = FALSE, ndecoded;
int black = 0;
size_t offs;
int x = 0, runlen;
bool black = false;
while (x < rp->width) {
huff_fillbuf(rp);
while(1) {
while (rp->hufbuf_len <= 24) {
if (EOF == (byte = s_read_one_byte(rp)))
break;
byte = reversebits[byte];
rp->hufbuf |= ((uint32_t)byte) << rp->hufbuf_len;
rp->hufbuf_len += 8;
}
if (rp->hufbuf_len == 0)
break;
if (eol || ((rp->hufbuf & 0x00ff) == 0)) {
/* EOL, look for next 1, ignore any number of 0's */
if (rp->hufbuf == 0) {
eol = TRUE;
rp->hufbuf_len = 0;
continue;
if ((rp->hufbuf & 0xff000000UL) == 0) {
if (!s_huff_skip_eol(rp)) {
rp->truncated = true;
break;
}
while ((rp->hufbuf & 0x0001) == 0) {
rp->hufbuf >>= 1;
rp->hufbuf_len--;
}
rp->hufbuf >>= 1;
rp->hufbuf_len--;
if (x == 0) /* ignore eol at start of line */
continue;
break;
}
ndecoded = huff_decode(&count, rp->hufbuf, rp->hufbuf_len, black);
if (ndecoded == 0) {
/* code was invalid, throw away one bit and try again */
rp->hufbuf >>= 1;
rp->hufbuf_len--;
continue;
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;
}
rp->hufbuf >>= ndecoded;
rp->hufbuf_len -= ndecoded;
count = MIN(count, rp->width - x);
if (runlen > rp->width - x) {
rp->lasterr |= BMP_ERR_PIXEL;
runlen = rp->width - x;
}
for (int i = 0; i < count; i++) {
offs = x * rp->result_bytes_per_pixel;
for (int i = 0; i < runlen; i++, x++) {
offs = (size_t) x * rp->result_bytes_per_pixel;
if (rp->result_indexed) {
line[offs] = black;
line[offs] = black ^ rp->c.huffman_black_is_zero;
} 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 + x);
line[offs] = rp->palette->color[black ^ rp->c.huffman_black_is_zero].red;
line[offs+1] = rp->palette->color[black ^ rp->c.huffman_black_is_zero].green;
line[offs+2] = rp->palette->color[black ^ rp->c.huffman_black_is_zero].blue;
s_int_to_result_format(rp, 8, line + offs);
}
x++;
}
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 & 0x80000000UL) == 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 & 0xffe00000UL) == 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
@@ -879,6 +878,9 @@ static inline void s_int_to_result_format(BMPREAD_R rp, int frombits, unsigned c
}
#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];
@@ -898,14 +900,14 @@ 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);
break;
default:
#ifdef DEBUG
logerr(rp->log, "Unexpected result format %d", rp->result_format);
logerr(rp->c.log, "Unexpected result format %d", rp->result_format);
exit(1);
#endif
break;
@@ -922,9 +924,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;
}
@@ -936,19 +938,19 @@ static void s_set_file_error(BMPREAD_R rp)
static void s_log_error_from_state(BMPREAD_R rp)
{
if (rp->panic)
logerr(rp->log, "An internal error occured.");
logerr(rp->c.log, "An internal error occured.");
if (rp->file_eof)
logerr(rp->log, "Unexpected end of file.");
logerr(rp->c.log, "Unexpected end of file.");
if (rp->file_err)
logsyserr(rp->log, "While reading file");
logsyserr(rp->c.log, "While reading file");
if (rp->invalid_index)
logerr(rp->log, "File contained invalid color index.");
logerr(rp->c.log, "File contained invalid color index.");
if (rp->invalid_delta)
logerr(rp->log, "Invalid delta pointing outside image area.");
logerr(rp->c.log, "Invalid delta pointing outside image area.");
if (rp->invalid_overrun)
logerr(rp->log, "RLE data overrunning image area.");
logerr(rp->c.log, "RLE data overrunning image area.");
if (rp->truncated)
logerr(rp->log, "Image was truncated.");
logerr(rp->c.log, "Image was truncated.");
}
@@ -957,13 +959,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;
}
@@ -972,16 +973,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;
}

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,29 +74,27 @@ 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");
logerr(rp->c.log, "Must call bmpread_load_info() before loading palette");
return BMP_RESULT_ERROR;
}
if (!rp->palette) {
logerr(rp->log, "Image has no palette");
logerr(rp->c.log, "Image has no palette");
return BMP_RESULT_ERROR;
}
if (!palette) {
logerr(rp->log, "palette is NULL");
logerr(rp->c.log, "palette is NULL");
return BMP_RESULT_ERROR;
}
memsize = rp->palette->numcolors * 4;
if (!*palette) {
if (!(*palette = malloc(memsize))) {
logsyserr(rp->log, "allocating palette");
logsyserr(rp->c.log, "allocating palette");
return BMP_RESULT_ERROR;
}
}
@@ -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/>
*/

226
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,16 +22,27 @@
#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
typedef struct Bmphandle *BMPHANDLE;
#if defined (WIN32) || defined (_WIN32)
#ifdef BMPLIB_LIB
#define APIDECL __declspec(dllexport)
#else
#define APIDECL __declspec(dllimport)
#endif
#else
#define APIDECL
#endif
typedef union 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;
@@ -113,22 +124,20 @@ typedef enum Bmpconv64 BMPCONV64;
/*
* BMP info header versions
*
* There doesn't seem to be consensus on whether the
* BITMAPINFOHEADER is version 1 (with the two Adobe
* extensions being v2 and v3) or version 3 (with the
* older BITMAPCIREHEADER and OS22XBITMAPHEADER being
* v1 and v2).
* I am going with BITMAPINFOHEADER = v3
* There doesn't seem to be consensus on whether the BITMAPINFOHEADER is
* version 1 (with the two Adobe extensions being v2 and v3) or version 3
* (with the older BITMAPCOREHEADER and OS22XBITMAPHEADER being v1 and v2).
* 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 +154,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 +173,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,93 +189,113 @@ 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);
enum BmpIntent {
BMP_INTENT_NONE,
BMP_INTENT_BUSINESS, /* saturation */
BMP_INTENT_GRAPHICS, /* relative colorimetric */
BMP_INTENT_IMAGES, /* perceptive */
BMP_INTENT_ABS_COLORIMETRIC /* absolute colorimetric */
};
typedef enum BmpIntent BMPINTENT;
BMPRESULT bmpread_load_info(BMPHANDLE h);
APIDECL BMPHANDLE bmpread_new(FILE *file);
BMPRESULT bmpread_dimensions(BMPHANDLE h,
int *width,
int *height,
int *channels,
int *bitsperchannel,
BMPORIENT *orientation);
APIDECL BMPRESULT bmpread_load_info(BMPHANDLE h);
int bmpread_width(BMPHANDLE h);
int bmpread_height(BMPHANDLE h);
int bmpread_channels(BMPHANDLE h);
int bmpread_bitsperchannel(BMPHANDLE h);
BMPORIENT bmpread_orientation(BMPHANDLE h);
APIDECL BMPRESULT bmpread_dimensions(BMPHANDLE h,
int *width,
int *height,
int *channels,
int *bitsperchannel,
BMPORIENT *orientation);
int bmpread_resolution_xdpi(BMPHANDLE h);
int bmpread_resolution_ydpi(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);
size_t bmpread_buffersize(BMPHANDLE h);
APIDECL int bmpread_resolution_xdpi(BMPHANDLE h);
APIDECL int bmpread_resolution_ydpi(BMPHANDLE h);
BMPRESULT bmpread_load_image(BMPHANDLE h, unsigned char **buffer);
BMPRESULT bmpread_load_line(BMPHANDLE h, unsigned char **buffer);
APIDECL size_t bmpread_buffersize(BMPHANDLE h);
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);
const char* bmpread_info_header_name(BMPHANDLE h);
int bmpread_info_header_size(BMPHANDLE h);
int bmpread_info_compression(BMPHANDLE h);
const char* bmpread_info_compression_name(BMPHANDLE h);
int bmpread_info_bitcount(BMPHANDLE h);
BMPRESULT bmpread_info_channel_bits(BMPHANDLE h, int *r, int *g, int *b, int *a);
APIDECL size_t bmpread_iccprofile_size(BMPHANDLE h);
APIDECL BMPRESULT bmpread_load_iccprofile(BMPHANDLE h, unsigned char **profile);
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 bitsperchannel);
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);
APIDECL BMPRESULT bmpwrite_set_huffman_img_fg_idx(BMPHANDLE h, int idx);
BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image);
BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line);
APIDECL BMPRESULT bmpwrite_set_iccprofile(BMPHANDLE h, size_t size,
const unsigned char *iccprofile);
APIDECL BMPRESULT bmpwrite_set_rendering_intent(BMPHANDLE h, BMPINTENT intent);
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);
APIDECL BMPRESULT bmp_set_huffman_t4black_value(BMPHANDLE h, int blackidx);
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.
@@ -303,9 +332,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);
int DEPR("use bmpread_bitsperchannel() instead") bmpread_bits_per_channel(BMPHANDLE h);
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 < (int) 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 < (int) 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 < (int) 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 < (int) 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 < (int) 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 < (int) 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 < (int) 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 < (int) 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 < (int) 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 > (int) 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;
}
}
}

View File

@@ -1,279 +0,0 @@
/* bmplib - huffman-codes.h
*
* Copyright (c) 2024, Rupert Weber, Luong Chi Mai.
*
* 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/>
*/
/*
* Modified Huffman terminating and makeup codes
*
* Source:
*
* INTRODUCTION TO COMPUTER VISION
* AND IMAGE PROCESSING
* by Luong Chi Mai
* Department of Pattern Recognition and Knowledge Engineering
* Institute of Information Technology, Hanoi, Vietnam
* E-mail: lcmai@ioit.ncst.ac.vn
*
* http://www.netnam.vn:80/unescocourse/computervision/104.htm
* (via Wayback machine, original site seems defunct)
*
* Excerpt:
*
* [...] Each line is coded as a series of alternating runs of white and black
* bits. Runs of 63 or less are coded with a terminating code. Runs of 64
* or greater require that a makeup code prefix the terminating code. The
* makeup codes are used to describe runs in multiples of 64 from 64 to
* 2560.
*
* [...] The protocol also assumes that the line begins with a run of white
* bits. If it doesn't, a run of white bits of 0 length must begin the
* encoded line. The encoding then alternates between black bits and white
* bits to the end of the line. Each scan line ends with a special EOL
* (end of line) character consisting of eleven zeros and a 1
* (000000000001).
*
* [...] a decoder seeing eight [zeroes] will recognize the end of line and
* continue scanning for a 1. Upon receiving the 1, it will then start a
* new line.
*
*/
struct Huffcode {
int number;
const char *bits;
};
static struct Huffcode 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" }, /* 00010110 ? orig: 00001110 */
{ 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" }, /* 001110011 */
{ 63, "00110100" },
};
static struct Huffcode 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 Huffcode 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" }, /* original 101010011 */
{ 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" },
{ 2170, "000000010101" },
{ 2240, "000000010110" },
{ 2304, "000000010111" },
{ 2368, "000000011100" },
{ 2432, "000000011101" },
{ 2496, "000000011110" },
{ 2560, "000000011111" },
/* { EOL, "000000000001" }, */
};
static struct Huffcode huff_makeup_black[] = { { 64, "0000001111" }, /* 000000111*/
{ 128, "000011001000" }, /* 00011001000 */
{ 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" },
{ 2170, "000000010101" },
{ 2240, "000000010110" },
{ 2304, "000000010111" },
{ 2368, "000000011100" },
{ 2432, "000000011101" },
{ 2496, "000000011110" },
{ 2560, "000000011111" },
/* { EOL, "000000000001" }, */
};

301
huffman.c
View File

@@ -23,201 +23,216 @@
#include <string.h>
#include <stdint.h>
#include <limits.h>
#include <stdarg.h>
#ifndef __STDC_NO_THREADS__
#include <threads.h>
#endif
#include <stdbool.h>
#include "config.h"
#include "bmplib.h"
#include "logging.h"
#include "bmp-common.h"
#include "huffman-codes.h"
#include "huffman.h"
#include "huffman-codes.h"
struct Node {
struct Node *l;
struct Node *r;
int value;
int terminal;
int makeup;
};
static int nnodes = 0;
static struct Node nodebuffer[2 * (ARR_SIZE(huff_term_black) + ARR_SIZE(huff_makeup_black)) + 1
+ 2 * (ARR_SIZE(huff_term_white) + ARR_SIZE(huff_makeup_white)) + 1];
static struct Node *black_tree;
static struct Node *white_tree;
static int s_findnode(uint32_t bits, int nbits, bool black, int *found);
static bool s_zerofill(BMPWRITE_R wp);
static void s_buildtree(void);
static void add_node(struct Node **node, const char *bits, int value, int makeup);
/*****************************************************************************
* 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.
****************************************************************************/
#ifndef __STDC_NO_THREADS__
static once_flag build_once = ONCE_FLAG_INIT;
#else
static int initialized = FALSE;
#endif
int huff_decode(int *result, uint32_t bits, int nbits, int black)
int huff_decode(BMPREAD_R rp, int black)
{
struct Node *node;
int bits_used = 0, sub, subresult;
int idx;
int bits_used = 0;
int result = 0;
#ifndef __STDC_NO_THREADS__
call_once(&build_once, s_buildtree);
#else
if (!initialized) {
s_buildtree();
initialized = TRUE;
}
#endif
do {
huff_fillbuf(rp);
bits_used = s_findnode(rp->hufbuf, rp->hufbuf_len, black, &idx);
if (idx == -1) {
/* invalid code */
return -1;
}
node = black ? black_tree : white_tree;
result += nodebuffer[idx].value;
rp->hufbuf <<= bits_used;
rp->hufbuf_len -= bits_used;
while (node && !node->terminal && bits_used < nbits) {
if (bits & 1)
node = node->r;
} 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 & 0x80000000UL)
idx = nodebuffer[idx].r;
else
node = node->l;
idx = nodebuffer[idx].l;
bits_used++;
bits >>= 1;
}
if (!node) {
/* invalid code */
return 0;
}
if (node->makeup) {
sub = huff_decode(&subresult, bits, nbits - bits_used, black);
if (!sub) {
return 0;
}
else {
*result = node->value + subresult;
return bits_used + sub;
}
} else {
*result = node->value;
return bits_used;
bits <<= 1;
}
*found = idx;
return idx != -1 ? bits_used : 0;
}
static void s_buildtree(void)
/*****************************************************************************
* huff_fillbuf()
****************************************************************************/
void huff_fillbuf(BMPREAD_R rp)
{
int i;
int byte;
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);
}
}
static void add_node(struct Node **node, const char *bits, int value, int makeup)
{
if (!*node) {
*node = &nodebuffer[nnodes++];
}
if (!*bits) {
/* we are on the final bit of the sequence */
(*node)->value = value;
(*node)->terminal = TRUE;
(*node)->makeup = makeup;
} else {
switch (*bits) {
case '0':
add_node(&((*node)->l), bits+1, value, makeup);
while (rp->hufbuf_len <= 24) {
if (EOF == (byte = getc(rp->file)))
break;
case '1':
add_node(&((*node)->r), bits+1, value, makeup);
break;
}
rp->bytes_read++;
rp->hufbuf |= (uint32_t)byte << (24 - rp->hufbuf_len);
rp->hufbuf_len += 8;
}
}
#ifdef NEVER
static void print_table(struct Huffcode *table, int size)
{
int i;
unsigned long buf;
/*****************************************************************************
* s_push()
****************************************************************************/
for (i = 0; i < size; i++) {
buf = 0;
for (int j = 0; j < strlen(table[i].bits); j++) {
buf <<= 1;
buf |= table[i].bits[j] == '1' ? 1 : 0;
}
printf ("%02d: 0x%02x, %d, %s\n", i, (int) buf, (int) strlen(table[i].bits), table[i].bits);
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;
}
static int depth = 0;
static void find_empty_nodes(struct Node *node)
/*****************************************************************************
* huff_encode()
****************************************************************************/
bool huff_encode(BMPWRITE_R wp, int val, bool black)
{
static char str[30];
const struct Huffcode *makeup, *term;
str[depth] = 0;
if (!node->terminal) {
if (!(node->l && node->r)) {
str[depth] = 0;
printf("node %s not full. l=%p r=%p\n", str, (void*)node->l, (void*)node->r);
}
if (node->l) {
str[depth++] = '0';
find_empty_nodes(node->l);
}
if (node->r) {
str[depth++] = '1';
find_empty_nodes(node->r);
}
if (val == -1) {
/* eol */
return s_push(wp, 1, 12);
}
if (black) {
makeup = huff_makeup_black;
term = huff_term_black;
} else {
//printf("%d: %s\n", node->value, str);
makeup = huff_makeup_white;
term = huff_term_white;
}
depth--;
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;
}
#endif
/*****************************************************************************
* 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->c.log, "writing Huffman bitmap");
return false;
}
wp->bytes_written++;
wp->hufbuf_len -= 8;
wp->hufbuf &= (1UL << wp->hufbuf_len) - 1;
}
return true;
}

View File

@@ -18,4 +18,10 @@
* If not, see <https://www.gnu.org/licenses/>
*/
int huff_decode(int *result, uint32_t bits, int nbits, int black);
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);

112
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,35 +22,45 @@
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <limits.h>
#include <stdbool.h>
#include <errno.h>
#include "config.h"
#include "logging.h"
#if defined(__GNUC__)
#define MAY_BE_UNUSED __attribute__((unused))
#else
#define MAY_BE_UNUSED
#endif
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 +74,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 +85,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 +105,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 +129,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 +155,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);
}
@@ -159,14 +175,15 @@ void logsyserr(LOG log, const char *fmt, ...)
* s_log()
*********************************************************/
static void s_log(LOG log, const char *file, int line, const char *function,
static void s_log(LOG log, const char *file MAY_BE_UNUSED, int line MAY_BE_UNUSED,
const char *function MAY_BE_UNUSED,
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 +191,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 +214,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,27 +231,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
return 0;
} else {
panic(log);
return false;
}
return 1;
return true;
}
@@ -245,9 +269,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;
}
@@ -281,7 +303,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,12 +1,23 @@
project('bmplib', 'c', default_options: ['c_std=c11'], version: '1.6.0')
project('bmplib', 'c', default_options: ['c_std=c11', 'warning_level=3'], version: '1.7.7')
cc = meson.get_compiler('c')
add_project_arguments(['-pedantic','-fvisibility=hidden'], language : 'c')
add_project_arguments('-pedantic', language : 'c')
add_project_arguments('-fvisibility=hidden', language: 'c')
if get_option('buildtype') == 'debug'
add_project_arguments('-DDEBUG', language: 'c')
elif get_option('buildtype') == 'release'
add_project_arguments('-DNDEBUG', language: 'c')
endif
if get_option('sanitize')
sanitize = [
'-fsanitize=signed-integer-overflow',
'-fsanitize=undefined',
'-fsanitize=float-divide-by-zero',
]
add_project_arguments(sanitize, language : 'c')
add_project_link_arguments(sanitize, language: 'c')
endif
m_dep = cc.find_library('m', required : false)
@@ -37,16 +48,24 @@ elif cc.sizeof('int') < 4
error('sizeof(int) must be at least 32 bit.')
endif
gen_huffman = executable('gen-huffman', 'gen-huffman.c')
huff_codes = custom_target('huffman-codes.h',
output: 'huffman-codes.h',
command: [gen_huffman, '@OUTPUT@'],
)
bmplib = shared_library('bmp',
bmplib_sources,
[bmplib_sources, huff_codes[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.',
)

View File

@@ -1 +1,2 @@
option('insanity_limit_mb', type: 'integer', min: 0, value: 500)
option('sanitize', type: 'boolean', value: false)

View File

@@ -1,56 +0,0 @@
/* bmplib - reversebits.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/>
*/
static int reversebits[] = {
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
};