mirror of
https://github.com/rupertwh/bmplib.git
synced 2026-04-16 11:12:42 +00:00
Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69f7e8baf3 | ||
|
|
7b105681f4 | ||
|
|
516944d992 | ||
|
|
4aa6c34663 | ||
|
|
bec2e3ac00 | ||
|
|
4cd1988d47 | ||
|
|
3bebc4c182 | ||
|
|
f19502f8e9 | ||
|
|
d000a25250 | ||
|
|
b723c6f6ef | ||
|
|
e8cd85b938 | ||
|
|
30495d92cf | ||
|
|
7f125a3d81 | ||
|
|
4626e2ac29 | ||
|
|
ba2da3a6ff | ||
|
|
052c5ade33 | ||
|
|
07497b7cea | ||
|
|
f6c7b97411 | ||
|
|
4e5af5b693 | ||
|
|
11de4eada4 | ||
|
|
5d6957bff1 | ||
|
|
4f9f82ca61 | ||
|
|
05dea4441d | ||
|
|
29854311ac | ||
|
|
28e6a281ae | ||
|
|
cdcd29026a | ||
|
|
c5384cdc86 | ||
|
|
9add28e803 | ||
|
|
093c984b84 | ||
|
|
d5338751d6 | ||
|
|
e6a84d2a34 | ||
|
|
26edf63068 | ||
|
|
4c7f1881c8 | ||
|
|
0b12d525f9 | ||
|
|
1dc99397fe | ||
|
|
c490f79e76 | ||
|
|
55c7e8d24a | ||
|
|
e5c39138ae | ||
|
|
05e69a2f51 | ||
|
|
8223b8038e | ||
|
|
6db7f5ff5f | ||
|
|
99598632f2 |
63
.clang-format
Normal file
63
.clang-format
Normal file
@@ -0,0 +1,63 @@
|
||||
# For more information, see:
|
||||
#
|
||||
# https://clang.llvm.org/docs/ClangFormat.html
|
||||
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
||||
#
|
||||
---
|
||||
|
||||
BasedOnStyle: WebKit
|
||||
|
||||
SortIncludes: false
|
||||
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignArrayOfStructures: Right
|
||||
AlignConsecutiveAssignments:
|
||||
Enabled: true
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: true
|
||||
AlignCompound: true
|
||||
AlignFunctionPointers: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveBitFields: AcrossComments
|
||||
AlignConsecutiveDeclarations: AcrossComments
|
||||
AlignConsecutiveMacros: AcrossComments
|
||||
AlignConsecutiveShortCaseStatements:
|
||||
Enabled: true
|
||||
AcrossEmptyLines: true
|
||||
AcrossComments: true
|
||||
AlignCaseColons: true
|
||||
AlignEscapedNewlines: LeftWithLastLine
|
||||
AlignOperands: Align
|
||||
|
||||
AllowAllArgumentsOnNextLine: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortEnumsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
|
||||
BitFieldColonSpacing: After
|
||||
|
||||
BreakAfterReturnType: Automatic
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Linux
|
||||
BreakBeforeTernaryOperators: false
|
||||
BreakStringLiterals: false
|
||||
IndentWidth: 8
|
||||
PointerAlignment: Right
|
||||
UseTab: ForIndentation
|
||||
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterCStyleCast: false
|
||||
|
||||
ColumnLimit: 80
|
||||
PenaltyBreakAssignment: 60
|
||||
PenaltyBreakBeforeFirstCallParameter: 100
|
||||
PenaltyBreakOpenParenthesis: 40
|
||||
PenaltyExcessCharacter: 1
|
||||
PenaltyBreakString: 60
|
||||
PenaltyReturnTypeOnItsOwnLine: 90
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -3,3 +3,9 @@ _debug
|
||||
_release
|
||||
*.sublime-workspace
|
||||
*.patch
|
||||
.vs
|
||||
[Dd]ebug
|
||||
[Rr]elease
|
||||
bmplib
|
||||
gen-huffman
|
||||
gen-reversebits
|
||||
|
||||
@@ -1,25 +1,8 @@
|
||||
# API -- Rupert's bmplib
|
||||
# Rupert's bmplib -- Full API Description (v1.7.1)
|
||||
|
||||
Refer to the *Quick Start Guide* (API-quick-start.md) for a quick intro to bmplib which describes only the minimal set of functions needed to read/write BMP files.
|
||||
|
||||
The API has grown quite a bit more than I had originally anticipated. But most
|
||||
of the provided functions are optional. So, to not scare you off right away,
|
||||
here is a short overview of the basic functions needed, which will be
|
||||
sufficient for many/most use cases (see also the two examples at the end of
|
||||
this document):
|
||||
|
||||
### Reading BMPs:
|
||||
```
|
||||
bmpread_new()
|
||||
bmpread_dimensions()
|
||||
bmpread_load_image()
|
||||
bmp_free()
|
||||
```
|
||||
### Writing BMPs:
|
||||
```
|
||||
bmpwrite_new()
|
||||
bmpwrite_set_dimensions()
|
||||
bmpwrite_save_image()
|
||||
bmp_free()
|
||||
```
|
||||
## 1. Functions for reading BMP files
|
||||
|
||||
### Get a handle
|
||||
@@ -34,6 +17,7 @@ handle.
|
||||
The handle cannot be reused to read multiple files.
|
||||
|
||||
|
||||
|
||||
### Read the file header
|
||||
```
|
||||
BMPRESULT bmpread_load_info(BMPHANDLE h)
|
||||
@@ -41,11 +25,13 @@ BMPRESULT bmpread_load_info(BMPHANDLE h)
|
||||
|
||||
bmplib reads the file header and checks validity. Possible return values are:
|
||||
- `BMP_RESULT_OK`: All is good, you can proceed to read the file.
|
||||
- `BMP_INSANE`: The file is valid, but huge. The default limit is 500MB. If
|
||||
you want to read the file anyway, use `bmpread_set_insanity_limit()` to
|
||||
increase the allowed file size. Otherwise, `bmpread_load_image()` will
|
||||
refuse to load the image.(You can build the library with a different
|
||||
default limit, using the meson option '-Dinsanity_limit_mb=nnn')
|
||||
- `BMP_INSANE`: The file is valid, but huge. The default limit is 500MB
|
||||
(relevant is the required buffer size to hold the complete image, not the
|
||||
file size. If you want to read the file anyway, use
|
||||
`bmpread_set_insanity_limit()` to increase the allowed file size.
|
||||
Otherwise, `bmpread_load_image()` will refuse to load the image. (You can
|
||||
build the library with a different default limit, using the meson
|
||||
option '-Dinsanity_limit_mb=nnn')
|
||||
- `BMP_RESULT_PNG` / `BMP_RESULT_JPEG`: It's not really a BMP file, but a
|
||||
wrapped PNG or JPEG. The file pointer is left in the correct state to be
|
||||
passed on to e.g. libpng or libjpeg.
|
||||
@@ -79,14 +65,14 @@ at a time, each returned as an `int`. Getting the horizontal and vertical
|
||||
resolutions in DPI is only available via these single functions.
|
||||
|
||||
Note, in order to use these functions, -- unlike with `bmpread_dimensions
|
||||
()` -- you must first(successfully) call `bmpread_load_info()`, otherwise
|
||||
()` -- you must first (successfully) call `bmpread_load_info()`, otherwise
|
||||
they will all return 0!
|
||||
|
||||
```
|
||||
int bmpread_width(BMPHANDLE h)
|
||||
int bmpread_height(BMPHANDLE h)
|
||||
int bmpread_channels(BMPHANDLE h)
|
||||
int bmpread_bits_per_channel(BMPHANDLE h)
|
||||
int bmpread_bitsperchannel(BMPHANDLE h)
|
||||
BMPORIENT bmpread_orientation(BMPHANDLE h)
|
||||
|
||||
int bmpread_resolution_xdpi(BMPHANDLE h)
|
||||
@@ -95,16 +81,16 @@ int bmpread_resolution_ydpi(BMPHANDLE h)
|
||||
```
|
||||
|
||||
#### top-down / bottom-up
|
||||
`orientation` is one of:
|
||||
- `BMPORIENT_BOTTOMUP`
|
||||
- `BMPORIENT_TOPDOWN`
|
||||
`*orientation` is one of:
|
||||
- `BMP_ORIENT_BOTTOMUP`
|
||||
- `BMP_ORIENT_TOPDOWN`
|
||||
|
||||
`bmpread_orientation()` or the orientation value returned by
|
||||
`bmpread_orientation()` or the `orientation` value returned by
|
||||
`bmpread_dimensions()` **is only relevant if you load the BMP file
|
||||
line-by-line**. In line-by-line mode (using `bmpread_load_line()`), the
|
||||
image data is always delivered in the order it is in the BMP file. The
|
||||
`orientation` value will tell you if it's top-down or bottom-up. On the
|
||||
other hand, when the whole image is loaded at once(using `bmpread_load_image
|
||||
other hand, when the whole image is loaded at once (using `bmpread_load_image
|
||||
()`), bmplib will **always** return the image top-down, regardless of how
|
||||
the BMP file is oriented. The `orientation` value will still indicate the
|
||||
orientation of the original BMP.
|
||||
@@ -119,7 +105,7 @@ Returns the buffer size you have to allocate for the whole image.
|
||||
### Indexed BMPs
|
||||
|
||||
By default, bmplib will interpret indexed (color palette) BMPs and return the
|
||||
image as 24-bit RGB data.
|
||||
image as 24-bit RGB data, same as non-indexed (RGB) BMPs.
|
||||
|
||||
If instead you want to keep the image as indexed, you have the option do so
|
||||
with these two functions:
|
||||
@@ -144,7 +130,7 @@ let bmplib allocate it for you (and then `free()` it, once you are done):
|
||||
|
||||
```
|
||||
unsigned char *palette;
|
||||
int numcolors;
|
||||
int numcolors;
|
||||
|
||||
numcolors = bmpread_num_palette_colors(h);
|
||||
|
||||
@@ -189,8 +175,8 @@ void bmpread_set_undefined(BMPHANDLE h, BMPUNDEFINED mode)
|
||||
```
|
||||
|
||||
`mode` can be one of:
|
||||
- `BMPUNDEFINED_LEAVE`
|
||||
- `BMPUNDEFINED_TO_ALPHA` (default)
|
||||
- `BMP_UNDEFINED_LEAVE`
|
||||
- `BMP_UNDEFINED_TO_ALPHA` (default)
|
||||
|
||||
Note: If you use `bmpread_load_palette()` to switch to loading the index data
|
||||
instead of RGB data, this setting will have no effect and undefined pixels
|
||||
@@ -205,23 +191,33 @@ BMPRESULT bmpread_set_64bit_conv(BMPHANDLE h, BMPCONV64 conv)
|
||||
```
|
||||
|
||||
If you don't do anything, 64bit BMPs will be read like any other BMP and the
|
||||
data will be returned as 16bit/channel sRGB RGBA. (Hopefully, see README.md)
|
||||
data will be returned as 16bit/channel sRGB RGBA.
|
||||
|
||||
But if you want to access the original s2.13 fixed-point components, or you
|
||||
don't want the linear-to-sRGB conversion, you can call `bmpread_is_64bit
|
||||
()` to inquire if a BMP file is 64bit and subsequently call
|
||||
`bmpread_set_64bit_conv()` with `conv` set to one of the following values:
|
||||
don't want the linear-to-sRGB conversion, you can use `bmpread_set_64bit_conv
|
||||
()` and `bmp_set_number_format()` to control how the image is returned:
|
||||
|
||||
- `BMP_CONV64_NONE`: no conversion is done, image data is returned as is
|
||||
(probably 16 bit per channel RGBA in s2.13 fixed-point)
|
||||
- `BMP_CONV64_16BIT`: image is returned as normal 16bit per channel, without
|
||||
converting from linear to sRGB-gamma.
|
||||
- `BMP_CONV64_16BIT_SRGB`: the default, original data is assumed to be s2.13
|
||||
fixed-point linear and converted to normal 16bit per channel with
|
||||
sRGB-gamma.
|
||||
Options for `bmpread_set_64bit()` are:
|
||||
|
||||
- `BMP_CONV64_SRGB`: the default, original data is assumed to be s2.13
|
||||
fixed-point linear and converted to sRGB-gamma.
|
||||
- `BMP_CONV64_LINEAR`: no gamma-conversion is applied to the image data.
|
||||
- `BMP_CONV64_NONE`: this option is just a shorthand for setting
|
||||
BMP_CONV64_LINEAR *and* BMP_FORMAT_S2_13. Image values are returned exactly
|
||||
as they are in the BMP file, without any conversion or attempt at
|
||||
interpretation.
|
||||
|
||||
|
||||
### Floating point
|
||||
### Setting a number format
|
||||
|
||||
By default, bmplib will always return the image data as 8-,16-, or 32-bit integer values. You can instead set the number format to floating point or fixed using:
|
||||
|
||||
```
|
||||
BMPRESULT bmp_set_number_format(BMPHANDLE h, BMPFORMAT format)
|
||||
```
|
||||
|
||||
(see below, *3. General functions for both reading/writing BMPs*)
|
||||
|
||||
|
||||
|
||||
### Huge files: bmpread_set_insanity_limit()
|
||||
@@ -290,12 +286,12 @@ are done with it.
|
||||
To determine the required buffer size, either divide the value from
|
||||
`bmpread_buffersize()` by the number of scanlines (= `bmpread_height()`), or
|
||||
calculate from the image dimensions returned by bmplib as width * channels *
|
||||
bits_per_channel / 8.
|
||||
bitsperchannel / 8.
|
||||
|
||||
```
|
||||
single_line_buffersize = bmpread_buffersize(h) / bmpread_height(h);
|
||||
/* or */
|
||||
single_line_buffersize = bmpread_width(h) * bmpread_channels(h) * bmpread_bits_per_channel(h) / 8;
|
||||
single_line_buffersize = bmpread_width(h) * bmpread_channels(h) * bmpread_bitsperchannel(h) / 8;
|
||||
```
|
||||
|
||||
Repeated calls to `bmpread_load_line()` will return each scan line, one after
|
||||
@@ -313,7 +309,8 @@ bottom-up. Almost all BMPs will be bottom-up. (see above)
|
||||
Invalid pixels may occur in indexed BMPs, both RLE and non-RLE. Invalid pixels
|
||||
either point beyond the given color palette, or they try to set pixels
|
||||
outside the image dimensions. Pixels containing an invalid color value will
|
||||
be set to zero, and attempts to point outside the image will be ignored.
|
||||
be set to the maximum allowed value, and attempts to point outside the image
|
||||
will be ignored.
|
||||
|
||||
In both cases, `bmpread_load_image()` and `bmpread_load_line()` will return
|
||||
BMP_RESULT_INVALID, unless the image is also truncated, then
|
||||
@@ -337,6 +334,20 @@ BMPRESULT bmpread_info_channel_bits(BMPHANDLE h, int *r, int *g, int *b, int *
|
||||
```
|
||||
|
||||
|
||||
### Release the handle
|
||||
|
||||
```
|
||||
void bmp_free(BMPHANDLE h)
|
||||
```
|
||||
|
||||
Frees all resources associated with the handle `h`. **Image data is not
|
||||
affected**, so you can call bmp_free() immediately after `bmpread_load_image
|
||||
()` and still use the returned image data.
|
||||
|
||||
Note: Any error message strings returned by `bmp_errmsg()` are invalidated by
|
||||
`bmp_free()` and must not be used anymore!
|
||||
|
||||
|
||||
|
||||
|
||||
## 2. Functions for writing BMP files
|
||||
@@ -352,7 +363,7 @@ BMPRESULT bmpwrite_set_dimensions(BMPHANDLE h,
|
||||
unsigned width,
|
||||
unsigned height,
|
||||
unsigned channels,
|
||||
unsigned bits_per_channel)
|
||||
unsigned bitsperchannel)
|
||||
|
||||
BMPRESULT bmpwrite_set_resolution(BMPHANDLE h, int xdpi, int ydpi)
|
||||
|
||||
@@ -360,8 +371,8 @@ BMPRESULT bmpwrite_set_resolution(BMPHANDLE h, int xdpi, int ydpi)
|
||||
|
||||
Note: the dimensions set with `bmpwrite_set_dimensions()` describe the source
|
||||
data that you pass to bmplib, *not* the output BMP format. Use
|
||||
`bmpwrite_set_output_bits()` and `bmpwrite_set_palette()` to modify the
|
||||
format written to the BMP file.
|
||||
`bmpwrite_set_output_bits()`, `bmpwrite_set_palette()`, and
|
||||
`bmpwrite_set_64bit()` to modify the format written to the BMP file.
|
||||
|
||||
|
||||
### Set the output format
|
||||
@@ -401,22 +412,43 @@ BMP for 3- or 4-color images, call `bmpwrite_allow_2bit()` before calling
|
||||
|
||||
#### RLE
|
||||
|
||||
Indexed images may optionally be written as RLE4 or RLE8 compressed BMPs.
|
||||
```
|
||||
BMPRESULT bmpwrite_set_rle(BMPHANDLE h, BMPRLETYPE type)
|
||||
BMPRESULT bmpwrite_allow_huffman(BMPHANDLE h)
|
||||
```
|
||||
|
||||
Indexed images may optionally be written run-lenght-encoded (RLE) bitmaps.
|
||||
Images with 16 or fewer colors can be written as either RLE4 or RLE8
|
||||
(default is RLE4), images with more than 16 colors only as RLE8.
|
||||
|
||||
To activate RLE compression, call
|
||||
Images with only 2 colors can also be written with 1-D Huffman encoding, but
|
||||
only after explicitly allowing it by calling `bmpwrite_allow_huffman()`
|
||||
(very few programs will be able to read Huffman encoded BMPs).
|
||||
|
||||
```
|
||||
BMPRESULT bmpwrite_set_rle(BMPHANDLE h, BMPRLETYPE type)
|
||||
```
|
||||
|
||||
with `type` set to one of the following values:
|
||||
To activate RLE compression, call `bmpwrite_set_rle()` with `type` set to one
|
||||
of the following values:
|
||||
- `BMP_RLE_NONE` no RLE compression, same as not calling `bmpwrite_set_rle()`
|
||||
at all
|
||||
- `BMP_RLE_AUTO` choose RLE4 or RLE8 based on number of colors in palette
|
||||
- `BMP_RLE_AUTO` choose RLE4, RLE8, or 1-D Huffman based on number of colors
|
||||
in palette
|
||||
- `BMP_RLE_RLE8` use RLE8, regardless of number of colors in palette
|
||||
|
||||
In order to write 1-D Huffman encoded bitmpas, the provided palette must have
|
||||
2 colors, RLE type must be set to `BMP_RLE_AUTO`, and `bmpwrite_allow_huffman
|
||||
()` must be called. Be aware that *very* few programs will be able to read
|
||||
Huffman encoded BMPs!
|
||||
|
||||
#### RLE24
|
||||
|
||||
RLE24 is an old OS/2 compression method. As the name suggests, it's 24-bit RLE
|
||||
for non-indexed images. Like Huffman encoding, it's very uncommon and only
|
||||
very few programs will be able to read these BMPs. Writing RLE24 bitmaps must
|
||||
be explicitly allowed by first calling `bmpwrite_allow_rle24()`.
|
||||
|
||||
In order to save an image as RLE24, the data must be provided as 8 bits per
|
||||
channel RGB (no alpha channel). Call `bmpwrite_set_rle()` with type set to
|
||||
`BMP_RLE_AUTO` and also call `bmpwrite_allow_rle24()` (in any order).
|
||||
|
||||
|
||||
### top-down / bottom-up
|
||||
|
||||
@@ -443,6 +475,26 @@ provide the image lines in the order according to the orientation you have
|
||||
chosen for the BMP file.
|
||||
|
||||
|
||||
|
||||
|
||||
### 64-bit RGBA BMPs
|
||||
|
||||
By default, bmplib will not write 64-bit BMPs because they are rather exotic and hardly any
|
||||
software can open them.
|
||||
|
||||
If you do want to write 64-bit BMPs, call
|
||||
|
||||
```
|
||||
BMPRESULT bmpwrite_set_64bit(BMPHANDLE h)
|
||||
```
|
||||
|
||||
In order to make use of the extended range available in 64-bit BMPs (-4.0 to +3.999...), you will probably want to provide the image buffer either as 32-bit float or as 16-bit s2.13 (and call `bmp_set_number_format()` accordingly).
|
||||
|
||||
Note: 64-bit BMPs store pixel values in *linear light*. Unlike when *reading* 64-bit BMPs, bmplib will not make any gamma/linear conversion while writing BMPs. You have to provide the proper linear values in the image buffer.
|
||||
|
||||
|
||||
|
||||
|
||||
### Write the image
|
||||
|
||||
```
|
||||
@@ -475,7 +527,7 @@ set with `bmpwrite_set_orientation()` (see above).
|
||||
### bmp_free()
|
||||
|
||||
```
|
||||
void bmp_free(BMPHANDLE h)
|
||||
void bmp_free(BMPHANDLE h)
|
||||
```
|
||||
|
||||
Frees all resources associated with the handle `h`. Image data is not
|
||||
@@ -496,6 +548,21 @@ description(s). The returned string is safe to use until any other
|
||||
bmplib-function is called with the same handle.
|
||||
|
||||
|
||||
### bmp_set_number_format()
|
||||
|
||||
```
|
||||
BMPRESULT bmp_set_number_format(BMPHANDLE h, BMPFORMAT format)
|
||||
```
|
||||
|
||||
sets the number format of the image buffer received from / passed to bmplib. `format` can be one of
|
||||
|
||||
- `BMP_FORMAT_INT` image buffer values are expected/returned as 8-, 16-, or 32-bit integers. (this is the default)
|
||||
- `BMP_FORMAT_FLOAT` image buffer values are expected/returned as 32-bit floating point numbers (C `float`).
|
||||
- `BMP_FORMAT_S2_13` image buffer values are expected/returned as s2.13 fixed point numbers. s2.13 is a 16-bit format with one sign bit, 2 integer bits, and 13 bits for the fractional part. Range is from -4.0 to +3.999...
|
||||
|
||||
For indexed images, `BMP_FORMAT_INT` is the only valid format.
|
||||
|
||||
|
||||
### bmp_version()
|
||||
|
||||
```
|
||||
@@ -576,13 +643,22 @@ Can safely be cast from/to int.
|
||||
|
||||
#### `BMPCONV64`
|
||||
|
||||
Used in `bmpread_set_64bit_conv()`. Possible value are:
|
||||
- `BMP_CONV64_16BIT_SRGB` (default)
|
||||
- `BMP_CONV64_16BIT`
|
||||
Used in `bmpread_set_64bit_conv()`. Possible values are:
|
||||
- `BMP_CONV64_SRGB` (default)
|
||||
- `BMP_CONV64_LINEAR`
|
||||
- `BMP_CONV64_NONE`
|
||||
|
||||
Can safely be cast from/to int.
|
||||
|
||||
#### `BMPFORMAT`
|
||||
|
||||
Used in `bmp_set_number_format()`. Possible values are:
|
||||
- `BMP_FORMAT_INT` (default)
|
||||
- `BMP_FORMAT_FLOAT` 32-bit floating point
|
||||
- `BMP_FORMAT_S2_13` s2.13 fixed point
|
||||
|
||||
|
||||
|
||||
## 5. Sample code
|
||||
|
||||
### Reading BMPs
|
||||
@@ -596,7 +672,7 @@ Can safely be cast from/to int.
|
||||
uint8_t *image_buffer;
|
||||
|
||||
|
||||
/* open a file and call bmpread_new() to get a BMPHANDLE,
|
||||
/* Open a file and call bmpread_new() to get a BMPHANDLE,
|
||||
* which will be used on all subsequent calls.
|
||||
*/
|
||||
|
||||
@@ -604,8 +680,8 @@ Can safely be cast from/to int.
|
||||
h = bmpread_new(file);
|
||||
|
||||
|
||||
/* get image dimensions
|
||||
* the color information (channels/bits) describes the data
|
||||
/* Get image dimensions
|
||||
* The color information (channels/bits) describes the data
|
||||
* that bmplib will return, NOT necessarily the BMP file.
|
||||
*/
|
||||
|
||||
@@ -614,24 +690,24 @@ Can safely be cast from/to int.
|
||||
width = bmpread_width(h);
|
||||
height = bmpread_height(h);
|
||||
channels = bmpread_channels(h);
|
||||
bitsperchannel = bmpread_bits_per_channel(h);
|
||||
bitsperchannel = bmpread_bitsperchannel(h);
|
||||
|
||||
|
||||
/* get required size for memory buffer and allocate memory */
|
||||
/* Get required size for memory buffer and allocate memory */
|
||||
|
||||
image_buffer = malloc(bmpread_buffersize(h));
|
||||
|
||||
|
||||
/* load the image and clean up: */
|
||||
/* Load the image and clean up: */
|
||||
|
||||
bmpread_load_image(h, &image_buffer);
|
||||
|
||||
bmp_free(h);
|
||||
fclose(file);
|
||||
|
||||
/* ready to use the image written to image_buffer */
|
||||
/* Ready to use the image written to image_buffer */
|
||||
|
||||
/* image data is always returned in host byte order as
|
||||
/* Image data is always returned in host byte order as
|
||||
* 8, 16, or 32 bits per channel RGB or RGBA data.
|
||||
* No padding.
|
||||
*/
|
||||
@@ -654,13 +730,13 @@ Can safely be cast from/to int.
|
||||
*/
|
||||
|
||||
|
||||
/* open a file for writing and get a BMPHANDLE */
|
||||
/* Open a file for writing and get a BMPHANDLE */
|
||||
|
||||
file = fopen("image.bmp", "wb");
|
||||
h = bmpwrite_new(file);
|
||||
|
||||
|
||||
/* inform bmplib of the image dimensions.
|
||||
/* Inform bmplib of the image dimensions.
|
||||
* The color information (channels, bits) refer to the format
|
||||
* your image buffer is in, not the format the BMP file should
|
||||
* be written in.
|
||||
@@ -669,7 +745,7 @@ Can safely be cast from/to int.
|
||||
bmpwrite_set_dimensions(h, width, height, channels, bitsperchannel);
|
||||
|
||||
|
||||
/* Optional: choose bit-depths (independantly for each channel,
|
||||
/* Optional: choose bit-depths (independently for each channel,
|
||||
* in the order R,G,B,A) for the BMP file. bmplib will choose
|
||||
* an appropriate BMP file format to accomodate those bitdepths.
|
||||
*/
|
||||
@@ -677,7 +753,7 @@ Can safely be cast from/to int.
|
||||
bmpwrite_set_output_bits(h, 5, 6, 5, 0);
|
||||
|
||||
|
||||
/* save data to file */
|
||||
/* Save data to file */
|
||||
|
||||
bmpwrite_save_image(h, image_buffer);
|
||||
|
||||
282
API-quick-start.md
Normal file
282
API-quick-start.md
Normal file
@@ -0,0 +1,282 @@
|
||||
# Rupert's bmplib -- Quick Start Guide
|
||||
|
||||
The API has grown quite a bit more than I had originally anticipated. But most
|
||||
of the provided functions are optional.
|
||||
|
||||
So, to not scare you off right away, I wrote this quick start guide version of
|
||||
the API description which only covers the basic functions. These basic
|
||||
functions will be sufficient for many/most use cases. (see also the two
|
||||
examples at the end of this document):
|
||||
|
||||
For the complete API, refer to the *Full API Description* (API-full.md).
|
||||
|
||||
## 1. Reading BMP files:
|
||||
|
||||
```
|
||||
bmpread_new()
|
||||
bmpread_dimensions()
|
||||
bmpread_load_image()
|
||||
bmp_free()
|
||||
```
|
||||
|
||||
### Get a handle
|
||||
```
|
||||
BMPHANDLE bmpread_new(FILE *file)
|
||||
```
|
||||
|
||||
`bmpread_new()` returns a handle which is used for all subsequent calls to
|
||||
bmplib. When you are done with the file, call `bmp_free()` to release this
|
||||
handle.
|
||||
|
||||
The handle cannot be reused to read multiple files.
|
||||
|
||||
### Get image dimensions
|
||||
|
||||
```
|
||||
BMPRESULT bmpread_dimensions(BMPHANDLE h,
|
||||
int *width,
|
||||
int *height,
|
||||
int *channels,
|
||||
int *bitsperchannel,
|
||||
BMPORIENT *orientation)
|
||||
```
|
||||
|
||||
Use `bmpread_dimensions()` to get all dimensions with one call. The return
|
||||
value will be the same as for `bmpread_load_info()` (see "3. Result codes"
|
||||
below and *Full API Description*).
|
||||
|
||||
The dimensions describe the image returned by bmplib, *not* necessarily the
|
||||
original BMP file.
|
||||
|
||||
`orientation` can be ignored, it is only relevant when loading the image
|
||||
line-by-line. Can be set to NULL. (see *Full API Description*)
|
||||
|
||||
|
||||
### Load the image
|
||||
|
||||
```
|
||||
BMPRESULT bmpread_load_image(BMPHANDLE h, unsigned char **pbuffer)
|
||||
```
|
||||
|
||||
Loads the complete image from the BMP file into the buffer pointed to by
|
||||
`pbuffer`. You can either allocate a buffer yourself or let `pbuffer` point
|
||||
to a NULL-pointer in which case bmplib will allocate an appropriate buffer.
|
||||
In the latter case, you will have to `free()` the buffer, once you are done
|
||||
with it.
|
||||
|
||||
If you allocate the buffer yourself, the buffer must be at least as large as
|
||||
the size returned by `bmpread_buffersize()` (see *Full API description*).
|
||||
|
||||
```
|
||||
unsigned char *buffer;
|
||||
|
||||
/* either: */
|
||||
buffer = NULL;
|
||||
bmpread_load_image(h, &buffer); /* bmplib will allocate the buffer */
|
||||
|
||||
/* or: */
|
||||
buffer = malloc(bmpread_buffersize(h));
|
||||
bmpread_load_image(h, &buffer); /* bmplib will use the provided buffer */
|
||||
|
||||
```
|
||||
|
||||
The image data is written to the buffer according to the returned dimensions
|
||||
(see above). 16-bit and 32-bit values are always written in host byte order,
|
||||
in the order R-G-B or R-G-B-A. The returned image is always top-down, i.e.
|
||||
data starts in the top left corner. Unlike BMPs which are (almost always)
|
||||
bottom-up.
|
||||
|
||||
If `bmpread_load_image()` returns BMP_RESULT_TRUNCATED or BMP_RESULT_INVALID,
|
||||
the file may have been damaged or simply contains invalid image data. Image
|
||||
data is loaded anyway as far as possible and may be partially usable.
|
||||
|
||||
|
||||
|
||||
### Release the handle
|
||||
|
||||
```
|
||||
void bmp_free(BMPHANDLE h)
|
||||
```
|
||||
|
||||
Frees all resources associated with the handle `h`. **Image data is not
|
||||
affected**, so you can call bmp_free() immediately after `bmpread_load_image
|
||||
()` and still use the returned image data.
|
||||
|
||||
Note: Any error message strings returned by `bmp_errmsg()` are invalidated by
|
||||
`bmp_free()` and must not be used anymore!
|
||||
|
||||
|
||||
|
||||
## 2. Writing BMP files:
|
||||
|
||||
```
|
||||
bmpwrite_new()
|
||||
bmpwrite_set_dimensions()
|
||||
bmpwrite_save_image()
|
||||
bmp_free()
|
||||
```
|
||||
|
||||
### Get a handle
|
||||
```
|
||||
BMPHANDLE bmpwrite_new(FILE *file)
|
||||
```
|
||||
|
||||
### Set image dimensions
|
||||
```
|
||||
BMPRESULT bmpwrite_set_dimensions(BMPHANDLE h,
|
||||
unsigned width,
|
||||
unsigned height,
|
||||
unsigned channels,
|
||||
unsigned bitsperchannel)
|
||||
```
|
||||
|
||||
Note: the dimensions set with `bmpwrite_set_dimensions()` describe the source
|
||||
data that you pass to bmplib, *not* the output BMP format. Use
|
||||
`bmpwrite_set_output_bits()`, `bmpwrite_set_palette()`, and
|
||||
`bmpwrite_set_64bit()` to modify the format written to the BMP file. (see *Full
|
||||
API description*)
|
||||
|
||||
|
||||
|
||||
### Write the image
|
||||
|
||||
```
|
||||
BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image)
|
||||
```
|
||||
|
||||
Write the whole image at once with `bmpwrite_save_image()`.
|
||||
|
||||
The image data pointed to by `image` must be in the format described
|
||||
by `bmpwrite_set_dimensions()`. Multi-byte values (16 or 32 bit) are expected
|
||||
in host byte order, the channels in the order R-G-B-(A).
|
||||
|
||||
Important: Image data must be provided top-down. (Even though the created BMP
|
||||
file will be bottom-up.)
|
||||
|
||||
### bmp_free()
|
||||
|
||||
```
|
||||
void bmp_free(BMPHANDLE h)
|
||||
```
|
||||
|
||||
Frees all resources associated with the handle `h`.
|
||||
|
||||
Note: Any error messages returned by `bmp_errmsg()` are invalidated by
|
||||
`bmp_free()` and cannot be used anymore.
|
||||
|
||||
|
||||
|
||||
|
||||
## 3. Result codes
|
||||
|
||||
#### `BMPRESULT`
|
||||
|
||||
Many bmplib functions return the success/failure of an operation as a
|
||||
`BMPRESULT`. It can have one of the following values:
|
||||
|
||||
- `BMP_RESULT_OK`
|
||||
- `BMP_RESULT_INVALID`
|
||||
- `BMP_RESULT_TRUNCATED`
|
||||
- `BMP_RESULT_INSANE`
|
||||
- `BMP_RESULT_PNG`
|
||||
- `BMP_RESULT_JPEG`
|
||||
- `BMP_RESULT_ERROR`
|
||||
|
||||
Can safely be cast from/to int. BMP_RESULT_OK is guaranteed to have the value 0.
|
||||
|
||||
|
||||
|
||||
## 5. Sample code
|
||||
|
||||
### Reading BMPs
|
||||
|
||||
```
|
||||
/* (all error checking left out for clarity) */
|
||||
|
||||
BMPHANDLE h;
|
||||
FILE *file;
|
||||
int width, height, channels, bitsperchannel;
|
||||
unsigned char *image_buffer;
|
||||
|
||||
|
||||
/* Open a file and call bmpread_new() to get a BMPHANDLE,
|
||||
* which will be used on all subsequent calls.
|
||||
*/
|
||||
|
||||
file = fopen("someimage.bmp", "rb");
|
||||
h = bmpread_new(file);
|
||||
|
||||
|
||||
/* Get image dimensions
|
||||
* The color information (channels/bits) describes the data
|
||||
* that bmplib will return, NOT necessarily the BMP file.
|
||||
* Setting orientation to NULL, image is always returned top-down.
|
||||
*/
|
||||
|
||||
bmpread_dimensions(h, &width, &height, &channels, &bitsperchannel, NULL);
|
||||
|
||||
|
||||
/* Load the image and clean up: */
|
||||
|
||||
image_buffer = NULL;
|
||||
bmpread_load_image(h, &image_buffer);
|
||||
|
||||
bmp_free(h);
|
||||
fclose(file);
|
||||
|
||||
|
||||
/* Ready to use the image written to image_buffer */
|
||||
|
||||
/* Image data is always returned in host byte order as
|
||||
* 8, 16, or 32 bits per channel RGB or RGBA data.
|
||||
* No padding.
|
||||
*/
|
||||
```
|
||||
|
||||
|
||||
### Writing BMPs
|
||||
|
||||
```
|
||||
/* (all error checking left out for clarity) */
|
||||
|
||||
BMPHANDLE h;
|
||||
FILE *file;
|
||||
unsigned char *image_buffer;
|
||||
int width, height, channels, bitsperchannel;
|
||||
|
||||
/* 'image_buffer' contains the image to be saved as either
|
||||
* 8, 16, or 32 bits per channel RGB or RGBA data in
|
||||
* host byte order without any padding
|
||||
*/
|
||||
|
||||
|
||||
/* Open a file for writing and get a BMPHANDLE */
|
||||
|
||||
file = fopen("image.bmp", "wb");
|
||||
h = bmpwrite_new(file);
|
||||
|
||||
|
||||
/* Inform bmplib of the image dimensions.
|
||||
* The color information (channels, bits) refer to the format
|
||||
* your image buffer is in, not the format the BMP file should
|
||||
* be written in.
|
||||
*/
|
||||
|
||||
bmpwrite_set_dimensions(h, width, height, channels, bitsperchannel);
|
||||
|
||||
|
||||
/* Optional: choose bit-depths (independently for each channel,
|
||||
* in the order R,G,B,A) for the BMP file. bmplib will choose
|
||||
* an appropriate BMP file format to accomodate those bitdepths.
|
||||
*/
|
||||
|
||||
bmpwrite_set_output_bits(h, 5, 6, 5, 0);
|
||||
|
||||
|
||||
/* Save data to file */
|
||||
|
||||
bmpwrite_save_image(h, image_buffer);
|
||||
|
||||
bmp_free(h);
|
||||
fclose(file);
|
||||
```
|
||||
54
README.md
54
README.md
@@ -6,15 +6,18 @@
|
||||
- Write any sensible BMP
|
||||
- Robustness! Don't let malformed BMPs bother us
|
||||
|
||||
## Current status (v1.4.6):
|
||||
Download [bmplib on github](https://github.com/rupertwh/bmplib).
|
||||
|
||||
## Current status (v1.7.1):
|
||||
### Reading BMP files:
|
||||
- 16/24/32 bit RGB(A) with any bits/channel combination
|
||||
(BI_RGB, BI_BITFIELDS, BI_ALPHABITFIELDS).
|
||||
- 64 bit RGBA (caveat see below)
|
||||
- 1/2/4/8 bit indexed (palette), including RLE4 and RLE8 compressed.
|
||||
- RLE24 compressed (OS/2).
|
||||
- optional line-by-line reading of BMPs, even RLE.
|
||||
- optionally return image data as float or s2.13
|
||||
- Huffman encoded (OS/2).
|
||||
- optional line-by-line reading of BMPs.
|
||||
- optionally return image data as float or s2.13 fixed point.
|
||||
|
||||
successful results from reading sample images from Jason Summers'
|
||||
fantastic [BMP Suite](https://entropymine.com/jason/bmpsuite/):
|
||||
@@ -27,7 +30,6 @@
|
||||
BMP_RESULT_PNG and leave the file pointer in the correct state to be
|
||||
passed on to either libpng or libjpeg. Works as designed. Don't want to
|
||||
create dependency on those libs.
|
||||
- Huffman-encoded OS/2 BMPs: see TODO.
|
||||
- We currently ignore icc-profiles and chromaticity/gamma values. See TODO.
|
||||
|
||||
|
||||
@@ -35,10 +37,12 @@
|
||||
- RGB(A) 16/24/32 bit.
|
||||
- 64-bit RGBA
|
||||
- any bit-depth combination for the RGBA channels.
|
||||
- Indexed 1/2/4/8 bit, optional RLE4 and RLE8 compression.
|
||||
- Indexed 1/2/4/8 bit, optional RLE4, RLE8, and 1-D Huffman compression.
|
||||
- RLE24 compression.
|
||||
- write BI_RGB when possible, BI_BITFIELDS only when
|
||||
necessary.
|
||||
- optional line-by-line writing of BMPs.
|
||||
- optionally supply image data as float or s2.13 fixed point.
|
||||
|
||||
|
||||
## Installation
|
||||
@@ -46,7 +50,7 @@
|
||||
### Download and compile bmplib library
|
||||
|
||||
To install the latest development version of the library under the default
|
||||
`/usr/local` prefix:
|
||||
`/usr/local` prefix on debian-like Linux:
|
||||
|
||||
```
|
||||
sudo apt install build-essential git meson pkg-config
|
||||
@@ -83,51 +87,55 @@ Includes:
|
||||
#include <bmplib.h>
|
||||
```
|
||||
|
||||
see API.md for the API documentation
|
||||
see API-quick-start.md and API-full.md for the API documentation
|
||||
|
||||
|
||||
### 64bit BMPs
|
||||
|
||||
64bit BMPs are a special breed. First of all, there is very little information
|
||||
about the format out there, let alone an 'official' spec from MS. It seems
|
||||
that the RGBA components are (always?) stored as s2.13 fixed-point numbers.
|
||||
And according to Jason Summers' BMP Suite the RGB components are encoded in
|
||||
that the RGBA components are stored as s2.13 fixed-point numbers. And
|
||||
according to Jason Summers' BMP Suite the RGB components are encoded in
|
||||
linear light. As that's the only sample of a 64-bit BMP I have, that's what I
|
||||
am going with for now. But that also means that there is no one obvious
|
||||
format in which to return the data.
|
||||
|
||||
Possible choices are:
|
||||
1. return the values untouched, which means the caller has to
|
||||
be aware of the s2.13 format. (BMP_CONV64_NONE)
|
||||
2. return the values as normal 16bit values, left in linear
|
||||
light (BMP_CONV64_16BIT)
|
||||
3. return the values as normal 16bit values, converted to sRGB
|
||||
gamma. (BMP_CONV64_16BIT_SRGB)
|
||||
be aware of the s2.13 format and linear gamma. (BMP_CONV64_NONE)
|
||||
2. return the values converted to 16-bit integers (or other selected
|
||||
number format), left in linear light (BMP_CONV64_LINEAR)
|
||||
3. return the values converted to 16-bit integers (or other selected
|
||||
numer format), converted to sRGB gamma. (BMP_CONV64_SRGB)
|
||||
|
||||
Choice 3 (16bit sRGB gamma) seems to be the most sensible default(and I made
|
||||
Choice 3 (16bit sRGB gamma) seems to be the most sensible default (and I made
|
||||
it the default), as it will work well for all callers which are not
|
||||
aware/don't care about 64bit BMPs and just want to use/diplay them like any
|
||||
other BMP. (Even though this goes against my original intent to not have
|
||||
bmplib do any color conversions.)
|
||||
|
||||
Note: the s2.13 format allows for negative values and values greater than 1!
|
||||
When converting to normal 16bit (BMP_CONV64_16BIT and BMP_CONV64_16BIT_SRGB),
|
||||
these values will be clipped to 0...1.
|
||||
When converting to 16bit integers, these values will be clipped to 0...1. In
|
||||
order to preserve the full possible range of 64bit BMP pixel values, the
|
||||
number format should be set to either BMP_FORMAT_FLOAT or BMP_FORMAT_S2_13.
|
||||
|
||||
In case the default (BMP_CONV64_16BIT_SRGB) doesn't work for you, bmplib now
|
||||
provides these two functions to check if a BMP is 64bit and to set the
|
||||
bmplib provides these functions to check if a BMP is 64bit and to set the
|
||||
conversion:
|
||||
- `bmpread_is_64bit()`
|
||||
- `bmpread_set_64bit_conv()`
|
||||
- `bmp_set_number_format()`
|
||||
|
||||
As to writing BMPs, by default bmplib will not write 64bit BMPs, as they are so exotic that only few applications will read them (other than native Microsoft tools, the new GIMP 3.0 is the only one I am aware of).
|
||||
Use `bmpwrite_set_64bit()` in order to write 64bit BMPs.
|
||||
|
||||
|
||||
## TODOs:
|
||||
### Definitely:
|
||||
|
||||
- [x] write indexed images.
|
||||
- [x] write RLE-compressed images (RLE4/RLE8 only. No OS/2 v2 BMPs).
|
||||
- [x] write RLE-compressed images ~~(RLE4/RLE8 only. No OS/2 v2 BMPs)~~.
|
||||
- [x] read RLE24-encoded BMPs.
|
||||
- [ ] read Huffman-encoded BMPs.
|
||||
- [x] read Huffman-encoded BMPs. (Still haven't found any real-life examples)
|
||||
- [x] line-by-line reading/writing. ~~Right now, the image can only be
|
||||
passed as a whole to/from bmplib.~~
|
||||
- [ ] read/write icc-profile and chromaticity/gamma values
|
||||
@@ -147,7 +155,7 @@ conversion:
|
||||
(which is the 'official' default) or let user pick one/multiple/all to
|
||||
be read in sequence.
|
||||
- [ ] Add a 'not-a-BMP-file' return type instead of just returning error.
|
||||
- [ ] icon- and pointer-files ("CI", "CP", "IC", "PT").
|
||||
- ~~[ ] icon- and pointer-files ("CI", "CP", "IC", "PT").~~
|
||||
- [x] 64-bits BMPs. (I changed my mind)
|
||||
|
||||
### Unclear:
|
||||
@@ -159,7 +167,7 @@ conversion:
|
||||
|
||||
### Non-feature (internal):
|
||||
|
||||
- [x] complete API description (see API.md)
|
||||
- [x] complete API description (see API-full.md and API-quick-start.md)
|
||||
- [x] bmp-read.c is getting too big, split into several files
|
||||
|
||||
|
||||
|
||||
185
bmp-common.c
185
bmp-common.c
@@ -3,18 +3,18 @@
|
||||
* Copyright (c) 2024, Rupert Weber.
|
||||
*
|
||||
* This file is part of bmplib.
|
||||
* bmplib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* bmplib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
* License along with this library.
|
||||
* If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
@@ -23,12 +23,16 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define BMPLIB_LIB
|
||||
|
||||
#include "config.h"
|
||||
#include "bmplib.h"
|
||||
#include "logging.h"
|
||||
#include "bmp-common.h"
|
||||
#include "bmp-read.h"
|
||||
#include "huffman.h"
|
||||
#include "bmp-write.h"
|
||||
|
||||
|
||||
@@ -60,7 +64,7 @@ API const char* bmp_errmsg(BMPHANDLE h)
|
||||
{
|
||||
if (!(h && (h->magic == HMAGIC_READ || h->magic == HMAGIC_WRITE)))
|
||||
return "BMPHANDLE is NULL or invalid";
|
||||
|
||||
|
||||
return logmsg(h->log);
|
||||
}
|
||||
|
||||
@@ -115,7 +119,7 @@ API void bmp_free(BMPHANDLE h)
|
||||
#ifdef DEBUG
|
||||
printf("bmp_free() called with invalid handle (0x%04x)\n",
|
||||
(unsigned int) h->magic);
|
||||
#endif
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -123,30 +127,32 @@ API void bmp_free(BMPHANDLE h)
|
||||
|
||||
|
||||
/********************************************************
|
||||
* cm_check_is_read_handle
|
||||
* cm_read_handle
|
||||
*******************************************************/
|
||||
|
||||
int cm_check_is_read_handle(BMPHANDLE h)
|
||||
BMPREAD cm_read_handle(BMPHANDLE h)
|
||||
{
|
||||
BMPREAD rp = (BMPREAD)(void*)h;
|
||||
|
||||
if (rp && rp->magic == HMAGIC_READ)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
return rp;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/********************************************************
|
||||
* bm_check_is_write_handle
|
||||
* cm_write_handle
|
||||
*******************************************************/
|
||||
|
||||
int cm_check_is_write_handle(BMPHANDLE h)
|
||||
BMPWRITE cm_write_handle(BMPHANDLE h)
|
||||
{
|
||||
BMPWRITE wp = (BMPWRITE)(void*)h;
|
||||
|
||||
if (wp && wp->magic == HMAGIC_WRITE)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
return wp;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -154,7 +160,7 @@ int cm_check_is_write_handle(BMPHANDLE h)
|
||||
* cm_gobble_up
|
||||
*******************************************************/
|
||||
|
||||
int cm_gobble_up(BMPREAD_R rp, int count)
|
||||
bool cm_gobble_up(BMPREAD_R rp, int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -163,15 +169,14 @@ int cm_gobble_up(BMPREAD_R rp, int count)
|
||||
if (feof(rp->file)) {
|
||||
rp->lasterr = BMP_ERR_TRUNCATED;
|
||||
logerr(rp->log, "unexpected end of file");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
rp->lasterr = BMP_ERR_FILEIO;
|
||||
logsyserr(rp->log, "error reading from file");
|
||||
}
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -187,9 +192,6 @@ int cm_count_bits(unsigned long v)
|
||||
{
|
||||
int bits = 0;
|
||||
|
||||
if (v < 0)
|
||||
v = -v;
|
||||
|
||||
while (v) {
|
||||
bits++;
|
||||
v >>= 1;
|
||||
@@ -221,18 +223,19 @@ const char* cm_format_name(enum BmpFormat format)
|
||||
|
||||
|
||||
|
||||
int cm_all_lessoreq_int(int limit, int n, ...)
|
||||
bool cm_all_lessoreq_int(int limit, int n, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int i, ret = TRUE;
|
||||
int i;
|
||||
bool ret = true;
|
||||
|
||||
if (n < 1)
|
||||
return TRUE;
|
||||
return true;
|
||||
|
||||
va_start(ap, n);
|
||||
for (i = 0; i < n; i++) {
|
||||
if (va_arg(ap, int) > limit) {
|
||||
ret = FALSE;
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -242,19 +245,20 @@ int cm_all_lessoreq_int(int limit, int n, ...)
|
||||
}
|
||||
|
||||
|
||||
int cm_all_equal_int(int n, ...)
|
||||
bool cm_all_equal_int(int n, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int first, i, ret = TRUE;
|
||||
int first, i;
|
||||
bool ret = true;
|
||||
|
||||
if (n < 2)
|
||||
return TRUE;
|
||||
return true;
|
||||
|
||||
va_start(ap, n);
|
||||
first = va_arg(ap, int);
|
||||
for (i = 1; i < n; i++) {
|
||||
if (va_arg(ap, int) != first) {
|
||||
ret = FALSE;
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -264,18 +268,19 @@ int cm_all_equal_int(int n, ...)
|
||||
}
|
||||
|
||||
|
||||
int cm_all_positive_int(int n, ...)
|
||||
bool cm_all_positive_int(int n, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int i, ret = TRUE;
|
||||
int i;
|
||||
bool ret = true;
|
||||
|
||||
if (n < 1)
|
||||
return TRUE;
|
||||
return true;
|
||||
|
||||
va_start(ap, n);
|
||||
for (i = 0; i < n; i++) {
|
||||
if (va_arg(ap, int) < 0) {
|
||||
ret = FALSE;
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -285,18 +290,19 @@ int cm_all_positive_int(int n, ...)
|
||||
}
|
||||
|
||||
|
||||
int cm_is_one_of(int candidate, int n, ...)
|
||||
bool cm_is_one_of(int n, int candidate, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int i, ret = FALSE;
|
||||
int i;
|
||||
bool ret = false;
|
||||
|
||||
if (n < 1)
|
||||
return TRUE;
|
||||
return true;
|
||||
|
||||
va_start(ap, n);
|
||||
va_start(ap, candidate);
|
||||
for (i = 0; i < n; i++) {
|
||||
if (va_arg(ap, int) == candidate) {
|
||||
ret = TRUE;
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -307,14 +313,14 @@ int cm_is_one_of(int candidate, int n, ...)
|
||||
|
||||
|
||||
|
||||
int cm_align4padding(unsigned long a)
|
||||
int cm_align4padding(unsigned long long a)
|
||||
{
|
||||
return cm_align4size(a) - a;
|
||||
return (int) (cm_align4size(a) - a);
|
||||
}
|
||||
|
||||
int cm_align2padding(unsigned long a)
|
||||
int cm_align2padding(unsigned long long a)
|
||||
{
|
||||
return cm_align2size(a) - a;
|
||||
return (int) (cm_align2size(a) - a);
|
||||
}
|
||||
|
||||
|
||||
@@ -324,91 +330,106 @@ int cm_align2padding(unsigned long a)
|
||||
* from little-endian files
|
||||
*********************************************************/
|
||||
|
||||
int write_u16_le(FILE *file, uint16_t val)
|
||||
bool write_u16_le(FILE *file, uint16_t val)
|
||||
{
|
||||
return (EOF != fputc(val & 0xff, file) &&
|
||||
EOF != fputc((val >> 8) & 0xff, file));
|
||||
}
|
||||
|
||||
|
||||
int write_u32_le(FILE *file, uint32_t val)
|
||||
bool write_u32_le(FILE *file, uint32_t val)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (EOF == fputc((val >> (i*8)) & 0xff, file))
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int read_u16_le(FILE *file, uint16_t *val)
|
||||
bool read_u16_le(FILE *file, uint16_t *val)
|
||||
{
|
||||
unsigned char buf[2];
|
||||
|
||||
if (2 != fread(buf, 1, 2, file))
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
*val = (buf[1] << 8) | buf[0];
|
||||
*val = ((unsigned)buf[1] << 8) | (unsigned)buf[0];
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int read_u32_le(FILE *file, uint32_t *val)
|
||||
bool read_u32_le(FILE *file, uint32_t *val)
|
||||
{
|
||||
unsigned char buf[4];
|
||||
|
||||
if (4 != fread(buf, 1, 4, file))
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
*val = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
|
||||
*val = ((uint32_t)buf[3] << 24) | ((uint32_t)buf[2] << 16) |
|
||||
((uint32_t)buf[1] << 8) | (uint32_t)buf[0];
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int write_s16_le(FILE *file, int16_t val)
|
||||
bool write_s16_le(FILE *file, int16_t val)
|
||||
{
|
||||
return (EOF != fputc(val & 0xff, file) &&
|
||||
EOF != fputc((val >> 8) & 0xff, file));
|
||||
return write_u16_le(file, (uint16_t)val);
|
||||
}
|
||||
|
||||
|
||||
int read_s16_le(FILE *file, int16_t *val)
|
||||
bool write_s32_le(FILE *file, int32_t val)
|
||||
{
|
||||
unsigned char buf[2];
|
||||
|
||||
if (2 != fread(buf, 1, 2, file))
|
||||
return 0;
|
||||
|
||||
*val = (((int16_t)(signed char)buf[1]) << 8) | (int16_t) buf[0];
|
||||
|
||||
return 1;
|
||||
return write_u32_le(file, (uint32_t)val);
|
||||
}
|
||||
|
||||
|
||||
int read_s32_le(FILE *file, int32_t *val)
|
||||
bool read_s16_le(FILE *file, int16_t *val)
|
||||
{
|
||||
unsigned char buf[4];
|
||||
uint16_t u16;
|
||||
|
||||
if (4 != fread(buf, 1, 4, file))
|
||||
return 0;
|
||||
if (!read_u16_le(file, &u16))
|
||||
return false;
|
||||
|
||||
*val = (((int32_t)(signed char)buf[3]) << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
|
||||
*val = (int16_t)u16;
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int write_s32_le(FILE *file, int32_t val)
|
||||
bool read_s32_le(FILE *file, int32_t *val)
|
||||
{
|
||||
int i;
|
||||
uint32_t u32;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (EOF == fputc((val >> (i*8)) & 0xff, file))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
if (!read_u32_le(file, &u32))
|
||||
return false;
|
||||
|
||||
*val = (int32_t)u32;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint32_t u32_from_le(const unsigned char *buf)
|
||||
{
|
||||
return (uint32_t)buf[3] << 24 | (uint32_t)buf[2] << 16 |
|
||||
(uint32_t)buf[1] << 8 | (uint32_t)buf[0];
|
||||
}
|
||||
|
||||
int32_t s32_from_le(const unsigned char *buf)
|
||||
{
|
||||
return (int32_t)u32_from_le(buf);
|
||||
}
|
||||
|
||||
uint16_t u16_from_le(const unsigned char *buf)
|
||||
{
|
||||
return (uint16_t)buf[1] << 8 | (uint16_t)buf[0];
|
||||
}
|
||||
|
||||
int16_t s16_from_le(const unsigned char *buf)
|
||||
{
|
||||
return (int16_t)u16_from_le(buf);
|
||||
}
|
||||
|
||||
188
bmp-common.h
188
bmp-common.h
@@ -3,27 +3,21 @@
|
||||
* Copyright (c) 2024, Rupert Weber.
|
||||
*
|
||||
* This file is part of bmplib.
|
||||
* bmplib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* bmplib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
* License along with this library.
|
||||
* If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#undef TRUE
|
||||
#define TRUE (1)
|
||||
|
||||
#undef FALSE
|
||||
#define FALSE (0)
|
||||
|
||||
#undef MAX
|
||||
#undef MIN
|
||||
#define MAX(a,b) ((a)>(b)?(a):(b))
|
||||
@@ -32,10 +26,13 @@
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define ATTR_CONST __attribute__((const))
|
||||
#define API __attribute__ ((visibility ("default")))
|
||||
#else
|
||||
#define ATTR_CONST
|
||||
#define API
|
||||
#endif
|
||||
|
||||
|
||||
union Pixel {
|
||||
unsigned int value[4];
|
||||
struct {
|
||||
@@ -48,7 +45,7 @@ union Pixel {
|
||||
|
||||
struct Colormask {
|
||||
union {
|
||||
unsigned long value[4];
|
||||
unsigned long long value[4];
|
||||
struct {
|
||||
unsigned long long red;
|
||||
unsigned long long green;
|
||||
@@ -57,21 +54,21 @@ struct Colormask {
|
||||
};
|
||||
} mask;
|
||||
union {
|
||||
unsigned long value[4];
|
||||
int value[4];
|
||||
struct {
|
||||
unsigned long red;
|
||||
unsigned long green;
|
||||
unsigned long blue;
|
||||
unsigned long alpha;
|
||||
int red;
|
||||
int green;
|
||||
int blue;
|
||||
int alpha;
|
||||
};
|
||||
} shift;
|
||||
union {
|
||||
int value[4];
|
||||
struct {
|
||||
int red;
|
||||
int green;
|
||||
int blue;
|
||||
int alpha;
|
||||
int red;
|
||||
int green;
|
||||
int blue;
|
||||
int alpha;
|
||||
};
|
||||
} bits;
|
||||
union {
|
||||
@@ -87,8 +84,8 @@ struct Colormask {
|
||||
|
||||
typedef struct Bmpread *BMPREAD;
|
||||
typedef struct Bmpwrite *BMPWRITE;
|
||||
typedef struct Bmpread * restrict BMPREAD_R;
|
||||
typedef struct Bmpwrite * restrict BMPWRITE_R;
|
||||
typedef struct Bmpread *restrict BMPREAD_R;
|
||||
typedef struct Bmpwrite *restrict BMPWRITE_R;
|
||||
|
||||
struct Palette {
|
||||
int numcolors;
|
||||
@@ -106,50 +103,52 @@ struct Bmpread {
|
||||
struct Bmpinfo *ih;
|
||||
unsigned int insanity_limit;
|
||||
int width;
|
||||
int height;
|
||||
unsigned height;
|
||||
enum BmpOrient orientation;
|
||||
int has_alpha; /* original BMP has alpha channel */
|
||||
bool has_alpha; /* original BMP has alpha channel */
|
||||
enum BmpUndefined undefined_mode;
|
||||
int we_allocated_buffer;
|
||||
int line_by_line;
|
||||
bool we_allocated_buffer;
|
||||
bool line_by_line;
|
||||
struct Palette *palette;
|
||||
struct Colormask cmask;
|
||||
/* result image dimensions */
|
||||
enum Bmpconv64 conv64;
|
||||
int conv64_explicit;
|
||||
bool conv64_explicit;
|
||||
int result_channels;
|
||||
int result_indexed;
|
||||
bool result_indexed;
|
||||
int result_bits_per_pixel;
|
||||
int result_bytes_per_pixel;
|
||||
int result_bits_per_channel;
|
||||
int result_bitsperchannel;
|
||||
enum BmpFormat result_format;
|
||||
int result_format_explicit;
|
||||
bool result_format_explicit;
|
||||
size_t result_size;
|
||||
/* state */
|
||||
unsigned long lasterr;
|
||||
int getinfo_called;
|
||||
bool getinfo_called;
|
||||
int getinfo_return;
|
||||
int jpeg;
|
||||
int png;
|
||||
int dimensions_queried;
|
||||
int dim_queried_width;
|
||||
int dim_queried_height;
|
||||
int dim_queried_channels;
|
||||
int dim_queried_bits_per_channel;
|
||||
int image_loaded;
|
||||
int rle;
|
||||
int rle_eol;
|
||||
int rle_eof;
|
||||
bool jpeg;
|
||||
bool png;
|
||||
bool dimensions_queried;
|
||||
bool dim_queried_width;
|
||||
bool dim_queried_height;
|
||||
bool dim_queried_channels;
|
||||
bool dim_queried_bitsperchannel;
|
||||
bool image_loaded;
|
||||
bool rle;
|
||||
bool rle_eol;
|
||||
bool rle_eof;
|
||||
int lbl_x; /* remember where we are in the image */
|
||||
int lbl_y; /* for line by line reading */
|
||||
int lbl_file_y; /* RLE files may be ahead of the image y */
|
||||
int truncated;
|
||||
int invalid_index;
|
||||
int invalid_delta;
|
||||
int invalid_overrun;
|
||||
int file_err;
|
||||
int file_eof;
|
||||
int panic;
|
||||
uint32_t hufbuf;
|
||||
int hufbuf_len;
|
||||
bool truncated;
|
||||
bool invalid_index;
|
||||
bool invalid_delta;
|
||||
bool invalid_overrun;
|
||||
bool file_err;
|
||||
bool file_eof;
|
||||
bool panic;
|
||||
|
||||
};
|
||||
|
||||
@@ -174,72 +173,80 @@ struct Bmpwrite {
|
||||
/* output */
|
||||
size_t bytes_written;
|
||||
size_t bytes_written_before_bitdata;
|
||||
int has_alpha;
|
||||
bool has_alpha;
|
||||
enum BmpOrient outorientation;
|
||||
struct Colormask cmask;
|
||||
int rle_requested;
|
||||
int rle;
|
||||
int allow_2bit; /* Windows CE, but many will not read it */
|
||||
int out64bit;
|
||||
enum BmpRLEtype rle_requested;
|
||||
int rle; /* 1, 4, or 8 */
|
||||
bool allow_2bit; /* Windows CE, but many will not read it */
|
||||
bool allow_huffman;
|
||||
bool allow_rle24;
|
||||
bool out64bit;
|
||||
int outbytes_per_pixel;
|
||||
int padding;
|
||||
int *group;
|
||||
int group_count;
|
||||
/* state */
|
||||
int outbits_set;
|
||||
int dimensions_set;
|
||||
int saveimage_done;
|
||||
int line_by_line;
|
||||
bool outbits_set;
|
||||
bool dimensions_set;
|
||||
bool saveimage_done;
|
||||
bool line_by_line;
|
||||
int lbl_y;
|
||||
uint32_t hufbuf;
|
||||
int hufbuf_len;
|
||||
};
|
||||
|
||||
|
||||
|
||||
int cm_all_lessoreq_int(int limit, int n, ...);
|
||||
int cm_all_equal_int(int n, ...);
|
||||
int cm_all_positive_int(int n, ...);
|
||||
int cm_is_one_of(int candidate, int n, ...);
|
||||
bool cm_all_lessoreq_int(int limit, int n, ...);
|
||||
bool cm_all_equal_int(int n, ...);
|
||||
bool cm_all_positive_int(int n, ...);
|
||||
bool cm_is_one_of(int n, int candidate, ...);
|
||||
|
||||
#define cm_align4size(a) ((((a) + 3) >> 2) << 2)
|
||||
#define cm_align2size(a) ((((a) + 1) >> 1) << 1)
|
||||
int cm_align4padding(unsigned long a);
|
||||
int cm_align2padding(unsigned long a);
|
||||
int cm_align4padding(unsigned long long a);
|
||||
int cm_align2padding(unsigned long long a);
|
||||
int cm_count_bits(unsigned long v);
|
||||
|
||||
int cm_gobble_up(BMPREAD_R rp, int count);
|
||||
int cm_check_is_read_handle(BMPHANDLE h);
|
||||
int cm_check_is_write_handle(BMPHANDLE h);
|
||||
bool cm_gobble_up(BMPREAD_R rp, int count);
|
||||
BMPREAD cm_read_handle(BMPHANDLE h);
|
||||
BMPWRITE cm_write_handle(BMPHANDLE h);
|
||||
|
||||
const char* cm_conv64_name(enum Bmpconv64 conv);
|
||||
const char* cm_format_name(enum BmpFormat format);
|
||||
|
||||
int write_u16_le(FILE *file, uint16_t val);
|
||||
int write_u32_le(FILE *file, uint32_t val);
|
||||
int read_u16_le(FILE *file, uint16_t *val);
|
||||
int read_u32_le(FILE *file, uint32_t *val);
|
||||
bool write_u16_le(FILE *file, uint16_t val);
|
||||
bool write_u32_le(FILE *file, uint32_t val);
|
||||
bool read_u16_le(FILE *file, uint16_t *val);
|
||||
bool read_u32_le(FILE *file, uint32_t *val);
|
||||
|
||||
int write_s16_le(FILE *file, int16_t val);
|
||||
int write_s32_le(FILE *file, int32_t val);
|
||||
int read_s16_le(FILE *file, int16_t *val);
|
||||
int read_s32_le(FILE *file, int32_t *val);
|
||||
bool write_s16_le(FILE *file, int16_t val);
|
||||
bool write_s32_le(FILE *file, int32_t val);
|
||||
bool read_s16_le(FILE *file, int16_t *val);
|
||||
bool read_s32_le(FILE *file, int32_t *val);
|
||||
|
||||
#define API __attribute__ ((visibility ("default")))
|
||||
uint32_t u32_from_le(const unsigned char *buf);
|
||||
int32_t s32_from_le(const unsigned char *buf);
|
||||
uint16_t u16_from_le(const unsigned char *buf);
|
||||
int16_t s16_from_le(const unsigned char *buf);
|
||||
|
||||
|
||||
#define HMAGIC_READ (0x44414552UL)
|
||||
#define HMAGIC_WRITE (0x54495257UL)
|
||||
#define HMAGIC_READ 0x44414552UL
|
||||
#define HMAGIC_WRITE 0x54495257UL
|
||||
|
||||
#define BMPFILE_BM (0x4d42)
|
||||
#define BMPFILE_BA (0x4142)
|
||||
#define BMPFILE_CI (0x4943)
|
||||
#define BMPFILE_CP (0x5043)
|
||||
#define BMPFILE_IC (0x4349)
|
||||
#define BMPFILE_PT (0x5450)
|
||||
#define BMPFILE_BM 0x4d42
|
||||
#define BMPFILE_BA 0x4142
|
||||
#define BMPFILE_CI 0x4943
|
||||
#define BMPFILE_CP 0x5043
|
||||
#define BMPFILE_IC 0x4349
|
||||
#define BMPFILE_PT 0x5450
|
||||
|
||||
|
||||
#define BMPFHSIZE (14)
|
||||
#define BMPIHSIZE_V3 (40)
|
||||
#define BMPIHSIZE_V4 (108)
|
||||
#define BMPFHSIZE 14
|
||||
#define BMPIHSIZE_V3 40
|
||||
#define BMPIHSIZE_V4 108
|
||||
#define BMPIHSIZE_OS22 64
|
||||
|
||||
typedef uint16_t WORD;
|
||||
typedef uint32_t DWORD;
|
||||
@@ -324,3 +331,10 @@ struct Bmpinfo {
|
||||
/* we set our own unique values: */
|
||||
#define BI_OS2_HUFFMAN 1001
|
||||
#define BI_OS2_RLE24 1002
|
||||
|
||||
|
||||
#define LCS_CALIBRATED_RGB 0
|
||||
#define LCS_sRGB 0x73524742 /* 'sRGB' */
|
||||
#define LCS_WINDOWS_COLOR_SPACE 0x57696e20 /* 'Win ' */
|
||||
#define PROFILE_LINKED 0x4c494e4b /* 'LINK' */
|
||||
#define PROFILE_EMBEDDED 0x4d424544 /* 'MBED' */
|
||||
|
||||
@@ -3,18 +3,18 @@
|
||||
* Copyright (c) 2024, Rupert Weber.
|
||||
*
|
||||
* This file is part of bmplib.
|
||||
* bmplib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* bmplib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
* License along with this library.
|
||||
* If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
@@ -22,13 +22,18 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <math.h>
|
||||
|
||||
#define BMPLIB_LIB
|
||||
|
||||
#include "config.h"
|
||||
#include "bmplib.h"
|
||||
#include "logging.h"
|
||||
#include "bmp-common.h"
|
||||
#include "huffman.h"
|
||||
#include "bmp-read.h"
|
||||
#include "reversebits.h"
|
||||
|
||||
|
||||
/*
|
||||
@@ -40,31 +45,37 @@
|
||||
* \ /
|
||||
* common prep work, \ /
|
||||
* buffer allocation, s_load_image_or_line()
|
||||
* sanity checks, etc. / \
|
||||
* / \
|
||||
* / \
|
||||
* 'supervision' s_read_whole_image() s_read_one_line()
|
||||
* \ /
|
||||
* \ /
|
||||
* \ /
|
||||
* sanity checks, etc. | |
|
||||
* | |
|
||||
* | |
|
||||
* s_read_whole_image() |
|
||||
* \ |
|
||||
* \ |
|
||||
* s_read_one_line()
|
||||
* |
|
||||
* s_read_rgb_line()
|
||||
* 'grunt work' s_read_indexed_line()
|
||||
* s_read_rle_line()
|
||||
* s_read_huffman_line()
|
||||
*/
|
||||
|
||||
static inline unsigned long s_scaleint(unsigned long val, int frombits, int tobits) ATTR_CONST;
|
||||
static void s_set_file_error(BMPREAD_R rp);
|
||||
static void s_log_error_from_state(BMPREAD_R rp);
|
||||
static int s_cont_error(BMPREAD_R rp);
|
||||
static int s_stopping_error(BMPREAD_R rp);
|
||||
static bool s_cont_error(BMPREAD_R rp);
|
||||
static bool s_stopping_error(BMPREAD_R rp);
|
||||
static inline int s_read_one_byte(BMPREAD_R rp);
|
||||
static inline void s_int_to_result_format(BMPREAD_R rp, int frombits, unsigned char *restrict px);
|
||||
|
||||
static BMPRESULT s_load_image_or_line(BMPREAD_R rp, unsigned char **restrict buffer, int line_by_line);
|
||||
static int s_read_rgb_line(BMPREAD_R rp, unsigned char *restrict line);
|
||||
static BMPRESULT s_load_image_or_line(BMPREAD_R rp, unsigned char **restrict buffer, bool line_by_line);
|
||||
static void s_read_rgb_line(BMPREAD_R rp, unsigned char *restrict line);
|
||||
static void s_read_indexed_line(BMPREAD_R rp, unsigned char *restrict line);
|
||||
static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
|
||||
int *restrict x, int *restrict yoff);
|
||||
static void s_read_huffman_line(BMPREAD_R rp, unsigned char *restrict line);
|
||||
|
||||
_Static_assert(sizeof(float) == 4, "sizeof(float) must be 4. Cannot build bmplib.");
|
||||
_Static_assert(sizeof(int) >= 4, "int must be at least 32bit. Cannot build bmplib.");
|
||||
|
||||
|
||||
/********************************************************
|
||||
@@ -75,12 +86,10 @@ API BMPRESULT bmpread_load_image(BMPHANDLE h, unsigned char **restrict buffer)
|
||||
{
|
||||
BMPREAD rp;
|
||||
|
||||
if (!(h && cm_check_is_read_handle(h)))
|
||||
if (!(rp = cm_read_handle(h)))
|
||||
return BMP_RESULT_ERROR;
|
||||
rp = (BMPREAD)(void*)h;
|
||||
|
||||
return s_load_image_or_line(rp, buffer, FALSE);
|
||||
|
||||
return s_load_image_or_line(rp, buffer, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -93,14 +102,13 @@ API BMPRESULT bmpread_load_line(BMPHANDLE h, unsigned char **restrict buffer)
|
||||
{
|
||||
BMPREAD rp;
|
||||
|
||||
if (!(h && cm_check_is_read_handle(h)))
|
||||
if (!(rp = cm_read_handle(h)))
|
||||
return BMP_RESULT_ERROR;
|
||||
rp = (BMPREAD)(void*)h;
|
||||
|
||||
logreset(rp->log); /* otherwise we might accumulate thousands */
|
||||
/* of log entries with large corrupt images */
|
||||
|
||||
return s_load_image_or_line(rp, buffer, TRUE);
|
||||
return s_load_image_or_line(rp, buffer, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -112,7 +120,7 @@ API BMPRESULT bmpread_load_line(BMPHANDLE h, unsigned char **restrict buffer)
|
||||
static void s_read_whole_image(BMPREAD_R rp, unsigned char *restrict image);
|
||||
static void s_read_one_line(BMPREAD_R rp, unsigned char *restrict image);
|
||||
|
||||
static BMPRESULT s_load_image_or_line(BMPREAD_R rp, unsigned char **restrict buffer, int line_by_line)
|
||||
static BMPRESULT s_load_image_or_line(BMPREAD_R rp, unsigned char **restrict buffer, bool line_by_line)
|
||||
{
|
||||
size_t buffer_size;
|
||||
|
||||
@@ -147,7 +155,7 @@ static BMPRESULT s_load_image_or_line(BMPREAD_R rp, unsigned char **restrict buf
|
||||
}
|
||||
|
||||
if (line_by_line)
|
||||
buffer_size = rp->width * rp->result_bytes_per_pixel;
|
||||
buffer_size = (size_t) rp->width * rp->result_bytes_per_pixel;
|
||||
else
|
||||
buffer_size = rp->result_size;
|
||||
if (!*buffer) { /* no buffer supplied, we will allocate one */
|
||||
@@ -155,17 +163,16 @@ static BMPRESULT s_load_image_or_line(BMPREAD_R rp, unsigned char **restrict buf
|
||||
logsyserr(rp->log, "allocating result buffer");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
rp->we_allocated_buffer = TRUE;
|
||||
}
|
||||
else {
|
||||
rp->we_allocated_buffer = FALSE;
|
||||
rp->we_allocated_buffer = true;
|
||||
} else {
|
||||
rp->we_allocated_buffer = false;
|
||||
}
|
||||
|
||||
if (rp->we_allocated_buffer || (rp->rle && (rp->undefined_mode == BMP_UNDEFINED_TO_ALPHA)))
|
||||
memset(*buffer, 0, buffer_size);
|
||||
|
||||
if (!line_by_line)
|
||||
rp->image_loaded = TRUE; /* point of no return */
|
||||
rp->image_loaded = true; /* point of no return */
|
||||
|
||||
if (!rp->line_by_line) { /* either whole image or first line */
|
||||
if (rp->bytes_read > rp->fh->offbits) {
|
||||
@@ -181,21 +188,19 @@ static BMPRESULT s_load_image_or_line(BMPREAD_R rp, unsigned char **restrict buf
|
||||
}
|
||||
|
||||
if (line_by_line) {
|
||||
rp->line_by_line = TRUE; /* don't set this earlier, or we won't */
|
||||
rp->line_by_line = true; /* don't set this earlier, or we won't */
|
||||
/* be able to identify first line */
|
||||
s_read_one_line(rp, *buffer);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
s_read_whole_image(rp, *buffer);
|
||||
}
|
||||
|
||||
s_log_error_from_state(rp);
|
||||
if (s_stopping_error(rp)) {
|
||||
rp->truncated = TRUE;
|
||||
rp->image_loaded = TRUE;
|
||||
rp->truncated = true;
|
||||
rp->image_loaded = true;
|
||||
return BMP_RESULT_TRUNCATED;
|
||||
}
|
||||
else if (s_cont_error(rp))
|
||||
} else if (s_cont_error(rp))
|
||||
return BMP_RESULT_INVALID;
|
||||
|
||||
return BMP_RESULT_OK;
|
||||
@@ -205,7 +210,7 @@ abort:
|
||||
free(*buffer);
|
||||
*buffer = NULL;
|
||||
}
|
||||
rp->image_loaded = TRUE;
|
||||
rp->image_loaded = true;
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
@@ -217,31 +222,17 @@ abort:
|
||||
|
||||
static void s_read_whole_image(BMPREAD_R rp, unsigned char *restrict image)
|
||||
{
|
||||
int x = 0, y, yoff = 1;
|
||||
int y, yoff = 1;
|
||||
size_t linesize, real_y;
|
||||
|
||||
linesize = (size_t) rp->width * (size_t) rp->result_bytes_per_pixel;
|
||||
linesize = (size_t) rp->width * rp->result_bytes_per_pixel;
|
||||
|
||||
for (y = 0; y < rp->height; y += yoff) {
|
||||
for (y = 0; y < (int) rp->height; y += yoff) {
|
||||
real_y = (rp->orientation == BMP_ORIENT_TOPDOWN) ? y : rp->height-1-y;
|
||||
if (rp->rle) {
|
||||
s_read_rle_line(rp, image + real_y * linesize, &x, &yoff);
|
||||
if (x >= rp->width)
|
||||
x = 0;
|
||||
}
|
||||
else if (rp->ih->bitcount <= 8) {
|
||||
s_read_indexed_line(rp, image + real_y * linesize);
|
||||
}
|
||||
else {
|
||||
s_read_rgb_line(rp, image + real_y * linesize);
|
||||
}
|
||||
s_read_one_line(rp, image + real_y * linesize);
|
||||
if (rp->rle_eof || s_stopping_error(rp))
|
||||
break;
|
||||
}
|
||||
if (y > rp->height) {
|
||||
logerr(rp->log, "RLE delta beyond image dimensions");
|
||||
rp->invalid_delta = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -261,33 +252,34 @@ static void s_read_one_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
|
||||
if (rp->lbl_file_y > rp->lbl_y) {
|
||||
; /* nothing to do, RLE skipped line */
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (rp->rle) {
|
||||
s_read_rle_line(rp, line, &rp->lbl_x, &yoff);
|
||||
}
|
||||
else {
|
||||
s_read_indexed_line(rp, line);
|
||||
} else {
|
||||
if (rp->ih->compression == BI_OS2_HUFFMAN) {
|
||||
s_read_huffman_line(rp, line);
|
||||
} else {
|
||||
s_read_indexed_line(rp, line);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(rp->rle_eof || s_stopping_error(rp))) {
|
||||
if (yoff > (int) rp->height - rp->lbl_file_y) {
|
||||
rp->invalid_delta = true;
|
||||
}
|
||||
rp->lbl_file_y += yoff;
|
||||
|
||||
if (rp->lbl_file_y > rp->height) {
|
||||
rp->invalid_delta = TRUE;
|
||||
}
|
||||
}
|
||||
if (rp->rle_eof)
|
||||
rp->lbl_file_y = rp->height;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
s_read_rgb_line(rp, line);
|
||||
}
|
||||
|
||||
rp->lbl_y++;
|
||||
if (rp->lbl_y >= rp->height) {
|
||||
rp->image_loaded = TRUE;
|
||||
if (rp->lbl_y >= (int) rp->height) {
|
||||
rp->image_loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,7 +289,7 @@ static void s_read_one_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
* s_read_rgb_line
|
||||
*******************************************************/
|
||||
|
||||
static inline int s_read_rgb_pixel(BMPREAD_R rp, union Pixel *restrict px);
|
||||
static inline bool s_read_rgb_pixel(BMPREAD_R rp, union Pixel *restrict px);
|
||||
static inline double s_s2_13_to_float(uint16_t s2_13);
|
||||
static inline double s_int_to_float(unsigned long ul, int bits);
|
||||
static inline void s_convert64(uint16_t *val64);
|
||||
@@ -305,12 +297,12 @@ static inline void s_convert64srgb(uint16_t *val64);
|
||||
static inline double s_srgb_gamma_float(double d);
|
||||
static inline uint16_t s_srgb_gamma_s2_13(uint16_t s2_13);
|
||||
|
||||
static int s_read_rgb_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
static void s_read_rgb_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
{
|
||||
int i, x, padding;
|
||||
union Pixel px;
|
||||
size_t offs;
|
||||
int bits = rp->result_bits_per_channel;
|
||||
int bits = rp->result_bitsperchannel;
|
||||
uint32_t pxval;
|
||||
double d;
|
||||
uint16_t s2_13;
|
||||
@@ -318,7 +310,7 @@ static int s_read_rgb_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
for (x = 0; x < rp->width; x++) {
|
||||
|
||||
if (!s_read_rgb_pixel(rp, &px)) {
|
||||
return FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
offs = x * rp->result_channels;
|
||||
@@ -339,8 +331,8 @@ static int s_read_rgb_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
break;
|
||||
default:
|
||||
logerr(rp->log, "Waaaaaaaaaaaaaah!");
|
||||
rp->panic = TRUE;
|
||||
return FALSE;
|
||||
rp->panic = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (rp->ih->bitcount == 64) {
|
||||
@@ -363,13 +355,12 @@ static int s_read_rgb_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
d = s_s2_13_to_float(px.value[i]);
|
||||
if (i < 3 && rp->conv64 == BMP_CONV64_SRGB)
|
||||
d = s_srgb_gamma_float(d);
|
||||
((float*)line)[offs+i] = d;
|
||||
((float*)line)[offs+i] = (float) d;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
for (i = 0; i < rp->result_channels; i++) {
|
||||
d = s_int_to_float(px.value[i], rp->cmask.bits.value[i]);
|
||||
((float*)line)[offs + i] = d;
|
||||
((float*)line)[offs + i] = (float) d;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -382,8 +373,7 @@ static int s_read_rgb_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
s2_13 = s_srgb_gamma_s2_13(s2_13);
|
||||
((uint16_t*)line)[offs+i] = s2_13;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
for (i = 0; i < rp->result_channels; i++) {
|
||||
d = s_int_to_float(px.value[i], rp->cmask.bits.value[i]);
|
||||
d = d * 8192.0 + 0.5;
|
||||
@@ -394,70 +384,60 @@ static int s_read_rgb_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
|
||||
default:
|
||||
logerr(rp->log, "Unknown format");
|
||||
rp->panic = TRUE;
|
||||
return FALSE;
|
||||
rp->panic = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
padding = cm_align4padding((rp->width * rp->ih->bitcount + 7) / 8);
|
||||
padding = cm_align4padding(((uint64_t)rp->width * rp->ih->bitcount + 7) / 8);
|
||||
if (!cm_gobble_up(rp, padding)) {
|
||||
s_set_file_error(rp);
|
||||
return FALSE;
|
||||
return;
|
||||
}
|
||||
rp->bytes_read += padding;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static inline double s_s2_13_to_float(uint16_t s2_13)
|
||||
{
|
||||
return ((double)((int16_t)s2_13)) / 8192.0;
|
||||
return (int16_t)s2_13 / 8192.0;
|
||||
}
|
||||
|
||||
|
||||
static inline double s_int_to_float(unsigned long ul, int bits)
|
||||
{
|
||||
return (double) ul / (double) ((1ULL<<bits)-1);
|
||||
return (double) ul / ((1ULL<<bits)-1);
|
||||
}
|
||||
|
||||
|
||||
static inline void s_convert64(uint16_t *val64)
|
||||
{
|
||||
int i;
|
||||
int32_t s;
|
||||
double d;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
s = val64[i];
|
||||
s = s << 16 >> 16; /* propagate sign bit */
|
||||
s *= 0xffff;
|
||||
s >>= 13;
|
||||
s = MAX(0, s);
|
||||
s = MIN(s, 0xffff);
|
||||
val64[i] = s;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
d = (double) (int16_t) val64[i];
|
||||
d /= 8192.0;
|
||||
d = MAX(0.0, d);
|
||||
d = MIN(d, 1.0);
|
||||
val64[i] = (uint16_t) (d * 0xffff + 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline void s_convert64srgb(uint16_t *val64)
|
||||
{
|
||||
int i;
|
||||
int32_t s;
|
||||
double v;
|
||||
double d;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
s = val64[i];
|
||||
s = s << 16 >> 16; /* propagate sign bit */
|
||||
if (i < 3) {
|
||||
v = (double) s / (1<<13);
|
||||
v = s_srgb_gamma_float(v);
|
||||
s = (int32_t) (v * (double) 0xffff);
|
||||
}
|
||||
else { /* don't apply gamma to alpha channel */
|
||||
s *= 0xffff;
|
||||
s >>= 13;
|
||||
}
|
||||
s = MAX(0, s);
|
||||
s = MIN(s, 0xffff);
|
||||
val64[i] = s;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
d = (double) (int16_t) val64[i];
|
||||
d /= 8192.0;
|
||||
d = MAX(0.0, d);
|
||||
d = MIN(d, 1.0);
|
||||
|
||||
/* apply gamma to RGB channels, but not to alpha channel */
|
||||
if (i < 3)
|
||||
d = s_srgb_gamma_float(d);
|
||||
|
||||
val64[i] = (uint16_t) (d * 0xffff + 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -477,7 +457,7 @@ static inline uint16_t s_srgb_gamma_s2_13(uint16_t s2_13)
|
||||
{
|
||||
double d;
|
||||
|
||||
d = (double) ((int16_t)s2_13) / 8192.0;
|
||||
d = (int16_t)s2_13 / 8192.0;
|
||||
d = s_srgb_gamma_float(d);
|
||||
return (uint16_t) (((int)(d * 8192.0 + 0.5)) & 0xffff);
|
||||
}
|
||||
@@ -488,7 +468,7 @@ static inline uint16_t s_srgb_gamma_s2_13(uint16_t s2_13)
|
||||
* s_read_rgb_pixel
|
||||
*******************************************************/
|
||||
|
||||
static inline int s_read_rgb_pixel(BMPREAD_R rp, union Pixel *restrict px)
|
||||
static inline bool s_read_rgb_pixel(BMPREAD_R rp, union Pixel *restrict px)
|
||||
{
|
||||
unsigned long long v;
|
||||
int i, byte;
|
||||
@@ -497,20 +477,20 @@ static inline int s_read_rgb_pixel(BMPREAD_R rp, union Pixel *restrict px)
|
||||
for (i = 0; i < rp->ih->bitcount; i+=8 ) {
|
||||
if (EOF == (byte = s_read_one_byte(rp))) {
|
||||
s_set_file_error(rp);
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
v |= ((unsigned long long)byte) << i;
|
||||
}
|
||||
|
||||
px->red = (v & rp->cmask.mask.red) >> rp->cmask.shift.red;
|
||||
px->green = (v & rp->cmask.mask.green) >> rp->cmask.shift.green;
|
||||
px->blue = (v & rp->cmask.mask.blue) >> rp->cmask.shift.blue;
|
||||
px->red = (unsigned int) ((v & rp->cmask.mask.red) >> rp->cmask.shift.red);
|
||||
px->green = (unsigned int) ((v & rp->cmask.mask.green) >> rp->cmask.shift.green);
|
||||
px->blue = (unsigned int) ((v & rp->cmask.mask.blue) >> rp->cmask.shift.blue);
|
||||
if (rp->has_alpha)
|
||||
px->alpha = (v & rp->cmask.mask.alpha) >> rp->cmask.shift.alpha;
|
||||
px->alpha = (unsigned int) ((v & rp->cmask.mask.alpha) >> rp->cmask.shift.alpha);
|
||||
else
|
||||
px->alpha = (1<<rp->result_bits_per_channel) - 1;
|
||||
px->alpha = (1ULL<<rp->result_bitsperchannel) - 1;
|
||||
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -519,14 +499,14 @@ static inline int s_read_rgb_pixel(BMPREAD_R rp, union Pixel *restrict px)
|
||||
* s_read_indexed_line
|
||||
* - 1/2/4/8 bits non-RLE indexed
|
||||
*******************************************************/
|
||||
static inline int s_read_n_bytes(BMPREAD_R rp, int n, unsigned long *restrict buff);
|
||||
static inline bool s_read_n_bytes(BMPREAD_R rp, int n, unsigned long *restrict buff);
|
||||
static inline unsigned long s_bits_from_buffer(unsigned long buf, int size,
|
||||
int nbits, int used_bits);
|
||||
|
||||
static void s_read_indexed_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
{
|
||||
int bits_used, buffer_size, x = 0, v;
|
||||
int done = FALSE;
|
||||
bool done = false;
|
||||
unsigned long buffer;
|
||||
size_t offs;
|
||||
|
||||
@@ -545,21 +525,20 @@ static void s_read_indexed_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
|
||||
if (v >= rp->palette->numcolors) {
|
||||
v = rp->palette->numcolors - 1;
|
||||
rp->invalid_index = TRUE;
|
||||
rp->invalid_index = true;
|
||||
}
|
||||
|
||||
offs = (size_t) x * (size_t) rp->result_bytes_per_pixel;
|
||||
offs = (size_t) x * rp->result_bytes_per_pixel;
|
||||
if (rp->result_indexed) {
|
||||
line[offs] = v;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
line[offs] = rp->palette->color[v].red;
|
||||
line[offs+1] = rp->palette->color[v].green;
|
||||
line[offs+2] = rp->palette->color[v].blue;
|
||||
s_int_to_result_format(rp, 8, line + offs);
|
||||
}
|
||||
if (++x == rp->width) {
|
||||
done = TRUE;
|
||||
done = true;
|
||||
break; /* discarding rest of buffer == padding */
|
||||
}
|
||||
}
|
||||
@@ -572,7 +551,7 @@ static void s_read_indexed_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
* s_read_n_bytes
|
||||
*******************************************************/
|
||||
|
||||
static inline int s_read_n_bytes(BMPREAD_R rp, int n, unsigned long *restrict buff)
|
||||
static inline bool s_read_n_bytes(BMPREAD_R rp, int n, unsigned long *restrict buff)
|
||||
{
|
||||
int byte;
|
||||
|
||||
@@ -580,12 +559,12 @@ static inline int s_read_n_bytes(BMPREAD_R rp, int n, unsigned long *restrict bu
|
||||
while (n--) {
|
||||
if (EOF == (byte = s_read_one_byte(rp))) {
|
||||
s_set_file_error(rp);
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
*buff <<= 8;
|
||||
*buff |= byte;
|
||||
}
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -621,14 +600,15 @@ static inline unsigned long s_bits_from_buffer(unsigned long buf, int size,
|
||||
static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
|
||||
int *restrict x, int *restrict yoff)
|
||||
{
|
||||
int repeat = FALSE, left_in_run = 0;
|
||||
int left_in_run = 0;
|
||||
bool repeat = false, padding = false, odd = false;
|
||||
int right, up;
|
||||
int padding = FALSE, odd = FALSE, v, r = 0, g = 0, b = 0;
|
||||
int v, r = 0, g = 0, b = 0;
|
||||
size_t offs;
|
||||
int bits = rp->ih->bitcount;
|
||||
|
||||
if (!(bits == 4 || bits == 8 || bits == 24)) {
|
||||
rp->panic = TRUE;
|
||||
rp->panic = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -656,7 +636,7 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
|
||||
}
|
||||
}
|
||||
|
||||
offs = (size_t) *x * (size_t) rp->result_bytes_per_pixel;
|
||||
offs = (size_t) *x * rp->result_bytes_per_pixel;
|
||||
if ((rp->undefined_mode == BMP_UNDEFINED_TO_ALPHA) && !rp->result_indexed)
|
||||
line[offs+3] = 0xff; /* set alpha to 1.0 for defined pixels */
|
||||
switch (bits) {
|
||||
@@ -676,25 +656,24 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
|
||||
}
|
||||
if (v >= rp->palette->numcolors) {
|
||||
v = rp->palette->numcolors - 1;
|
||||
rp->invalid_index = TRUE;
|
||||
rp->invalid_index = true;
|
||||
}
|
||||
if (rp->result_indexed) {
|
||||
line[offs] = v;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
line[offs] = rp->palette->color[v].red;
|
||||
line[offs+1] = rp->palette->color[v].green;
|
||||
line[offs+2] = rp->palette->color[v].blue;
|
||||
s_int_to_result_format(rp, 8, line+offs);
|
||||
s_int_to_result_format(rp, 8, line + offs);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
*x += 1;
|
||||
if (*x >= rp->width) {
|
||||
rp->rle_eol = FALSE; /* EOL detected by width, not by RLE-code */
|
||||
rp->rle_eol = false; /* EOL detected by width, not by RLE-code */
|
||||
if (left_in_run) {
|
||||
rp->invalid_overrun = TRUE;
|
||||
rp->invalid_overrun = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -715,10 +694,10 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
|
||||
s_set_file_error(rp);
|
||||
break;
|
||||
}
|
||||
padding = FALSE;
|
||||
odd = FALSE;
|
||||
padding = false;
|
||||
odd = false;
|
||||
left_in_run = v;
|
||||
repeat = TRUE;
|
||||
repeat = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -731,21 +710,21 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
|
||||
/* start literal run */
|
||||
if (v > 2) {
|
||||
left_in_run = v;
|
||||
repeat = FALSE;
|
||||
repeat = false;
|
||||
|
||||
switch (bits) {
|
||||
case 8:
|
||||
case 24:
|
||||
padding = v & 0x01 ? TRUE : FALSE;
|
||||
padding = v & 0x01 ? true : false;
|
||||
break;
|
||||
case 4:
|
||||
if ((v+1)%4 >= 2)
|
||||
padding = TRUE;
|
||||
padding = true;
|
||||
else
|
||||
padding = FALSE;
|
||||
padding = false;
|
||||
break;
|
||||
}
|
||||
odd = FALSE;
|
||||
odd = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -753,7 +732,7 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
|
||||
if (v == 0) {
|
||||
if (*x != 0 || rp->rle_eol) {
|
||||
*x = rp->width;
|
||||
rp->rle_eol = TRUE;
|
||||
rp->rle_eol = true;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
@@ -761,7 +740,7 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
|
||||
|
||||
/* end of bitmap */
|
||||
if (v == 1) {
|
||||
rp->rle_eof = TRUE;
|
||||
rp->rle_eof = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -772,7 +751,7 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
|
||||
break;
|
||||
}
|
||||
if (right >= rp->width - *x) {
|
||||
rp->invalid_delta = TRUE;
|
||||
rp->invalid_delta = true;
|
||||
break;
|
||||
}
|
||||
*x += right;
|
||||
@@ -784,12 +763,115 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
|
||||
}
|
||||
|
||||
logerr(rp->log, "Should never get here! (x=%d, byte=%d)", (int) *x, (int) v);
|
||||
rp->panic = TRUE;
|
||||
rp->panic = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/********************************************************
|
||||
* s_read_huffman_line
|
||||
*******************************************************/
|
||||
static bool s_huff_skip_eol(BMPREAD_R rp);
|
||||
static bool s_huff_find_eol(BMPREAD_R rp);
|
||||
|
||||
static void s_read_huffman_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
{
|
||||
size_t offs;
|
||||
int x = 0, runlen;
|
||||
bool black = false;
|
||||
|
||||
while (x < rp->width) {
|
||||
huff_fillbuf(rp);
|
||||
|
||||
if (rp->hufbuf_len == 0)
|
||||
break;
|
||||
|
||||
if ((rp->hufbuf & 0x00ff) == 0) {
|
||||
if (!s_huff_skip_eol(rp)) {
|
||||
rp->truncated = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (x == 0) /* ignore eol at start of line */
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
runlen = huff_decode(rp, black);
|
||||
if (runlen == -1) {
|
||||
/* code was invalid, look for next eol */
|
||||
rp->lasterr |= BMP_ERR_PIXEL;
|
||||
if (!s_huff_find_eol(rp))
|
||||
rp->truncated = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (runlen > rp->width - x) {
|
||||
rp->lasterr |= BMP_ERR_PIXEL;
|
||||
runlen = rp->width - x;
|
||||
}
|
||||
|
||||
for (int i = 0; i < runlen; i++, x++) {
|
||||
offs = (size_t) x * rp->result_bytes_per_pixel;
|
||||
if (rp->result_indexed) {
|
||||
line[offs] = black;
|
||||
} else {
|
||||
line[offs] = rp->palette->color[black].red;
|
||||
line[offs+1] = rp->palette->color[black].green;
|
||||
line[offs+2] = rp->palette->color[black].blue;
|
||||
s_int_to_result_format(rp, 8, line + offs);
|
||||
}
|
||||
}
|
||||
black = !black;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool s_huff_skip_eol(BMPREAD_R rp)
|
||||
{
|
||||
huff_fillbuf(rp);
|
||||
while (rp->hufbuf_len > 0) {
|
||||
if (rp->hufbuf == 0) {
|
||||
rp->hufbuf_len = 0;
|
||||
huff_fillbuf(rp);
|
||||
continue;
|
||||
}
|
||||
while ((rp->hufbuf & 0x0001) == 0) {
|
||||
rp->hufbuf >>= 1;
|
||||
rp->hufbuf_len--;
|
||||
}
|
||||
rp->hufbuf >>= 1;
|
||||
rp->hufbuf_len--;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static bool s_huff_find_eol(BMPREAD_R rp)
|
||||
{
|
||||
/* look for the next full 12-bit eol sequence,
|
||||
* discard anything else
|
||||
*/
|
||||
huff_fillbuf (rp);
|
||||
while (rp->hufbuf_len > 11)
|
||||
{
|
||||
if ((rp->hufbuf & 0x07ff) == 0) {
|
||||
rp->hufbuf >>= 11;
|
||||
rp->hufbuf_len -= 11;
|
||||
return s_huff_skip_eol (rp);
|
||||
}
|
||||
rp->hufbuf >>= 1;
|
||||
rp->hufbuf_len -= 1;
|
||||
if (rp->hufbuf_len < 12)
|
||||
huff_fillbuf (rp);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* s_int_to_result_format
|
||||
@@ -805,12 +887,15 @@ static inline void s_int_to_result_format(BMPREAD_R rp, int frombits, unsigned c
|
||||
if (rp->result_format == BMP_FORMAT_INT)
|
||||
return;
|
||||
#ifdef DEBUG
|
||||
if (frombits > rp->result_bits_per_channel) {
|
||||
printf("This is bad, frombits must be <= bits_per_channel");
|
||||
if (frombits > rp->result_bitsperchannel) {
|
||||
printf("This is bad, frombits must be <= result_bitsperchannel");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
for (c = rp->result_channels - 1; c >= 0; c--) {
|
||||
/* going backwards because we are converting in place and
|
||||
* always growing, never shrinking
|
||||
*/
|
||||
switch (frombits) {
|
||||
case 8:
|
||||
v = px[c];
|
||||
@@ -830,7 +915,7 @@ static inline void s_int_to_result_format(BMPREAD_R rp, int frombits, unsigned c
|
||||
}
|
||||
switch (rp->result_format) {
|
||||
case BMP_FORMAT_FLOAT:
|
||||
((float*)px)[c] = (double) v / ((1ULL<<frombits)-1);
|
||||
((float*)px)[c] = (float) ((double) v / ((1ULL<<frombits)-1));
|
||||
break;
|
||||
case BMP_FORMAT_S2_13:
|
||||
((uint16_t*)px)[c] = (uint16_t) ((double) v / ((1ULL<<frombits)-1) * 8192.0 + 0.5);
|
||||
@@ -854,9 +939,9 @@ static inline void s_int_to_result_format(BMPREAD_R rp, int frombits, unsigned c
|
||||
static void s_set_file_error(BMPREAD_R rp)
|
||||
{
|
||||
if (feof(rp->file))
|
||||
rp->file_eof = TRUE;
|
||||
rp->file_eof = true;
|
||||
else
|
||||
rp->file_err = TRUE;
|
||||
rp->file_err = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -889,13 +974,12 @@ static void s_log_error_from_state(BMPREAD_R rp)
|
||||
* s_cont_error
|
||||
*******************************************************/
|
||||
|
||||
static int s_cont_error(BMPREAD_R rp)
|
||||
static bool s_cont_error(BMPREAD_R rp)
|
||||
{
|
||||
if (rp->invalid_index ||
|
||||
rp->invalid_overrun) {
|
||||
return TRUE;
|
||||
if (rp->invalid_index || rp->invalid_overrun) {
|
||||
return true;
|
||||
}
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -904,16 +988,16 @@ static int s_cont_error(BMPREAD_R rp)
|
||||
* s_stopping_error
|
||||
*******************************************************/
|
||||
|
||||
static int s_stopping_error(BMPREAD_R rp)
|
||||
static bool s_stopping_error(BMPREAD_R rp)
|
||||
{
|
||||
if (rp->truncated ||
|
||||
if (rp->truncated ||
|
||||
rp->invalid_delta ||
|
||||
rp->file_err ||
|
||||
rp->file_eof ||
|
||||
rp->file_err ||
|
||||
rp->file_eof ||
|
||||
rp->panic) {
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -938,31 +1022,5 @@ static inline int s_read_one_byte(BMPREAD_R rp)
|
||||
|
||||
static inline unsigned long s_scaleint(unsigned long val, int frombits, int tobits)
|
||||
{
|
||||
return (unsigned long) ((double) val * ((1<<tobits)-1) / ((1<<frombits)-1) + 0.5);
|
||||
|
||||
#ifdef NEVER
|
||||
/* nice, but has some slight off-by-one rounding errors */
|
||||
unsigned long result;
|
||||
int spaceleft;
|
||||
|
||||
/* scaling down, easy */
|
||||
if (frombits >= tobits)
|
||||
return val >> (frombits - tobits);
|
||||
|
||||
if (frombits < 1)
|
||||
return 0UL;
|
||||
|
||||
/* scaling up */
|
||||
result = val << (tobits - frombits);
|
||||
spaceleft = tobits - frombits;
|
||||
|
||||
while (spaceleft > 0) {
|
||||
if (spaceleft >= frombits)
|
||||
result |= val << (spaceleft - frombits);
|
||||
else
|
||||
result |= val >> (frombits - spaceleft);
|
||||
spaceleft -= frombits;
|
||||
}
|
||||
return result;
|
||||
#endif
|
||||
return (unsigned long) ((double) val * ((1ULL<<tobits)-1) / ((1ULL<<frombits)-1) + 0.5);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define BMPLIB_LIB
|
||||
|
||||
#include "config.h"
|
||||
#include "bmplib.h"
|
||||
@@ -48,9 +51,8 @@ API int bmpread_num_palette_colors(BMPHANDLE h)
|
||||
{
|
||||
BMPREAD rp;
|
||||
|
||||
if (!(h && cm_check_is_read_handle(h)))
|
||||
if (!(rp = cm_read_handle(h)))
|
||||
return 0;
|
||||
rp = (BMPREAD)(void*)h;
|
||||
|
||||
if (rp->palette)
|
||||
return rp->palette->numcolors;
|
||||
@@ -72,10 +74,8 @@ API BMPRESULT bmpread_load_palette(BMPHANDLE h, unsigned char **palette)
|
||||
int i,c;
|
||||
size_t memsize;
|
||||
|
||||
|
||||
if (!(h && cm_check_is_read_handle(h)))
|
||||
return 0;
|
||||
rp = (BMPREAD)(void*)h;
|
||||
if (!(rp = cm_read_handle(h)))
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
if (!rp->getinfo_called) {
|
||||
logerr(rp->log, "Must call bmpread_load_info() before loading palette");
|
||||
@@ -102,9 +102,9 @@ API BMPRESULT bmpread_load_palette(BMPHANDLE h, unsigned char **palette)
|
||||
|
||||
/* irreversible. image will be returned as indexed pixels */
|
||||
if (!rp->result_indexed) {
|
||||
rp->result_indexed = TRUE;
|
||||
rp->dimensions_queried = FALSE;
|
||||
rp->dim_queried_channels = FALSE;
|
||||
rp->result_indexed = true;
|
||||
rp->dimensions_queried = false;
|
||||
rp->dim_queried_channels = false;
|
||||
rp->result_channels = 1;
|
||||
if (!br_set_resultbits(rp))
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
735
bmp-read.c
735
bmp-read.c
File diff suppressed because it is too large
Load Diff
10
bmp-read.h
10
bmp-read.h
@@ -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);
|
||||
|
||||
1080
bmp-write.c
1080
bmp-write.c
File diff suppressed because it is too large
Load Diff
@@ -3,18 +3,18 @@
|
||||
* Copyright (c) 2024, Rupert Weber.
|
||||
*
|
||||
* This file is part of bmplib.
|
||||
* bmplib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* bmplib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
* License along with this library.
|
||||
* If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
|
||||
198
bmplib.h
198
bmplib.h
@@ -3,18 +3,18 @@
|
||||
* Copyright (c) 2024, Rupert Weber.
|
||||
*
|
||||
* This file is part of bmplib.
|
||||
* bmplib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* bmplib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
* License along with this library.
|
||||
* If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
@@ -22,15 +22,26 @@
|
||||
#ifndef BMPLIB_H
|
||||
#define BMPLIB_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define DEPR(m) __attribute__ ((deprecated(m)))
|
||||
#define DEPR(m) __attribute__ ((deprecated(m)))
|
||||
#else
|
||||
#define DEPR(m)
|
||||
#define DEPR(m)
|
||||
#endif
|
||||
|
||||
#if defined (WIN32) || defined (_WIN32)
|
||||
#ifdef BMPLIB_LIB
|
||||
#define APIDECL __declspec(dllexport)
|
||||
#else
|
||||
#define APIDECL __declspec(dllimport)
|
||||
#endif
|
||||
#else
|
||||
#define APIDECL
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct Bmphandle *BMPHANDLE;
|
||||
|
||||
|
||||
@@ -75,13 +86,13 @@ typedef struct Bmphandle *BMPHANDLE;
|
||||
* sufficiently high limit.
|
||||
*/
|
||||
enum Bmpresult {
|
||||
BMP_RESULT_OK = 0,
|
||||
BMP_RESULT_INVALID,
|
||||
BMP_RESULT_TRUNCATED,
|
||||
BMP_RESULT_INSANE,
|
||||
BMP_RESULT_PNG,
|
||||
BMP_RESULT_JPEG,
|
||||
BMP_RESULT_ERROR,
|
||||
BMP_RESULT_OK = 0,
|
||||
BMP_RESULT_INVALID,
|
||||
BMP_RESULT_TRUNCATED,
|
||||
BMP_RESULT_INSANE,
|
||||
BMP_RESULT_PNG,
|
||||
BMP_RESULT_JPEG,
|
||||
BMP_RESULT_ERROR,
|
||||
};
|
||||
typedef enum Bmpresult BMPRESULT;
|
||||
|
||||
@@ -101,11 +112,11 @@ typedef enum Bmpresult BMPRESULT;
|
||||
* - bmp_set_number_format(BMP_FORMAT_S2_13).
|
||||
*/
|
||||
enum Bmpconv64 {
|
||||
BMP_CONV64_SRGB = 0, /* default */
|
||||
BMP_CONV64_LINEAR = 1,
|
||||
BMP_CONV64_16BIT_SRGB DEPR("use BMP_CONV64_SRGB instead") = 0,
|
||||
BMP_CONV64_16BIT DEPR("use BMP_CONV64_LINEAR instead") = 1,
|
||||
BMP_CONV64_NONE
|
||||
BMP_CONV64_SRGB = 0, /* default */
|
||||
BMP_CONV64_LINEAR = 1,
|
||||
BMP_CONV64_16BIT_SRGB DEPR("use BMP_CONV64_SRGB instead") = 0,
|
||||
BMP_CONV64_16BIT DEPR("use BMP_CONV64_LINEAR instead") = 1,
|
||||
BMP_CONV64_NONE
|
||||
};
|
||||
typedef enum Bmpconv64 BMPCONV64;
|
||||
|
||||
@@ -121,14 +132,14 @@ typedef enum Bmpconv64 BMPCONV64;
|
||||
* I am going with BITMAPINFOHEADER = v3
|
||||
*/
|
||||
enum BmpInfoVer {
|
||||
BMPINFO_CORE_OS21 = 1, /* 12 bytes */
|
||||
BMPINFO_OS22, /* 16 / 40(!) / 64 bytes */
|
||||
BMPINFO_V3, /* 40 bytes */
|
||||
BMPINFO_V3_ADOBE1, /* 52 bytes, unofficial */
|
||||
BMPINFO_V3_ADOBE2, /* 56 bytes, unofficial */
|
||||
BMPINFO_V4, /* 108 bytes */
|
||||
BMPINFO_V5, /* 124 bytes */
|
||||
BMPINFO_FUTURE /* future versions, larger than 124 bytes */
|
||||
BMPINFO_CORE_OS21 = 1, /* 12 bytes */
|
||||
BMPINFO_OS22, /* 16 / 40(!) / 64 bytes */
|
||||
BMPINFO_V3, /* 40 bytes */
|
||||
BMPINFO_V3_ADOBE1, /* 52 bytes, unofficial */
|
||||
BMPINFO_V3_ADOBE2, /* 56 bytes, unofficial */
|
||||
BMPINFO_V4, /* 108 bytes */
|
||||
BMPINFO_V5, /* 124 bytes */
|
||||
BMPINFO_FUTURE /* future versions, larger than 124 bytes */
|
||||
};
|
||||
typedef enum BmpInfoVer BMPINFOVER;
|
||||
|
||||
@@ -145,9 +156,9 @@ typedef enum BmpInfoVer BMPINFOVER;
|
||||
* table size.
|
||||
*/
|
||||
enum BmpRLEtype {
|
||||
BMP_RLE_NONE,
|
||||
BMP_RLE_AUTO,
|
||||
BMP_RLE_RLE8
|
||||
BMP_RLE_NONE,
|
||||
BMP_RLE_AUTO,
|
||||
BMP_RLE_RLE8
|
||||
};
|
||||
typedef enum BmpRLEtype BMPRLETYPE;
|
||||
|
||||
@@ -164,9 +175,9 @@ typedef enum BmpRLEtype BMPRLETYPE;
|
||||
*
|
||||
*/
|
||||
enum BmpUndefined {
|
||||
BMP_UNDEFINED_LEAVE,
|
||||
BMP_UNDEFINED_TO_ZERO DEPR("use BMP_UNDEFINED_LEAVE instead") = 0,
|
||||
BMP_UNDEFINED_TO_ALPHA /* default */
|
||||
BMP_UNDEFINED_LEAVE,
|
||||
BMP_UNDEFINED_TO_ZERO DEPR("use BMP_UNDEFINED_LEAVE instead") = 0,
|
||||
BMP_UNDEFINED_TO_ALPHA /* default */
|
||||
};
|
||||
typedef enum BmpUndefined BMPUNDEFINED;
|
||||
|
||||
@@ -180,95 +191,100 @@ typedef enum BmpUndefined BMPUNDEFINED;
|
||||
* still gives the orientation of the BMP file.
|
||||
*/
|
||||
enum BmpOrient {
|
||||
BMP_ORIENT_BOTTOMUP,
|
||||
BMP_ORIENT_TOPDOWN
|
||||
BMP_ORIENT_BOTTOMUP,
|
||||
BMP_ORIENT_TOPDOWN
|
||||
};
|
||||
typedef enum BmpOrient BMPORIENT;
|
||||
|
||||
|
||||
enum BmpFormat {
|
||||
BMP_FORMAT_INT,
|
||||
BMP_FORMAT_FLOAT,
|
||||
BMP_FORMAT_S2_13
|
||||
BMP_FORMAT_INT,
|
||||
BMP_FORMAT_FLOAT,
|
||||
BMP_FORMAT_S2_13
|
||||
};
|
||||
typedef enum BmpFormat BMPFORMAT;
|
||||
|
||||
|
||||
BMPHANDLE bmpread_new(FILE *file);
|
||||
APIDECL BMPHANDLE bmpread_new(FILE *file);
|
||||
|
||||
BMPRESULT bmpread_load_info(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpread_load_info(BMPHANDLE h);
|
||||
|
||||
BMPRESULT bmpread_dimensions(BMPHANDLE h,
|
||||
int *width,
|
||||
int *height,
|
||||
int *channels,
|
||||
int *bitsperchannel,
|
||||
BMPORIENT *orientation);
|
||||
APIDECL BMPRESULT bmpread_dimensions(BMPHANDLE h,
|
||||
int *width,
|
||||
int *height,
|
||||
int *channels,
|
||||
int *bitsperchannel,
|
||||
BMPORIENT *orientation);
|
||||
|
||||
int bmpread_width(BMPHANDLE h);
|
||||
int bmpread_height(BMPHANDLE h);
|
||||
int bmpread_channels(BMPHANDLE h);
|
||||
int bmpread_bits_per_channel(BMPHANDLE h);
|
||||
BMPORIENT bmpread_orientation(BMPHANDLE h);
|
||||
APIDECL int bmpread_width(BMPHANDLE h);
|
||||
APIDECL int bmpread_height(BMPHANDLE h);
|
||||
APIDECL int bmpread_channels(BMPHANDLE h);
|
||||
APIDECL int bmpread_bitsperchannel(BMPHANDLE h);
|
||||
APIDECL BMPORIENT bmpread_orientation(BMPHANDLE h);
|
||||
|
||||
int bmpread_resolution_xdpi(BMPHANDLE h);
|
||||
int bmpread_resolution_ydpi(BMPHANDLE h);
|
||||
APIDECL int bmpread_resolution_xdpi(BMPHANDLE h);
|
||||
APIDECL int bmpread_resolution_ydpi(BMPHANDLE h);
|
||||
|
||||
size_t bmpread_buffersize(BMPHANDLE h);
|
||||
APIDECL size_t bmpread_buffersize(BMPHANDLE h);
|
||||
|
||||
BMPRESULT bmpread_load_image(BMPHANDLE h, unsigned char **buffer);
|
||||
BMPRESULT bmpread_load_line(BMPHANDLE h, unsigned char **buffer);
|
||||
APIDECL BMPRESULT bmpread_load_image(BMPHANDLE h, unsigned char **buffer);
|
||||
APIDECL BMPRESULT bmpread_load_line(BMPHANDLE h, unsigned char **buffer);
|
||||
|
||||
|
||||
int bmpread_num_palette_colors(BMPHANDLE h);
|
||||
BMPRESULT bmpread_load_palette(BMPHANDLE h, unsigned char **palette);
|
||||
APIDECL int bmpread_num_palette_colors(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpread_load_palette(BMPHANDLE h, unsigned char **palette);
|
||||
|
||||
|
||||
void bmpread_set_undefined(BMPHANDLE h, BMPUNDEFINED mode);
|
||||
void bmpread_set_insanity_limit(BMPHANDLE h, size_t limit);
|
||||
APIDECL void bmpread_set_undefined(BMPHANDLE h, BMPUNDEFINED mode);
|
||||
APIDECL void bmpread_set_insanity_limit(BMPHANDLE h, size_t limit);
|
||||
|
||||
int bmpread_is_64bit(BMPHANDLE h);
|
||||
BMPRESULT bmpread_set_64bit_conv(BMPHANDLE h, BMPCONV64 conv);
|
||||
APIDECL int bmpread_is_64bit(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpread_set_64bit_conv(BMPHANDLE h, BMPCONV64 conv);
|
||||
|
||||
BMPINFOVER bmpread_info_header_version(BMPHANDLE h);
|
||||
int bmpread_info_header_size(BMPHANDLE h);
|
||||
int bmpread_info_compression(BMPHANDLE h);
|
||||
int bmpread_info_bitcount(BMPHANDLE h);
|
||||
const char* bmpread_info_header_name(BMPHANDLE h);
|
||||
const char* bmpread_info_compression_name(BMPHANDLE h);
|
||||
BMPRESULT bmpread_info_channel_bits(BMPHANDLE h, int *r, int *g, int *b, int *a);
|
||||
APIDECL BMPINFOVER bmpread_info_header_version(BMPHANDLE h);
|
||||
APIDECL const char* bmpread_info_header_name(BMPHANDLE h);
|
||||
APIDECL int bmpread_info_header_size(BMPHANDLE h);
|
||||
APIDECL int bmpread_info_compression(BMPHANDLE h);
|
||||
APIDECL const char* bmpread_info_compression_name(BMPHANDLE h);
|
||||
APIDECL int bmpread_info_bitcount(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpread_info_channel_bits(BMPHANDLE h, int *r, int *g, int *b, int *a);
|
||||
|
||||
|
||||
|
||||
|
||||
BMPHANDLE bmpwrite_new(FILE *file);
|
||||
APIDECL BMPHANDLE bmpwrite_new(FILE *file);
|
||||
|
||||
BMPRESULT bmpwrite_set_dimensions(BMPHANDLE h,
|
||||
unsigned width,
|
||||
unsigned height,
|
||||
unsigned channels,
|
||||
unsigned bits_per_channel);
|
||||
BMPRESULT bmpwrite_set_resolution(BMPHANDLE h, int xdpi, int ydpi);
|
||||
BMPRESULT bmpwrite_set_output_bits(BMPHANDLE h, int red, int green, int blue, int alpha);
|
||||
BMPRESULT bmpwrite_set_palette(BMPHANDLE h, int numcolors, const unsigned char *palette);
|
||||
BMPRESULT bmpwrite_allow_2bit(BMPHANDLE h);
|
||||
BMPRESULT bmpwrite_set_rle(BMPHANDLE h, BMPRLETYPE type);
|
||||
BMPRESULT bmpwrite_set_orientation(BMPHANDLE h, BMPORIENT orientation);
|
||||
BMPRESULT bmpwrite_set_64bit(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpwrite_set_dimensions(BMPHANDLE h,
|
||||
unsigned width,
|
||||
unsigned height,
|
||||
unsigned channels,
|
||||
unsigned bitsperchannel);
|
||||
APIDECL BMPRESULT bmpwrite_set_resolution(BMPHANDLE h, int xdpi, int ydpi);
|
||||
APIDECL BMPRESULT bmpwrite_set_output_bits(BMPHANDLE h, int red, int green, int blue, int alpha);
|
||||
APIDECL BMPRESULT bmpwrite_set_palette(BMPHANDLE h, int numcolors, const unsigned char *palette);
|
||||
APIDECL BMPRESULT bmpwrite_allow_2bit(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpwrite_allow_huffman(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpwrite_allow_rle24(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpwrite_set_rle(BMPHANDLE h, BMPRLETYPE type);
|
||||
APIDECL BMPRESULT bmpwrite_set_orientation(BMPHANDLE h, BMPORIENT orientation);
|
||||
APIDECL BMPRESULT bmpwrite_set_64bit(BMPHANDLE h);
|
||||
|
||||
BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image);
|
||||
BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line);
|
||||
APIDECL BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image);
|
||||
APIDECL BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line);
|
||||
|
||||
|
||||
BMPRESULT bmp_set_number_format(BMPHANDLE h, BMPFORMAT format);
|
||||
APIDECL BMPRESULT bmp_set_number_format(BMPHANDLE h, BMPFORMAT format);
|
||||
|
||||
void bmp_free(BMPHANDLE h);
|
||||
APIDECL void bmp_free(BMPHANDLE h);
|
||||
|
||||
const char* bmp_errmsg(BMPHANDLE h);
|
||||
APIDECL const char* bmp_errmsg(BMPHANDLE h);
|
||||
|
||||
const char* bmp_version(void);
|
||||
APIDECL const char* bmp_version(void);
|
||||
|
||||
|
||||
/* these errorcodes aren't part of the API yet, but will be.
|
||||
* currently only used internally by bmplib.
|
||||
*/
|
||||
|
||||
#define BMP_ERRTYPE_HARD 0x0000000f
|
||||
#define BMP_ERR_FILEIO 0x00000001
|
||||
@@ -300,8 +316,10 @@ const char* bmp_version(void);
|
||||
* removed from future versions:
|
||||
*/
|
||||
|
||||
int DEPR("use bmpread_orientation() instead") bmpread_topdown(BMPHANDLE h);
|
||||
void DEPR("use bmpread_set_undefined() instead") bmpread_set_undefined_to_alpha(BMPHANDLE h, int mode);
|
||||
APIDECL int DEPR("use bmpread_orientation() instead") bmpread_topdown(BMPHANDLE h);
|
||||
APIDECL void DEPR("use bmpread_set_undefined() instead") bmpread_set_undefined_to_alpha(BMPHANDLE h, int mode);
|
||||
APIDECL int DEPR("use bmpread_bitsperchannel() instead") bmpread_bits_per_channel(BMPHANDLE h);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -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
245
gen-huffman-codes.h
Normal 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
253
gen-huffman.c
Normal file
@@ -0,0 +1,253 @@
|
||||
/* bmplib - gen-huffman.c
|
||||
*
|
||||
* Copyright (c) 2024, Rupert Weber.
|
||||
*
|
||||
* This file is part of bmplib.
|
||||
* bmplib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
* If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
|
||||
/* This program generates the header file "huffman-codes.h" */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#if (! __bool_true_false_are_defined)
|
||||
typedef int bool;
|
||||
#define true 1
|
||||
#define false 0
|
||||
#endif
|
||||
|
||||
#include "gen-huffman-codes.h"
|
||||
|
||||
|
||||
#define ARR_SIZE(a) (sizeof a / sizeof a[0])
|
||||
|
||||
struct Node {
|
||||
int l;
|
||||
int r;
|
||||
int value;
|
||||
bool terminal;
|
||||
bool makeup;
|
||||
};
|
||||
|
||||
static int nnodes = 0;
|
||||
|
||||
static struct Node nodebuffer[416];
|
||||
|
||||
static int black_tree = -1;
|
||||
static int white_tree = -1;
|
||||
|
||||
|
||||
static void s_buildtree(void);
|
||||
static void add_node(int *nodeidx, const char *bits, int value, bool makeup);
|
||||
static unsigned short str2bits(const char *str);
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
struct Node *n = nodebuffer;
|
||||
FILE *file;
|
||||
const char *src_name = "huffman-codes.h";
|
||||
const char *this_name = "gen-huffman.c";
|
||||
|
||||
if (argc == 2) {
|
||||
if (!(file = fopen(argv[1], "w"))) {
|
||||
perror(argv[1]);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
file = stdout;
|
||||
}
|
||||
|
||||
s_buildtree();
|
||||
|
||||
fprintf(file, "/* bmplib - %s\n", src_name);
|
||||
fprintf(file, " *\n"
|
||||
" * Copyright (c) 2024, Rupert Weber.\n"
|
||||
" *\n"
|
||||
" * This file is part of bmplib.\n"
|
||||
" * bmplib is free software: you can redistribute it and/or modify\n"
|
||||
" * it under the terms of the GNU Lesser General Public License as\n"
|
||||
" * published by the Free Software Foundation, either version 3 of\n"
|
||||
" * the License, or (at your option) any later version.\n"
|
||||
" *\n");
|
||||
fprintf(file, " * This program is distributed in the hope that it will be useful,\n"
|
||||
" * but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
|
||||
" * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
|
||||
" * GNU Lesser General Public License for more details.\n"
|
||||
" *\n"
|
||||
" * You should have received a copy of the GNU Lesser General Public\n"
|
||||
" * License along with this library.\n"
|
||||
" * If not, see <https://www.gnu.org/licenses/>\n"
|
||||
" */\n\n");
|
||||
fprintf(file, "/* This file is auto-generated by %s */\n\n\n", this_name);
|
||||
|
||||
fputs("struct Node {\n"
|
||||
"\tsigned short l;\n"
|
||||
"\tsigned short r;\n"
|
||||
"\tshort value;\n"
|
||||
"\tchar terminal;\n"
|
||||
"\tchar makeup;\n"
|
||||
"};\n\n",
|
||||
file);
|
||||
|
||||
fputs("struct Huffcode {\n"
|
||||
"\tunsigned char bits;\n"
|
||||
"\tunsigned char nbits;\n"
|
||||
"};\n\n",
|
||||
file);
|
||||
|
||||
fprintf(file, "static const int blackroot = %d;\n", black_tree);
|
||||
fprintf(file, "static const int whiteroot = %d;\n\n\n", white_tree);
|
||||
fprintf(file, "static const struct Node nodebuffer[] = {\n");
|
||||
for (i = 0; i < ARR_SIZE(nodebuffer); i++) {
|
||||
fprintf(file, "\t{ %3d, %3d, %4d, %d, %d },\n",
|
||||
n[i].l, n[i].r, n[i].value, n[i].terminal, n[i].makeup);
|
||||
}
|
||||
fputs("};\n\n", file);
|
||||
|
||||
fputs("static const struct Huffcode huff_term_black[] = {\n\t", file);
|
||||
for (i = 0; i < ARR_SIZE(huff_term_black); i++) {
|
||||
fprintf(file, "{ 0x%02hx, %2d },",
|
||||
str2bits(huff_term_black[i].bits),
|
||||
(int) strlen(huff_term_black[i].bits));
|
||||
if ((i+1) % 4 == 0 && i != ARR_SIZE(huff_term_black) - 1)
|
||||
fputs("\n\t", file);
|
||||
else
|
||||
fputs(" ", file);
|
||||
}
|
||||
fputs("\n};\n\n", file);
|
||||
|
||||
fputs("static const struct Huffcode huff_term_white[] = {\n\t", file);
|
||||
for (i = 0; i < ARR_SIZE(huff_term_white); i++) {
|
||||
fprintf(file, "{ 0x%02hx, %2d },",
|
||||
str2bits(huff_term_white[i].bits),
|
||||
(int) strlen(huff_term_white[i].bits));
|
||||
if ((i+1) % 4 == 0 && i != ARR_SIZE(huff_term_white) - 1)
|
||||
fputs("\n\t", file);
|
||||
else
|
||||
fputs(" ", file);
|
||||
}
|
||||
fputs("\n};\n\n", file);
|
||||
|
||||
fputs("static const struct Huffcode huff_makeup_black[] = {\n\t", file);
|
||||
for (i = 0; i < ARR_SIZE(huff_makeup_black); i++) {
|
||||
fprintf(file, "{ 0x%02hx, %2d },",
|
||||
str2bits(huff_makeup_black[i].bits),
|
||||
(int) strlen(huff_makeup_black[i].bits));
|
||||
if ((i+1) % 4 == 0 && i != ARR_SIZE(huff_makeup_black) - 1)
|
||||
fputs("\n\t", file);
|
||||
else
|
||||
fputs(" ", file);
|
||||
}
|
||||
fputs("\n};\n\n", file);
|
||||
|
||||
fputs("static const struct Huffcode huff_makeup_white[] = {\n\t", file);
|
||||
for (i = 0; i < ARR_SIZE(huff_makeup_white); i++) {
|
||||
fprintf(file, "{ 0x%02hx, %2d },",
|
||||
str2bits(huff_makeup_white[i].bits),
|
||||
(int) strlen(huff_makeup_white[i].bits));
|
||||
if ((i+1) % 4 == 0 && i != ARR_SIZE(huff_makeup_white) - 1)
|
||||
fputs("\n\t", file);
|
||||
else
|
||||
fputs(" ", file);
|
||||
}
|
||||
fputs("\n};\n\n", file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static unsigned short str2bits(const char *str)
|
||||
{
|
||||
unsigned short value = 0;
|
||||
|
||||
while (*str) {
|
||||
value <<= 1;
|
||||
value |= *str - '0';
|
||||
str++;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* s_buildtree()
|
||||
****************************************************************************/
|
||||
|
||||
static void s_buildtree(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(nodebuffer, 0, sizeof nodebuffer);
|
||||
|
||||
for (i = 0; i < ARR_SIZE(huff_term_black); i++) {
|
||||
add_node(&black_tree, huff_term_black[i].bits,
|
||||
huff_term_black[i].number, false);
|
||||
}
|
||||
for (i = 0; i < ARR_SIZE(huff_makeup_black); i++) {
|
||||
add_node(&black_tree, huff_makeup_black[i].bits,
|
||||
huff_makeup_black[i].number, true);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARR_SIZE(huff_term_white); i++) {
|
||||
add_node(&white_tree, huff_term_white[i].bits,
|
||||
huff_term_white[i].number, false);
|
||||
}
|
||||
for (i = 0; i < ARR_SIZE(huff_makeup_white); i++) {
|
||||
add_node(&white_tree, huff_makeup_white[i].bits,
|
||||
huff_makeup_white[i].number, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* add_node()
|
||||
****************************************************************************/
|
||||
|
||||
static void add_node(int *nodeidx, const char *bits, int value, bool makeup)
|
||||
{
|
||||
if (*nodeidx == -1) {
|
||||
*nodeidx = nnodes++;
|
||||
nodebuffer[*nodeidx].l = -1;
|
||||
nodebuffer[*nodeidx].r = -1;
|
||||
}
|
||||
if (nnodes > ARR_SIZE(nodebuffer)) {
|
||||
printf("too many nodes (have %d, max is %d)\n",
|
||||
nnodes, (int) ARR_SIZE(nodebuffer));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!*bits) {
|
||||
/* we are on the final bit of the sequence */
|
||||
nodebuffer[*nodeidx].value = value;
|
||||
nodebuffer[*nodeidx].terminal = true;
|
||||
nodebuffer[*nodeidx].makeup = makeup;
|
||||
} else {
|
||||
switch (*bits) {
|
||||
case '0':
|
||||
add_node(&nodebuffer[*nodeidx].l, bits+1, value, makeup);
|
||||
break;
|
||||
case '1':
|
||||
add_node(&nodebuffer[*nodeidx].r, bits+1, value, makeup);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
82
gen-reversebits.c
Normal file
82
gen-reversebits.c
Normal file
@@ -0,0 +1,82 @@
|
||||
/* bmplib - gen-reversebits.c
|
||||
*
|
||||
* Copyright (c) 2024, Rupert Weber.
|
||||
*
|
||||
* This file is part of bmplib.
|
||||
* bmplib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
* If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static int reverse(int val, int bits)
|
||||
{
|
||||
int mask;
|
||||
|
||||
bits /= 2;
|
||||
if (bits == 0)
|
||||
return val;
|
||||
mask = (1 << bits) - 1;
|
||||
return (reverse(val & mask, bits) << bits) | reverse(val >> bits, bits);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int reversed, i;
|
||||
FILE *file;
|
||||
const char *src_name = "reversebits.h";
|
||||
const char *this_name = "gen-reversebits.c";
|
||||
|
||||
if (argc == 2) {
|
||||
if (!(file = fopen(argv[1], "w"))) {
|
||||
perror(argv[1]);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
file = stdout;
|
||||
}
|
||||
|
||||
fprintf(file, "/* bmplib - %s\n", src_name);
|
||||
fprintf(file, " *\n"
|
||||
" * Copyright (c) 2024, Rupert Weber.\n"
|
||||
" *\n"
|
||||
" * This file is part of bmplib.\n"
|
||||
" * bmplib is free software: you can redistribute it and/or modify\n"
|
||||
" * it under the terms of the GNU Lesser General Public License as\n"
|
||||
" * published by the Free Software Foundation, either version 3 of\n"
|
||||
" * the License, or (at your option) any later version.\n"
|
||||
" *\n");
|
||||
fprintf(file, " * This program is distributed in the hope that it will be useful,\n"
|
||||
" * but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
|
||||
" * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
|
||||
" * GNU Lesser General Public License for more details.\n"
|
||||
" *\n"
|
||||
" * You should have received a copy of the GNU Lesser General Public\n"
|
||||
" * License along with this library.\n"
|
||||
" * If not, see <https://www.gnu.org/licenses/>\n"
|
||||
" */\n\n");
|
||||
fprintf(file, "/* This file is auto-generated by %s */\n\n\n", this_name);
|
||||
|
||||
fprintf(file, "static const unsigned char reversebits[] = {\n\t");
|
||||
for (i = 0; i < 256; i++) {
|
||||
reversed = reverse(i, 8);
|
||||
fprintf(file, "0x%02x, ", reversed);
|
||||
if ((i + 1) % 8 == 0 && i < 255)
|
||||
fprintf(file, "\n\t");
|
||||
}
|
||||
fprintf(file, "\n};\n");
|
||||
return 0;
|
||||
}
|
||||
1
google0f997b088a966545.html
Normal file
1
google0f997b088a966545.html
Normal file
@@ -0,0 +1 @@
|
||||
google-site-verification: google0f997b088a966545.html
|
||||
240
huffman.c
Normal file
240
huffman.c
Normal file
@@ -0,0 +1,240 @@
|
||||
/* bmplib - huffman.c
|
||||
*
|
||||
* Copyright (c) 2024, Rupert Weber.
|
||||
*
|
||||
* This file is part of bmplib.
|
||||
* bmplib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
* If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "bmplib.h"
|
||||
#include "logging.h"
|
||||
#include "bmp-common.h"
|
||||
#include "reversebits.h"
|
||||
#include "huffman.h"
|
||||
#include "huffman-codes.h"
|
||||
|
||||
|
||||
|
||||
static int s_findnode(uint32_t bits, int nbits, bool black, int *found);
|
||||
static bool s_zerofill(BMPWRITE_R wp);
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* huff_decode()
|
||||
*
|
||||
* Decodes the rp->hufbuf_len long bit sequence given in rp->hufbuf.
|
||||
* Direction is from lowest to highest bit.
|
||||
* Returns -1 if no valid terminating code is found.
|
||||
* EOL is _not_ handled, must be done by caller.
|
||||
****************************************************************************/
|
||||
|
||||
int huff_decode(BMPREAD_R rp, int black)
|
||||
{
|
||||
int idx;
|
||||
int bits_used = 0;
|
||||
int result = 0;
|
||||
|
||||
do {
|
||||
huff_fillbuf(rp);
|
||||
bits_used = s_findnode(rp->hufbuf, rp->hufbuf_len, black, &idx);
|
||||
if (idx == -1) {
|
||||
/* invalid code */
|
||||
return -1;
|
||||
}
|
||||
|
||||
result += nodebuffer[idx].value;
|
||||
rp->hufbuf >>= bits_used;
|
||||
rp->hufbuf_len -= bits_used;
|
||||
|
||||
} while (nodebuffer[idx].makeup && result < INT_MAX - 2560);
|
||||
|
||||
return nodebuffer[idx].makeup ? -1 : result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* s_findnode()
|
||||
****************************************************************************/
|
||||
|
||||
static int s_findnode(uint32_t bits, int nbits, bool black, int *found)
|
||||
{
|
||||
int idx;
|
||||
int bits_used = 0;
|
||||
|
||||
idx = black ? blackroot : whiteroot;
|
||||
|
||||
while (idx != -1 && !nodebuffer[idx].terminal && bits_used < nbits) {
|
||||
if (bits & 1)
|
||||
idx = nodebuffer[idx].r;
|
||||
else
|
||||
idx = nodebuffer[idx].l;
|
||||
bits_used++;
|
||||
bits >>= 1;
|
||||
}
|
||||
*found = idx;
|
||||
return idx != -1 ? bits_used : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* huff_fillbuf()
|
||||
****************************************************************************/
|
||||
|
||||
void huff_fillbuf(BMPREAD_R rp)
|
||||
{
|
||||
int byte;
|
||||
|
||||
while (rp->hufbuf_len <= 24) {
|
||||
if (EOF == (byte = getc(rp->file)))
|
||||
break;
|
||||
rp->bytes_read++;
|
||||
byte = reversebits[byte];
|
||||
rp->hufbuf |= ((uint32_t)byte) << rp->hufbuf_len;
|
||||
rp->hufbuf_len += 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* s_push()
|
||||
****************************************************************************/
|
||||
|
||||
static bool s_push(BMPWRITE_R wp, int bits, int nbits)
|
||||
{
|
||||
if (nbits > 32 - wp->hufbuf_len) {
|
||||
if (!huff_flush(wp))
|
||||
return false;
|
||||
}
|
||||
wp->hufbuf <<= nbits;
|
||||
wp->hufbuf |= bits;
|
||||
wp->hufbuf_len += nbits;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* huff_encode()
|
||||
****************************************************************************/
|
||||
|
||||
bool huff_encode(BMPWRITE_R wp, int val, bool black)
|
||||
{
|
||||
const struct Huffcode *makeup, *term;
|
||||
|
||||
if (val == -1) {
|
||||
/* eol */
|
||||
return s_push(wp, 1, 12);
|
||||
}
|
||||
|
||||
if (black) {
|
||||
makeup = huff_makeup_black;
|
||||
term = huff_term_black;
|
||||
} else {
|
||||
makeup = huff_makeup_white;
|
||||
term = huff_term_white;
|
||||
}
|
||||
|
||||
while (val > 63) {
|
||||
int n = MIN(2560 / 64, val / 64);
|
||||
if (!s_push(wp, makeup[n - 1].bits, makeup[n - 1].nbits))
|
||||
return false;
|
||||
val -= n * 64;
|
||||
}
|
||||
if (!s_push(wp, term[val].bits, term[val].nbits))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* huff_encode_eol()
|
||||
****************************************************************************/
|
||||
|
||||
bool huff_encode_eol(BMPWRITE_R wp)
|
||||
{
|
||||
return huff_encode(wp, -1, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* huff_encode_rtc()
|
||||
****************************************************************************/
|
||||
|
||||
bool huff_encode_rtc(BMPWRITE_R wp)
|
||||
{
|
||||
if (!s_zerofill(wp))
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (!huff_encode_eol(wp))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* s_zerofill()
|
||||
*
|
||||
* add fill 0s up to next byte boundary
|
||||
****************************************************************************/
|
||||
|
||||
static bool s_zerofill(BMPWRITE_R wp)
|
||||
{
|
||||
int n = 8 - wp->hufbuf_len % 8;
|
||||
|
||||
if (n < 8)
|
||||
return s_push(wp, 0, n);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* huff_flush()
|
||||
****************************************************************************/
|
||||
|
||||
bool huff_flush(BMPWRITE_R wp)
|
||||
{
|
||||
int byte;
|
||||
|
||||
while (wp->hufbuf_len >= 8) {
|
||||
byte = 0x00ff & (wp->hufbuf >> (wp->hufbuf_len - 8));
|
||||
if (EOF == putc(byte, wp->file)) {
|
||||
logsyserr(wp->log, "writing Huffman bitmap");
|
||||
return false;
|
||||
}
|
||||
wp->bytes_written++;
|
||||
wp->hufbuf_len -= 8;
|
||||
wp->hufbuf &= (1UL << wp->hufbuf_len) - 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
27
huffman.h
Normal file
27
huffman.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/* bmplib - huffman.h
|
||||
*
|
||||
* Copyright (c) 2024, Rupert Weber.
|
||||
*
|
||||
* This file is part of bmplib.
|
||||
* bmplib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
* If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
int huff_decode(BMPREAD_R rp, int black);
|
||||
void huff_fillbuf(BMPREAD_R rp);
|
||||
|
||||
bool huff_encode(BMPWRITE_R wp, int val, bool black);
|
||||
bool huff_encode_eol(BMPWRITE_R wp);
|
||||
bool huff_encode_rtc(BMPWRITE_R wp);
|
||||
bool huff_flush(BMPWRITE_R wp);
|
||||
104
logging.c
104
logging.c
@@ -3,18 +3,18 @@
|
||||
* Copyright (c) 2024, Rupert Weber.
|
||||
*
|
||||
* This file is part of bmplib.
|
||||
* bmplib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* bmplib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
* License along with this library.
|
||||
* If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "config.h"
|
||||
@@ -29,28 +31,30 @@
|
||||
|
||||
|
||||
struct Log {
|
||||
size_t size;
|
||||
char *buffer;
|
||||
int size;
|
||||
char *buffer;
|
||||
bool panic;
|
||||
};
|
||||
|
||||
|
||||
/* logerr(log, fmt, ...) and logsyserr(log, fmt, ...) are
|
||||
* printf-style logging functions.
|
||||
*
|
||||
* Use logsyserr() where perror() would be used, logerr()
|
||||
* otherwise.
|
||||
*
|
||||
* 'separator' and 'inter' can have any length
|
||||
* 'air' is just there so we don't need to realloc every
|
||||
* single time.
|
||||
*/
|
||||
|
||||
|
||||
static const char separator[]="\n"; /* separator between log entries */
|
||||
static const char inter[]=": "; /* between own message and sys err text */
|
||||
static const int air = 80; /* how much more than required we allocate */
|
||||
static const int air = 80; /* how much more than required we allocate */
|
||||
|
||||
static int s_allocate(LOG log, size_t add_chars);
|
||||
static bool s_allocate(LOG log, size_t add_chars);
|
||||
static void s_log(LOG log, const char *file, int line, const char *function,
|
||||
const char *etxt, const char *fmt, va_list args);
|
||||
const char *etxt, const char *fmt, va_list args);
|
||||
static void panic(LOG log);
|
||||
#ifdef DEBUG
|
||||
static int s_add_file_etc(LOG log, const char *file, int line, const char *function);
|
||||
@@ -64,7 +68,7 @@ static int s_add_file_etc(LOG log, const char *file, int line, const char *funct
|
||||
|
||||
LOG logcreate(void)
|
||||
{
|
||||
LOG log;
|
||||
LOG log;
|
||||
|
||||
if (!(log = malloc(sizeof *log)))
|
||||
return NULL;
|
||||
@@ -75,7 +79,7 @@ LOG logcreate(void)
|
||||
void logfree(LOG log)
|
||||
{
|
||||
if (log) {
|
||||
if (log->size != (size_t)-1 && log->buffer)
|
||||
if (log->size > 0 && log->buffer)
|
||||
free(log->buffer);
|
||||
free(log);
|
||||
}
|
||||
@@ -95,10 +99,16 @@ void logreset(LOG log)
|
||||
|
||||
const char* logmsg(LOG log)
|
||||
{
|
||||
if (log && log->buffer)
|
||||
return log->buffer;
|
||||
else
|
||||
return "";
|
||||
if (log) {
|
||||
if (log->panic) {
|
||||
return "PANIC! bmplib encountered an error while "
|
||||
"trying to set an error message";
|
||||
}
|
||||
else if (log->buffer) {
|
||||
return log->buffer;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
@@ -113,17 +123,17 @@ void logerr_(LOG log, const char *file, int line, const char *function, const ch
|
||||
void logerr(LOG log, const char *fmt, ...)
|
||||
#endif
|
||||
{
|
||||
va_list args;
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
va_start(args, fmt);
|
||||
|
||||
#ifdef DEBUG
|
||||
s_log(log, file, line, function, NULL, fmt, args);
|
||||
s_log(log, file, line, function, NULL, fmt, args);
|
||||
#else
|
||||
s_log(log, NULL, 0, NULL, NULL, fmt, args);
|
||||
s_log(log, NULL, 0, NULL, NULL, fmt, args);
|
||||
#endif
|
||||
|
||||
va_end(args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
@@ -139,18 +149,18 @@ void logsyserr(LOG log, const char *fmt, ...)
|
||||
#endif
|
||||
{
|
||||
va_list args;
|
||||
const char *etxt;
|
||||
const char *etxt;
|
||||
|
||||
va_start(args, fmt);
|
||||
etxt = strerror(errno);
|
||||
va_start(args, fmt);
|
||||
etxt = strerror(errno);
|
||||
|
||||
#ifdef DEBUG
|
||||
s_log(log, file, line, function, etxt, fmt, args);
|
||||
s_log(log, file, line, function, etxt, fmt, args);
|
||||
#else
|
||||
s_log(log, NULL, 0, NULL, etxt, fmt, args);
|
||||
s_log(log, NULL, 0, NULL, etxt, fmt, args);
|
||||
#endif
|
||||
|
||||
va_end(args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
@@ -160,13 +170,13 @@ void logsyserr(LOG log, const char *fmt, ...)
|
||||
*********************************************************/
|
||||
|
||||
static void s_log(LOG log, const char *file, int line, const char *function,
|
||||
const char *etxt, const char *fmt, va_list args)
|
||||
const char *etxt, const char *fmt, va_list args)
|
||||
{
|
||||
va_list argsdup;
|
||||
int len = 0,addl_len, required_len;
|
||||
va_list argsdup;
|
||||
int len = 0,addl_len, required_len;
|
||||
|
||||
if (log->size == (size_t)-1)
|
||||
return; /* log is set to a string literal (panic) */
|
||||
if (log->panic)
|
||||
return;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (!s_add_file_etc(log, file, line, function))
|
||||
@@ -174,7 +184,7 @@ static void s_log(LOG log, const char *file, int line, const char *function,
|
||||
#endif
|
||||
|
||||
if (log->buffer)
|
||||
len = strlen(log->buffer);
|
||||
len = strlen(log->buffer);
|
||||
|
||||
va_copy(argsdup, args);
|
||||
addl_len = vsnprintf(NULL, 0, fmt, argsdup);
|
||||
@@ -197,7 +207,7 @@ static void s_log(LOG log, const char *file, int line, const char *function,
|
||||
#endif
|
||||
|
||||
if (log->size - len <= vsnprintf(log->buffer + len, log->size - len,
|
||||
fmt, args)) {
|
||||
fmt, args)) {
|
||||
panic(log);
|
||||
return;
|
||||
}
|
||||
@@ -214,28 +224,34 @@ static void s_log(LOG log, const char *file, int line, const char *function,
|
||||
* s_allocate()
|
||||
*********************************************************/
|
||||
|
||||
static int s_allocate(LOG log, size_t add_chars)
|
||||
static bool s_allocate(LOG log, size_t add_chars)
|
||||
{
|
||||
char *tmp;
|
||||
size_t newsize;
|
||||
|
||||
if (log->size == (size_t)-1)
|
||||
return 0; /* log is set to a string literal (panic) */
|
||||
if (log->panic)
|
||||
return false;
|
||||
|
||||
add_chars += air;
|
||||
|
||||
newsize = log->size + add_chars;
|
||||
newsize = (size_t) log->size + add_chars;
|
||||
if (newsize > INT_MAX) {
|
||||
panic(log);
|
||||
return false;
|
||||
}
|
||||
|
||||
tmp = realloc(log->buffer, newsize);
|
||||
if (tmp) {
|
||||
log->buffer = tmp;
|
||||
if (log->size == 0)
|
||||
log->buffer[0] = 0;
|
||||
log->size = newsize;
|
||||
} else {
|
||||
panic(log);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -246,9 +262,7 @@ static int s_allocate(LOG log, size_t add_chars)
|
||||
|
||||
static void panic(LOG log)
|
||||
{
|
||||
log->size = (size_t) -1;
|
||||
log->buffer = "PANIC! bmplib encountered an error while trying to set "
|
||||
"an error message";
|
||||
log->panic = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -282,7 +296,7 @@ static int s_add_file_etc(LOG log, const char *file, int line, const char *funct
|
||||
}
|
||||
|
||||
if (log->size - len <= snprintf(log->buffer + len, log->size - len,
|
||||
"[%s, line %d, %s()] ", file, line, function)) {
|
||||
"[%s, line %d, %s()] ", file, line, function)) {
|
||||
panic(log);
|
||||
return 0;
|
||||
}
|
||||
|
||||
28
logging.h
28
logging.h
@@ -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
|
||||
|
||||
|
||||
|
||||
25
meson.build
25
meson.build
@@ -1,4 +1,4 @@
|
||||
project('bmplib', 'c', default_options: ['c_std=c11'], version: '1.4.7')
|
||||
project('bmplib', 'c', default_options: ['c_std=c11'], version: '1.7.3')
|
||||
|
||||
cc = meson.get_compiler('c')
|
||||
|
||||
@@ -27,6 +27,7 @@ bmplib_sources = ['bmp-read.c',
|
||||
'bmp-read-loadimage.c',
|
||||
'bmp-read-loadindexed.c',
|
||||
'bmp-common.c',
|
||||
'huffman.c',
|
||||
'logging.c']
|
||||
|
||||
|
||||
@@ -37,15 +38,31 @@ elif cc.sizeof('int') < 4
|
||||
endif
|
||||
|
||||
|
||||
gen_huffman = executable('gen-huffman', 'gen-huffman.c')
|
||||
huff_codes = custom_target('huffman-codes.h',
|
||||
output: 'huffman-codes.h',
|
||||
command: [gen_huffman, '@OUTPUT@'],
|
||||
)
|
||||
|
||||
gen_reversebits = executable('gen-reversebits', 'gen-reversebits.c')
|
||||
reversebits = custom_target('reversebits.h',
|
||||
output: 'reversebits.h',
|
||||
command: [gen_reversebits, '@OUTPUT@'],
|
||||
)
|
||||
|
||||
|
||||
bmplib = shared_library('bmp',
|
||||
bmplib_sources,
|
||||
[bmplib_sources, huff_codes[0], reversebits[0]],
|
||||
version: meson.project_version(),
|
||||
install: true,
|
||||
dependencies: m_dep)
|
||||
dependencies: m_dep,
|
||||
)
|
||||
|
||||
pkg_mod = import('pkgconfig')
|
||||
pkg_mod.generate(libraries: bmplib,
|
||||
version: meson.project_version(),
|
||||
name: 'libbmp',
|
||||
filebase: 'libbmp',
|
||||
description: 'Library for reading/writing Windows BMP files.')
|
||||
description: 'Library for reading/writing Windows BMP files.',
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user