mirror of
https://github.com/rupertwh/bmplib.git
synced 2026-04-05 22:01:09 +00:00
Compare commits
143 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7910ac36b | ||
|
|
7ece494541 | ||
|
|
c8ead48e33 | ||
|
|
8b38742ea3 | ||
|
|
3a673da403 | ||
|
|
fabbe4f8af | ||
|
|
22ef9d73aa | ||
|
|
abd3ea2db2 | ||
|
|
3c5723b740 | ||
|
|
8dd7182bf1 | ||
|
|
07d3bf6086 | ||
|
|
5cd33b22ca | ||
|
|
10657851d1 | ||
|
|
044fa148ed | ||
|
|
b9244c0766 | ||
|
|
84a0f0f49d | ||
|
|
1b67440d29 | ||
|
|
5166bdb1e7 | ||
|
|
c02804cdbf | ||
|
|
fd8583d568 | ||
|
|
845223bf62 | ||
|
|
952c10324f | ||
|
|
668cc352e3 | ||
|
|
1bba98e395 | ||
|
|
0815eb3a7b | ||
|
|
94dceb5812 | ||
|
|
c733595aed | ||
|
|
920ba8ea7e | ||
|
|
0882f1796a | ||
|
|
da95bcb0ae | ||
|
|
e5584c3b0f | ||
|
|
3af484b238 | ||
|
|
6c33d975b5 | ||
|
|
0ab592c357 | ||
|
|
e9b047b857 | ||
|
|
71b3853327 | ||
|
|
5ce902ce1b | ||
|
|
abf93c1a77 | ||
|
|
bc7dd1423c | ||
|
|
100715fe9d | ||
|
|
e195d1d918 | ||
|
|
3989e07e05 | ||
|
|
df8d9e44ca | ||
|
|
7d2ff7fca5 | ||
|
|
2ba0910acc | ||
|
|
7494855af6 | ||
|
|
1d9a3faad4 | ||
|
|
3d31aa93cd | ||
|
|
91d93cba9b | ||
|
|
b43fd940e6 | ||
|
|
61a4caf6ef | ||
|
|
4942795e14 | ||
|
|
52801cd5d5 | ||
|
|
0e4e611955 | ||
|
|
58ebfc8dc7 | ||
|
|
1e3f4abe16 | ||
|
|
be9882e04f | ||
|
|
3218cd6e65 | ||
|
|
142427e179 | ||
|
|
0580565bf3 | ||
|
|
7523138fbb | ||
|
|
592605e06f | ||
|
|
eeae2205c5 | ||
|
|
2d7763de7f | ||
|
|
7513352a7b | ||
|
|
ea7b93ce64 | ||
|
|
1b3c8a1081 | ||
|
|
1ede644b71 | ||
|
|
1b9c8af659 | ||
|
|
a4800cb684 | ||
|
|
ed07c4f618 | ||
|
|
9297d88483 | ||
|
|
c0c2513c7d | ||
|
|
352a231632 | ||
|
|
72f8085c41 | ||
|
|
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 | ||
|
|
7b3ca4cf47 | ||
|
|
bdb3482b89 | ||
|
|
056079ae80 | ||
|
|
393a02a67f | ||
|
|
da6905b6ee | ||
|
|
5e8db7a16c | ||
|
|
39ce995f05 | ||
|
|
bbabf02cd9 | ||
|
|
f65c0efca8 | ||
|
|
337cac7461 | ||
|
|
31a4b5464a | ||
|
|
47547159c2 | ||
|
|
b5a066a076 | ||
|
|
a98834f7e9 | ||
|
|
5b3988fbe9 | ||
|
|
f47fdcdd17 | ||
|
|
6cc3ef5e91 | ||
|
|
cc85e1994a | ||
|
|
2df70ff08e | ||
|
|
e8a95dfc33 | ||
|
|
749ba2dad0 | ||
|
|
264b210ef8 | ||
|
|
af6bfbf465 | ||
|
|
eaf28a9cdd | ||
|
|
07c4b1d6f4 | ||
|
|
6db96b34e7 |
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
|
||||
|
||||
942
API-full.md
Normal file
942
API-full.md
Normal file
@@ -0,0 +1,942 @@
|
||||
# Rupert's bmplib -- Full API Description (v1.8.0)
|
||||
|
||||
Refer to the *Quick Start Guide* (API-quick-start.md) for a quick intro to bmplib which describes only the minimal set of functions needed to read/write BMP files.
|
||||
|
||||
## 1. Functions for reading BMP files
|
||||
|
||||
### 1.2 Get a handle
|
||||
|
||||
```c
|
||||
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.
|
||||
|
||||
### 1.3 Read the file header
|
||||
|
||||
```c
|
||||
BMPRESULT bmpread_load_info(BMPHANDLE h)
|
||||
```
|
||||
|
||||
bmplib reads the file header and checks validity. Possible return values are:
|
||||
|
||||
- `BMP_RESULT_OK`: All is good, you can proceed to read the file.
|
||||
- `BMP_INSANE`: The file is valid, but huge. The default limit is 500MB
|
||||
(relevant is the required buffer size to hold the complete image, not the
|
||||
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.
|
||||
- `BMP_RESULT_ERROR`: The file cannot be read. Either it's not a valid or an
|
||||
unsupported BMP or there was some file error. Use `bmp_errmsg()` to get an
|
||||
idea what went wrong.
|
||||
|
||||
Calling `bmpread_load_info()` is optional when you use `bmpread_dimensions()`
|
||||
(see below).
|
||||
|
||||
### 1.4 Get image dimensions
|
||||
|
||||
```c
|
||||
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. It is not
|
||||
necessary to call `bmpread_load_info()` first. The return value will be the
|
||||
same as for `bmpread_load_info()`.
|
||||
|
||||
The dimensions describe the image returned by bmplib, *not* necessarily the
|
||||
original BMP file.
|
||||
|
||||
Alternatively, you can use the following functions to receive the values one
|
||||
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
|
||||
they will all return 0!
|
||||
|
||||
```c
|
||||
int bmpread_width(BMPHANDLE h);
|
||||
int bmpread_height(BMPHANDLE h);
|
||||
int bmpread_channels(BMPHANDLE h);
|
||||
int bmpread_bitsperchannel(BMPHANDLE h);
|
||||
BMPORIENT bmpread_orientation(BMPHANDLE h);
|
||||
|
||||
int bmpread_resolution_xdpi(BMPHANDLE h);
|
||||
int bmpread_resolution_ydpi(BMPHANDLE h);
|
||||
```
|
||||
|
||||
#### 1.4.1. top-down / bottom-up
|
||||
|
||||
`*orientation` is one of:
|
||||
|
||||
- `BMP_ORIENT_BOTTOMUP`
|
||||
- `BMP_ORIENT_TOPDOWN`
|
||||
|
||||
`bmpread_orientation()` or the `orientation` value returned by
|
||||
`bmpread_dimensions()` **is only relevant if you load the BMP file
|
||||
line-by-line**. In line-by-line mode (using `bmpread_load_line()`), the
|
||||
image data is always delivered in the order it is in the BMP file. The
|
||||
`orientation` value will tell you if it's top-down or bottom-up. On the
|
||||
other hand, when the whole image is loaded at once (using `bmpread_load_image
|
||||
()`), bmplib will **always** return the image top-down, regardless of how
|
||||
the BMP file is oriented. The `orientation` value will still indicate the
|
||||
orientation of the original BMP.
|
||||
|
||||
#### 1.4.2. Required size for buffer to receive image
|
||||
|
||||
```c
|
||||
size_t bmpread_buffersize(BMPHANDLE h);
|
||||
```
|
||||
|
||||
Returns the buffer size you have to allocate for the whole image.
|
||||
|
||||
### 1.5. Image type
|
||||
|
||||
```c
|
||||
BMPIMAGETYPE bmpread_image_type(BMPHANDLE h);
|
||||
```
|
||||
|
||||
Returns one of
|
||||
- `BMP_IMAGETYPE_NONE` type hasn't been determined (yet). Either
|
||||
`bmpread_load_info()` hasn't been called yet, or the file is not a valid
|
||||
BMP file.
|
||||
- `BMP_IMAGETYPE_BM` bitmap (normal image)
|
||||
- `BMP_IMAGETYPE_BA` bitmap array
|
||||
- `BMP_IMAGETYPE_CI` color icon
|
||||
- `BMP_IMAGETYPE_CP` color pointer
|
||||
- `BMP_IMAGETYPE_IC` icon (monochrome)
|
||||
- `BMP_IMAGETYPE_PT` pointer (monochrome)
|
||||
|
||||
Other than bitmap arrays (in which case `bmpread_load_info()` will have
|
||||
returned `BMP_RESULT_ARRAY`) and the limitations that apply to icon and
|
||||
pointer files (see below) the returned type is purely informational and no
|
||||
special actions need to be taken.
|
||||
|
||||
|
||||
### 1.6. Indexed BMPs
|
||||
|
||||
By default, bmplib will interpret indexed (color palette) BMPs and return the
|
||||
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:
|
||||
|
||||
```c
|
||||
int bmpread_num_palette_colors(BMPHANDLE h);
|
||||
BMPRESULT bmpread_load_palette(BMPHANDLE h, unsigned char **palette);
|
||||
```
|
||||
|
||||
`bmpread_num_palette_colors()` will return 0 for non-indexed images, otherwise
|
||||
it will return the number of entries in the color palette.
|
||||
|
||||
`bmpread_load_palette()` will retrieve the color palette and store it in the
|
||||
character buffer pointed to by `palette`.
|
||||
|
||||
The colors are stored 4 bytes per entry: The first three bytes are the red,
|
||||
green, and blue values in that order, the 4th byte is always set to zero. So
|
||||
the palette buffer will contain "rgb0rgb0rgb0...".
|
||||
|
||||
As with the main image buffer, you can either provide one for the palette or
|
||||
let bmplib allocate it for you (and then `free()` it, once you are done):
|
||||
|
||||
```c
|
||||
unsigned char *palette;
|
||||
int numcolors;
|
||||
|
||||
numcolors = bmpread_num_palette_colors(h);
|
||||
|
||||
/* either: */
|
||||
palette = NULL;
|
||||
bmpread_load_palette(h, &palette); /* bmplib will allocate the palette buffer */
|
||||
|
||||
/* or: */
|
||||
palette = malloc(4 * numcolors);
|
||||
bmpread_load_palette(h, &palette); /* bmplib will use the provided buffer */
|
||||
```
|
||||
|
||||
Note: Once you have called `bmpread_load_palette()`, both `bmpread_load_image
|
||||
()` and `bmpread_load_line()` will return the image as 8-bit indexes into the
|
||||
palette, you *cannot go back* to the default of loading the image as 24-bit
|
||||
RGB data. After you loaded the palette, calls to `bmpread_dimensions
|
||||
()`, `bmpread_buffersize()`, etc. will reflect that change. Also,
|
||||
`bmpread_set_undefined()` will have no effect, as indexed images cannot have
|
||||
an alpha channel (see below).
|
||||
|
||||
#### 1.6.1. Undefined pixels
|
||||
|
||||
RLE-encoded BMP files may have undefined pixels, either by using early
|
||||
end-of-line or end-of-file codes, or by using delta codes to skip part of the
|
||||
image. That is not an error, but a feature of RLE. bmplib default is to make
|
||||
such pixels transparent. RLE-encoded BMPs will therefore always be returned
|
||||
with an alpha channel by default, whether the file has such undefined pixels
|
||||
or not (because bmplib doesn't know beforehand if there will be any undefined
|
||||
pixels).
|
||||
|
||||
You can change this behaviour by calling `bmpread_set_undefined()`, with
|
||||
`mode` set to `BMP_UNDEFINED_LEAVE`. In that case, the returned image will
|
||||
have no alpha channel, and undefined pixels will not change the image buffer.
|
||||
So whatever was in the image buffer before loading the image will remain
|
||||
untouched by undefined pixels. (Note: if you let bmplib allocate the image
|
||||
buffer, it will always be initialized to zero before loading the image). This
|
||||
function has no effect on non-RLE BMPs.
|
||||
|
||||
```c
|
||||
void bmpread_set_undefined(BMPHANDLE h, BMPUNDEFINED mode);
|
||||
```
|
||||
|
||||
`mode` can be one of:
|
||||
|
||||
- `BMP_UNDEFINED_LEAVE`
|
||||
- `BMP_UNDEFINED_TO_ALPHA` (default)
|
||||
|
||||
Note: If you use `bmpread_load_palette()` to switch to loading the index data
|
||||
instead of RGB data, this setting will have no effect and undefined pixels
|
||||
will always be left alone! (see above)
|
||||
|
||||
### 1.7. ICC color profiles
|
||||
|
||||
```c
|
||||
size_t bmpread_iccprofile_size(BMPHANDLE h);
|
||||
BMPRESULT bmpread_load_iccprofile(BMPHANDLE h, unsigned char **pprofile);
|
||||
```
|
||||
|
||||
Use `bmpread_iccprofile_size()` to query the size (or existence) of an
|
||||
embedded color profile. If the BMP file doesn't contain a profile, the return
|
||||
value is 0.
|
||||
|
||||
bmplib does not interpret or apply embedded ICC color profiles. The profile is
|
||||
simply returned 'as is', image data is not affected in any way.
|
||||
|
||||
`bmpread_load_iccprofile()` loads the profile into the buffer pointed to by
|
||||
`*pprofile`. As with loading image and palette data, you can either allocate
|
||||
the buffer yourself or pass a pointer to a NULL-pointer and let bmplib
|
||||
allocate an appropriate buffer, e.g.:
|
||||
|
||||
```c
|
||||
unsigned char *profile = NULL;
|
||||
if (bmpread_iccprofile_size(h) > 0)
|
||||
bmpread_load_iccprofile(h, &profile);
|
||||
```
|
||||
|
||||
|
||||
### 1.8. Optional settings for 64bit BMPs
|
||||
|
||||
```c
|
||||
int bmpread_is_64bit(BMPHANDLE h);
|
||||
BMPRESULT bmpread_set_64bit_conv(BMPHANDLE h, BMPCONV64 conv);
|
||||
```
|
||||
|
||||
If you don't do anything, 64bit BMPs will be read like any other BMP and the
|
||||
data will be returned as 16bit/channel sRGB RGBA.
|
||||
|
||||
But if you want to access the original s2.13 fixed-point components, or you
|
||||
don't want the linear-to-sRGB conversion, you can use `bmpread_set_64bit_conv
|
||||
()` and `bmp_set_number_format()` to control how the image is returned.
|
||||
|
||||
64bit BMP pixel values are in the [-4...4) range, beyond the usual
|
||||
[0...1]. Unless you load the image as `BMP_FORMAT_S2_13` or `BMP_FORMAT_FLOAT`,
|
||||
the values will be clipped to [0...1].
|
||||
|
||||
Options for `bmpread_set_64bit()` are:
|
||||
|
||||
- `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 also* calling `bmp_set_number_format()` with
|
||||
`BMP_FORMAT_S2_13`. Image values are returned exactly as they are in the BMP
|
||||
file, without any conversion or attempt at interpretation.
|
||||
|
||||
### 1.9. 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:
|
||||
|
||||
```c
|
||||
BMPRESULT bmp_set_number_format(BMPHANDLE h, BMPFORMAT format);
|
||||
```
|
||||
|
||||
(see below, *3. General functions for both reading/writing BMPs*)
|
||||
|
||||
### 1.10. Huge files: bmpread_set_insanity_limit()
|
||||
|
||||
bmplib will refuse to load images beyond a certain size (default 500MB) and
|
||||
instead return `BMP_RESULT_INSANE`. If you want to load the image anyway, call
|
||||
`bmpread_set_insanity_limit()` at any time before calling `bmpread_load_image()`.
|
||||
`limit` is the new allowed size in bytes (not MB!).
|
||||
|
||||
```c
|
||||
void bmpread_set_insanity_limit(BMPHANDLE h, size_t limit);
|
||||
```
|
||||
|
||||
### 1.11. OS/2 bitmap arrays (type 'BA')
|
||||
|
||||
Bitmap arrays are meant to contain several versions (with different
|
||||
resolutions and color depths) of the same image. They are not meant to
|
||||
contain different images like e.g. several scanned pages.
|
||||
|
||||
If a BMP file is of type 'BA', `bmpread_load_info()` will return
|
||||
`BMP_RESULT_ARRAY`. Use the following functions to to query the number and
|
||||
type of contained images and get a new `BMPHANDLE` for any of the contained
|
||||
images:
|
||||
|
||||
```c
|
||||
int bmpread_array_num(BMPHANDLE h);
|
||||
BMPRESULT bmpread_array_info(BMPHANDLE h, struct BmpArrayInfo *ai, int idx);
|
||||
|
||||
struct BmpArrayInfo {
|
||||
BMPHANDLE handle;
|
||||
BMPIMAGETYPE type;
|
||||
int width, height;
|
||||
int ncolors; /* 0 = RGB */
|
||||
int screenwidth, screenheight; /* typically 0, or 1024x768 for 'hi-res' */
|
||||
};
|
||||
```
|
||||
|
||||
`bmpread_array_num()` returns the number of images contained in the array.
|
||||
|
||||
`bmpread_array_info()` fills the supplied `struct BmpArrayInfo` with
|
||||
information about any of the contained images:
|
||||
- `handle`: A handle that can be used with all the usual bmpread_* functions
|
||||
to read the respective image. These handles for array images don't have to
|
||||
be freed individually, it is sufficient to call `bmp_free()` on the
|
||||
main array handle once the images have been loaded.
|
||||
- `type`: Image type, one of
|
||||
- `BMP_IMAGETYPE_BM` bitmap (normal image)
|
||||
- `BMP_IMAGETYPE_CI` color icon
|
||||
- `BMP_IMAGETYPE_CP` color pointer
|
||||
- `BMP_IMAGETYPE_IC` icon (monochrome)
|
||||
- `BMP_IMAGETYPE_PT` pointer (monochrome)
|
||||
- `width, height`: Width and height of the image
|
||||
- `ncolors`: number of colors (2/16/256) for indexed images, or 0 for RGB
|
||||
images
|
||||
- `screenwidth, screenheight`: The screen size the image was intended for.
|
||||
Typically 0, or 1024x768 for 'hi-res'.
|
||||
|
||||
NOTE: You must not interleave calls to `bmpread_load_line()` for several array
|
||||
images or simultanously call `bmpread_load_image()` from different threads.
|
||||
First completely read one image before proceeding to the next one.
|
||||
|
||||
#### 1.10.1 OS/2 icons and pointers
|
||||
|
||||
OS/2 icons and pointers are often contained in bitmap arrays.
|
||||
|
||||
Some limitations apply for loading icons and pointers:
|
||||
- The image is always returned as 8 bit per channel RGBA, loading index values
|
||||
and palette is not supported.
|
||||
- Maximum supported size is 512x512. This shouldn't be limiting for actual
|
||||
legacy icons and pointers.
|
||||
- For RLE images, undefined mode is always assumed to be
|
||||
`BMP_UNDEFINED_LEAVE`, regardless of what `bmpread_set_undefined()` was set
|
||||
to. (Because icons and pointers have their own mask-based transparency
|
||||
scheme.)
|
||||
|
||||
|
||||
### 1.12. Load the image
|
||||
|
||||
#### 1.12.1. bmpread_load_image()
|
||||
|
||||
```c
|
||||
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()`.
|
||||
|
||||
```c
|
||||
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). Multi-byte 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. (See above, "Getting information...")
|
||||
|
||||
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.
|
||||
|
||||
#### 1.12.2. bmpread_load_line()
|
||||
|
||||
```c
|
||||
BMPRESULT bmpread_load_line(BMPHANDLE h, unsigned char **pbuffer);
|
||||
```
|
||||
|
||||
Loads a single scan line from the BMP file into the buffer pointed to by
|
||||
`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 call `free()` on the buffer, once you
|
||||
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 *
|
||||
bitsperchannel / 8.
|
||||
|
||||
```c
|
||||
single_line_buffersize = bmpread_buffersize(h) / bmpread_height(h);
|
||||
/* or */
|
||||
single_line_buffersize = bmpread_width(h) * bmpread_channels(h) * bmpread_bitsperchannel(h) / 8;
|
||||
```
|
||||
|
||||
Repeated calls to `bmpread_load_line()` will return each scan line, one after
|
||||
the other.
|
||||
|
||||
Important: when reading the image this way, line by line, the orientation
|
||||
(`bmpread_orientation()`) of the original BMP matters! The lines will be
|
||||
returned in whichever order they are stored in the BMP. Use the value
|
||||
returned by `bmpread_orientation()` to determine if it is top-down or
|
||||
bottom-up. Almost all BMPs will be bottom-up. (see above)
|
||||
|
||||
### 1.13. Invalid pixels
|
||||
|
||||
Invalid pixels may occur in indexed BMPs, both RLE and non-RLE. Invalid pixels
|
||||
either point beyond the given color palette, or they try to set pixels
|
||||
outside the image dimensions. Pixels containing an invalid color value will
|
||||
be set to the maximum allowed value, and attempts to point outside the image
|
||||
will be ignored.
|
||||
|
||||
In both cases, `bmpread_load_image()` and `bmpread_load_line()` will return
|
||||
`BMP_RESULT_INVALID`, unless the image is also truncated, then
|
||||
`BMP_RESULT_TRUNCATED` is returned.
|
||||
|
||||
### 1.14. Query info about the BMP file
|
||||
|
||||
Note: these functions return information about the original BMP file being
|
||||
read. They do *not* describe the format of the returned image data, which may
|
||||
be different!
|
||||
|
||||
```c
|
||||
BMPINFOVER bmpread_info_header_version(BMPHANDLE h);
|
||||
int bmpread_info_header_size(BMPHANDLE h);
|
||||
int bmpread_info_compression(BMPHANDLE h);
|
||||
int bmpread_info_bitcount(BMPHANDLE h);
|
||||
const char* bmpread_info_header_name(BMPHANDLE h);
|
||||
const char* bmpread_info_compression_name(BMPHANDLE h);
|
||||
BMPRESULT bmpread_info_channel_bits(BMPHANDLE h, int *r, int *g, int *b, int *a);
|
||||
```
|
||||
|
||||
### 1.15. Release the handle
|
||||
|
||||
```c
|
||||
void bmp_free(BMPHANDLE h);
|
||||
```
|
||||
|
||||
Frees all resources associated with the handle `h`. **Image data is not
|
||||
affected**, so you can call `bmp_free()` immediately after `bmpread_load_image()`
|
||||
and still use the returned image data.
|
||||
|
||||
Note: Any error message strings returned by `bmp_errmsg()` are invalidated by
|
||||
`bmp_free()` and must not be used anymore!
|
||||
|
||||
## 2. Functions for writing BMP files
|
||||
|
||||
### 2.1. Get a handle
|
||||
|
||||
```c
|
||||
BMPHANDLE bmpwrite_new(FILE *file);
|
||||
```
|
||||
|
||||
### 2.2. Set image dimensions
|
||||
|
||||
```c
|
||||
BMPRESULT bmpwrite_set_dimensions(BMPHANDLE h,
|
||||
unsigned width,
|
||||
unsigned height,
|
||||
unsigned channels,
|
||||
unsigned bitsperchannel);
|
||||
|
||||
BMPRESULT bmpwrite_set_resolution(BMPHANDLE h, int xdpi, int ydpi);
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
### 2.3. Set the output format
|
||||
|
||||
Optional: set the bit-depth for each output channel. bmplib will otherwise
|
||||
choose appropriate bit-depths for your image. The bit-depth per channel can
|
||||
be anywhere between 0 and 32, inclusive. In sum, the bits must be at least 1
|
||||
and must not exceed 32.
|
||||
|
||||
```c
|
||||
BMPRESULT bmpwrite_set_output_bits(BMPHANDLE h, int red, int green, int blue, int alpha);
|
||||
```
|
||||
|
||||
### 2.4. Indexed images
|
||||
|
||||
```c
|
||||
BMPRESULT bmpwrite_set_palette(BMPHANDLE h, int numcolors, unsigned char *palette);
|
||||
BMPRESULT bmpwrite_allow_2bit(BMPHANDLE h);
|
||||
```
|
||||
|
||||
You can write 1/2/4/8-bit indexed images by providing a color palette with
|
||||
`bmpwrite_set_palette()`. The palette entries must be 4 bytes each, the first
|
||||
three bytes are the red, green, and blue values in that order, the 4th byte
|
||||
is padding and will be ignored.
|
||||
|
||||
If you provide a palette, the image data you provide has to be 1 channel, 8
|
||||
bits per pixel, regardless of the palette size. The pixel values must be
|
||||
indexing the color palette. Invalid index values (which go beyond the color
|
||||
palette) will be silently clipped. bmplib will choose the appropriate
|
||||
bit-depth for the BMP according to the number of color-entries in your
|
||||
palette.
|
||||
|
||||
By default, bmplib will not write 2-bit indexed BMPs (supposedly a Windows CE
|
||||
relict), as many readers will refuse to open these. If you do want a 2-bit
|
||||
BMP for 3- or 4-color images, call `bmpwrite_allow_2bit()` before calling
|
||||
`bmpwrite_save_image()`.
|
||||
|
||||
#### 2.4.1. RLE
|
||||
|
||||
```c
|
||||
BMPRESULT bmpwrite_set_rle(BMPHANDLE h, BMPRLETYPE type);
|
||||
BMPRESULT bmpwrite_allow_huffman(BMPHANDLE h);
|
||||
BMPRESULT bmpwrite_set_huffman_img_fg_idx(BMPHANDLE h, int idx);
|
||||
```
|
||||
|
||||
Indexed images may optionally be written as run-length-encoded (RLE) bitmaps.
|
||||
Images with 16 or fewer colors can be written as either RLE4 or RLE8
|
||||
(default is RLE4), images with more than 16 colors only as RLE8.
|
||||
|
||||
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).
|
||||
|
||||
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, RLE8, or 1-D Huffman based on number of colors
|
||||
in palette
|
||||
- `BMP_RLE_RLE8` use RLE8, regardless of number of colors in palette
|
||||
|
||||
|
||||
#### 2.4.2. 1-D Huffman encoding
|
||||
In order to write 1-D Huffman encoded bitmpas,
|
||||
|
||||
- the provided palette must have 2 colors,
|
||||
- RLE type must be set to `BMP_RLE_AUTO`,
|
||||
- and `bmpwrite_allow_huffman()` must be called.
|
||||
|
||||
Be aware that *very* few programs will be able to read Huffman encoded BMPs!
|
||||
|
||||
In order to get the best compression result, you should also call
|
||||
`bmpwrite_set_huffman_img_fg_idx()` to specify which color index (0 or 1) in
|
||||
the image corresponds to the foreground color. Huffman compression is
|
||||
optimized for scanned text, meaning short runs of foreground color and long(er)
|
||||
runs of background color. This will not change the appearance of the
|
||||
image, but setting it correctly will result in better compression.
|
||||
|
||||
|
||||
#### 2.4.3. 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).
|
||||
|
||||
|
||||
### 2.5. top-down / bottom-up
|
||||
|
||||
By default, bmplib will write BMP files bottom-up, which is how BMP files are
|
||||
usually orientated.
|
||||
|
||||
For non-RLE files, you have the option to change the orientation to top-down.
|
||||
(RLE files always have to be written in the default bottom-up orientation.)
|
||||
|
||||
```c
|
||||
BMPRESULT bmpwrite_set_orientation(BMPHANDLE h, BMPORIENT orientation);
|
||||
```
|
||||
|
||||
with `orientation` set to one of the following values:
|
||||
|
||||
- `BMPORIENT_BOTTOMUP`
|
||||
- `BMPORIENT_TOPDOWN`
|
||||
|
||||
Note: When writing the whole image at once using `bmpwrite_save_image()`, the
|
||||
image buffer you provide must **always** be in top-down orientation,
|
||||
regardless of the orientation chosen for the BMP file.
|
||||
|
||||
When writing the image line-by-line using `bmpwrite_save_line()`, you must
|
||||
provide the image lines in the order according to the orientation you have
|
||||
chosen for the BMP file.
|
||||
|
||||
|
||||
### 2.6. ICC color profiles
|
||||
|
||||
```c
|
||||
BMPRESULT bmpwrite_set_iccprofile(BMPHANDLE h, size_t size,
|
||||
const unsigned char *iccprofile);
|
||||
BMPRESULT bmpwrite_set_rendering_intent(BMPHANDLE h, BMPINTENT intent);
|
||||
```
|
||||
|
||||
Use `bmpwrite_set_iccprofile()` to write an embedded ICC color profile to the
|
||||
BMP file.
|
||||
|
||||
bmplib will not interpret or validate the supplied profile in any way.
|
||||
|
||||
Setting a color profile or rendering intent will disable Huffman and RLE24 encodings.
|
||||
(color profiles require a BITMAPV5HEADER, but those encodings would require
|
||||
an older OS/2 info header, instead.)
|
||||
|
||||
You can optionally specify a rendering intent with `bmpwrite_set_rendering_intent()`,
|
||||
where `intent` is one of:
|
||||
- `BMP_INTENT_NONE`
|
||||
- `BMP_INTENT_BUSINESS` (= saturation)
|
||||
- `BMP_INTENT_GRAPHICS` (= relative colorimetric)
|
||||
- `BMP_INTENT_IMAGES` (= perceptive)
|
||||
- `BMP_INTENT_ABS_COLORIMETRIC` (= absolute colorimetric)
|
||||
|
||||
|
||||
### 2.7. 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
|
||||
|
||||
```c
|
||||
BMPRESULT bmpwrite_set_64bit(BMPHANDLE h);
|
||||
```
|
||||
|
||||
In order to make use of the extended range available in 64-bit BMPs
|
||||
(-4.0 to +3.999...), you will probably want to provide the image buffer
|
||||
either as 32-bit float or as 16-bit s2.13 (and call `bmp_set_number_format()`
|
||||
accordingly).
|
||||
|
||||
Note: 64-bit BMPs store pixel values in *linear light*. Unlike when *reading*
|
||||
64-bit BMPs, bmplib will not make any gamma/linear conversion while writing
|
||||
BMPs. You have to provide the proper linear values in the image buffer.
|
||||
|
||||
### 2.8. Write the image
|
||||
|
||||
```c
|
||||
BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image);
|
||||
BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line);
|
||||
```
|
||||
|
||||
Write either the whole image at once with `bmpwrite_save_image()` or one line
|
||||
at a time with `bmpwrite_save_line()`.
|
||||
|
||||
The image data pointed to by `image` or `line` 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). Indexed data must be
|
||||
supplied as 8 bit per pixel, even when writing lower bit (1/2/4) BMPs
|
||||
(see above).
|
||||
|
||||
Important: When writing the whole image at once using `bmpwrite_save_image()`,
|
||||
the image data must be provided top-down (same as is returned by
|
||||
`bmpread_load_image()`). When using `bmpwrite_save_line()` to write the image
|
||||
line-by-line, the image data must be provided according to the orientation
|
||||
set with `bmpwrite_set_orientation()` (see above).
|
||||
|
||||
## 3. General functions for both reading/writing BMPs
|
||||
|
||||
### 3.1. bmp_free()
|
||||
|
||||
```c
|
||||
void bmp_free(BMPHANDLE h);
|
||||
```
|
||||
|
||||
Frees all resources associated with the handle `h`. Image data is not
|
||||
affected, so you can call `bmp_free()` immediately after `bmpread_load_image()`
|
||||
and still use the returned image data. Note: Any error messages returned
|
||||
by `bmp_errmsg()` are invalidated by `bmp_free()` and cannot be used
|
||||
anymore!
|
||||
|
||||
### 3.2. bmp_errmsg()
|
||||
|
||||
```c
|
||||
const char* bmp_errmsg(BMPHANDLE h);
|
||||
```
|
||||
|
||||
Returns a zero-terminated character string containing the last error
|
||||
description(s). The returned string is safe to use until any other
|
||||
bmplib-function is called with the same handle or the handle is freed with
|
||||
`bmp_free()`.
|
||||
|
||||
### 3.3. bmp_set_number_format()
|
||||
|
||||
```c
|
||||
BMPRESULT bmp_set_number_format(BMPHANDLE h, BMPFORMAT format);
|
||||
```
|
||||
|
||||
sets the number format of the image buffer received from / passed to bmplib. `format` can be one of
|
||||
|
||||
- `BMP_FORMAT_INT` image buffer values are expected/returned as 8-, 16-, or
|
||||
32-bit integers. (this is the default)
|
||||
- `BMP_FORMAT_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.
|
||||
|
||||
### 3.4. bmp_version()
|
||||
|
||||
```c
|
||||
const char* bmp_version(void);
|
||||
```
|
||||
|
||||
Returns a zero-terminated character string containing the version of bmplib.
|
||||
|
||||
### 3.5. bmp_set_huffman_t4black_value()
|
||||
|
||||
```c
|
||||
BMPRESULT bmp_set_huffman_t4black_value(BMPHANDLE h, int blackidx);
|
||||
```
|
||||
|
||||
(not to be confused with `bmpwrite_set_huffman_img_fg_idx()`, which serves an
|
||||
entirely different purpose, see above.)
|
||||
|
||||
ITU-T T.4 defines 'black' and 'white' pixel sequences (referring to fore- and
|
||||
background, respectively), but it doesn't prescribe which of those is
|
||||
represented by 0 or 1. That would have to be defined by a BMP specification,
|
||||
but documentation on Huffman BMPs is close to non-existent.
|
||||
|
||||
Current consensus seems to be that 'black' is 1, i.e. indexing the second
|
||||
color in the palette, and 'white' is 0, i.e. indexing the first color. This
|
||||
is the default for bmplib.
|
||||
|
||||
In case that's wrong (in fact it's not even clear if there is a right and a
|
||||
wrong), `bmp_set_huffman_t4black_value()` can be used to set the pixel value
|
||||
of 'black' to either 0 or 1 (and white to the respective opposite).
|
||||
|
||||
Can be used both for reading and writing BMPs.
|
||||
|
||||
Changing this value will invert the image colors!
|
||||
|
||||
Reasons to use this function:
|
||||
|
||||
- You know that bmplib's default assumption of 'black'=1 is wrong, and you
|
||||
want to set it to 0. (In that case, please also drop a note on github.)
|
||||
- You don't care either way, but you want to be sure to get consistent
|
||||
behaviour, in case bmplib's default is ever changed in light of new
|
||||
information/documentation.
|
||||
- You need to interface with other software that you know assumes 'black'=0.
|
||||
|
||||
|
||||
## 4. Data types and constants
|
||||
|
||||
#### `BMPHANDLE`
|
||||
|
||||
Returned by `bmpread_new()` and `bmpwrite_new()`. Identifies the current
|
||||
operation for all subsequent calls to bmplib-functions.
|
||||
|
||||
#### `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` will always have the vaue 0.
|
||||
The rest will have values in increasing order. So it would be possible to do
|
||||
something like:
|
||||
|
||||
```
|
||||
if (bmpread_load_image(h, &buffer) <= BMP_RESULT_TRUNCATED) {
|
||||
/* use image - might be ok or (partially) corrupt */
|
||||
}
|
||||
else {
|
||||
printf("Couldn't get any image data: %s\n", bmp_errmsg(h));
|
||||
}
|
||||
```
|
||||
|
||||
#### `BMPINFOVER`
|
||||
|
||||
Returned by `bmpread_info_header_version()`. Possible values are:
|
||||
|
||||
- `BMPINFO_CORE_OS21` BITMAPCOREHEADER aka OS21XBITMAPHEADER (12 bytes)
|
||||
- `BMPINFO_OS22` OS22XBITMAPHEADER (16-64 bytes)
|
||||
- `BMPINFO_V3` BITMAPINFOHEADER (40 bytes)
|
||||
- `BMPINFO_V3_ADOBE1` BITMAPINFOHEADER with additional RGB masks (52 bytes)
|
||||
- `BMPINFO_V3_ADOBE2` BITMAPINFOHEADER with additional RGBA masks (56 bytes)
|
||||
- `BMPINFO_V4,` BITMAPV4HEADER (108 bytes)
|
||||
- `BMPINFO_V5,` BITMAPV5HEADER (124 bytes)
|
||||
- `BMPINFO_FUTURE` possible future info header (> 124 bytes)
|
||||
|
||||
Can safely be cast from/to int. The values will always be strictly increasing
|
||||
from `BMPINFO_CORE_OS21` to `BMPINFO_FUTURE`.
|
||||
|
||||
#### `BMPRLETYPE`
|
||||
|
||||
Used in `bmpwrite_set_rle()`. Possible values are:
|
||||
|
||||
- `BMP_RLE_NONE` No RLE
|
||||
- `BMP_RLE_AUTO` RLE4 or RLE8, chosen based on number of colors in palette
|
||||
- `BMP_RLE_RLE8` Use RLE8 for any number of colors in palette
|
||||
|
||||
Can safely be cast from/to int.
|
||||
|
||||
#### `BMPUNDEFINED`
|
||||
|
||||
Used in `bmpread_set_undefined()`. Possible values are:
|
||||
|
||||
- `BMP_UNDEFINED_TO_ALPHA` (default)
|
||||
- `BMP_UNDEFINED_LEAVE`
|
||||
|
||||
Can safely be cast from/to int.
|
||||
|
||||
#### `BMPCONV64`
|
||||
|
||||
Used in `bmpread_set_64bit_conv()`. Possible values are:
|
||||
|
||||
- `BMP_CONV64_SRGB` (default)
|
||||
- `BMP_CONV64_LINEAR`
|
||||
- `BMP_CONV64_NONE`
|
||||
|
||||
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
|
||||
|
||||
### 5.1. Reading BMPs
|
||||
|
||||
```c
|
||||
/* (all error checking left out for clarity) */
|
||||
|
||||
BMPHANDLE h;
|
||||
FILE *file;
|
||||
int width, height, channels, bitsperchannel, orientation;
|
||||
uint8_t *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.
|
||||
*/
|
||||
|
||||
bmpread_load_info(h);
|
||||
|
||||
width = bmpread_width(h);
|
||||
height = bmpread_height(h);
|
||||
channels = bmpread_channels(h);
|
||||
bitsperchannel = bmpread_bitsperchannel(h);
|
||||
|
||||
|
||||
/* Get required size for memory buffer and allocate memory */
|
||||
|
||||
image_buffer = malloc(bmpread_buffersize(h));
|
||||
|
||||
|
||||
/* 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 */
|
||||
|
||||
/* Image data is always returned in host byte order as
|
||||
* 8, 16, or 32 bits per channel RGB or RGBA data.
|
||||
* No padding.
|
||||
*/
|
||||
```
|
||||
|
||||
### 5.2. Writing BMPs
|
||||
|
||||
```c
|
||||
/* (all error checking left out for clarity) */
|
||||
|
||||
BMPHANDLE h;
|
||||
FILE *file;
|
||||
uint8_t *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);
|
||||
```
|
||||
285
API-quick-start.md
Normal file
285
API-quick-start.md
Normal file
@@ -0,0 +1,285 @@
|
||||
# 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:
|
||||
|
||||
```c
|
||||
bmpread_new();
|
||||
bmpread_dimensions();
|
||||
bmpread_load_image();
|
||||
bmp_free();
|
||||
```
|
||||
|
||||
### Get a handle
|
||||
|
||||
```c
|
||||
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
|
||||
|
||||
```c
|
||||
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
|
||||
|
||||
```c
|
||||
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*).
|
||||
|
||||
```c
|
||||
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
|
||||
|
||||
```c
|
||||
void bmp_free(BMPHANDLE h);
|
||||
```
|
||||
|
||||
Frees all resources associated with the handle `h`. **Image data is not
|
||||
affected**, so you can call `bmp_free()` immediately after `bmpread_load_image()`
|
||||
and still use the returned image data.
|
||||
|
||||
Note: Any error message strings returned by `bmp_errmsg()` are invalidated by
|
||||
`bmp_free()` and must not be used anymore!
|
||||
|
||||
|
||||
|
||||
## 2. Writing BMP files:
|
||||
|
||||
```c
|
||||
bmpwrite_new();
|
||||
bmpwrite_set_dimensions();
|
||||
bmpwrite_save_image();
|
||||
bmp_free();
|
||||
```
|
||||
|
||||
### Get a handle
|
||||
|
||||
```c
|
||||
BMPHANDLE bmpwrite_new(FILE *file);
|
||||
```
|
||||
|
||||
### Set image dimensions
|
||||
|
||||
```c
|
||||
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
|
||||
|
||||
```c
|
||||
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()
|
||||
|
||||
```c
|
||||
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
|
||||
|
||||
```c
|
||||
/* (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
|
||||
|
||||
```c
|
||||
/* (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);
|
||||
```
|
||||
394
API.md
394
API.md
@@ -1,394 +0,0 @@
|
||||
# API -- Rupert's bmplib
|
||||
|
||||
|
||||
## 1. Functions for reading BMP files
|
||||
|
||||
### Getting a handle: bmpread_new()
|
||||
```
|
||||
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.
|
||||
|
||||
### Read the file header: bmpread_load_info()
|
||||
```
|
||||
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_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.
|
||||
- `BMP_RESULT_ERROR`: The file cannot be read. Either it's not a valid or an unsupported BMP or there was some file error. Use `bmp_errmsg()` to get an idea what went wrong.
|
||||
|
||||
Calling `bmpread_load_info()` is optional when you use `bmpread_dimensions()` (see below).
|
||||
|
||||
### Getting information about image dimensions: bmpread_dimensions() et al.
|
||||
```
|
||||
BMPRESULT bmpread_dimensions(BMPHANDLE h,
|
||||
int *width,
|
||||
int *height,
|
||||
int *channels,
|
||||
int *bitsperchannel,
|
||||
int *topdown)
|
||||
```
|
||||
Use `bmpread_dimensions()` to get all dimensions with one call. It is not necessary to call `bmpread_load_info()` first. The return value will be the same as for `bmpread_load_info()`.
|
||||
|
||||
The dimensions describe the image returned by bmplib, *not* necesarily the original BMP file.
|
||||
|
||||
Alternatively, you can use the following functions to receive the values one 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 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_topdown(BMPHANDLE h)
|
||||
|
||||
int bmpread_resolution_xdpi(BMPHANDLE h)
|
||||
int bmpread_resolution_ydpi(BMPHANDLE h)
|
||||
|
||||
```
|
||||
#### top-down / bottom-up
|
||||
`bmpread_topdown()` or the topdown 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 topdown value will tell you if it's top-down or bottom-up. On the other hand, when the whole image is loaded at once (using `bmpread_load_image()`), bmplib will *always* return the image top-down, regardless of how the BMP file is oriented. The topdown value will still indicate the orientation of the original BMP.
|
||||
|
||||
### Required size for buffer to receive image: bmpread_buffersize():
|
||||
```
|
||||
size_t bmpread_buffersize(BMPHANDLE h)
|
||||
```
|
||||
Returns the buffer size you have to allocate for the whole image.
|
||||
|
||||
### bmpread_load_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()`.
|
||||
```
|
||||
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). Multi-byte 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. (See above, "Getting information...")
|
||||
|
||||
If bmpread_load_image() return 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 maybe partially usable.
|
||||
|
||||
### bmpread_load_line()
|
||||
```
|
||||
BMPRESULT bmpread_load_line(BMPHANDLE h, unsigned char **bufferp)
|
||||
```
|
||||
Loads a single scan line from the BMP file into the buffer pointed to by `bufferp`.
|
||||
You can either allocate a buffer yourself or let `bufferp` point to a NULL-pointer in which case bmplib will allocate an appropriate buffer. In the latter case, you will have to call `free()` on the buffer, once you 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.
|
||||
```
|
||||
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;
|
||||
```
|
||||
|
||||
Repeated calls to `bmpread_load_line()` will return each scan line, one after the other.
|
||||
|
||||
Important: when reading the image this way, line by line, the orientation (`bmpread_topdown()`) of the original BMP matters! The lines will be returned in whichever order they are stored in the BMP. Use the value returned by `bmpread_topdown()` to determine if it is top-down or bottom-up. Almost all BMPs will be bottom-up. (see above)
|
||||
|
||||
### Indexed BMPs
|
||||
By default, bmplib will interpret indexed (color palette) BMPs and return the image as 24-bit RGB data.
|
||||
|
||||
If instead you want to keep the image as indexed, you have the option do so with these two functions:
|
||||
```
|
||||
int bmpread_num_palette_colors(BMPHANDLE h)
|
||||
BMPRESULT bmpread_load_palette(BMPHANDLE h, unsigned char **palette)
|
||||
```
|
||||
`bmpread_num_palette_colors()` will return 0 for non-indexed images, otherwise it will return the number of entries in the color palette.
|
||||
|
||||
`bmpread_load_palette()` will retrieve the color palette and store it in the character buffer pointed to by `palette`. The colors are stored 4 bytes per entry: The first three bytes are the red, green, and blue values in that order, the 4th byte is always set to zero. So the palette buffer will contain "rgb0rgb0rgb0...".
|
||||
As with the main image buffer, you can either provide one for the palette or let bmplib allocate it for you (and then `free()` it, once you are done):
|
||||
|
||||
```
|
||||
unsigned char *palette;
|
||||
int numcolors;
|
||||
|
||||
numcolors = bmpread_num_palette_colors(h);
|
||||
|
||||
/* either: */
|
||||
palette = NULL;
|
||||
bmpread_load_palette(h, &palette); /* bmplib will allocate the palette buffer */
|
||||
|
||||
/* or: */
|
||||
palette = malloc(4 * numcolors);
|
||||
bmpread_load_palette(h, &palette); /* bmplib will use the provided buffer */
|
||||
|
||||
```
|
||||
|
||||
Note: Once you have called `bmpread_load_palette()`, both `bmpread_load_image()` and `bmpread_load_line()` will return the image as 8-bit indexes into the palette, you *cannot go back* to the default of loading the image as 24-bit RGB data. After you loaded the palette, calls to `bmpread_dimensions()`, `bmpread_buffersize()`, etc. will reflect that change.
|
||||
Also, `bmpread_set_undefined_to_alpha()` will have no effect.
|
||||
|
||||
|
||||
|
||||
### Huge files: bmpread_set_insanity_limit()
|
||||
bmplib will refuse to load images beyond a certain size (default 500MB) and instead return BMP_RESULT_INSANE. If you want to load the image anyway, call `bmpread_set_insanity_limit()` at any time before
|
||||
calling `bmpread_load_image()`. `limit` is the new allowed size in bytes. (not MB!)
|
||||
```
|
||||
void bmpread_set_insanity_limit(BMPHANDLE h, size_t limit)
|
||||
```
|
||||
|
||||
### Undefined and invalid pixels: bmpread_set_undefined_to_alpha()
|
||||
#### Undefined pixels
|
||||
RLE-encoded BMP files may have undefined pixels, either by using early end-of-line or end-of-file codes, or by using delta codes to skip part of the image. That is not an error, but a feature of RLE. bmplib default is to make such pixels transparent. RLE-encoded BMPs will therefore always be returned with an alpha channel by default, whether the file has such undefined pixels or not (because bmplib doesn't know beforehand if there will be any undefined pixels). You can change this behaviour by calling
|
||||
`bmpread_set_undefined_to_alpha()`, with the second argument `yes` set to 0. In that case, the returned image will have no alpha channel, and undefined pixels will be set to zero. This function has no effect on non-RLE BMPs.
|
||||
```
|
||||
void bmpread_set_undefined_to_alpha(BMPHANDLE h, int yes)
|
||||
```
|
||||
|
||||
Note: this setting will have no effect if you use `bmpread_load_palette()` to switch to loading the indexed data! (see above)
|
||||
|
||||
#### Invalid pixels
|
||||
Invalid pixels may occur in indexed BMPs, both RLE and non-RLE. Invalid pixels either point beyond the given color palette, or they try to set pixels outside the image dimensions. Pixels containing an invalid color enty will be set to zero, and attempts to point outside the image will be ignored.
|
||||
In both cases, `bmpread_load_image()` and `bmpread_load_line()` will return BMP_RESULT_INVALID, unless the image is also truncated, then BMP_RESULT_TRUNCATED is returned.
|
||||
|
||||
### Query info about the BMP file
|
||||
Note: these functions return information about the original BMP file being read. They do *not* describe the format of the returned image data, which may be different!
|
||||
```
|
||||
int 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)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
## 2. Functions for writing BMP files
|
||||
|
||||
### bmpwrite_new()
|
||||
```
|
||||
BMPHANDLE bmpwrite_new(FILE *file)
|
||||
```
|
||||
|
||||
### Setting image dimensions
|
||||
```
|
||||
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)
|
||||
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
|
||||
### Set the output format
|
||||
Optional: set the bit-depth for each output channel. bmplib will otherwise choose appropriate bit-depths for your image.
|
||||
The bit-depth per channel can be anywhere between 0 and 32, inclusive. In sum, the bits must be at least 1 and must not exceed 32.
|
||||
|
||||
```
|
||||
BMPRESULT bmpwrite_set_output_bits(BMPHANDLE h, int red, int green, int blue, int alpha)
|
||||
```
|
||||
|
||||
### Indexed images
|
||||
|
||||
```
|
||||
BMPRESULT bmpwrite_set_palette(BMPHANDLE h, int numcolors, unsigned char *palette)
|
||||
BMPRESULT bmpwrite_allow_2bit(BMPHANDLE h)
|
||||
```
|
||||
You can write 1/2/4/8-bit indexed images by providing a color palette with `bmpwrite_set_palette()`. The palette entries must be 4 bytes each, the first three bytes are the red, green, and blue values in that order, the 4th byte is padding and will be ignored.
|
||||
|
||||
If you provide a palette, the image data you provide has to be 1 channel, 8 bits per pixel, and bmplib will write an indexed BMP.
|
||||
bmplib will choose the appropriate bit-depth for the BMP according to the number of color-entries in your palette.
|
||||
By default, bmplib will not write 2-bit indexed BMPs (supposedly a Windows CE relict), as many readers will refuse to open these. If you do want a 2-bit BMP for 3- or 4-color images, call `bmpwrite_allow_2bit()` before calling `bmpwrite_save_image()`.
|
||||
|
||||
RLE compression is not supported yet.
|
||||
|
||||
|
||||
|
||||
|
||||
### Write the image
|
||||
```
|
||||
BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image)
|
||||
BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line)
|
||||
```
|
||||
Write either the whole image at once with `bmpwrite_save_image()` or one line at a time with `bmpwrite_save_line()`.
|
||||
The image data pointed to by `image` or `line` 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). Indexed data must be supplied as 8 bit per pixel, even when writing lower bit (1/2/4) BMPs (see above).
|
||||
Important: When writing the whole image at once using `bmpwrite_save_image()`, the image data must be provided top-down (same as is returned by `bmpread_load_image()`). When using `bmpwrite_save_line()` to write the image line-by-line, the image data must be provided bottom-up (i.e. starting with the bottom-most line).
|
||||
In both cases, the actual BMP will be written bottom-up, which is the only 'officially' correct orientation.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 3. Functions for both reading/writing BMPs
|
||||
|
||||
### bmp_free()
|
||||
```
|
||||
void bmp_free(BMPHANDLE h)
|
||||
```
|
||||
Frees all resources associated with the handle `h`. Image data is not affected, so you can call bmp_free() immediately after bmpread_load_image() and still use the returned image data.
|
||||
Note: Any error messages returned by `bmp_errmsg()` are invalidated by `bmp_free()` and cannot be used anymore.
|
||||
|
||||
|
||||
### bmp_errmsg()
|
||||
```
|
||||
const char* bmp_errmsg(BMPHANDLE h)
|
||||
```
|
||||
Returns a zero-terminated character string containing the last error description(s). The returned string is safe to use until any other bmplib-function is called.
|
||||
|
||||
|
||||
### bmp_version()
|
||||
```
|
||||
const char* bmp_version(void)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
## 4. Data types and constants
|
||||
|
||||
`BMPHANDLE`
|
||||
|
||||
Returned by `bmpread_new()` and `bmpwrite_new()`.
|
||||
Identifies the current operation for all subsequent
|
||||
calls to bmplib-functions.
|
||||
|
||||
`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_ERROR`
|
||||
- `BMP_RESULT_TRUNCATED`
|
||||
- `BMP_RESULT_INVALID`
|
||||
- `BMP_RESULT_PNG`
|
||||
- `BMP_RESULT_JPEG`
|
||||
- `BMP_RESULT_INSANE`
|
||||
|
||||
`BMP_RESULT_OK` will always have the vaue 0, all other result codes should only be referred to by name, their values might change in the future.
|
||||
|
||||
|
||||
|
||||
|
||||
## 5. Sample code
|
||||
|
||||
### Reading BMPs
|
||||
|
||||
```
|
||||
/* (all error checking left out for clarity) */
|
||||
|
||||
BMPHANDLE h;
|
||||
FILE *file;
|
||||
int width, height, channels, bitsperchannel, topdown;
|
||||
uint8_t *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.
|
||||
*/
|
||||
|
||||
bmpread_load_info(h);
|
||||
|
||||
width = bmpread_width(h);
|
||||
height = bmpread_height(h);
|
||||
channels = bmpread_channels(h);
|
||||
bitsperchannel = bmpread_bits_per_channel(h);
|
||||
|
||||
|
||||
/* get required size for memory buffer and allocate memory */
|
||||
|
||||
image_buffer = malloc(bmpread_buffersize(h));
|
||||
|
||||
|
||||
/* 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 */
|
||||
|
||||
/* 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;
|
||||
uint8_t *image_buffer;
|
||||
int width, height, channel, 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, bits_per_channel);
|
||||
|
||||
|
||||
|
||||
/* Optional: choose bit-depths (independantly 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);
|
||||
```
|
||||
|
||||
128
README.md
128
README.md
@@ -6,12 +6,20 @@
|
||||
- Write any sensible BMP
|
||||
- Robustness! Don't let malformed BMPs bother us
|
||||
|
||||
## Current status (v1.4.0):
|
||||
Download [bmplib on github](https://github.com/rupertwh/bmplib).
|
||||
|
||||
## Current status (v1.8.0):
|
||||
### Reading BMP files:
|
||||
- 16/24/32 bit RGB(A) with any bits/channel combination (BI_RGB, BI_BITFIELDS, BI_ALPHABITFIELDS).
|
||||
- 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.
|
||||
- Huffman encoded (OS/2).
|
||||
- OS/2 bitmap arrays.
|
||||
- OS/2 icons and pointers.
|
||||
- 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/):
|
||||
@@ -19,30 +27,33 @@
|
||||
- most 'questionable' files (see below)
|
||||
- some 'bad' files
|
||||
|
||||
Questionable files that failed:
|
||||
Questionable files that fail:
|
||||
- embedded JPEG and PNG. Not really a fail. We return BMP_RESULT_JPEG or
|
||||
BMP_RESULT_PNG and leave the file pointer in the correct state
|
||||
to be passed on to either libpng or libjpeg. Works as designed. Don't
|
||||
want to create dependency on those libs.
|
||||
- Huffman-encoded OS/2 BMPs: see TODO.
|
||||
- 64bit: No plans to implement until officially established.
|
||||
- We currently ignore icc-profiles and chromaticity/gamma
|
||||
values. See TODO.
|
||||
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.
|
||||
- We currently ignore chromaticity/gamma values from V4+ headers. See TODO.
|
||||
|
||||
|
||||
### Writing BMP files:
|
||||
- RGB(A) 16/24/32 bit.
|
||||
- 64-bit RGBA
|
||||
- any bit-depth combination for the RGBA channels.
|
||||
- Indexed 1/2/4/8 bit, no RLE-compression.
|
||||
- write BI_RGB when possible, BI_(ALPHA)BITFIELDS only when
|
||||
- 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
|
||||
|
||||
### Download and compile bmplib library
|
||||
To install the latest development version of the library under the default `/usr/local` prefix:
|
||||
|
||||
To install the latest development version of the library under the default
|
||||
`/usr/local` prefix on debian-like Linux:
|
||||
|
||||
```
|
||||
sudo apt install build-essential git meson pkg-config
|
||||
git clone https://github.com/rupertwh/bmplib.git
|
||||
@@ -63,68 +74,89 @@ ninja install
|
||||
|
||||
|
||||
### Use bmplib in your program
|
||||
|
||||
A minimalistic `meson.build` for a program that uses bmplib:
|
||||
|
||||
```
|
||||
project('mytest', 'c')
|
||||
bmpdep = dependency('libbmp')
|
||||
executable('mytest', 'main.c', dependencies: [bmpdep])
|
||||
```
|
||||
|
||||
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 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 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
|
||||
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 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.
|
||||
|
||||
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.
|
||||
- [ ] write RLE-compressed images (RLE4/RLE8 only. No OS/2 v2 BMPs).
|
||||
- [x] read RLE24-encoded BMPs.
|
||||
- [ ] read Huffman-encoded BMPs.
|
||||
- [x] line-by-line reading/writing. ~~Right now, the image can only be
|
||||
passed as a whole to/from bmplib.~~
|
||||
- [ ] read/write icc-profile and chromaticity/gamma values
|
||||
- [x] sanity checks for size of of image / palette. Require confirmation
|
||||
above a certain size (~ 500MB?)
|
||||
- [x] store undefined pixels (RLE delta and early EOL/EOF) as alpha
|
||||
|
||||
- [ ] read/write chromaticity/gamma values
|
||||
|
||||
|
||||
### Maybe:
|
||||
- [x] passing indexed data and palette to user (optionally) instead of RGB-data.
|
||||
|
||||
- [ ] interpret icc-profile, to enable giving at least sRGB/not-sRGB info.
|
||||
(Like sRGB / probably-sRGB / maybe-sRGB). Torn on that one, would
|
||||
need dependency on liblcms2.
|
||||
- [ ] "BA"-files (bitmap-arrays). Either return the first bitmap only
|
||||
(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").
|
||||
- [ ] 64-bits BMPs.
|
||||
(Like sRGB / probably-sRGB / maybe-sRGB). Torn on that one, would need
|
||||
dependency on liblcms2.
|
||||
|
||||
### Unclear:
|
||||
|
||||
- [ ] platforms: I am writing bmplib on Linux/intel (Ubuntu) using meson.
|
||||
Suggestions welcome on what is necessary to build on other
|
||||
platforms/cpus. And Windows?
|
||||
|
||||
|
||||
### Non-feature (internal):
|
||||
- [x] complete API description (see API.md)
|
||||
- [x] bmp-read.c is getting too big, split into several files
|
||||
|
||||
|
||||
|
||||
|
||||
## Misc:
|
||||
- [x] License: probably LPGL3? That's what I'm going with for now.
|
||||
|
||||
|
||||
|
||||
Cheers,
|
||||
|
||||
Rupert
|
||||
|
||||
bmplibinfo@gmail.com
|
||||
|
||||
(be sure to include 'BMP' anywhere in the subject line, otherwise mail will
|
||||
go straight into spam folder)
|
||||
(be sure to include 'BMP' anywhere in the subject line, otherwise mail will go
|
||||
straight into spam folder)
|
||||
|
||||
320
bmp-common.c
320
bmp-common.c
@@ -1,20 +1,20 @@
|
||||
/* bmplib - bmp-common.c
|
||||
*
|
||||
* Copyright (c) 2024, Rupert Weber.
|
||||
* Copyright (c) 2024, 2025, 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,22 +23,19 @@
|
||||
#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"
|
||||
|
||||
|
||||
struct Bmphandle {
|
||||
struct {
|
||||
uint32_t magic;
|
||||
LOG log;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
@@ -58,10 +55,57 @@ API const char* bmp_version(void)
|
||||
|
||||
API const char* bmp_errmsg(BMPHANDLE h)
|
||||
{
|
||||
if (!(h && (h->magic == HMAGIC_READ || h->magic == HMAGIC_WRITE)))
|
||||
if (!(h && (h->common.magic == HMAGIC_READ || h->common.magic == HMAGIC_WRITE)))
|
||||
return "BMPHANDLE is NULL or invalid";
|
||||
|
||||
return logmsg(h->log);
|
||||
|
||||
return logmsg(h->common.log);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* bmp_set_number_format
|
||||
*******************************************************/
|
||||
|
||||
API BMPRESULT bmp_set_number_format(BMPHANDLE h, enum BmpFormat format)
|
||||
{
|
||||
if (!h)
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
switch (h->common.magic) {
|
||||
case HMAGIC_READ:
|
||||
return br_set_number_format(&h->read, format);
|
||||
|
||||
case HMAGIC_WRITE:
|
||||
return bw_set_number_format(&h->write, format);
|
||||
|
||||
default:
|
||||
#ifdef DEBUG
|
||||
printf("bmp_set_number_format() called with invalid handle (0x%04x)\n",
|
||||
(unsigned int) h->common.magic);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* bmp_set_huffman_t4black_value
|
||||
*******************************************************/
|
||||
|
||||
API BMPRESULT bmp_set_huffman_t4black_value(BMPHANDLE h, int blackidx)
|
||||
{
|
||||
if (!h)
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
if (!(h->common.magic == HMAGIC_READ || h->common.magic == HMAGIC_WRITE))
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
h->common.huffman_black_is_zero = !blackidx;
|
||||
|
||||
return BMP_RESULT_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,19 +119,19 @@ API void bmp_free(BMPHANDLE h)
|
||||
if (!h)
|
||||
return;
|
||||
|
||||
switch (h->magic) {
|
||||
switch (h->common.magic) {
|
||||
case HMAGIC_READ:
|
||||
br_free((BMPREAD)(void*)h);
|
||||
br_free(&h->read);
|
||||
break;
|
||||
case HMAGIC_WRITE:
|
||||
bw_free((BMPWRITE)(void*)h);
|
||||
bw_free(&h->write);
|
||||
break;
|
||||
|
||||
default:
|
||||
#ifdef DEBUG
|
||||
printf("bmp_free() called with invalid handle (0x%04x)\n",
|
||||
(unsigned int) h->magic);
|
||||
#endif
|
||||
(unsigned int) h->common.magic);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -95,38 +139,52 @@ API void bmp_free(BMPHANDLE h)
|
||||
|
||||
|
||||
/********************************************************
|
||||
* cm_check_is_read_handle
|
||||
* cm_read_handle
|
||||
*******************************************************/
|
||||
|
||||
int cm_check_is_read_handle(BMPHANDLE h)
|
||||
BMPREAD cm_read_handle(BMPHANDLE h)
|
||||
{
|
||||
BMPREAD rp = (BMPREAD)(void*)h;
|
||||
if (h && h->common.magic == HMAGIC_READ)
|
||||
return &h->read;
|
||||
|
||||
if (rp && rp->magic == HMAGIC_READ)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/********************************************************
|
||||
* cm_write_handle
|
||||
*******************************************************/
|
||||
|
||||
BMPWRITE cm_write_handle(BMPHANDLE h)
|
||||
{
|
||||
if (h && h->common.magic == HMAGIC_WRITE)
|
||||
return &h->write;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/********************************************************
|
||||
* cm_gobble_up
|
||||
*******************************************************/
|
||||
|
||||
int cm_gobble_up(FILE *file, int count, LOG log)
|
||||
bool cm_gobble_up(BMPREAD_R rp, int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (EOF == getc(file)) {
|
||||
if (feof(file))
|
||||
logerr(log, "unexpected end of file");
|
||||
else
|
||||
logsyserr(log, "error reading from file");
|
||||
return FALSE;
|
||||
if (EOF == getc(rp->file)) {
|
||||
if (feof(rp->file)) {
|
||||
rp->lasterr = BMP_ERR_TRUNCATED;
|
||||
logerr(rp->c.log, "unexpected end of file");
|
||||
} else {
|
||||
rp->lasterr = BMP_ERR_FILEIO;
|
||||
logsyserr(rp->c.log, "error reading from file");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -142,9 +200,6 @@ int cm_count_bits(unsigned long v)
|
||||
{
|
||||
int bits = 0;
|
||||
|
||||
if (v < 0)
|
||||
v = -v;
|
||||
|
||||
while (v) {
|
||||
bits++;
|
||||
v >>= 1;
|
||||
@@ -154,18 +209,41 @@ int cm_count_bits(unsigned long v)
|
||||
|
||||
|
||||
|
||||
int cm_all_lessoreq_int(int limit, int n, ...)
|
||||
const char* cm_conv64_name(enum Bmpconv64 conv)
|
||||
{
|
||||
switch (conv) {
|
||||
case BMP_CONV64_SRGB : return "BMP_CONV64_SRGB";
|
||||
case BMP_CONV64_LINEAR: return "BMP_CONV64_LINEAR";
|
||||
case BMP_CONV64_NONE : return "BMP_CONV64_NONE";}
|
||||
return "(invalid)";
|
||||
}
|
||||
|
||||
|
||||
const char* cm_format_name(enum BmpFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case BMP_FORMAT_INT : return "BMP_FORMAT_INT";
|
||||
case BMP_FORMAT_FLOAT: return "BMP_FORMAT_FLOAT";
|
||||
case BMP_FORMAT_S2_13: return "BMP_FORMAT_S2_13";
|
||||
}
|
||||
return "(invalid)";
|
||||
}
|
||||
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -175,19 +253,20 @@ int cm_all_lessoreq_int(int limit, int n, ...)
|
||||
}
|
||||
|
||||
|
||||
int cm_all_equal_int(int n, ...)
|
||||
bool cm_all_equal_int(int n, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int first, i, ret = TRUE;
|
||||
int first, i;
|
||||
bool ret = true;
|
||||
|
||||
if (n < 2)
|
||||
return TRUE;
|
||||
return true;
|
||||
|
||||
va_start(ap, n);
|
||||
first = va_arg(ap, int);
|
||||
for (i = 1; i < n; i++) {
|
||||
if (va_arg(ap, int) != first) {
|
||||
ret = FALSE;
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -197,18 +276,19 @@ int cm_all_equal_int(int n, ...)
|
||||
}
|
||||
|
||||
|
||||
int cm_all_positive_int(int n, ...)
|
||||
bool cm_all_positive_int(int n, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int i, ret = TRUE;
|
||||
int i;
|
||||
bool ret = true;
|
||||
|
||||
if (n < 1)
|
||||
return TRUE;
|
||||
return true;
|
||||
|
||||
va_start(ap, n);
|
||||
for (i = 0; i < n; i++) {
|
||||
if (va_arg(ap, int) < 0) {
|
||||
ret = FALSE;
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -218,17 +298,32 @@ int cm_all_positive_int(int n, ...)
|
||||
}
|
||||
|
||||
|
||||
|
||||
int cm_align4padding(unsigned long a)
|
||||
bool cm_is_one_of(int n, int candidate, ...)
|
||||
{
|
||||
return cm_align4size(a) - a;
|
||||
va_list ap;
|
||||
int i;
|
||||
bool ret = false;
|
||||
|
||||
if (n < 1)
|
||||
return true;
|
||||
|
||||
va_start(ap, candidate);
|
||||
for (i = 0; i < n; i++) {
|
||||
if (va_arg(ap, int) == candidate) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cm_align2padding(unsigned long a)
|
||||
{
|
||||
return cm_align2size(a) - a;
|
||||
}
|
||||
|
||||
int cm_align4padding(unsigned long long a)
|
||||
{
|
||||
return (int) (cm_align4size(a) - a);
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************
|
||||
@@ -236,91 +331,136 @@ 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];
|
||||
return write_u32_le(file, (uint32_t)val);
|
||||
}
|
||||
|
||||
if (2 != fread(buf, 1, 2, file))
|
||||
return 0;
|
||||
bool read_s16_le(FILE *file, int16_t *val)
|
||||
{
|
||||
uint16_t u16;
|
||||
|
||||
*val = (((int16_t)(signed char)buf[1]) << 8) | (int16_t) buf[0];
|
||||
if (!read_u16_le(file, &u16))
|
||||
return false;
|
||||
|
||||
return 1;
|
||||
if (u16 >= 0x8000U)
|
||||
*val = (int16_t)(u16 - 0x8000U) - 32767 - 1;
|
||||
else
|
||||
*val = (int16_t)u16;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool read_s32_le(FILE *file, int32_t *val)
|
||||
{
|
||||
uint32_t u32;
|
||||
|
||||
if (!read_u32_le(file, &u32))
|
||||
return false;
|
||||
|
||||
if (u32 >= 0x80000000UL)
|
||||
*val = (int32_t)(u32 - 0x80000000UL) - 0x7fffffffL - 1;
|
||||
else
|
||||
*val = (int32_t)u32;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int read_s32_le(FILE *file, int32_t *val)
|
||||
|
||||
uint32_t u32_from_le(const unsigned char *buf)
|
||||
{
|
||||
unsigned char buf[4];
|
||||
return (uint32_t)buf[3] << 24 | (uint32_t)buf[2] << 16 |
|
||||
(uint32_t)buf[1] << 8 | (uint32_t)buf[0];
|
||||
}
|
||||
|
||||
if (4 != fread(buf, 1, 4, file))
|
||||
return 0;
|
||||
int32_t s32_from_le(const unsigned char *buf)
|
||||
{
|
||||
return (int32_t)u32_from_le(buf);
|
||||
}
|
||||
|
||||
*val = (((int32_t)(signed char)buf[3]) << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
|
||||
uint16_t u16_from_le(const unsigned char *buf)
|
||||
{
|
||||
return (uint16_t)buf[1] << 8 | (uint16_t)buf[0];
|
||||
}
|
||||
|
||||
return 1;
|
||||
int16_t s16_from_le(const unsigned char *buf)
|
||||
{
|
||||
return (int16_t)u16_from_le(buf);
|
||||
}
|
||||
|
||||
|
||||
int write_s32_le(FILE *file, int32_t val)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (EOF == fputc((val >> (i*8)) & 0xff, file))
|
||||
return 0;
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* cm_infoheader_name
|
||||
*****************************************************************************/
|
||||
|
||||
const char* cm_infoheader_name(enum BmpInfoVer infoversion)
|
||||
{
|
||||
switch (infoversion) {
|
||||
case BMPINFO_CORE_OS21 : return "OS21XBITMAPHEADER";
|
||||
case BMPINFO_OS22 : return "OS22XBITMAPHEADER";
|
||||
case BMPINFO_V3 : return "BITMAPINFOHEADER";
|
||||
case BMPINFO_V3_ADOBE1 : return "BITMAPINFOHEADER + RGB mask";
|
||||
case BMPINFO_V3_ADOBE2 : return "BITMAPINFOHEADER + RGBA mask";
|
||||
case BMPINFO_V4 : return "BITMAPV4HEADER";
|
||||
case BMPINFO_V5 : return "BITMAPV5HEADER";
|
||||
case BMPINFO_FUTURE : return "unknown future version";
|
||||
default:
|
||||
return "invalid infoheader version";
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
419
bmp-common.h
419
bmp-common.h
@@ -1,35 +1,37 @@
|
||||
/* bmplib - bmp-common.h
|
||||
*
|
||||
* Copyright (c) 2024, Rupert Weber.
|
||||
* Copyright (c) 2024, 2025, 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))
|
||||
#define MIN(a,b) ((a)<(b)?(a):(b))
|
||||
#define ARR_SIZE(a) ((int) (sizeof a / sizeof (a)[0]))
|
||||
|
||||
#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];
|
||||
@@ -43,215 +45,313 @@ union Pixel {
|
||||
|
||||
struct Colormask {
|
||||
union {
|
||||
unsigned long value[4];
|
||||
unsigned long long value[4];
|
||||
struct {
|
||||
unsigned long red;
|
||||
unsigned long green;
|
||||
unsigned long blue;
|
||||
unsigned long alpha;
|
||||
unsigned long long red;
|
||||
unsigned long long green;
|
||||
unsigned long long blue;
|
||||
unsigned long long alpha;
|
||||
};
|
||||
} 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 {
|
||||
double val[4];
|
||||
struct {
|
||||
double red;
|
||||
double green;
|
||||
double blue;
|
||||
double alpha;
|
||||
};
|
||||
} maxval;
|
||||
};
|
||||
|
||||
typedef struct Bmpread * restrict BMPREAD_R;
|
||||
|
||||
struct Palette {
|
||||
int numcolors;
|
||||
union Pixel color[1];
|
||||
};
|
||||
|
||||
|
||||
struct Bmpcommon {
|
||||
uint32_t magic;
|
||||
LOG log;
|
||||
bool huffman_black_is_zero; /* defaults to false */
|
||||
};
|
||||
|
||||
enum ReadState {
|
||||
RS_INIT,
|
||||
RS_EXPECT_ICON_MASK,
|
||||
RS_HEADER_OK,
|
||||
RS_DIMENSIONS_QUERIED,
|
||||
RS_LOAD_STARTED,
|
||||
RS_LOAD_DONE,
|
||||
RS_ARRAY,
|
||||
RS_FATAL,
|
||||
};
|
||||
|
||||
struct Bmpread {
|
||||
struct {
|
||||
uint32_t magic;
|
||||
LOG log;
|
||||
};
|
||||
struct Bmpcommon c;
|
||||
FILE *file;
|
||||
unsigned long bytes_read; /* number of bytes we have read from the file */
|
||||
size_t bytes_read; /* number of bytes we have read from the file */
|
||||
struct Bmpfile *fh;
|
||||
struct Bmpinfo *ih;
|
||||
struct Arraylist *arrayimgs;
|
||||
int narrayimgs;
|
||||
bool is_arrayimg;
|
||||
unsigned int insanity_limit;
|
||||
int width;
|
||||
int height;
|
||||
int topdown;
|
||||
int has_alpha; /* original BMP has alpha channel */
|
||||
int undefined_to_alpha;
|
||||
int wipe_buffer;
|
||||
int we_allocated_buffer;
|
||||
int line_by_line;
|
||||
enum BmpOrient orientation;
|
||||
bool is_icon;
|
||||
bool icon_is_mono;
|
||||
bool has_alpha; /* original BMP has alpha channel */
|
||||
enum BmpUndefined undefined_mode;
|
||||
bool we_allocated_buffer;
|
||||
struct Palette *palette;
|
||||
struct Colormask colormask;
|
||||
struct Colormask cmask;
|
||||
unsigned char *icon_mono_and;
|
||||
unsigned char *icon_mono_xor;
|
||||
int icon_mono_width;
|
||||
int icon_mono_height;
|
||||
/* result image dimensions */
|
||||
enum Bmpconv64 conv64;
|
||||
int result_channels;
|
||||
int result_indexed;
|
||||
int result_bits_per_pixel;
|
||||
int result_bytes_per_pixel;
|
||||
int result_bits_per_channel;
|
||||
int result_bitsperchannel;
|
||||
enum BmpFormat result_format;
|
||||
size_t result_size;
|
||||
bool conv64_explicit;
|
||||
bool result_indexed;
|
||||
bool result_format_explicit;
|
||||
/* state */
|
||||
int getinfo_called;
|
||||
unsigned long lasterr;
|
||||
enum ReadState read_state;
|
||||
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 dim_queried_width;
|
||||
bool dim_queried_height;
|
||||
bool dim_queried_channels;
|
||||
bool dim_queried_bitsperchannel;
|
||||
bool iccprofile_size_queried;
|
||||
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_pixels;
|
||||
int invalid_delta;
|
||||
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;
|
||||
};
|
||||
|
||||
int cm_all_lessoreq_int(int limit, int n, ...);
|
||||
int cm_all_equal_int(int n, ...);
|
||||
int cm_all_positive_int(int n, ...);
|
||||
#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_count_bits(unsigned long v);
|
||||
enum WriteState {
|
||||
WS_INIT,
|
||||
WS_DIMENSIONS_SET,
|
||||
WS_SAVE_STARTED,
|
||||
WS_SAVE_DONE,
|
||||
WS_FATAL,
|
||||
};
|
||||
|
||||
int cm_gobble_up(FILE *file, int count, LOG log);
|
||||
int cm_check_is_read_handle(BMPHANDLE h);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
#define API __attribute__ ((visibility ("default")))
|
||||
struct Bmpwrite {
|
||||
struct Bmpcommon c;
|
||||
FILE *file;
|
||||
struct Bmpfile *fh;
|
||||
struct Bmpinfo *ih;
|
||||
int width;
|
||||
int height;
|
||||
/* input */
|
||||
int source_channels;
|
||||
int source_bitsperchannel;
|
||||
int source_bytes_per_pixel;
|
||||
enum BmpFormat source_format;
|
||||
bool source_has_alpha;
|
||||
struct Palette *palette;
|
||||
int palette_size; /* sizeof palette in bytes */
|
||||
unsigned char *iccprofile;
|
||||
int iccprofile_size;
|
||||
/* output */
|
||||
uint64_t bytes_written;
|
||||
size_t bytes_written_before_bitdata;
|
||||
enum BmpOrient outorientation;
|
||||
bool huffman_fg_idx;
|
||||
struct Colormask cmask;
|
||||
enum BmpRLEtype rle_requested;
|
||||
int rle; /* 1, 4, 8, or 24 */
|
||||
bool allow_2bit; /* Windows CE */
|
||||
bool allow_huffman; /* OS/2 */
|
||||
bool allow_rle24; /* OS/2 */
|
||||
bool out64bit;
|
||||
int outbytes_per_pixel;
|
||||
int padding;
|
||||
int *group;
|
||||
int group_count;
|
||||
/* state */
|
||||
enum WriteState write_state;
|
||||
bool outbits_set;
|
||||
int lbl_y;
|
||||
uint32_t hufbuf;
|
||||
int hufbuf_len;
|
||||
};
|
||||
|
||||
|
||||
#define HMAGIC_READ (0x44414552UL)
|
||||
#define HMAGIC_WRITE (0x54495257UL)
|
||||
union Bmphandle {
|
||||
struct Bmpcommon common;
|
||||
struct Bmpread read;
|
||||
struct Bmpwrite write;
|
||||
};
|
||||
|
||||
|
||||
typedef struct Bmpread *BMPREAD;
|
||||
typedef struct Bmpwrite *BMPWRITE;
|
||||
typedef struct Bmpread *restrict BMPREAD_R;
|
||||
typedef struct Bmpwrite *restrict BMPWRITE_R;
|
||||
|
||||
|
||||
bool cm_all_lessoreq_int(int limit, int n, ...);
|
||||
bool cm_all_equal_int(int n, ...);
|
||||
bool cm_all_positive_int(int n, ...);
|
||||
bool cm_is_one_of(int n, int candidate, ...);
|
||||
|
||||
#define cm_align4size(a) (((a) + 3) & ~3ULL)
|
||||
int cm_align4padding(unsigned long long a);
|
||||
int cm_count_bits(unsigned long v);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
uint32_t u32_from_le(const unsigned char *buf);
|
||||
int32_t s32_from_le(const unsigned char *buf);
|
||||
uint16_t u16_from_le(const unsigned char *buf);
|
||||
int16_t s16_from_le(const unsigned char *buf);
|
||||
|
||||
const char* cm_infoheader_name(enum BmpInfoVer infoversion);
|
||||
|
||||
|
||||
#define HMAGIC_READ 0x44414552UL
|
||||
#define HMAGIC_WRITE 0x54495257UL
|
||||
|
||||
/* There seems to be ambiguity about whether the 40-byte
|
||||
* BITMAPINFOHEADER is version 1 or version 3. Both make
|
||||
* sense, depending on whether you consider the
|
||||
* BITMAPCORE/OS2 versions v1 and v2, or if you consider
|
||||
* the Adobe-extensions (supposedly at one time
|
||||
* MS-'official') v2 and v3.
|
||||
* I am going with BITMAPINFOHEADER = v3.
|
||||
*/
|
||||
#define BMPFILE_BM 0x4d42
|
||||
#define BMPFILE_BA 0x4142
|
||||
#define BMPFILE_CI 0x4943
|
||||
#define BMPFILE_CP 0x5043
|
||||
#define BMPFILE_IC 0x4349
|
||||
#define BMPFILE_PT 0x5450
|
||||
|
||||
|
||||
#define BMPFILE_BM (0x4d42)
|
||||
#define BMPFILE_BA (0x4142)
|
||||
#define BMPFILE_CI (0x4943)
|
||||
#define BMPFILE_CP (0x5043)
|
||||
#define BMPFILE_IC (0x4349)
|
||||
#define BMPFILE_PT (0x5450)
|
||||
|
||||
|
||||
#define BMPFHSIZE (14)
|
||||
#define BMPIHSIZE_V3 (40)
|
||||
#define BMPIHSIZE_V4 (108)
|
||||
|
||||
typedef uint16_t WORD;
|
||||
typedef uint32_t DWORD;
|
||||
typedef int32_t LONG;
|
||||
typedef uint8_t BYTE;
|
||||
#define BMPFHSIZE 14
|
||||
#define BMPIHSIZE_V3 40
|
||||
#define BMPIHSIZE_V4 108
|
||||
#define BMPIHSIZE_OS22 64
|
||||
#define BMPIHSIZE_V5 124
|
||||
|
||||
struct Bmpfile {
|
||||
WORD type; /* "BM" */
|
||||
DWORD size; /* bytes in file */
|
||||
WORD reserved1;
|
||||
WORD reserved2;
|
||||
DWORD offbits;
|
||||
uint16_t type; /* "BM" */
|
||||
uint32_t size; /* bytes in file */
|
||||
uint16_t reserved1;
|
||||
uint16_t reserved2;
|
||||
uint32_t offbits;
|
||||
};
|
||||
|
||||
struct Bmpinfo {
|
||||
/* BITMAPINFOHEADER (40 bytes) */
|
||||
DWORD size; /* sizof struct */
|
||||
LONG width;
|
||||
LONG height;
|
||||
WORD planes;
|
||||
WORD bitcount;
|
||||
DWORD compression;
|
||||
DWORD sizeimage; /* 0 ok for uncompressed */
|
||||
LONG xpelspermeter;
|
||||
LONG ypelspermeter;
|
||||
DWORD clrused;
|
||||
DWORD clrimportant;
|
||||
uint32_t size; /* sizof struct */
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
uint16_t planes;
|
||||
uint16_t bitcount;
|
||||
uint32_t compression;
|
||||
uint32_t sizeimage; /* 0 ok for uncompressed */
|
||||
int32_t xpelspermeter;
|
||||
int32_t ypelspermeter;
|
||||
uint32_t clrused;
|
||||
uint32_t clrimportant;
|
||||
/* BITMAPV4INFOHEADER (108 bytes) */
|
||||
DWORD redmask;
|
||||
DWORD greenmask;
|
||||
DWORD bluemask;
|
||||
DWORD alphamask;
|
||||
DWORD cstype;
|
||||
LONG redX;
|
||||
LONG redY;
|
||||
LONG redZ;
|
||||
LONG greenX;
|
||||
LONG greenY;
|
||||
LONG greenZ;
|
||||
LONG blueX;
|
||||
LONG blueY;
|
||||
LONG blueZ;
|
||||
DWORD gammared;
|
||||
DWORD gammagreen;
|
||||
DWORD gammablue;
|
||||
uint32_t redmask;
|
||||
uint32_t greenmask;
|
||||
uint32_t bluemask;
|
||||
uint32_t alphamask;
|
||||
uint32_t cstype;
|
||||
int32_t redX;
|
||||
int32_t redY;
|
||||
int32_t redZ;
|
||||
int32_t greenX;
|
||||
int32_t greenY;
|
||||
int32_t greenZ;
|
||||
int32_t blueX;
|
||||
int32_t blueY;
|
||||
int32_t blueZ;
|
||||
uint32_t gammared;
|
||||
uint32_t gammagreen;
|
||||
uint32_t gammablue;
|
||||
/* BITMAPV5INFOHEADER (124 bytes) */
|
||||
DWORD intent;
|
||||
DWORD profiledata;
|
||||
DWORD profilesize;
|
||||
DWORD reserved;
|
||||
uint32_t intent;
|
||||
uint32_t profiledata;
|
||||
uint32_t profilesize;
|
||||
uint32_t reserved;
|
||||
|
||||
/* OS22XBITMAPHEADER */
|
||||
WORD resolution; /* = 0 */
|
||||
WORD orientation; /* = 0 */
|
||||
WORD halftone_alg;
|
||||
DWORD halftone_parm1;
|
||||
DWORD halftone_parm2;
|
||||
DWORD color_encoding; /* = 0 (RGB) */
|
||||
DWORD app_id;
|
||||
uint16_t resolution; /* = 0 */
|
||||
uint16_t orientation; /* = 0 */
|
||||
uint16_t halftone_alg;
|
||||
uint32_t halftone_parm1;
|
||||
uint32_t halftone_parm2;
|
||||
uint32_t color_encoding; /* = 0 (RGB) */
|
||||
uint32_t app_id;
|
||||
|
||||
/* internal only, not from file: */
|
||||
enum BmpInfoVer version;
|
||||
/* internal only, not from file: */
|
||||
enum BmpInfoVer version;
|
||||
};
|
||||
|
||||
struct Bmparray {
|
||||
uint16_t type;
|
||||
uint32_t size;
|
||||
uint32_t offsetnext;
|
||||
uint16_t screenwidth;
|
||||
uint16_t screenheight;
|
||||
};
|
||||
|
||||
#define IH_PROFILEDATA_OFFSET (14L + 112L)
|
||||
|
||||
#define MAX_ICCPROFILE_SIZE (1UL << 20)
|
||||
|
||||
|
||||
#define BI_RGB 0
|
||||
@@ -271,3 +371,16 @@ struct Bmpinfo {
|
||||
/* we set our own unique values: */
|
||||
#define BI_OS2_HUFFMAN 1001
|
||||
#define BI_OS2_RLE24 1002
|
||||
|
||||
|
||||
#define LCS_CALIBRATED_RGB 0
|
||||
#define LCS_sRGB 0x73524742 /* 'sRGB' */
|
||||
#define LCS_WINDOWS_COLOR_SPACE 0x57696e20 /* 'Win ' */
|
||||
#define PROFILE_LINKED 0x4c494e4b /* 'LINK' */
|
||||
#define PROFILE_EMBEDDED 0x4d424544 /* 'MBED' */
|
||||
|
||||
|
||||
#define LCS_GM_BUSINESS 1
|
||||
#define LCS_GM_GRAPHICS 2
|
||||
#define LCS_GM_IMAGES 4
|
||||
#define LCS_GM_ABS_COLORIMETRIC 8
|
||||
|
||||
360
bmp-read-icons.c
Normal file
360
bmp-read-icons.c
Normal file
@@ -0,0 +1,360 @@
|
||||
/* bmplib - bmp-read-icons.c
|
||||
*
|
||||
* Copyright (c) 2025, 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>
|
||||
|
||||
#define BMPLIB_LIB
|
||||
|
||||
#include "config.h"
|
||||
#include "bmplib.h"
|
||||
#include "logging.h"
|
||||
#include "bmp-common.h"
|
||||
#include "bmp-read-icons.h"
|
||||
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* bmpread_array_num
|
||||
*******************************************************/
|
||||
|
||||
API int bmpread_array_num(BMPHANDLE h)
|
||||
{
|
||||
BMPREAD rp;
|
||||
|
||||
if (!(rp = cm_read_handle(h)))
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
if (rp->read_state != RS_ARRAY) {
|
||||
logerr(rp->c.log, "Not a bitmap array");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return rp->narrayimgs;
|
||||
}
|
||||
|
||||
|
||||
/********************************************************
|
||||
* bmpread_array_info
|
||||
*******************************************************/
|
||||
|
||||
API BMPRESULT bmpread_array_info(BMPHANDLE h, struct BmpArrayInfo *ai, int idx)
|
||||
{
|
||||
BMPREAD rp;
|
||||
|
||||
if (!(rp = cm_read_handle(h)))
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
if (rp->read_state != RS_ARRAY) {
|
||||
logerr(rp->c.log, "Not a bitmap array");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
if (idx < 0 || idx >= rp->narrayimgs) {
|
||||
logerr(rp->c.log, "Invalid array index %d. Max is %d", idx, rp->narrayimgs - 1);
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
if (!ai) {
|
||||
logerr(rp->c.log, "Invalid array info pointer (NULL)");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
struct Arraylist *img = &rp->arrayimgs[idx];
|
||||
BMPREAD imgrp = (BMPREAD)img->handle;
|
||||
|
||||
memset(ai, 0, sizeof *ai);
|
||||
|
||||
ai->type = imgrp->fh->type;
|
||||
ai->handle = img->handle;
|
||||
ai->width = imgrp->width;
|
||||
ai->height = imgrp->height;
|
||||
if (imgrp->ih->bitcount <= 8)
|
||||
ai->ncolors = 1 << imgrp->ih->bitcount;
|
||||
else
|
||||
ai->ncolors = 0;
|
||||
ai->screenwidth = img->ah.screenwidth;
|
||||
ai->screenheight = img->ah.screenheight;
|
||||
|
||||
return BMP_RESULT_OK;
|
||||
}
|
||||
|
||||
|
||||
/********************************************************
|
||||
* icon_read_array
|
||||
*******************************************************/
|
||||
static bool s_read_array_header(BMPREAD_R rp, struct Bmparray *ah);
|
||||
static void s_array_header_from_file_header(struct Bmparray *ah, struct Bmpfile *fh);
|
||||
|
||||
bool icon_read_array(BMPREAD_R rp)
|
||||
{
|
||||
struct Arraylist *imgs = NULL;
|
||||
struct Bmparray ah = { 0 };
|
||||
int n = 0;
|
||||
const int nmax = 16;
|
||||
bool invalid = false;
|
||||
|
||||
if (!(imgs = calloc(nmax, sizeof *imgs))) {
|
||||
logsyserr(rp->c.log, "Allocating bitmap array list");
|
||||
rp->lasterr = BMP_ERR_MEMORY;
|
||||
return false;
|
||||
}
|
||||
|
||||
s_array_header_from_file_header(&ah, rp->fh);
|
||||
|
||||
while (n < nmax) {
|
||||
if (ah.type != BMPFILE_BA) {
|
||||
logerr(rp->c.log, "Invalid BMP type (0x%04x), expected 'BA'", (unsigned) ah.type);
|
||||
invalid = true;
|
||||
rp->lasterr = BMP_ERR_HEADER;
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(&imgs[n].ah, &ah, sizeof ah);
|
||||
|
||||
imgs[n].handle = bmpread_new(rp->file);
|
||||
if (imgs[n].handle) {
|
||||
if (BMP_RESULT_OK == bmpread_load_info(imgs[n].handle)) {
|
||||
((BMPREAD)imgs[n].handle)->is_arrayimg = true;
|
||||
n++;
|
||||
} else {
|
||||
bmp_free(imgs[n].handle);
|
||||
invalid = true;
|
||||
rp->lasterr = BMP_ERR_HEADER;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
logerr(rp->c.log, "Failed to create handle for array image");
|
||||
invalid = true;
|
||||
rp->lasterr = BMP_ERR_MEMORY;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ah.offsetnext)
|
||||
break;
|
||||
|
||||
#if ( LONG_MAX <= 0x7fffffffL )
|
||||
if (ah.offsetnext > (unsigned long)LONG_MAX) {
|
||||
logerr(rp->c.log, "Invalid offset to next array image: %lu", (unsigned long)ah.offsetnext);
|
||||
invalid = true;
|
||||
rp->lasterr = BMP_ERR_HEADER;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
if (fseek(rp->file, ah.offsetnext, SEEK_SET)) {
|
||||
logsyserr(rp->c.log, "Seeking next array header");
|
||||
invalid = true;
|
||||
rp->lasterr = BMP_ERR_FILEIO;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!s_read_array_header(rp, &ah)) {
|
||||
invalid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rp->arrayimgs = imgs;
|
||||
rp->narrayimgs = n;
|
||||
|
||||
return !invalid;
|
||||
}
|
||||
|
||||
|
||||
/********************************************************
|
||||
* s_read_array_header
|
||||
*******************************************************/
|
||||
|
||||
static bool s_read_array_header(BMPREAD_R rp, struct Bmparray *ah)
|
||||
{
|
||||
if (read_u16_le(rp->file, &ah->type) &&
|
||||
read_u32_le(rp->file, &ah->size) &&
|
||||
read_u32_le(rp->file, &ah->offsetnext) &&
|
||||
read_u16_le(rp->file, &ah->screenwidth) &&
|
||||
read_u16_le(rp->file, &ah->screenheight)) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (feof(rp->file)) {
|
||||
logerr(rp->c.log, "unexpected end-of-file while reading "
|
||||
"array header");
|
||||
rp->lasterr = BMP_ERR_TRUNCATED;
|
||||
} else {
|
||||
logsyserr(rp->c.log, "error reading array header");
|
||||
rp->lasterr = BMP_ERR_FILEIO;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/********************************************************
|
||||
* s_array_header_from_file_header
|
||||
*******************************************************/
|
||||
|
||||
static void s_array_header_from_file_header(struct Bmparray *ah, struct Bmpfile *fh)
|
||||
{
|
||||
ah->type = fh->type;
|
||||
ah->size = fh->size;
|
||||
ah->offsetnext = (uint32_t)fh->reserved2 << 16 | fh->reserved1;
|
||||
ah->screenwidth = fh->offbits & 0xffff;
|
||||
ah->screenheight = (fh->offbits >> 16) & 0xffff;
|
||||
}
|
||||
|
||||
|
||||
/********************************************************
|
||||
* icon_load_masks
|
||||
*******************************************************/
|
||||
|
||||
long icon_load_masks(BMPREAD_R rp)
|
||||
{
|
||||
/* OS/2 icons and pointers contain 1-bit AND and XOR masks, stacked in a single
|
||||
* image. For monochrome (IC/PT), that's all the image; for color (CI/CP), these are
|
||||
* followed by a complete color image (including headers), the masks are only used
|
||||
* for transparency information.
|
||||
*/
|
||||
|
||||
BMPHANDLE hmono = NULL;
|
||||
BMPREAD rpmono;
|
||||
unsigned char *monobuf = NULL;
|
||||
size_t bufsize;
|
||||
unsigned bmptype = rp->fh->type;
|
||||
long posmono = 0, poscolor = 0;
|
||||
|
||||
if (fseek(rp->file, -14, SEEK_CUR)) {
|
||||
logsyserr(rp->c.log, "Seeking to start of icon/pointer");
|
||||
goto abort;
|
||||
}
|
||||
|
||||
if (-1 == (posmono = ftell(rp->file))) {
|
||||
logsyserr(rp->c.log, "Saving file position");
|
||||
goto abort;
|
||||
}
|
||||
|
||||
/* first, load monochrome XOR/AND bitmap. We'll use the
|
||||
* AND bitmap as alpha channel for the color bitmap
|
||||
*/
|
||||
|
||||
if (!(hmono = bmpread_new(rp->file))) {
|
||||
logerr(rp->c.log, "Getting handle for monochrome XOR/AND map");
|
||||
goto abort;
|
||||
}
|
||||
rpmono = cm_read_handle(hmono);
|
||||
|
||||
rpmono->read_state = RS_EXPECT_ICON_MASK;
|
||||
if (BMP_RESULT_OK != bmpread_load_info(hmono)) {
|
||||
logerr(rp->c.log, "%s", bmp_errmsg(hmono));
|
||||
goto abort;
|
||||
}
|
||||
|
||||
if (rpmono->fh->type != bmptype) {
|
||||
logerr(rp->c.log, "File type mismatch. Have 0x%04x, expected 0x%04x",
|
||||
(unsigned)rpmono->fh->type, bmptype);
|
||||
}
|
||||
|
||||
if (rp->fh->type == BMPFILE_CI || rp->fh->type == BMPFILE_CP) {
|
||||
if (-1 == (poscolor = ftell(rp->file))) {
|
||||
logsyserr(rp->c.log, "Saving position of color header");
|
||||
goto abort;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(rpmono->width > 0 && rpmono->height > 0 && rpmono->width <=512 && rpmono->height <= 512)) {
|
||||
logerr(rp->c.log, "Invalid icon/pointer dimensions: %dx%d", rpmono->width, rpmono->height);
|
||||
goto abort;
|
||||
}
|
||||
|
||||
if (rpmono->ih->bitcount != 1) {
|
||||
logerr(rp->c.log, "Invalid icon/pointer monochrome bitcount: %d", rpmono->ih->bitcount);
|
||||
goto abort;
|
||||
}
|
||||
|
||||
if (rpmono->height & 1) {
|
||||
logerr(rp->c.log, "Invalid odd icon/pointer height: %d (must be even)", rpmono->height);
|
||||
goto abort;
|
||||
}
|
||||
|
||||
int width, height, bitsperchannel, channels;
|
||||
if (BMP_RESULT_OK != bmpread_dimensions(hmono, &width, &height, &channels, &bitsperchannel, NULL)) {
|
||||
logerr(rp->c.log, "%s", bmp_errmsg(hmono));
|
||||
goto abort;
|
||||
}
|
||||
|
||||
height /= 2; /* mochrome contains two stacked bitmaps (AND and XOR) */
|
||||
|
||||
if (channels != 3 || bitsperchannel != 8) {
|
||||
logerr(rp->c.log, "Unexpected result color depth for monochrome image: "
|
||||
"%d channels, %d bits/channel", channels, bitsperchannel);
|
||||
goto abort;
|
||||
}
|
||||
|
||||
/* store the AND/XOR bitmaps in the main BMPREAD struct */
|
||||
|
||||
bufsize = bmpread_buffersize(hmono);
|
||||
if (BMP_RESULT_OK != bmpread_load_image(hmono, &monobuf)) {
|
||||
logerr(rp->c.log, "%s", bmp_errmsg(hmono));
|
||||
goto abort;
|
||||
}
|
||||
|
||||
if (!(bufsize > 0 && monobuf != NULL)) {
|
||||
logerr(rp->c.log, "Panic! unkown error while loading monochrome bitmap");
|
||||
goto abort;
|
||||
}
|
||||
|
||||
if (!(rp->icon_mono_and = malloc(width * height))) {
|
||||
logsyserr(rp->c.log, "Allocating mono AND bitmap");
|
||||
goto abort;
|
||||
}
|
||||
if (!(rp->icon_mono_xor = malloc(width * height))) {
|
||||
logsyserr(rp->c.log, "Allocating mono XOR bitmap");
|
||||
goto abort;
|
||||
}
|
||||
|
||||
for (int i = 0; i < width * height; i++)
|
||||
rp->icon_mono_and[i] = 255 - monobuf[3 * i];
|
||||
|
||||
for (int i = 0; i < width * height; i++)
|
||||
rp->icon_mono_xor[i] = monobuf[3 * (width * height + i)];
|
||||
|
||||
rp->icon_mono_width = width;
|
||||
rp->icon_mono_height = height;
|
||||
free(monobuf);
|
||||
monobuf = NULL;
|
||||
bmp_free(hmono);
|
||||
hmono = NULL;
|
||||
|
||||
if (rp->fh->type == BMPFILE_CI || rp->fh->type == BMPFILE_CP)
|
||||
return poscolor;
|
||||
|
||||
return posmono;
|
||||
|
||||
abort:
|
||||
if (hmono)
|
||||
bmp_free(hmono);
|
||||
if (monobuf)
|
||||
free(monobuf);
|
||||
return -1;
|
||||
}
|
||||
28
bmp-read-icons.h
Normal file
28
bmp-read-icons.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/* bmplib - bmp-read-icons.h
|
||||
*
|
||||
* Copyright (c) 2025, 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 Arraylist {
|
||||
struct Bmparray ah;
|
||||
BMPHANDLE handle;
|
||||
};
|
||||
|
||||
long icon_load_masks(BMPREAD_R rp);
|
||||
bool icon_read_array(BMPREAD_R rp);
|
||||
1038
bmp-read-loadimage.c
1038
bmp-read-loadimage.c
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
/* bmplib - bmp-read-loadindexed.c
|
||||
*
|
||||
* Copyright (c) 2024, Rupert Weber.
|
||||
* Copyright (c) 2024, 2025, Rupert Weber.
|
||||
*
|
||||
* This file is part of bmplib.
|
||||
* bmplib is free software: you can redistribute it and/or modify
|
||||
@@ -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,25 +74,38 @@ API BMPRESULT bmpread_load_palette(BMPHANDLE h, unsigned char **palette)
|
||||
int i,c;
|
||||
size_t memsize;
|
||||
|
||||
if (!(rp = cm_read_handle(h)))
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
if (!(h && cm_check_is_read_handle(h)))
|
||||
return 0;
|
||||
rp = (BMPREAD)(void*)h;
|
||||
if (rp->read_state < RS_HEADER_OK) {
|
||||
logerr(rp->c.log, "Must call bmpread_load_info() before loading palette");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
if (rp->read_state >= RS_LOAD_STARTED) {
|
||||
logerr(rp->c.log, "Cannot load palette after image data");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
if (!rp->palette) {
|
||||
logerr(rp->log, "Image has no palette");
|
||||
logerr(rp->c.log, "Image has no palette");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
if (!palette) {
|
||||
logerr(rp->log, "palette is NULL");
|
||||
logerr(rp->c.log, "palette is NULL");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
if (rp->result_format != BMP_FORMAT_INT) {
|
||||
logerr(rp->c.log, "Palette can only be loaded when number format is BMP_FORMAT_INT");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
memsize = rp->palette->numcolors * 4;
|
||||
if (!*palette) {
|
||||
if (!(*palette = malloc(memsize))) {
|
||||
logsyserr(rp->log, "allocating palette");
|
||||
logsyserr(rp->c.log, "allocating palette");
|
||||
rp->read_state = RS_FATAL;
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
}
|
||||
@@ -98,14 +113,14 @@ 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->result_indexed = true;
|
||||
rp->read_state = MIN(RS_HEADER_OK, rp->read_state);
|
||||
rp->dim_queried_channels = false;
|
||||
rp->result_channels = 1;
|
||||
rp->result_bits_per_pixel = 8;
|
||||
rp->result_bytes_per_pixel = 1;
|
||||
rp->result_bits_per_channel = 8;
|
||||
rp->result_size = (size_t) rp->width * (size_t) rp->height;
|
||||
if (!br_set_resultbits(rp)) {
|
||||
rp->read_state = RS_FATAL;
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < rp->palette->numcolors; i++) {
|
||||
|
||||
1589
bmp-read.c
1589
bmp-read.c
File diff suppressed because it is too large
Load Diff
12
bmp-read.h
12
bmp-read.h
@@ -1,21 +1,23 @@
|
||||
/* bmplib - bmp-read.h
|
||||
*
|
||||
* Copyright (c) 2024, Rupert Weber.
|
||||
* Copyright (c) 2024, 2025, 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);
|
||||
bool br_set_resultbits(BMPREAD_R rp);
|
||||
BMPRESULT br_set_number_format(BMPREAD_R rp, enum BmpFormat format);
|
||||
|
||||
1989
bmp-write.c
1989
bmp-write.c
File diff suppressed because it is too large
Load Diff
11
bmp-write.h
11
bmp-write.h
@@ -1,22 +1,23 @@
|
||||
/* bmplib - bmp-write.h
|
||||
*
|
||||
* Copyright (c) 2024, Rupert Weber.
|
||||
* Copyright (c) 2024, 2025, 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 bw_free(BMPWRITE wp);
|
||||
BMPRESULT bw_set_number_format(BMPWRITE wp, enum BmpFormat format);
|
||||
|
||||
373
bmplib.h
373
bmplib.h
@@ -1,20 +1,20 @@
|
||||
/* bmplib - bmplib.h
|
||||
*
|
||||
* Copyright (c) 2024, Rupert Weber.
|
||||
* Copyright (c) 2024, 2025, 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,14 +22,33 @@
|
||||
#ifndef BMPLIB_H
|
||||
#define BMPLIB_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define DEPR(m) __attribute__ ((deprecated(m)))
|
||||
#else
|
||||
#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 union Bmphandle *BMPHANDLE;
|
||||
|
||||
|
||||
/*
|
||||
* result codes
|
||||
*
|
||||
* BMP_RESULT_OK all is good, proceed
|
||||
* BMP_RESULT_OK all is good, proceed.
|
||||
*
|
||||
* BMP_RESULT_ERROR something went wrong, image wasn't read
|
||||
* from / written to file.
|
||||
@@ -45,7 +64,8 @@ extern "C" {
|
||||
* BMP_RESULT_INVALID Some or all of the pixel values were
|
||||
* invalid. This happens when indexed
|
||||
* images point to colors beyond the given
|
||||
* palette size. If the image is also
|
||||
* palette size or to pixels outside of the
|
||||
* image dimensions. If the image is also
|
||||
* truncated, BMP_RESULT_TRUNCATED will be
|
||||
* returned instead.
|
||||
*
|
||||
@@ -65,106 +85,289 @@ extern "C" {
|
||||
* bmpread_set_insanity_limit() to set a new
|
||||
* sufficiently high limit.
|
||||
*
|
||||
****************************************************************/
|
||||
|
||||
|
||||
|
||||
* BMP_RESULT_ARRAY The BMP file contains an OS/2 bitmap array.
|
||||
*
|
||||
*/
|
||||
enum Bmpresult {
|
||||
BMP_RESULT_OK = 0,
|
||||
BMP_RESULT_ERROR,
|
||||
BMP_RESULT_TRUNCATED,
|
||||
BMP_RESULT_INVALID,
|
||||
BMP_RESULT_PNG,
|
||||
BMP_RESULT_JPEG,
|
||||
BMP_RESULT_INSANE,
|
||||
BMP_RESULT_OK = 0,
|
||||
BMP_RESULT_INVALID,
|
||||
BMP_RESULT_TRUNCATED,
|
||||
BMP_RESULT_INSANE,
|
||||
BMP_RESULT_PNG,
|
||||
BMP_RESULT_JPEG,
|
||||
BMP_RESULT_ERROR,
|
||||
BMP_RESULT_ARRAY
|
||||
};
|
||||
typedef enum Bmpresult BMPRESULT;
|
||||
|
||||
|
||||
/*
|
||||
* 64-bit BMPs: conversion of RGBA (16bit) values
|
||||
*
|
||||
* BMP_CONV64_SRGB (default) Assume components are
|
||||
* stored in linear light and convert
|
||||
* to sRGB gamma.
|
||||
*
|
||||
* BMP_CONV64_LINEAR No gamma conversion.
|
||||
*
|
||||
* BMP_CONV64_NONE Leave components as they are. This is
|
||||
* a shortcut for the combination
|
||||
* - bmpread_set_64bit_conv(BMP_CONV_LINEAR) and
|
||||
* - bmp_set_number_format(BMP_FORMAT_S2_13).
|
||||
*/
|
||||
enum Bmpconv64 {
|
||||
BMP_CONV64_SRGB = 0, /* default */
|
||||
BMP_CONV64_LINEAR,
|
||||
BMP_CONV64_NONE,
|
||||
};
|
||||
typedef enum Bmpconv64 BMPCONV64;
|
||||
|
||||
|
||||
/*
|
||||
* BMP info header versions
|
||||
*
|
||||
* There doesn't seem to be consensus on whether the BITMAPINFOHEADER is
|
||||
* version 1 (with the two Adobe extensions being v2 and v3) or version 3
|
||||
* (with the older BITMAPCOREHEADER and OS22XBITMAPHEADER being v1 and v2).
|
||||
* I am going with BITMAPINFOHEADER = v3.
|
||||
*/
|
||||
enum BmpInfoVer {
|
||||
BMPINFO_CORE_OS21 = 1, /* 12 bytes */
|
||||
BMPINFO_OS22, /* 16 / 40(!) / 64 bytes */
|
||||
BMPINFO_V3, /* 40 bytes */
|
||||
BMPINFO_V3_ADOBE1, /* 52 bytes, unofficial */
|
||||
BMPINFO_V3_ADOBE2, /* 56 bytes, unofficial */
|
||||
BMPINFO_V4, /* 108 bytes */
|
||||
BMPINFO_V5, /* 124 bytes */
|
||||
BMPINFO_FUTURE, /* future versions, larger than 124 bytes */
|
||||
BMPINFO_CORE_OS21 = 1, /* 12 bytes */
|
||||
BMPINFO_OS22, /* 16 / 40(!) / up to 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;
|
||||
|
||||
|
||||
/*
|
||||
* RLE type
|
||||
*
|
||||
* BMP_RLE_NONE no RLE
|
||||
*
|
||||
* BMP_RLE_AUTO RLE4 for color tables with 16 or fewer
|
||||
* colors.
|
||||
*
|
||||
* BMP_RLE_RLE8 always use RLE8, regardless of color
|
||||
* table size.
|
||||
*/
|
||||
enum BmpRLEtype {
|
||||
BMP_RLE_NONE,
|
||||
BMP_RLE_AUTO,
|
||||
BMP_RLE_RLE8
|
||||
};
|
||||
typedef enum BmpRLEtype BMPRLETYPE;
|
||||
|
||||
|
||||
/*
|
||||
* undefined pixels in RLE images
|
||||
*
|
||||
* BMP_UNDEFINED_LEAVE leaves image buffer at whatever pixel value it was
|
||||
* initialized to. (0, i.e. first entry in color table if
|
||||
* buffer was allocated by bmplib).
|
||||
*
|
||||
* BMP_UNDEFINED_TO_ALPHA (default) make undefined pixels
|
||||
* transparent. Always adds an alpha
|
||||
* channel to the result.
|
||||
*
|
||||
*/
|
||||
enum BmpUndefined {
|
||||
BMP_UNDEFINED_LEAVE,
|
||||
BMP_UNDEFINED_TO_ALPHA, /* default */
|
||||
};
|
||||
typedef enum BmpUndefined BMPUNDEFINED;
|
||||
|
||||
|
||||
/*
|
||||
* orientation
|
||||
*
|
||||
* Only relevant when reading the image line-by-line.
|
||||
* When reading the image as a whole, it will *always*
|
||||
* be returned in top-down orientation. bmpread_orientation()
|
||||
* still gives the orientation of the BMP file.
|
||||
*/
|
||||
enum BmpOrient {
|
||||
BMP_ORIENT_BOTTOMUP,
|
||||
BMP_ORIENT_TOPDOWN
|
||||
};
|
||||
typedef enum BmpOrient BMPORIENT;
|
||||
|
||||
|
||||
/*
|
||||
* number format
|
||||
*
|
||||
* format of input/output RGB(A) image data. (indexed image data
|
||||
* is always 8-bit integer).
|
||||
*
|
||||
* BMP_FORMAT_INT 8/16/32-bit integer
|
||||
*
|
||||
* BMP_FORMAT_FLOAT 32-bit float
|
||||
*
|
||||
* BMP_FORMAT_S2_13 16-bit s2.13 fixed point with range from
|
||||
* -4.0 to +3.999...
|
||||
*/
|
||||
enum BmpFormat {
|
||||
BMP_FORMAT_INT,
|
||||
BMP_FORMAT_FLOAT,
|
||||
BMP_FORMAT_S2_13
|
||||
};
|
||||
typedef enum BmpFormat BMPFORMAT;
|
||||
|
||||
|
||||
enum BmpIntent {
|
||||
BMP_INTENT_NONE,
|
||||
BMP_INTENT_BUSINESS, /* saturation */
|
||||
BMP_INTENT_GRAPHICS, /* relative colorimetric */
|
||||
BMP_INTENT_IMAGES, /* perceptive */
|
||||
BMP_INTENT_ABS_COLORIMETRIC /* absolute colorimetric */
|
||||
};
|
||||
typedef enum BmpIntent BMPINTENT;
|
||||
|
||||
|
||||
enum BmpImagetype {
|
||||
BMP_IMAGETYPE_NONE,
|
||||
BMP_IMAGETYPE_BM = 0x4d42,
|
||||
BMP_IMAGETYPE_CI = 0x4943,
|
||||
BMP_IMAGETYPE_CP = 0x5043,
|
||||
BMP_IMAGETYPE_IC = 0x4349,
|
||||
BMP_IMAGETYPE_PT = 0x5450,
|
||||
BMP_IMAGETYPE_BA = 0x4142
|
||||
};
|
||||
typedef enum BmpImagetype BMPIMAGETYPE;
|
||||
|
||||
struct BmpArrayInfo {
|
||||
BMPHANDLE handle;
|
||||
BMPIMAGETYPE type;
|
||||
int width, height;
|
||||
int ncolors; /* 0 = RGB */
|
||||
int screenwidth, screenheight; /* typically 0, or 1024x768 for 'hi-res' */
|
||||
};
|
||||
|
||||
|
||||
|
||||
typedef struct Bmphandle *BMPHANDLE;
|
||||
typedef enum Bmpresult BMPRESULT;
|
||||
APIDECL BMPHANDLE bmpread_new(FILE *file);
|
||||
|
||||
APIDECL BMPRESULT bmpread_load_info(BMPHANDLE h);
|
||||
APIDECL BMPIMAGETYPE bmpread_image_type(BMPHANDLE h);
|
||||
|
||||
APIDECL BMPRESULT bmpread_dimensions(BMPHANDLE h,
|
||||
int *width,
|
||||
int *height,
|
||||
int *channels,
|
||||
int *bitsperchannel,
|
||||
BMPORIENT *orientation);
|
||||
|
||||
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);
|
||||
|
||||
APIDECL int bmpread_resolution_xdpi(BMPHANDLE h);
|
||||
APIDECL int bmpread_resolution_ydpi(BMPHANDLE h);
|
||||
|
||||
APIDECL size_t bmpread_buffersize(BMPHANDLE h);
|
||||
|
||||
APIDECL BMPRESULT bmpread_load_image(BMPHANDLE h, unsigned char **buffer);
|
||||
APIDECL BMPRESULT bmpread_load_line(BMPHANDLE h, unsigned char **buffer);
|
||||
|
||||
APIDECL int bmpread_num_palette_colors(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpread_load_palette(BMPHANDLE h, unsigned char **palette);
|
||||
|
||||
APIDECL void bmpread_set_undefined(BMPHANDLE h, BMPUNDEFINED mode);
|
||||
APIDECL void bmpread_set_insanity_limit(BMPHANDLE h, size_t limit);
|
||||
|
||||
APIDECL int bmpread_is_64bit(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpread_set_64bit_conv(BMPHANDLE h, BMPCONV64 conv);
|
||||
|
||||
APIDECL size_t bmpread_iccprofile_size(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpread_load_iccprofile(BMPHANDLE h, unsigned char **profile);
|
||||
|
||||
|
||||
BMPHANDLE bmpread_new(FILE *file);
|
||||
|
||||
BMPRESULT bmpread_load_info(BMPHANDLE h);
|
||||
|
||||
BMPRESULT bmpread_dimensions(BMPHANDLE h,
|
||||
int *width,
|
||||
int *height,
|
||||
int *channels,
|
||||
int *bitsperchannel,
|
||||
int *topdown);
|
||||
|
||||
int bmpread_width(BMPHANDLE h);
|
||||
int bmpread_height(BMPHANDLE h);
|
||||
int bmpread_channels(BMPHANDLE h);
|
||||
int bmpread_bits_per_channel(BMPHANDLE h);
|
||||
int bmpread_topdown(BMPHANDLE h);
|
||||
|
||||
int bmpread_resolution_xdpi(BMPHANDLE h);
|
||||
int bmpread_resolution_ydpi(BMPHANDLE h);
|
||||
|
||||
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 int bmpread_array_num(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpread_array_info(BMPHANDLE h, struct BmpArrayInfo *ai, int idx);
|
||||
|
||||
|
||||
int bmpread_num_palette_colors(BMPHANDLE h);
|
||||
BMPRESULT bmpread_load_palette(BMPHANDLE h, unsigned char **palette);
|
||||
|
||||
|
||||
void bmpread_set_undefined_to_alpha(BMPHANDLE h, int yes);
|
||||
void bmpread_set_insanity_limit(BMPHANDLE h, size_t limit);
|
||||
|
||||
|
||||
|
||||
int 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);
|
||||
APIDECL BMPRESULT bmpwrite_set_dimensions(BMPHANDLE h,
|
||||
unsigned width,
|
||||
unsigned height,
|
||||
unsigned channels,
|
||||
unsigned bitsperchannel);
|
||||
APIDECL BMPRESULT bmpwrite_set_resolution(BMPHANDLE h, int xdpi, int ydpi);
|
||||
APIDECL BMPRESULT bmpwrite_set_output_bits(BMPHANDLE h, int red, int green, int blue, int alpha);
|
||||
APIDECL BMPRESULT bmpwrite_set_palette(BMPHANDLE h, int numcolors, const unsigned char *palette);
|
||||
APIDECL BMPRESULT bmpwrite_allow_2bit(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpwrite_allow_huffman(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpwrite_allow_rle24(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpwrite_set_rle(BMPHANDLE h, BMPRLETYPE type);
|
||||
APIDECL BMPRESULT bmpwrite_set_orientation(BMPHANDLE h, BMPORIENT orientation);
|
||||
APIDECL BMPRESULT bmpwrite_set_64bit(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpwrite_set_huffman_img_fg_idx(BMPHANDLE h, int idx);
|
||||
|
||||
BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image);
|
||||
BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line);
|
||||
APIDECL BMPRESULT bmpwrite_set_iccprofile(BMPHANDLE h, size_t size,
|
||||
const unsigned char *iccprofile);
|
||||
APIDECL BMPRESULT bmpwrite_set_rendering_intent(BMPHANDLE h, BMPINTENT intent);
|
||||
|
||||
APIDECL BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image);
|
||||
APIDECL BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line);
|
||||
|
||||
|
||||
|
||||
void bmp_free(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmp_set_number_format(BMPHANDLE h, BMPFORMAT format);
|
||||
APIDECL BMPRESULT bmp_set_huffman_t4black_value(BMPHANDLE h, int blackidx);
|
||||
|
||||
const char* bmp_errmsg(BMPHANDLE h);
|
||||
APIDECL void bmp_free(BMPHANDLE h);
|
||||
|
||||
APIDECL const char* bmp_errmsg(BMPHANDLE h);
|
||||
|
||||
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
|
||||
#define BMP_ERR_MEMORY 0x00000002
|
||||
#define BMP_ERR_INTERNAL 0x00000004
|
||||
|
||||
#define BMP_ERRTYPE_DATA 0x0000fff0
|
||||
#define BMP_ERR_PIXEL 0x00000010
|
||||
#define BMP_ERR_TRUNCATED 0x00000020
|
||||
#define BMP_ERR_HEADER 0x00000040
|
||||
#define BMP_ERR_INSANE 0x00000080
|
||||
#define BMP_ERR_UNSUPPORTED 0x00000100
|
||||
#define BMP_ERR_JPEG 0x00000200
|
||||
#define BMP_ERR_PNG 0x00000400
|
||||
#define BMP_ERR_DIMENSIONS 0x00000800
|
||||
#define BMP_ERR_INVALID 0x00001000
|
||||
|
||||
#define BMP_ERRTYPE_USER 0x0fff0000
|
||||
#define BMP_ERR_CONV64 0x00010000
|
||||
#define BMP_ERR_FORMAT 0x00020000
|
||||
#define BMP_ERR_NULL 0x00040000
|
||||
#define BMP_ERR_PALETTE 0x00080000
|
||||
#define BMP_ERR_NOINFO 0x00100000
|
||||
#define BMP_ERR_UNDEFMODE 0x00200000
|
||||
|
||||
const char* bmp_version(void);
|
||||
|
||||
|
||||
#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, 2025, 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, 2025, 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 && __STDC_VERSION__ < 202000L)
|
||||
typedef int bool;
|
||||
#define true 1
|
||||
#define false 0
|
||||
#endif
|
||||
|
||||
#include "gen-huffman-codes.h"
|
||||
|
||||
|
||||
#define ARR_SIZE(a) (sizeof a / sizeof a[0])
|
||||
|
||||
struct Node {
|
||||
int l;
|
||||
int r;
|
||||
int value;
|
||||
bool terminal;
|
||||
bool makeup;
|
||||
};
|
||||
|
||||
static int nnodes = 0;
|
||||
|
||||
static struct Node nodebuffer[416];
|
||||
|
||||
static int black_tree = -1;
|
||||
static int white_tree = -1;
|
||||
|
||||
|
||||
static void s_buildtree(void);
|
||||
static void add_node(int *nodeidx, const char *bits, int value, bool makeup);
|
||||
static unsigned short str2bits(const char *str);
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
struct Node *n = nodebuffer;
|
||||
FILE *file;
|
||||
const char *src_name = "huffman-codes.h";
|
||||
const char *this_name = "gen-huffman.c";
|
||||
|
||||
if (argc == 2) {
|
||||
if (!(file = fopen(argv[1], "w"))) {
|
||||
perror(argv[1]);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
file = stdout;
|
||||
}
|
||||
|
||||
s_buildtree();
|
||||
|
||||
fprintf(file, "/* bmplib - %s\n", src_name);
|
||||
fprintf(file, " *\n"
|
||||
" * Copyright (c) 2024, Rupert Weber.\n"
|
||||
" *\n"
|
||||
" * This file is part of bmplib.\n"
|
||||
" * bmplib is free software: you can redistribute it and/or modify\n"
|
||||
" * it under the terms of the GNU Lesser General Public License as\n"
|
||||
" * published by the Free Software Foundation, either version 3 of\n"
|
||||
" * the License, or (at your option) any later version.\n"
|
||||
" *\n");
|
||||
fprintf(file, " * This program is distributed in the hope that it will be useful,\n"
|
||||
" * but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
|
||||
" * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
|
||||
" * GNU Lesser General Public License for more details.\n"
|
||||
" *\n"
|
||||
" * You should have received a copy of the GNU Lesser General Public\n"
|
||||
" * License along with this library.\n"
|
||||
" * If not, see <https://www.gnu.org/licenses/>\n"
|
||||
" */\n\n");
|
||||
fprintf(file, "/* This file is auto-generated by %s */\n\n\n", this_name);
|
||||
|
||||
fputs("struct Node {\n"
|
||||
"\tsigned short l;\n"
|
||||
"\tsigned short r;\n"
|
||||
"\tshort value;\n"
|
||||
"\tchar terminal;\n"
|
||||
"\tchar makeup;\n"
|
||||
"};\n\n",
|
||||
file);
|
||||
|
||||
fputs("struct Huffcode {\n"
|
||||
"\tunsigned char bits;\n"
|
||||
"\tunsigned char nbits;\n"
|
||||
"};\n\n",
|
||||
file);
|
||||
|
||||
fprintf(file, "static const int blackroot = %d;\n", black_tree);
|
||||
fprintf(file, "static const int whiteroot = %d;\n\n\n", white_tree);
|
||||
fprintf(file, "static const struct Node nodebuffer[] = {\n");
|
||||
for (i = 0; i < (int) ARR_SIZE(nodebuffer); i++) {
|
||||
fprintf(file, "\t{ %3d, %3d, %4d, %d, %d },\n",
|
||||
n[i].l, n[i].r, n[i].value, n[i].terminal, n[i].makeup);
|
||||
}
|
||||
fputs("};\n\n", file);
|
||||
|
||||
fputs("static const struct Huffcode huff_term_black[] = {\n\t", file);
|
||||
for (i = 0; i < (int) ARR_SIZE(huff_term_black); i++) {
|
||||
fprintf(file, "{ 0x%02hx, %2d },",
|
||||
str2bits(huff_term_black[i].bits),
|
||||
(int) strlen(huff_term_black[i].bits));
|
||||
if ((i+1) % 4 == 0 && i != ARR_SIZE(huff_term_black) - 1)
|
||||
fputs("\n\t", file);
|
||||
else
|
||||
fputs(" ", file);
|
||||
}
|
||||
fputs("\n};\n\n", file);
|
||||
|
||||
fputs("static const struct Huffcode huff_term_white[] = {\n\t", file);
|
||||
for (i = 0; i < (int) ARR_SIZE(huff_term_white); i++) {
|
||||
fprintf(file, "{ 0x%02hx, %2d },",
|
||||
str2bits(huff_term_white[i].bits),
|
||||
(int) strlen(huff_term_white[i].bits));
|
||||
if ((i+1) % 4 == 0 && i != ARR_SIZE(huff_term_white) - 1)
|
||||
fputs("\n\t", file);
|
||||
else
|
||||
fputs(" ", file);
|
||||
}
|
||||
fputs("\n};\n\n", file);
|
||||
|
||||
fputs("static const struct Huffcode huff_makeup_black[] = {\n\t", file);
|
||||
for (i = 0; i < (int) ARR_SIZE(huff_makeup_black); i++) {
|
||||
fprintf(file, "{ 0x%02hx, %2d },",
|
||||
str2bits(huff_makeup_black[i].bits),
|
||||
(int) strlen(huff_makeup_black[i].bits));
|
||||
if ((i+1) % 4 == 0 && i != ARR_SIZE(huff_makeup_black) - 1)
|
||||
fputs("\n\t", file);
|
||||
else
|
||||
fputs(" ", file);
|
||||
}
|
||||
fputs("\n};\n\n", file);
|
||||
|
||||
fputs("static const struct Huffcode huff_makeup_white[] = {\n\t", file);
|
||||
for (i = 0; i < (int) ARR_SIZE(huff_makeup_white); i++) {
|
||||
fprintf(file, "{ 0x%02hx, %2d },",
|
||||
str2bits(huff_makeup_white[i].bits),
|
||||
(int) strlen(huff_makeup_white[i].bits));
|
||||
if ((i+1) % 4 == 0 && i != ARR_SIZE(huff_makeup_white) - 1)
|
||||
fputs("\n\t", file);
|
||||
else
|
||||
fputs(" ", file);
|
||||
}
|
||||
fputs("\n};\n\n", file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static unsigned short str2bits(const char *str)
|
||||
{
|
||||
unsigned short value = 0;
|
||||
|
||||
while (*str) {
|
||||
value <<= 1;
|
||||
value |= *str - '0';
|
||||
str++;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* s_buildtree()
|
||||
****************************************************************************/
|
||||
|
||||
static void s_buildtree(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(nodebuffer, 0, sizeof nodebuffer);
|
||||
|
||||
for (i = 0; i < (int) ARR_SIZE(huff_term_black); i++) {
|
||||
add_node(&black_tree, huff_term_black[i].bits,
|
||||
huff_term_black[i].number, false);
|
||||
}
|
||||
for (i = 0; i < (int) ARR_SIZE(huff_makeup_black); i++) {
|
||||
add_node(&black_tree, huff_makeup_black[i].bits,
|
||||
huff_makeup_black[i].number, true);
|
||||
}
|
||||
|
||||
for (i = 0; i < (int) ARR_SIZE(huff_term_white); i++) {
|
||||
add_node(&white_tree, huff_term_white[i].bits,
|
||||
huff_term_white[i].number, false);
|
||||
}
|
||||
for (i = 0; i < (int) ARR_SIZE(huff_makeup_white); i++) {
|
||||
add_node(&white_tree, huff_makeup_white[i].bits,
|
||||
huff_makeup_white[i].number, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* add_node()
|
||||
****************************************************************************/
|
||||
|
||||
static void add_node(int *nodeidx, const char *bits, int value, bool makeup)
|
||||
{
|
||||
if (*nodeidx == -1) {
|
||||
*nodeidx = nnodes++;
|
||||
nodebuffer[*nodeidx].l = -1;
|
||||
nodebuffer[*nodeidx].r = -1;
|
||||
}
|
||||
if (nnodes > (int) ARR_SIZE(nodebuffer)) {
|
||||
printf("too many nodes (have %d, max is %d)\n",
|
||||
nnodes, (int) ARR_SIZE(nodebuffer));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!*bits) {
|
||||
/* we are on the final bit of the sequence */
|
||||
nodebuffer[*nodeidx].value = value;
|
||||
nodebuffer[*nodeidx].terminal = true;
|
||||
nodebuffer[*nodeidx].makeup = makeup;
|
||||
} else {
|
||||
switch (*bits) {
|
||||
case '0':
|
||||
add_node(&nodebuffer[*nodeidx].l, bits+1, value, makeup);
|
||||
break;
|
||||
case '1':
|
||||
add_node(&nodebuffer[*nodeidx].r, bits+1, value, makeup);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
1
google0f997b088a966545.html
Normal file
1
google0f997b088a966545.html
Normal file
@@ -0,0 +1 @@
|
||||
google-site-verification: google0f997b088a966545.html
|
||||
238
huffman.c
Normal file
238
huffman.c
Normal file
@@ -0,0 +1,238 @@
|
||||
/* bmplib - huffman.c
|
||||
*
|
||||
* Copyright (c) 2024, 2025, 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 "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 & 0x80000000UL)
|
||||
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++;
|
||||
rp->hufbuf |= (uint32_t)byte << (24 - 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->c.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, 2025, 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);
|
||||
113
logging.c
113
logging.c
@@ -1,20 +1,20 @@
|
||||
/* bmplib - logging.c
|
||||
*
|
||||
* Copyright (c) 2024, Rupert Weber.
|
||||
* Copyright (c) 2024, 2025, Rupert Weber.
|
||||
*
|
||||
* This file is part of bmplib.
|
||||
* bmplib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* bmplib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
* License along with this library.
|
||||
* If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
@@ -22,35 +22,45 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "logging.h"
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define MAY_BE_UNUSED __attribute__((unused))
|
||||
#else
|
||||
#define MAY_BE_UNUSED
|
||||
#endif
|
||||
|
||||
|
||||
struct Log {
|
||||
size_t size;
|
||||
char *buffer;
|
||||
int size;
|
||||
char *buffer;
|
||||
bool panic;
|
||||
};
|
||||
|
||||
|
||||
/* logerr(log, fmt, ...) and logsyserr(log, fmt, ...) are
|
||||
* printf-style logging functions.
|
||||
*
|
||||
* Use logsyserr() where perror() would be used, logerr()
|
||||
* otherwise.
|
||||
*
|
||||
* 'separator' and 'inter' can have any length
|
||||
* 'air' is just there so we don't need to realloc every
|
||||
* single time.
|
||||
*/
|
||||
|
||||
|
||||
static const char separator[]="\n"; /* separator between log entries */
|
||||
static const char inter[]=": "; /* between own message and sys err text */
|
||||
static const int air = 80; /* how much more than required we allocate */
|
||||
static const int air = 80; /* how much more than required we allocate */
|
||||
|
||||
static int s_allocate(LOG log, size_t add_chars);
|
||||
static bool s_allocate(LOG log, size_t add_chars);
|
||||
static void s_log(LOG log, const char *file, int line, const char *function,
|
||||
const char *etxt, const char *fmt, va_list args);
|
||||
const char *etxt, const char *fmt, va_list args);
|
||||
static void panic(LOG log);
|
||||
#ifdef DEBUG
|
||||
static int s_add_file_etc(LOG log, const char *file, int line, const char *function);
|
||||
@@ -64,7 +74,7 @@ static int s_add_file_etc(LOG log, const char *file, int line, const char *funct
|
||||
|
||||
LOG logcreate(void)
|
||||
{
|
||||
LOG log;
|
||||
LOG log;
|
||||
|
||||
if (!(log = malloc(sizeof *log)))
|
||||
return NULL;
|
||||
@@ -75,7 +85,7 @@ LOG logcreate(void)
|
||||
void logfree(LOG log)
|
||||
{
|
||||
if (log) {
|
||||
if (log->size != (size_t)-1 && log->buffer)
|
||||
if (log->size > 0 && log->buffer)
|
||||
free(log->buffer);
|
||||
free(log);
|
||||
}
|
||||
@@ -95,10 +105,16 @@ void logreset(LOG log)
|
||||
|
||||
const char* logmsg(LOG log)
|
||||
{
|
||||
if (log && log->buffer)
|
||||
return log->buffer;
|
||||
else
|
||||
return "";
|
||||
if (log) {
|
||||
if (log->panic) {
|
||||
return "PANIC! bmplib encountered an error while "
|
||||
"trying to set an error message";
|
||||
}
|
||||
else if (log->buffer) {
|
||||
return log->buffer;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
@@ -113,17 +129,17 @@ void logerr_(LOG log, const char *file, int line, const char *function, const ch
|
||||
void logerr(LOG log, const char *fmt, ...)
|
||||
#endif
|
||||
{
|
||||
va_list args;
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
va_start(args, fmt);
|
||||
|
||||
#ifdef DEBUG
|
||||
s_log(log, file, line, function, NULL, fmt, args);
|
||||
s_log(log, file, line, function, NULL, fmt, args);
|
||||
#else
|
||||
s_log(log, NULL, 0, NULL, NULL, fmt, args);
|
||||
s_log(log, NULL, 0, NULL, NULL, fmt, args);
|
||||
#endif
|
||||
|
||||
va_end(args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
@@ -139,18 +155,18 @@ void logsyserr(LOG log, const char *fmt, ...)
|
||||
#endif
|
||||
{
|
||||
va_list args;
|
||||
const char *etxt;
|
||||
const char *etxt;
|
||||
|
||||
va_start(args, fmt);
|
||||
etxt = strerror(errno);
|
||||
va_start(args, fmt);
|
||||
etxt = strerror(errno);
|
||||
|
||||
#ifdef DEBUG
|
||||
s_log(log, file, line, function, etxt, fmt, args);
|
||||
s_log(log, file, line, function, etxt, fmt, args);
|
||||
#else
|
||||
s_log(log, NULL, 0, NULL, etxt, fmt, args);
|
||||
s_log(log, NULL, 0, NULL, etxt, fmt, args);
|
||||
#endif
|
||||
|
||||
va_end(args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
@@ -159,14 +175,15 @@ void logsyserr(LOG log, const char *fmt, ...)
|
||||
* s_log()
|
||||
*********************************************************/
|
||||
|
||||
static void s_log(LOG log, const char *file, int line, const char *function,
|
||||
static void s_log(LOG log, const char *file MAY_BE_UNUSED, int line MAY_BE_UNUSED,
|
||||
const char *function MAY_BE_UNUSED,
|
||||
const char *etxt, const char *fmt, va_list args)
|
||||
{
|
||||
va_list argsdup;
|
||||
int len = 0,addl_len, required_len;
|
||||
va_list argsdup;
|
||||
int len = 0,addl_len, required_len;
|
||||
|
||||
if (log->size == (size_t)-1)
|
||||
return; /* log is set to a string literal (panic) */
|
||||
if (log->panic)
|
||||
return;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (!s_add_file_etc(log, file, line, function))
|
||||
@@ -174,7 +191,7 @@ static void s_log(LOG log, const char *file, int line, const char *function,
|
||||
#endif
|
||||
|
||||
if (log->buffer)
|
||||
len = strlen(log->buffer);
|
||||
len = strlen(log->buffer);
|
||||
|
||||
va_copy(argsdup, args);
|
||||
addl_len = vsnprintf(NULL, 0, fmt, argsdup);
|
||||
@@ -197,7 +214,7 @@ static void s_log(LOG log, const char *file, int line, const char *function,
|
||||
#endif
|
||||
|
||||
if (log->size - len <= vsnprintf(log->buffer + len, log->size - len,
|
||||
fmt, args)) {
|
||||
fmt, args)) {
|
||||
panic(log);
|
||||
return;
|
||||
}
|
||||
@@ -214,28 +231,34 @@ static void s_log(LOG log, const char *file, int line, const char *function,
|
||||
* s_allocate()
|
||||
*********************************************************/
|
||||
|
||||
static int s_allocate(LOG log, size_t add_chars)
|
||||
static bool s_allocate(LOG log, size_t add_chars)
|
||||
{
|
||||
char *tmp;
|
||||
size_t newsize;
|
||||
|
||||
if (log->size == (size_t)-1)
|
||||
return 0; /* log is set to a string literal (panic) */
|
||||
if (log->panic)
|
||||
return false;
|
||||
|
||||
add_chars += air;
|
||||
|
||||
newsize = log->size + add_chars;
|
||||
newsize = (size_t) log->size + add_chars;
|
||||
if (newsize > INT_MAX) {
|
||||
panic(log);
|
||||
return false;
|
||||
}
|
||||
|
||||
tmp = realloc(log->buffer, newsize);
|
||||
if (tmp) {
|
||||
log->buffer = tmp;
|
||||
if (log->size == 0)
|
||||
log->buffer[0] = 0;
|
||||
log->size = newsize;
|
||||
} else {
|
||||
panic(log);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -246,9 +269,7 @@ static int s_allocate(LOG log, size_t add_chars)
|
||||
|
||||
static void panic(LOG log)
|
||||
{
|
||||
log->size = (size_t) -1;
|
||||
log->buffer = "PANIC! bmplib encountered an error while trying to set "
|
||||
"an error message";
|
||||
log->panic = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -282,7 +303,7 @@ static int s_add_file_etc(LOG log, const char *file, int line, const char *funct
|
||||
}
|
||||
|
||||
if (log->size - len <= snprintf(log->buffer + len, log->size - len,
|
||||
"[%s, line %d, %s()] ", file, line, function)) {
|
||||
"[%s, line %d, %s()] ", file, line, function)) {
|
||||
panic(log);
|
||||
return 0;
|
||||
}
|
||||
|
||||
32
logging.h
32
logging.h
@@ -1,20 +1,20 @@
|
||||
/* bmplib - logging.h
|
||||
*
|
||||
* Copyright (c) 2024, Rupert Weber.
|
||||
* Copyright (c) 2024, 2025, 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
|
||||
|
||||
|
||||
@@ -53,4 +53,4 @@ void logreset(LOG log);
|
||||
|
||||
void logreport(LOG log);
|
||||
|
||||
#endif /* LOGGING_H */
|
||||
#endif /* LOGGING_H */
|
||||
|
||||
104
meson.build
104
meson.build
@@ -1,14 +1,26 @@
|
||||
project('bmplib', 'c', default_options: ['c_std=c11'], version: '1.4.1')
|
||||
project('bmplib', 'c', default_options: ['c_std=c11', 'warning_level=3'], version: '1.8.0')
|
||||
|
||||
cc = meson.get_compiler('c')
|
||||
|
||||
add_project_arguments(['-pedantic','-fvisibility=hidden'], language : 'c')
|
||||
|
||||
add_project_arguments('-pedantic', language : 'c')
|
||||
add_project_arguments('-fvisibility=hidden', language: 'c')
|
||||
if get_option('buildtype') == 'debug'
|
||||
add_project_arguments('-DDEBUG', language: 'c')
|
||||
elif get_option('buildtype') == 'release'
|
||||
add_project_arguments('-DNDEBUG', language: 'c')
|
||||
endif
|
||||
|
||||
if get_option('sanitize')
|
||||
sanitize = [
|
||||
'-fsanitize=signed-integer-overflow',
|
||||
'-fsanitize=undefined',
|
||||
'-fsanitize=float-divide-by-zero',
|
||||
]
|
||||
add_project_arguments(sanitize, language : 'c')
|
||||
add_project_link_arguments(sanitize, language: 'c')
|
||||
endif
|
||||
|
||||
m_dep = cc.find_library('m', required : false)
|
||||
|
||||
conf_data = configuration_data()
|
||||
conf_data.set('insanity_limit_mb', get_option('insanity_limit_mb'))
|
||||
@@ -25,17 +37,97 @@ bmplib_sources = ['bmp-read.c',
|
||||
'bmp-write.c',
|
||||
'bmp-read-loadimage.c',
|
||||
'bmp-read-loadindexed.c',
|
||||
'bmp-read-icons.c',
|
||||
'bmp-common.c',
|
||||
'huffman.c',
|
||||
'logging.c']
|
||||
|
||||
|
||||
if cc.sizeof('float') != 4
|
||||
error('sizeof(float) must be 4 bytes.')
|
||||
elif cc.sizeof('int') < 4
|
||||
error('sizeof(int) must be at least 32 bit.')
|
||||
endif
|
||||
|
||||
gen_huffman = executable('gen-huffman', 'gen-huffman.c')
|
||||
huff_codes = custom_target('huffman-codes.h',
|
||||
output: 'huffman-codes.h',
|
||||
command: [gen_huffman, '@OUTPUT@'],
|
||||
)
|
||||
|
||||
|
||||
bmplib = shared_library('bmp',
|
||||
bmplib_sources,
|
||||
[bmplib_sources, huff_codes[0]],
|
||||
version: meson.project_version(),
|
||||
install: true)
|
||||
install: true,
|
||||
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.',
|
||||
)
|
||||
|
||||
test_read_conversions = executable('test_read_conversions',
|
||||
'test-read-conversions.c',
|
||||
'bmp-common.c',
|
||||
'bmp-read.c',
|
||||
'bmp-read-icons.c',
|
||||
'bmp-write.c',
|
||||
'huffman.c',
|
||||
'logging.c',
|
||||
huff_codes[0],
|
||||
dependencies: m_dep,
|
||||
)
|
||||
|
||||
test_read_io = executable('test_read_io',
|
||||
'test-read-io.c',
|
||||
'bmp-common.c',
|
||||
'bmp-read.c',
|
||||
'bmp-read-icons.c',
|
||||
'bmp-write.c',
|
||||
'huffman.c',
|
||||
'logging.c',
|
||||
'test-fileio-pipes.c',
|
||||
huff_codes[0],
|
||||
dependencies: m_dep,
|
||||
)
|
||||
|
||||
test_write_io = executable('test_write_io',
|
||||
'test-write-io.c',
|
||||
'bmp-common.c',
|
||||
'bmp-read.c',
|
||||
'bmp-read-icons.c',
|
||||
'bmp-read-loadimage.c',
|
||||
'huffman.c',
|
||||
'logging.c',
|
||||
'test-fileio-pipes.c',
|
||||
huff_codes[0],
|
||||
dependencies: m_dep,
|
||||
)
|
||||
|
||||
test('read - s_s2_13_to_float', test_read_conversions, args : ['s_s2_13_to_float'])
|
||||
test('read - s_convert64', test_read_conversions, args : ['s_convert64'])
|
||||
test('read - s_float_to_s2_13', test_read_conversions, args : ['s_float_to_s2_13'])
|
||||
test('read - roundtrip_s2.13-float-s2.13', test_read_conversions, args : ['roundtrip_s2.13-float-s2.13'])
|
||||
test('read - s_srgb_gamma_float', test_read_conversions, args : ['s_srgb_gamma_float'])
|
||||
test('read - s_int8_to_result_format/float', test_read_conversions, args : ['s_int8_to_result_format', 'float'])
|
||||
test('read - s_int8_to_result_format/int', test_read_conversions, args : ['s_int8_to_result_format', 'int'])
|
||||
test('read - s_int8_to_result_format/s2.13', test_read_conversions, args : ['s_int8_to_result_format', 's2.13'])
|
||||
test('read - s_buffer32_fill', test_read_io, args : ['s_buffer32_fill'])
|
||||
test('read - s_buffer32_bits', test_read_io, args : ['s_buffer32_bits'])
|
||||
test('read - s_read_rgb_pixel', test_read_io, args : ['s_read_rgb_pixel'])
|
||||
test('read - read_u16_le', test_read_io, args : ['read_u16_le'])
|
||||
test('read - read_s16_le', test_read_io, args : ['read_s16_le'])
|
||||
test('read - read_u32_le', test_read_io, args : ['read_u32_le'])
|
||||
test('read - read_s32_le', test_read_io, args : ['read_s32_le'])
|
||||
test('write - write_u32_le', test_write_io, args : ['write_u32_le'])
|
||||
test('write - write_s32_le', test_write_io, args : ['write_s32_le'])
|
||||
test('write - write_u16_le', test_write_io, args : ['write_u16_le'])
|
||||
test('write - write_s16_le', test_write_io, args : ['write_s16_le'])
|
||||
test('write - s_imgrgb_to_outbytes/int', test_write_io, args : ['s_imgrgb_to_outbytes', 'int'])
|
||||
test('write - s_imgrgb_to_outbytes/float', test_write_io, args : ['s_imgrgb_to_outbytes', 'float'])
|
||||
test('write - s_imgrgb_to_outbytes/s2.13', test_write_io, args : ['s_imgrgb_to_outbytes', 's2.13'])
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
option('insanity_limit_mb', type: 'integer', min: 0, value: 500)
|
||||
option('sanitize', type: 'boolean', value: false)
|
||||
|
||||
1
meson_options.txt
Symbolic link
1
meson_options.txt
Symbolic link
@@ -0,0 +1 @@
|
||||
meson.options
|
||||
145
test-fileio-pipes.c
Normal file
145
test-fileio-pipes.c
Normal file
@@ -0,0 +1,145 @@
|
||||
/* bmplib - test-fileio-pipes.c
|
||||
*
|
||||
* Copyright (c) 2025, 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/>
|
||||
*/
|
||||
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <test-fileio-pipes.h>
|
||||
|
||||
|
||||
FILE* provide_as_file(const unsigned char *data, size_t size)
|
||||
{
|
||||
int fd[2] = { -1, -1 };
|
||||
FILE *file_read = NULL, *file_write = NULL;
|
||||
|
||||
|
||||
if (pipe(fd)) {
|
||||
perror("pipe");
|
||||
goto abort;
|
||||
}
|
||||
|
||||
if (!(file_read = fdopen(fd[0], "rb"))) {
|
||||
perror("fdopen read pipe");
|
||||
goto abort;
|
||||
}
|
||||
|
||||
if (!(file_write = fdopen(fd[1], "w"))) {
|
||||
perror("fdopen write pipe");
|
||||
goto abort;
|
||||
}
|
||||
|
||||
if (size != fwrite(data, 1, size, file_write)) {
|
||||
perror("fwrite to pipe");
|
||||
goto abort;
|
||||
}
|
||||
|
||||
fclose(file_write);
|
||||
return file_read;
|
||||
|
||||
abort:
|
||||
if (file_write)
|
||||
fclose(file_write);
|
||||
else if (fd[1] != -1)
|
||||
close(fd[1]);
|
||||
if (file_read)
|
||||
fclose(file_read);
|
||||
else if (fd[0] != -1)
|
||||
close(fd[0]);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
FILE* open_write_pipe(struct WritePipe **hwp)
|
||||
{
|
||||
int fd[2] = { -1, -1 };
|
||||
FILE *file_read = NULL, *file_write = NULL;
|
||||
struct WritePipe *wp = NULL;
|
||||
|
||||
if (!hwp)
|
||||
return NULL;
|
||||
*hwp = NULL;
|
||||
|
||||
if (!(wp = malloc(sizeof *wp))) {
|
||||
perror(__func__);
|
||||
goto abort;
|
||||
}
|
||||
memset(wp, 0, sizeof *wp);
|
||||
|
||||
if (pipe(fd)) {
|
||||
perror("pipe");
|
||||
goto abort;
|
||||
}
|
||||
|
||||
if (!(file_read = fdopen(fd[0], "rb"))) {
|
||||
perror("fdopen read pipe");
|
||||
goto abort;
|
||||
}
|
||||
|
||||
if (!(file_write = fdopen(fd[1], "w"))) {
|
||||
perror("fdopen write pipe");
|
||||
goto abort;
|
||||
}
|
||||
|
||||
wp->file_read = file_read;
|
||||
*hwp = wp;
|
||||
|
||||
return file_write;
|
||||
|
||||
abort:
|
||||
if (file_write)
|
||||
fclose(file_write);
|
||||
else if (fd[1] != -1)
|
||||
close(fd[1]);
|
||||
|
||||
if (file_read)
|
||||
fclose(file_read);
|
||||
else if (fd[0] != -1)
|
||||
close(fd[0]);
|
||||
|
||||
if (wp)
|
||||
free(wp);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int data_from_write_pipe(struct WritePipe *wp, unsigned char *buffer, int size)
|
||||
{
|
||||
int nread;
|
||||
|
||||
if (!(wp && buffer)) {
|
||||
fprintf(stderr, "%s(): invalid NULL argument(s)\n", __func__);
|
||||
exit(3);
|
||||
}
|
||||
if (!wp->file_read) {
|
||||
fprintf(stderr, "%s(): FILE* is NULL\n", __func__);
|
||||
exit(3);
|
||||
}
|
||||
nread = fread(buffer, 1, size, wp->file_read);
|
||||
fclose(wp->file_read);
|
||||
free(wp);
|
||||
|
||||
return nread;
|
||||
}
|
||||
29
test-fileio-pipes.h
Normal file
29
test-fileio-pipes.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/* bmplib - test-fileio-pipes.h
|
||||
*
|
||||
* Copyright (c) 2025, 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 WritePipe {
|
||||
FILE *file_read;
|
||||
};
|
||||
|
||||
|
||||
FILE* provide_as_file(const unsigned char *data, size_t size);
|
||||
|
||||
FILE* open_write_pipe(struct WritePipe **hwp);
|
||||
int data_from_write_pipe(struct WritePipe *wp, unsigned char *buffer, int size);
|
||||
247
test-read-conversions.c
Normal file
247
test-read-conversions.c
Normal file
@@ -0,0 +1,247 @@
|
||||
/* bmplib - test-read-conversions.c
|
||||
*
|
||||
* Copyright (c) 2025, 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 "bmp-read-loadimage.c"
|
||||
|
||||
#define ARRAY_LEN(a) ((int)(sizeof (a) / sizeof ((a)[0])))
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
const char *func, *subtest ="";
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Invalid invocation\n");
|
||||
return 2;
|
||||
}
|
||||
func = argv[1];
|
||||
if (argc >= 3)
|
||||
subtest = argv[2];
|
||||
|
||||
if (!strcmp(func, "s_float_to_s2_13")) {
|
||||
|
||||
struct {
|
||||
double d;
|
||||
uint16_t expected;
|
||||
} data[] = {
|
||||
{ .d = -4.0, .expected = 0x8000 },
|
||||
{ .d = -5.0, .expected = 0x8000 },
|
||||
{ .d = -1.0, .expected = 0xe000 },
|
||||
{ .d = 0.0, .expected = 0x0000 },
|
||||
{ .d = 1.0, .expected = 0x2000 },
|
||||
{ .d = 3.99987793, .expected = 0x7fff },
|
||||
{ .d = 4.0, .expected = 0x7fff },
|
||||
{ .d = 20.0, .expected = 0x7fff },
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LEN(data); i++) {
|
||||
uint16_t s2_13 = s_float_to_s2_13(data[i].d);
|
||||
if (s2_13 != data[i].expected) {
|
||||
printf("%s() failed on data set %d: %f\n", func, i, data[i].d);
|
||||
printf("expected 0x%04x, got 0x%04x\n", data[i].expected, s2_13);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (!strcmp(func, "s_s2_13_to_float")) {
|
||||
|
||||
struct {
|
||||
uint16_t s2_13;
|
||||
double expected;
|
||||
} data[] = {
|
||||
{ .s2_13 = 0x2000u, .expected = 1.0},
|
||||
{ .s2_13 = 0xe000u, .expected = -1.0},
|
||||
{ .s2_13 = 0, .expected = 0.0},
|
||||
{ .s2_13 = 0x7fffu, .expected = 3.99987793},
|
||||
{ .s2_13 = 0x8000u, .expected = -4.0},
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LEN(data); i++) {
|
||||
double d = s_s2_13_to_float(data[i].s2_13);
|
||||
printf("is %.12f, expected %.12f\n", d, data[i].expected);
|
||||
if (fabs(d - data[i].expected) > 0.000000001) {
|
||||
printf("%s() failed on data set %d: 0x%04x\n", func, i, data[i].s2_13);
|
||||
printf("expected %.12f, got %.12f\n", data[i].expected, d);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (!strcmp(func, "roundtrip_s2.13-float-s2.13")) {
|
||||
|
||||
for (uint32_t u = 0; u <= 0xffffu; u++) {
|
||||
double d = s_s2_13_to_float(u);
|
||||
uint16_t u16 = s_float_to_s2_13(d);
|
||||
if (u != u16) {
|
||||
printf("%s for 0x%04x failed:\n", func, (unsigned)u);
|
||||
printf("expected 0x%04x, got 0x%04x\n", (unsigned)u, (unsigned)u16);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (!strcmp(func, "s_convert64")) {
|
||||
|
||||
struct {
|
||||
uint16_t val64[4];
|
||||
uint16_t expected[4];
|
||||
} data[] = {
|
||||
{ .val64 = {0, 0, 0, 0}, .expected = {0, 0, 0, 0} },
|
||||
{ .val64 = {0x2000, 0xe000, 0x2000, 0xffff}, .expected = {0xffff, 0, 0xffff, 0} },
|
||||
{ .val64 = {0x4000, 0x1000, 0x4000, 0x1000}, .expected = {0xffff, 0x8000, 0xffff, 0x8000} },
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LEN(data); i++) {
|
||||
s_convert64(data[i].val64);
|
||||
for (int j = 0; j < 4; j++) {
|
||||
if (data[i].val64[j] != data[i].expected[j]) {
|
||||
printf("%s() failed on data set %d\n", func, i);
|
||||
printf("expected 0x%04x, got 0x%04x\n", (unsigned)data[i].expected[j],
|
||||
(unsigned)data[i].val64[j]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (!strcmp(func, "s_srgb_gamma_float")) {
|
||||
|
||||
struct {
|
||||
double lin;
|
||||
double expected;
|
||||
} data[] = {
|
||||
{ .lin = 0.0, .expected = 0.0},
|
||||
{ .lin = 1.0, .expected = 1.0},
|
||||
{ .lin = 0.1, .expected = 0.349190213},
|
||||
{ .lin = 0.5, .expected = 0.735356983},
|
||||
{ .lin = 0.9, .expected = 0.954687172},
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LEN(data); i++) {
|
||||
double d = s_srgb_gamma_float(data[i].lin);
|
||||
printf("is %.12f, expected %.12f\n", d, data[i].expected);
|
||||
if (fabs(d - data[i].expected) > 0.000000001) {
|
||||
printf("%s() failed on data set %d: %f\n", func, i, data[i].lin);
|
||||
printf("expected %.12f, got %.12f\n", data[i].expected, d);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (!strcmp(func, "s_int8_to_result_format") && !strcmp(subtest, "float")) {
|
||||
|
||||
struct Bmpread br = { .result_format = BMP_FORMAT_FLOAT, .result_bitsperchannel = 32 };
|
||||
struct {
|
||||
int channels;
|
||||
int rgba[4];
|
||||
float expected[4];
|
||||
} data[] = {
|
||||
{ .channels = 3, .rgba = { 0, 0, 0, 0}, .expected = { 0.0, 0.0, 0.0, 0.0 } },
|
||||
{ .channels = 3, .rgba = { 255, 255, 255, 255}, .expected = { 1.0, 1.0, 1.0, 0.0 } },
|
||||
{ .channels = 4, .rgba = { 127, 128, 1, 254}, .expected = { 0.4980392, 0.5019608, 0.0039216, 0.9960784 } },
|
||||
};
|
||||
float floatbuf[4] = { 0 };
|
||||
|
||||
for (int i = 0; i < ARRAY_LEN(data); i++) {
|
||||
br.result_channels = data[i].channels;
|
||||
s_int8_to_result_format(&br, data[i].rgba, (unsigned char*) floatbuf);
|
||||
//printf("is %.12f, expected %.12f\n", d, data[i].expected);
|
||||
for (int j = 0; j < 4; j++) {
|
||||
if (fabs(floatbuf[j] - data[i].expected[j]) > 0.0000001) {
|
||||
printf("%s()/%s - failed on data set %d:\n", func, subtest, i);
|
||||
printf("expected %.12f, got %.12f\n", data[i].expected[j], floatbuf[j]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (!strcmp(func, "s_int8_to_result_format") && !strcmp(subtest, "int")) {
|
||||
|
||||
struct Bmpread br = { .result_format = BMP_FORMAT_INT, .result_bitsperchannel = 8 };
|
||||
struct {
|
||||
int channels;
|
||||
int rgba[4];
|
||||
unsigned expected[4];
|
||||
} data[] = {
|
||||
{ .channels = 3, .rgba = { 0, 0, 0, 0}, .expected = { 0, 0, 0, 0 } },
|
||||
{ .channels = 3, .rgba = { 255, 255, 255, 255}, .expected = { 255, 255, 255, 0 } },
|
||||
{ .channels = 4, .rgba = { 127, 128, 1, 254}, .expected = { 127, 128, 1, 254 } },
|
||||
};
|
||||
uint8_t int8buf[4] = { 0 };
|
||||
|
||||
for (int i = 0; i < ARRAY_LEN(data); i++) {
|
||||
br.result_channels = data[i].channels;
|
||||
s_int8_to_result_format(&br, data[i].rgba, (unsigned char*) int8buf);
|
||||
for (int j = 0; j < 4; j++) {
|
||||
if (int8buf[j] != data[i].expected[j]) {
|
||||
printf("%s()/%s - failed on data set %d:\n", func, subtest, i);
|
||||
printf("expected %u, got %u\n", (unsigned)data[i].expected[j], (unsigned)int8buf[j]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (!strcmp(func, "s_int8_to_result_format") && !strcmp(subtest, "s2.13")) {
|
||||
|
||||
struct Bmpread br = { .result_format = BMP_FORMAT_S2_13, .result_bitsperchannel = 16 };
|
||||
struct {
|
||||
int channels;
|
||||
int rgba[4];
|
||||
uint16_t expected[4];
|
||||
} data[] = {
|
||||
{ .channels = 3, .rgba = { 0, 0, 0, 0}, .expected = { 0, 0, 0, 0 } },
|
||||
{ .channels = 3, .rgba = { 255, 255, 255, 255}, .expected = { 0x2000, 0x2000, 0x2000, 0 } },
|
||||
{ .channels = 4, .rgba = { 127, 128, 1, 254}, .expected = { 0x0ff0, 0x1010, 0x0020, 0x1fe0 } },
|
||||
};
|
||||
uint16_t s213buf[4] = { 0 };
|
||||
|
||||
for (int i = 0; i < ARRAY_LEN(data); i++) {
|
||||
br.result_channels = data[i].channels;
|
||||
s_int8_to_result_format(&br, data[i].rgba, (unsigned char*) s213buf);
|
||||
for (int j = 0; j < 4; j++) {
|
||||
if (s213buf[j] != data[i].expected[j]) {
|
||||
printf("%s()/%s - failed on data set %d:\n", func, subtest, i);
|
||||
printf("expected %u, got %u\n", (unsigned)data[i].expected[j], (unsigned)s213buf[j]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Invalid test '%s'\n", func);
|
||||
return 2;
|
||||
}
|
||||
377
test-read-io.c
Normal file
377
test-read-io.c
Normal file
@@ -0,0 +1,377 @@
|
||||
/* bmplib - test-read-io.c
|
||||
*
|
||||
* Copyright (c) 2025, 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 "bmp-read-loadimage.c"
|
||||
|
||||
#include "test-fileio-pipes.h"
|
||||
|
||||
#define ARRAY_LEN(a) ((int)(sizeof (a) / sizeof ((a)[0])))
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
const char *func /*, *subtest =""*/;
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Invalid invocation\n");
|
||||
return 2;
|
||||
}
|
||||
func = argv[1];
|
||||
/*if (argc >= 3)
|
||||
subtest = argv[2];*/
|
||||
|
||||
|
||||
if (!strcmp(func, "s_buffer32_fill")) {
|
||||
struct Bmpread br = { 0 };
|
||||
struct Buffer32 buf32;
|
||||
|
||||
struct {
|
||||
unsigned char *filedata;
|
||||
size_t datalen;
|
||||
uint32_t expected_data;
|
||||
int expected_n;
|
||||
bool expected_return;
|
||||
} data[] = {
|
||||
{ .filedata = (unsigned char[]){0x00,0x00,0x00,0x00},
|
||||
.datalen = 4,
|
||||
.expected_n = 32,
|
||||
.expected_data = 0,
|
||||
.expected_return = true },
|
||||
{ .filedata = (unsigned char[]){0x01,0x02,0x03,0x04},
|
||||
.datalen = 4,
|
||||
.expected_n = 32,
|
||||
.expected_data = 0x01020304UL,
|
||||
.expected_return = true },
|
||||
{ .filedata = (unsigned char[]){0x01,0x02,0x03},
|
||||
.datalen = 3,
|
||||
.expected_n = 24,
|
||||
.expected_data = 0x01020300UL,
|
||||
.expected_return = true },
|
||||
{ .filedata = (unsigned char[]){ 0 },
|
||||
.datalen = 0,
|
||||
.expected_n = 0,
|
||||
.expected_data = 0x0UL,
|
||||
.expected_return = false },
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LEN(data); i++) {
|
||||
br.file = provide_as_file(data[i].filedata, data[i].datalen);
|
||||
if (!br.file)
|
||||
return 2;
|
||||
bool result = s_buffer32_fill(&br, &buf32);
|
||||
if (result != data[i].expected_return || buf32.buffer != data[i].expected_data ||
|
||||
buf32.n != data[i].expected_n) {
|
||||
printf("%s() failed on dataset %d:\n", func, i);
|
||||
printf("expected %s/0x%08lx/%d, got %s/0x%08lx/%d\n", data[i].expected_return ? "true" : "false",
|
||||
(unsigned long)data[i].expected_data,
|
||||
data[i].expected_n,
|
||||
result ? "true" : "false",
|
||||
(unsigned long)buf32.buffer, buf32.n);
|
||||
return 1;
|
||||
fclose(br.file);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (!strcmp(func, "s_buffer32_bits")) {
|
||||
struct Buffer32 buf32 = { 0 };
|
||||
|
||||
struct {
|
||||
uint32_t buffer;
|
||||
int n;
|
||||
int request;
|
||||
uint32_t expected;
|
||||
} data[] = {
|
||||
{ .buffer = 0x0f000000UL, .n = 8, .request = 8, .expected = 0x0f },
|
||||
{ .buffer = 0x34ffffffUL, .n = 16, .request = 4, .expected = 0x03 },
|
||||
{ .buffer = 0x1234ffffUL, .n = 32, .request = 8, .expected = 0x12 },
|
||||
{ .buffer = 0x8234ffffUL, .n = 16, .request = 2, .expected = 0x02 },
|
||||
{ .buffer = 0x8234ffffUL, .n = 16, .request = 1, .expected = 0x01 },
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LEN(data); i++) {
|
||||
buf32.buffer = data[i].buffer;
|
||||
buf32.n = data[i].n;
|
||||
uint32_t result = s_buffer32_bits(&buf32, data[i].request);
|
||||
if (result != data[i].expected) {
|
||||
printf("%s() failed on dataset %d:\n", func, i);
|
||||
printf("expected 0x%08lx, got 0x%08lx\n",
|
||||
(unsigned long)data[i].expected,
|
||||
(unsigned long)result);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (!strcmp(func, "s_read_rgb_pixel")) {
|
||||
struct Bmpread br = { 0 };
|
||||
struct Bmpinfo ih = { 0 };
|
||||
union Pixel px = { 0 };
|
||||
|
||||
br.ih = &ih;
|
||||
|
||||
struct {
|
||||
unsigned char *filedata;
|
||||
size_t datalen;
|
||||
int bitcount;
|
||||
uint64_t mask[4];
|
||||
int shift[4];
|
||||
uint32_t expected[4];
|
||||
bool has_alpha;
|
||||
int result_bitsperchannel;
|
||||
} data[] = {
|
||||
{ .filedata = (unsigned char[]){0x03,0x02,0x01,0x00},
|
||||
.datalen = 4,
|
||||
.bitcount = 32,
|
||||
.mask = { 0xff0000, 0xff00, 0xff, 0 },
|
||||
.shift = { 16, 8, 0, 0 },
|
||||
.has_alpha = false,
|
||||
.expected = { 1, 2, 3, 255 },
|
||||
.result_bitsperchannel = 8,
|
||||
},
|
||||
{ .filedata = (unsigned char[]){0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08},
|
||||
.datalen = 8,
|
||||
.bitcount = 64,
|
||||
.mask = { 0xffffULL, 0xffff0000ULL, 0xffff00000000ULL, 0xffff000000000000ULL },
|
||||
.shift = { 0, 16, 32, 48 },
|
||||
.has_alpha = true,
|
||||
.expected = { 0x0201, 0x0403, 0x0605, 0x0807 },
|
||||
.result_bitsperchannel = 16,
|
||||
},
|
||||
{ .filedata = (unsigned char[]){ 0x12, 0x34 },
|
||||
.datalen = 2,
|
||||
.bitcount = 16,
|
||||
.mask = { 0x0f, 0xf0, 0x0f00, 0xf000 },
|
||||
.shift = { 0, 4, 8, 12 },
|
||||
.has_alpha = true,
|
||||
.expected = { 2, 1, 4, 3 },
|
||||
.result_bitsperchannel = 8,
|
||||
},
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LEN(data); i++) {
|
||||
br.file = provide_as_file(data[i].filedata, data[i].datalen);
|
||||
if (!br.file)
|
||||
return 2;
|
||||
|
||||
br.cmask.mask.red = data[i].mask[0];
|
||||
br.cmask.mask.green = data[i].mask[1];
|
||||
br.cmask.mask.blue = data[i].mask[2];
|
||||
br.cmask.mask.alpha = data[i].mask[3];
|
||||
br.cmask.shift.red = data[i].shift[0];
|
||||
br.cmask.shift.green = data[i].shift[1];
|
||||
br.cmask.shift.blue = data[i].shift[2];
|
||||
br.cmask.shift.alpha = data[i].shift[3];
|
||||
br.has_alpha = data[i].has_alpha;
|
||||
br.result_bitsperchannel = data[i].result_bitsperchannel;
|
||||
ih.bitcount = data[i].bitcount;
|
||||
|
||||
if (!s_read_rgb_pixel(&br, &px)) {
|
||||
printf("%s(): EOF or file error (.filedata too short?)\n", func);
|
||||
return 2;
|
||||
}
|
||||
|
||||
for (int j = 0; j < 4; j++) {
|
||||
if (px.value[j] != data[i].expected[j]) {
|
||||
printf("%s() failed on dataset %d, val %d:\n", func, i, j);
|
||||
printf("expected %d, got %d\n",
|
||||
(int)data[i].expected[j],
|
||||
(int)px.value[j]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
fclose(br.file);
|
||||
br.file = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(func, "read_u16_le")) {
|
||||
|
||||
struct {
|
||||
unsigned char *filedata;
|
||||
int datalen;
|
||||
unsigned expected;
|
||||
} data[] = {
|
||||
{ .filedata = (unsigned char[]){0x00,0x00},
|
||||
.datalen = 2, .expected = 0 },
|
||||
{ .filedata = (unsigned char[]){0x01,0x00},
|
||||
.datalen = 2, .expected = 1 },
|
||||
{ .filedata = (unsigned char[]){0xfe,0xff},
|
||||
.datalen = 2, .expected = 65534 },
|
||||
{ .filedata = (unsigned char[]){0xff,0xff},
|
||||
.datalen = 2, .expected = 65535 },
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LEN(data); i++) {
|
||||
FILE *file = provide_as_file(data[i].filedata, data[i].datalen);
|
||||
if (!file)
|
||||
return 2;
|
||||
|
||||
uint16_t result = 0;
|
||||
if (!read_u16_le(file, &result)) {
|
||||
perror(func);
|
||||
return 3;
|
||||
}
|
||||
if (result != data[i].expected) {
|
||||
printf("%s() failed on dataset %d:\n", func, i);
|
||||
printf("expected 0x%04x, got 0x%04x\n",
|
||||
(unsigned)data[i].expected,
|
||||
(unsigned)result);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(func, "read_s16_le")) {
|
||||
|
||||
struct {
|
||||
unsigned char *filedata;
|
||||
int datalen;
|
||||
int expected;
|
||||
} data[] = {
|
||||
{ .filedata = (unsigned char[]){0x00,0x00},
|
||||
.datalen = 2, .expected = 0 },
|
||||
{ .filedata = (unsigned char[]){0x01,0x00},
|
||||
.datalen = 2, .expected = 1 },
|
||||
{ .filedata = (unsigned char[]){0xff,0xff},
|
||||
.datalen = 2, .expected = -1 },
|
||||
{ .filedata = (unsigned char[]){0x00,0x80},
|
||||
.datalen = 2, .expected = -32768 },
|
||||
{ .filedata = (unsigned char[]){0x01,0x80},
|
||||
.datalen = 2, .expected = -32767 },
|
||||
{ .filedata = (unsigned char[]){0xfe,0x7f},
|
||||
.datalen = 2, .expected = 32766 },
|
||||
{ .filedata = (unsigned char[]){0xff,0x7f},
|
||||
.datalen = 2, .expected = 32767 },
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LEN(data); i++) {
|
||||
FILE *file = provide_as_file(data[i].filedata, data[i].datalen);
|
||||
if (!file)
|
||||
return 2;
|
||||
|
||||
int16_t result = 0;
|
||||
if (!read_s16_le(file, &result)) {
|
||||
perror(func);
|
||||
return 3;
|
||||
}
|
||||
if (result != data[i].expected) {
|
||||
printf("%s() failed on dataset %d:\n", func, i);
|
||||
printf("expected %d, got %d\n", (int)data[i].expected,
|
||||
(int)result);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(func, "read_u32_le")) {
|
||||
|
||||
struct {
|
||||
unsigned char *filedata;
|
||||
int datalen;
|
||||
uint32_t expected;
|
||||
} data[] = {
|
||||
{ .filedata = (unsigned char[]){0x00,0x00,0x00,0x00},
|
||||
.datalen = 4, .expected = 0 },
|
||||
{ .filedata = (unsigned char[]){0x01,0x00,0x00,0x00},
|
||||
.datalen = 4, .expected = 1 },
|
||||
{ .filedata = (unsigned char[]){0xfe,0xff,0xff,0xff},
|
||||
.datalen = 4, .expected = 0xfffffffeUL },
|
||||
{ .filedata = (unsigned char[]){0xff,0xff,0xff,0xff},
|
||||
.datalen = 4, .expected = 0xffffffffUL },
|
||||
{ .filedata = (unsigned char[]){0x12,0x34,0x56,0x78},
|
||||
.datalen = 4, .expected = 0x78563412UL },
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LEN(data); i++) {
|
||||
FILE *file = provide_as_file(data[i].filedata, data[i].datalen);
|
||||
if (!file)
|
||||
return 2;
|
||||
|
||||
uint32_t result = 0;
|
||||
if (!read_u32_le(file, &result)) {
|
||||
perror(func);
|
||||
return 3;
|
||||
}
|
||||
if (result != data[i].expected) {
|
||||
printf("%s() failed on dataset %d:\n", func, i);
|
||||
printf("expected 0x%08lx, got 0x%08lx\n",
|
||||
(unsigned long)data[i].expected,
|
||||
(unsigned long)result);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(func, "read_s32_le")) {
|
||||
|
||||
struct {
|
||||
unsigned char *filedata;
|
||||
int datalen;
|
||||
long expected;
|
||||
} data[] = {
|
||||
{ .filedata = (unsigned char[]){0x00,0x00,0x00,0x00},
|
||||
.datalen = 4, .expected = 0 },
|
||||
{ .filedata = (unsigned char[]){0x01,0x00,0x00,0x00},
|
||||
.datalen = 4, .expected = 1 },
|
||||
{ .filedata = (unsigned char[]){0xff,0xff,0xff,0xff},
|
||||
.datalen = 4, .expected = -1 },
|
||||
{ .filedata = (unsigned char[]){0x00,0x00,0x00,0x80},
|
||||
.datalen = 4, .expected = -2147483648L },
|
||||
{ .filedata = (unsigned char[]){0x01,0x00,0x00,0x80},
|
||||
.datalen = 4, .expected = -2147483647L },
|
||||
{ .filedata = (unsigned char[]){0xfe,0xff,0xff,0x7f},
|
||||
.datalen = 4, .expected = 2147483646L },
|
||||
{ .filedata = (unsigned char[]){0xff,0xff,0xff,0x7f},
|
||||
.datalen = 4, .expected = 2147483647L },
|
||||
{ .filedata = (unsigned char[]){0x12,0x34,0x56,0x78},
|
||||
.datalen = 4, .expected = 2018915346L },
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LEN(data); i++) {
|
||||
FILE *file = provide_as_file(data[i].filedata, data[i].datalen);
|
||||
if (!file)
|
||||
return 2;
|
||||
|
||||
int32_t result = 0;
|
||||
if (!read_s32_le(file, &result)) {
|
||||
perror(func);
|
||||
return 3;
|
||||
}
|
||||
if (result != data[i].expected) {
|
||||
printf("%s() failed on dataset %d:\n", func, i);
|
||||
printf("expected %ld, got %ld\n", (long)data[i].expected,
|
||||
(long)result);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Invalid test '%s'\n", func);
|
||||
return 2;
|
||||
}
|
||||
BIN
test-write-helper.ods
Normal file
BIN
test-write-helper.ods
Normal file
Binary file not shown.
431
test-write-io.c
Normal file
431
test-write-io.c
Normal file
@@ -0,0 +1,431 @@
|
||||
/* bmplib - test-read-io.c
|
||||
*
|
||||
* Copyright (c) 2025, 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 "bmp-write.c"
|
||||
|
||||
#include "test-fileio-pipes.h"
|
||||
|
||||
#define ARRAY_LEN(a) ((int)(sizeof (a) / sizeof ((a)[0])))
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
const char *func, *subtest ="";
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Invalid invocation\n");
|
||||
return 2;
|
||||
}
|
||||
func = argv[1];
|
||||
if (argc >= 3)
|
||||
subtest = argv[2];
|
||||
|
||||
|
||||
|
||||
if (!strcmp(func, "write_u32_le")) {
|
||||
const int expected_len = 4;
|
||||
|
||||
struct {
|
||||
uint32_t value;
|
||||
unsigned char *expected;
|
||||
} data[] = {
|
||||
{ .expected = (unsigned char[]){0x00,0x00,0x00,0x00}, .value = 0 },
|
||||
{ .expected = (unsigned char[]){0x01,0x00,0x00,0x00}, .value = 1 },
|
||||
{ .expected = (unsigned char[]){0xfe,0xff,0xff,0xff}, .value = 0xfffffffeUL },
|
||||
{ .expected = (unsigned char[]){0xff,0xff,0xff,0xff}, .value = 0xffffffffUL },
|
||||
{ .expected = (unsigned char[]){0x12,0x34,0x56,0x78}, .value = 0x78563412UL },
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LEN(data); i++) {
|
||||
struct WritePipe *wp = NULL;
|
||||
unsigned char buf[expected_len];
|
||||
|
||||
FILE *file = open_write_pipe(&wp);
|
||||
if (!file)
|
||||
return 2;
|
||||
|
||||
if (!write_u32_le(file, data[i].value)) {
|
||||
perror(func);
|
||||
return 3;
|
||||
}
|
||||
fflush(file); /* important, otherwise read will get stuck */
|
||||
|
||||
int nbytes = data_from_write_pipe(wp, buf, (int) sizeof buf);
|
||||
fclose(file);
|
||||
if (nbytes != expected_len) {
|
||||
printf("%s() failed on dataset %d (%lu):\n", func, i, (unsigned long)data[i].value);
|
||||
printf("expected to read %d bytes, got %d bytes\n", expected_len, nbytes);
|
||||
return 1;
|
||||
}
|
||||
if (memcmp(data[i].expected, buf, expected_len) != 0) {
|
||||
printf("%s() failed on dataset %d (%lu):\n", func, i, (unsigned long)data[i].value);
|
||||
printf("expected 0x%02x%02x%02x%02x, got 0x%02x%02x%02x%02x\n",
|
||||
(unsigned)data[i].expected[0], (unsigned)data[i].expected[1],
|
||||
(unsigned)data[i].expected[2], (unsigned)data[i].expected[3],
|
||||
(unsigned)buf[0], (unsigned)buf[1], (unsigned)buf[2], (unsigned)buf[3]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (!strcmp(func, "write_s32_le")) {
|
||||
const int expected_len = 4;
|
||||
|
||||
struct {
|
||||
int32_t value;
|
||||
unsigned char *expected;
|
||||
} data[] = {
|
||||
{ .expected = (unsigned char[]){0x00,0x00,0x00,0x00}, .value = 0 },
|
||||
{ .expected = (unsigned char[]){0x01,0x00,0x00,0x00}, .value = 1 },
|
||||
{ .expected = (unsigned char[]){0xff,0xff,0xff,0xff}, .value = -1 },
|
||||
{ .expected = (unsigned char[]){0x00,0x00,0x00,0x80}, .value = -2147483648L },
|
||||
{ .expected = (unsigned char[]){0x01,0x00,0x00,0x80}, .value = -2147483647L },
|
||||
{ .expected = (unsigned char[]){0xfe,0xff,0xff,0x7f}, .value = 2147483646L },
|
||||
{ .expected = (unsigned char[]){0xff,0xff,0xff,0x7f}, .value = 2147483647L },
|
||||
{ .expected = (unsigned char[]){0x12,0x34,0x56,0x78}, .value = 2018915346L },
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LEN(data); i++) {
|
||||
struct WritePipe *wp = NULL;
|
||||
unsigned char buf[expected_len];
|
||||
|
||||
FILE *file = open_write_pipe(&wp);
|
||||
if (!file)
|
||||
return 2;
|
||||
|
||||
if (!write_s32_le(file, data[i].value)) {
|
||||
perror(func);
|
||||
return 3;
|
||||
}
|
||||
fflush(file); /* important, otherwise read will get stuck */
|
||||
|
||||
int nbytes = data_from_write_pipe(wp, buf, (int) sizeof buf);
|
||||
fclose(file);
|
||||
if (nbytes != expected_len) {
|
||||
printf("%s() failed on dataset %d (%ld):\n", func, i, (long)data[i].value);
|
||||
printf("expected to read %d bytes, got %d bytes\n", expected_len, nbytes);
|
||||
return 1;
|
||||
}
|
||||
if (memcmp(data[i].expected, buf, expected_len) != 0) {
|
||||
printf("%s() failed on dataset %d (%ld):\n", func, i, (long)data[i].value);
|
||||
printf("expected 0x%02x%02x%02x%02x, got 0x%02x%02x%02x%02x\n",
|
||||
(unsigned)data[i].expected[0], (unsigned)data[i].expected[1],
|
||||
(unsigned)data[i].expected[2], (unsigned)data[i].expected[3],
|
||||
(unsigned)buf[0], (unsigned)buf[1], (unsigned)buf[2], (unsigned)buf[3]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (!strcmp(func, "write_u16_le")) {
|
||||
const int expected_len = 2;
|
||||
|
||||
struct {
|
||||
uint16_t value;
|
||||
unsigned char *expected;
|
||||
} data[] = {
|
||||
{ .expected = (unsigned char[]){0x00,0x00}, .value = 0 },
|
||||
{ .expected = (unsigned char[]){0x01,0x00}, .value = 1 },
|
||||
{ .expected = (unsigned char[]){0xfe,0xff}, .value = 65534 },
|
||||
{ .expected = (unsigned char[]){0xff,0xff}, .value = 65535 },
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LEN(data); i++) {
|
||||
struct WritePipe *wp = NULL;
|
||||
unsigned char buf[expected_len];
|
||||
|
||||
FILE *file = open_write_pipe(&wp);
|
||||
if (!file)
|
||||
return 2;
|
||||
|
||||
if (!write_u16_le(file, data[i].value)) {
|
||||
perror(func);
|
||||
return 3;
|
||||
}
|
||||
fflush(file); /* important, otherwise read will get stuck */
|
||||
|
||||
int nbytes = data_from_write_pipe(wp, buf, (int) sizeof buf);
|
||||
fclose(file);
|
||||
if (nbytes != expected_len) {
|
||||
printf("%s() failed on dataset %d (%u):\n", func, i, (unsigned)data[i].value);
|
||||
printf("expected to read %d bytes, got %d bytes\n", expected_len, nbytes);
|
||||
return 1;
|
||||
}
|
||||
if (memcmp(data[i].expected, buf, expected_len) != 0) {
|
||||
printf("%s() failed on dataset %d (%u):\n", func, i, (unsigned)data[i].value);
|
||||
printf("expected 0x%02x%02x, got 0x%02x%02x\n",
|
||||
(unsigned)data[i].expected[0], (unsigned)data[i].expected[1],
|
||||
(unsigned)buf[0], (unsigned)buf[1]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (!strcmp(func, "write_s16_le")) {
|
||||
const int expected_len = 2;
|
||||
|
||||
struct {
|
||||
int16_t value;
|
||||
unsigned char *expected;
|
||||
} data[] = {
|
||||
{ .expected = (unsigned char[]){0x00,0x00}, .value = 0 },
|
||||
{ .expected = (unsigned char[]){0x01,0x00}, .value = 1 },
|
||||
{ .expected = (unsigned char[]){0x00,0x80}, .value = -32768 },
|
||||
{ .expected = (unsigned char[]){0x01,0x80}, .value = -32767 },
|
||||
{ .expected = (unsigned char[]){0xfe,0x7f}, .value = 32766 },
|
||||
{ .expected = (unsigned char[]){0xff,0x7f}, .value = 32767 },
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LEN(data); i++) {
|
||||
struct WritePipe *wp = NULL;
|
||||
unsigned char buf[expected_len];
|
||||
|
||||
FILE *file = open_write_pipe(&wp);
|
||||
if (!file)
|
||||
return 2;
|
||||
|
||||
if (!write_s16_le(file, data[i].value)) {
|
||||
perror(func);
|
||||
return 3;
|
||||
}
|
||||
fflush(file); /* important, otherwise read will get stuck */
|
||||
|
||||
int nbytes = data_from_write_pipe(wp, buf, (int) sizeof buf);
|
||||
fclose(file);
|
||||
if (nbytes != expected_len) {
|
||||
printf("%s() failed on dataset %d (%d):\n", func, i, (int)data[i].value);
|
||||
printf("expected to read %d bytes, got %d bytes\n", expected_len, nbytes);
|
||||
return 1;
|
||||
}
|
||||
if (memcmp(data[i].expected, buf, expected_len) != 0) {
|
||||
printf("%s() failed on dataset %d (%d):\n", func, i, (int)data[i].value);
|
||||
printf("expected 0x%02x%02x, got 0x%02x%02x\n",
|
||||
(unsigned)data[i].expected[0], (unsigned)data[i].expected[1],
|
||||
(unsigned)buf[0], (unsigned)buf[1]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (!strcmp(func, "s_imgrgb_to_outbytes") && !strcmp(subtest, "int")) {
|
||||
struct Bmpwrite bw = { 0 };
|
||||
|
||||
struct {
|
||||
int src_channels;
|
||||
bool src_has_alpha;
|
||||
BMPFORMAT src_format;
|
||||
int src_bitsperchannel;
|
||||
bool out_64bit;
|
||||
struct Colormask masks;
|
||||
const unsigned char *imgbytes;
|
||||
unsigned long long expected;
|
||||
} data[] = {
|
||||
{ .src_channels = 1, .src_has_alpha = false, .src_format = BMP_FORMAT_INT,
|
||||
.src_bitsperchannel = 8, .out_64bit = false,
|
||||
.masks.mask = { .red = 0xff, .green = 0xff, .blue = 0xff },
|
||||
.masks.shift = { .red = 0, .green = 8, .blue = 16 },
|
||||
.masks.bits = { .red = 8, .green = 8, .blue = 8},
|
||||
.masks.maxval = { .red = 255.0, .green = 255.0, .blue = 255.0},
|
||||
.imgbytes = (unsigned char[]){ 0x45 },
|
||||
.expected = 0x00454545ULL,
|
||||
},
|
||||
{ .src_channels = 3, .src_has_alpha = false, .src_format = BMP_FORMAT_INT,
|
||||
.src_bitsperchannel = 8, .out_64bit = false,
|
||||
.masks.mask = { .red = 0xff, .green = 0xff, .blue = 0xff },
|
||||
.masks.shift = { .red = 0, .green = 8, .blue = 16 },
|
||||
.masks.bits = { .red = 8, .green = 8, .blue = 8},
|
||||
.masks.maxval = { .red = 255.0, .green = 255.0, .blue = 255.0},
|
||||
.imgbytes = (unsigned char[]){ 0x12, 0x34, 0x45 },
|
||||
.expected = 0x00453412ULL,
|
||||
},
|
||||
{ .src_channels = 4, .src_has_alpha = true, .src_format = BMP_FORMAT_INT,
|
||||
.src_bitsperchannel = 8, .out_64bit = false,
|
||||
.masks.mask = { .red = 0xff, .green = 0xff, .blue = 0xff, .alpha = 0xff },
|
||||
.masks.shift = { .red = 0, .green = 8, .blue = 16, .alpha = 24 },
|
||||
.masks.bits = { .red = 8, .green = 8, .blue = 8, .alpha = 8},
|
||||
.masks.maxval = { .red = 255.0, .green = 255.0, .blue = 255.0, .alpha = 255.0 },
|
||||
.imgbytes = (unsigned char[]){ 0x12, 0x34, 0x45, 0x67 },
|
||||
.expected = 0x67453412ULL,
|
||||
},
|
||||
{ .src_channels = 4, .src_has_alpha = true, .src_format = BMP_FORMAT_INT,
|
||||
.src_bitsperchannel = 8, .out_64bit = false,
|
||||
.masks.mask = { .red = 0x3ff, .green = 0x3ff, .blue = 0x3ff, .alpha = 0x1 },
|
||||
.masks.shift = { .red = 0, .green = 10, .blue = 20, .alpha = 30 },
|
||||
.masks.bits = { .red = 10, .green = 10, .blue = 10, .alpha = 1 },
|
||||
.masks.maxval = { .red = 1023.0 , .green = 1023.0 , .blue = 1023.0 , .alpha = 1.0 },
|
||||
.imgbytes = (unsigned char[]){ 0x79, 0x11, 0xe6, 0xff },
|
||||
.expected = 0x79b111e5ULL,
|
||||
},
|
||||
{ .src_channels = 4, .src_has_alpha = true, .src_format = BMP_FORMAT_INT,
|
||||
.src_bitsperchannel = 16, .out_64bit = false,
|
||||
.masks.mask = { .red = 0xff, .green = 0x1ff, .blue = 0x3ff, .alpha = 0x1f },
|
||||
.masks.shift = { .red = 0, .green = 8, .blue = 17, .alpha = 27 },
|
||||
.masks.bits = { .red = 8, .green = 9, .blue = 10, .alpha = 5 },
|
||||
.masks.maxval = { .red = 255.0 , .green = 511.0 , .blue = 1023.0 , .alpha = 31.0 },
|
||||
|
||||
.imgbytes = (unsigned char[]){ 0x79, 0x00, 0x32, 0x09, 0x45, 0xf5, 0x00, 0x80 },
|
||||
.expected = 0x87a81200ULL,
|
||||
},
|
||||
{ .src_channels = 4, .src_has_alpha = true, .src_format = BMP_FORMAT_INT,
|
||||
.src_bitsperchannel = 32, .out_64bit = false,
|
||||
.masks.mask = { .red = 0xff, .green = 0x1ff, .blue = 0x3ff, .alpha = 0x1f },
|
||||
.masks.shift = { .red = 0, .green = 8, .blue = 17, .alpha = 27 },
|
||||
.masks.bits = { .red = 8, .green = 9, .blue = 10, .alpha = 5 },
|
||||
.masks.maxval = { .red = 255.0 , .green = 511.0 , .blue = 1023.0 , .alpha = 31.0 },
|
||||
|
||||
.imgbytes = (unsigned char[]){ 0xf8, 0x15, 0xd3, 0x89, 0xcc, 0x15, 0x6f, 0x55,
|
||||
0x5f, 0xd5, 0xfb, 0x1d, 0x19, 0xc8, 0x60, 0x36 },
|
||||
.expected = 0x38f0ab89ULL,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
for (int i = 0; i < ARRAY_LEN(data); i++) {
|
||||
bw.source_channels = data[i].src_channels;
|
||||
bw.source_has_alpha = data[i].src_has_alpha;
|
||||
bw.source_format = data[i].src_format;
|
||||
bw.source_bitsperchannel = data[i].src_bitsperchannel;
|
||||
bw.out64bit = data[i].out_64bit;
|
||||
memcpy(&bw.cmask, &data[i].masks, sizeof data[i].masks);
|
||||
|
||||
unsigned long long result = s_imgrgb_to_outbytes(&bw, data[i].imgbytes);
|
||||
if (result != data[i].expected) {
|
||||
printf("%s() - %s failed on dataset %d:\n", func, subtest, i);
|
||||
printf("expected 0x%016llx, got 0x%016llx\n",
|
||||
data[i].expected, result);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (!strcmp(func, "s_imgrgb_to_outbytes") && !strcmp(subtest, "float")) {
|
||||
struct Bmpwrite bw = { 0 };
|
||||
|
||||
struct {
|
||||
int src_channels;
|
||||
bool src_has_alpha;
|
||||
BMPFORMAT src_format;
|
||||
int src_bitsperchannel;
|
||||
bool out_64bit;
|
||||
struct Colormask masks;
|
||||
const float *imgvals;
|
||||
unsigned long long expected;
|
||||
} data[] = {
|
||||
{ .src_channels = 4, .src_has_alpha = true, .src_format = BMP_FORMAT_FLOAT,
|
||||
.src_bitsperchannel = 32, .out_64bit = false,
|
||||
.masks.mask = { .red = 0xff, .green = 0x1ff, .blue = 0x3ff, .alpha = 0x1f },
|
||||
.masks.shift = { .red = 0, .green = 8, .blue = 17, .alpha = 27 },
|
||||
.masks.bits = { .red = 8, .green = 9, .blue = 10, .alpha = 5 },
|
||||
.masks.maxval = { .red = 255.0 , .green = 511.0 , .blue = 1023.0 , .alpha = 31.0 },
|
||||
.imgvals = (float[]){ 0.3, 1.0, 0.78923, 0.6 },
|
||||
.expected = 0x9e4fff4dULL,
|
||||
},
|
||||
{ .src_channels = 4, .src_has_alpha = true, .src_format = BMP_FORMAT_FLOAT,
|
||||
.src_bitsperchannel = 32, .out_64bit = false,
|
||||
.masks.mask = { .red = 0xff, .green = 0x1ff, .blue = 0x3ff, .alpha = 0x1f },
|
||||
.masks.shift = { .red = 0, .green = 8, .blue = 17, .alpha = 27 },
|
||||
.masks.bits = { .red = 8, .green = 9, .blue = 10, .alpha = 5 },
|
||||
.masks.maxval = { .red = 255.0 , .green = 511.0 , .blue = 1023.0 , .alpha = 31.0 },
|
||||
.imgvals = (float[]){ 0.3, 3.2, 0.78923, -1.5 },
|
||||
.expected = 0x64fff4dULL,
|
||||
|
||||
},
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LEN(data); i++) {
|
||||
bw.source_channels = data[i].src_channels;
|
||||
bw.source_has_alpha = data[i].src_has_alpha;
|
||||
bw.source_format = data[i].src_format;
|
||||
bw.source_bitsperchannel = data[i].src_bitsperchannel;
|
||||
bw.out64bit = data[i].out_64bit;
|
||||
memcpy(&bw.cmask, &data[i].masks, sizeof data[i].masks);
|
||||
|
||||
unsigned long long result = s_imgrgb_to_outbytes(&bw, (unsigned char*)data[i].imgvals);
|
||||
if (result != data[i].expected) {
|
||||
printf("%s() - %s failed on dataset %d:\n", func, subtest, i);
|
||||
printf("expected 0x%016llx, got 0x%016llx\n",
|
||||
data[i].expected, result);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (!strcmp(func, "s_imgrgb_to_outbytes") && !strcmp(subtest, "s2.13")) {
|
||||
struct Bmpwrite bw = { 0 };
|
||||
|
||||
struct {
|
||||
int src_channels;
|
||||
bool src_has_alpha;
|
||||
BMPFORMAT src_format;
|
||||
int src_bitsperchannel;
|
||||
bool out_64bit;
|
||||
struct Colormask masks;
|
||||
const unsigned char *imgbytes;
|
||||
unsigned long long expected;
|
||||
} data[] = {
|
||||
{ .src_channels = 4, .src_has_alpha = true, .src_format = BMP_FORMAT_S2_13,
|
||||
.src_bitsperchannel = 16, .out_64bit = false,
|
||||
.masks.mask = { .red = 0xff, .green = 0x7ff, .blue = 0x3ff, .alpha = 0x7 },
|
||||
.masks.shift = { .red = 0, .green = 8, .blue = 19, .alpha = 29 },
|
||||
.masks.bits = { .red = 8, .green = 11, .blue = 10, .alpha = 3 },
|
||||
.masks.maxval = { .red = 255.0 , .green = 2047.0 , .blue = 1023.0 , .alpha = 7.0 },
|
||||
.imgbytes = (unsigned char[]){ 0x81, 0x0e, 0x7d, 0x67, 0x41, 0x19, 0x68, 0xb1 },
|
||||
.expected = 0x193fff74ULL,
|
||||
|
||||
},
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LEN(data); i++) {
|
||||
bw.source_channels = data[i].src_channels;
|
||||
bw.source_has_alpha = data[i].src_has_alpha;
|
||||
bw.source_format = data[i].src_format;
|
||||
bw.source_bitsperchannel = data[i].src_bitsperchannel;
|
||||
bw.out64bit = data[i].out_64bit;
|
||||
memcpy(&bw.cmask, &data[i].masks, sizeof data[i].masks);
|
||||
|
||||
unsigned long long result = s_imgrgb_to_outbytes(&bw, (unsigned char*)data[i].imgbytes);
|
||||
if (result != data[i].expected) {
|
||||
printf("%s() - %s failed on dataset %d:\n", func, subtest, i);
|
||||
printf("expected 0x%016llx, got 0x%016llx\n",
|
||||
data[i].expected, result);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
fprintf(stderr, "Invalid test '%s'\n", func);
|
||||
return 2;
|
||||
}
|
||||
Reference in New Issue
Block a user