mirror of
https://github.com/rupertwh/bmplib.git
synced 2026-04-10 00:03:22 +00:00
Compare commits
117 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 |
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);
|
||||
```
|
||||
686
API.md
686
API.md
@@ -1,686 +0,0 @@
|
||||
# API -- Rupert's bmplib
|
||||
|
||||
The API has grown quite a bit more than I had originally anticipated. But most
|
||||
of the provided functions are optional. So, to not scare you off right away,
|
||||
here is a short overview of the basic functions needed, which will be
|
||||
sufficient for many/most use cases (see also the two examples at the end of
|
||||
this document):
|
||||
|
||||
### Reading BMPs:
|
||||
```
|
||||
bmpread_new()
|
||||
bmpread_dimensions()
|
||||
bmpread_load_image()
|
||||
bmp_free()
|
||||
```
|
||||
### Writing BMPs:
|
||||
```
|
||||
bmpwrite_new()
|
||||
bmpwrite_set_dimensions()
|
||||
bmpwrite_save_image()
|
||||
bmp_free()
|
||||
```
|
||||
## 1. Functions for reading BMP files
|
||||
|
||||
### Get a handle
|
||||
```
|
||||
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
|
||||
```
|
||||
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).
|
||||
|
||||
|
||||
### Get image dimensions
|
||||
```
|
||||
BMPRESULT bmpread_dimensions(BMPHANDLE h,
|
||||
int *width,
|
||||
int *height,
|
||||
int *channels,
|
||||
int *bitsperchannel,
|
||||
BMPORIENT *orientation)
|
||||
```
|
||||
|
||||
Use `bmpread_dimensions()` to get all dimensions with one call. 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!
|
||||
|
||||
```
|
||||
int bmpread_width(BMPHANDLE h)
|
||||
int bmpread_height(BMPHANDLE h)
|
||||
int bmpread_channels(BMPHANDLE h)
|
||||
int bmpread_bits_per_channel(BMPHANDLE h)
|
||||
BMPORIENT bmpread_orientation(BMPHANDLE h)
|
||||
|
||||
int bmpread_resolution_xdpi(BMPHANDLE h)
|
||||
int bmpread_resolution_ydpi(BMPHANDLE h)
|
||||
|
||||
```
|
||||
|
||||
#### top-down / bottom-up
|
||||
`orientation` is one of:
|
||||
- `BMPORIENT_BOTTOMUP`
|
||||
- `BMPORIENT_TOPDOWN`
|
||||
|
||||
`bmpread_orientation()` or the orientation value returned by
|
||||
`bmpread_dimensions()` **is only relevant if you load the BMP file
|
||||
line-by-line**. In line-by-line mode (using `bmpread_load_line()`), the
|
||||
image data is always delivered in the order it is in the BMP file. The
|
||||
`orientation` value will tell you if it's top-down or bottom-up. On the
|
||||
other hand, when the whole image is loaded at once(using `bmpread_load_image
|
||||
()`), bmplib will **always** return the image top-down, regardless of how
|
||||
the BMP file is oriented. The `orientation` value will still indicate the
|
||||
orientation of the original BMP.
|
||||
|
||||
#### Required size for buffer to receive image
|
||||
```
|
||||
size_t bmpread_buffersize(BMPHANDLE h)
|
||||
```
|
||||
Returns the buffer size you have to allocate for the whole image.
|
||||
|
||||
|
||||
### 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()` will have no effect, as indexed images cannot have
|
||||
an alpha channel (see below).
|
||||
|
||||
#### 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.
|
||||
|
||||
```
|
||||
void bmpread_set_undefined(BMPHANDLE h, BMPUNDEFINED mode)
|
||||
```
|
||||
|
||||
`mode` can be one of:
|
||||
- `BMPUNDEFINED_LEAVE`
|
||||
- `BMPUNDEFINED_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)
|
||||
|
||||
|
||||
### Optional settings for 64bit BMPs
|
||||
|
||||
```
|
||||
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. (Hopefully, see README.md)
|
||||
|
||||
But if you want to access the original s2.13 fixed-point components, or you
|
||||
don't want the linear-to-sRGB conversion, you can call `bmpread_is_64bit
|
||||
()` to inquire if a BMP file is 64bit and subsequently call
|
||||
`bmpread_set_64bit_conv()` with `conv` set to one of the following values:
|
||||
|
||||
- `BMP_CONV64_NONE`: no conversion is done, image data is returned as is
|
||||
(probably 16 bit per channel RGBA in s2.13 fixed-point)
|
||||
- `BMP_CONV64_16BIT`: image is returned as normal 16bit per channel, without
|
||||
converting from linear to sRGB-gamma.
|
||||
- `BMP_CONV64_16BIT_SRGB`: the default, original data is assumed to be s2.13
|
||||
fixed-point linear and converted to normal 16bit per channel with
|
||||
sRGB-gamma.
|
||||
|
||||
|
||||
### Floating point
|
||||
|
||||
|
||||
### 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)
|
||||
```
|
||||
|
||||
|
||||
### Load the 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()` returns BMP_RESULT_TRUNCATED or BMP_RESULT_INVALID,
|
||||
the file may have been damaged or simply contains invalid image data. Image
|
||||
data is loaded anyway as far as possible and may be partially usable.
|
||||
|
||||
#### bmpread_load_line()
|
||||
```
|
||||
BMPRESULT bmpread_load_line(BMPHANDLE h, unsigned char **pbuffer)
|
||||
```
|
||||
|
||||
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 *
|
||||
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_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)
|
||||
|
||||
|
||||
### Invalid pixels
|
||||
|
||||
Invalid pixels may occur in indexed BMPs, both RLE and non-RLE. Invalid pixels
|
||||
either point beyond the given color palette, or they try to set pixels
|
||||
outside the image dimensions. Pixels containing an invalid color value will
|
||||
be set to zero, and attempts to point outside the image will be ignored.
|
||||
|
||||
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!
|
||||
|
||||
```
|
||||
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)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
## 2. Functions for writing BMP files
|
||||
|
||||
### Get a handle
|
||||
```
|
||||
BMPHANDLE bmpwrite_new(FILE *file)
|
||||
```
|
||||
|
||||
### Set 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, 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()`.
|
||||
|
||||
#### RLE
|
||||
|
||||
Indexed images may optionally be written as RLE4 or RLE8 compressed BMPs.
|
||||
Images with 16 or fewer colors can be written as either RLE4 or RLE8
|
||||
(default is RLE4), images with more than 16 colors only as RLE8.
|
||||
|
||||
To activate RLE compression, call
|
||||
|
||||
```
|
||||
BMPRESULT bmpwrite_set_rle(BMPHANDLE h, BMPRLETYPE type)
|
||||
```
|
||||
|
||||
with `type` set to one of the following values:
|
||||
- `BMP_RLE_NONE` no RLE compression, same as not calling `bmpwrite_set_rle()`
|
||||
at all
|
||||
- `BMP_RLE_AUTO` choose RLE4 or RLE8 based on number of colors in palette
|
||||
- `BMP_RLE_RLE8` use RLE8, regardless of number of colors in palette
|
||||
|
||||
|
||||
### 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.)
|
||||
|
||||
```
|
||||
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.
|
||||
|
||||
|
||||
### 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 according to the orientation
|
||||
set with `bmpwrite_set_orientation()` (see above).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 3. General functions for both reading/writing BMPs
|
||||
|
||||
### bmp_free()
|
||||
|
||||
```
|
||||
void bmp_free(BMPHANDLE h)
|
||||
```
|
||||
|
||||
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 with the same handle.
|
||||
|
||||
|
||||
### bmp_version()
|
||||
|
||||
```
|
||||
const char* bmp_version(void)
|
||||
```
|
||||
|
||||
Returns a zero-terminated character string containing the version of bmplib.
|
||||
|
||||
|
||||
|
||||
|
||||
## 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/40/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_TO_ZERO`
|
||||
|
||||
Can safely be cast from/to int.
|
||||
|
||||
#### `BMPCONV64`
|
||||
|
||||
Used in `bmpread_set_64bit_conv()`. Possible value are:
|
||||
- `BMP_CONV64_16BIT_SRGB` (default)
|
||||
- `BMP_CONV64_16BIT`
|
||||
- `BMP_CONV64_NONE`
|
||||
|
||||
Can safely be cast from/to int.
|
||||
|
||||
## 5. Sample code
|
||||
|
||||
### Reading BMPs
|
||||
|
||||
```
|
||||
/* (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_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, 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 (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);
|
||||
```
|
||||
86
README.md
86
README.md
@@ -6,15 +6,20 @@
|
||||
- Write any sensible BMP
|
||||
- Robustness! Don't let malformed BMPs bother us
|
||||
|
||||
## Current status (v1.4.6):
|
||||
Download [bmplib on github](https://github.com/rupertwh/bmplib).
|
||||
|
||||
## Current status (v1.8.0):
|
||||
### Reading BMP files:
|
||||
- 16/24/32 bit RGB(A) with any bits/channel combination
|
||||
(BI_RGB, BI_BITFIELDS, BI_ALPHABITFIELDS).
|
||||
- 64 bit RGBA (caveat see below)
|
||||
- 1/2/4/8 bit indexed (palette), including RLE4 and RLE8 compressed.
|
||||
- RLE24 compressed (OS/2).
|
||||
- optional line-by-line reading of BMPs, even RLE.
|
||||
- optionally return image data as float or s2.13
|
||||
- Huffman encoded (OS/2).
|
||||
- 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/):
|
||||
@@ -22,23 +27,24 @@
|
||||
- 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.
|
||||
- We currently ignore icc-profiles and chromaticity/gamma values. See TODO.
|
||||
- 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, optional RLE4 and RLE8 compression.
|
||||
- Indexed 1/2/4/8 bit, optional RLE4, RLE8, and 1-D Huffman compression.
|
||||
- RLE24 compression.
|
||||
- write BI_RGB when possible, BI_BITFIELDS only when
|
||||
necessary.
|
||||
- optional line-by-line writing of BMPs.
|
||||
- optionally supply image data as float or s2.13 fixed point.
|
||||
|
||||
|
||||
## Installation
|
||||
@@ -46,7 +52,7 @@
|
||||
### Download and compile bmplib library
|
||||
|
||||
To install the latest development version of the library under the default
|
||||
`/usr/local` prefix:
|
||||
`/usr/local` prefix on debian-like Linux:
|
||||
|
||||
```
|
||||
sudo apt install build-essential git meson pkg-config
|
||||
@@ -83,72 +89,61 @@ Includes:
|
||||
#include <bmplib.h>
|
||||
```
|
||||
|
||||
see API.md for the API documentation
|
||||
see API-quick-start.md and API-full.md for the API documentation
|
||||
|
||||
|
||||
### 64bit BMPs
|
||||
|
||||
64bit BMPs are a special breed. First of all, there is very little information
|
||||
about the format out there, let alone an 'official' spec from MS. It seems
|
||||
that the RGBA components are (always?) stored as s2.13 fixed-point numbers.
|
||||
And according to Jason Summers' BMP Suite the RGB components are encoded in
|
||||
that the RGBA components are stored as s2.13 fixed-point numbers. And
|
||||
according to Jason Summers' BMP Suite the RGB components are encoded in
|
||||
linear light. As that's the only sample of a 64-bit BMP I have, that's what I
|
||||
am going with for now. But that also means that there is no one obvious
|
||||
format in which to return the data.
|
||||
|
||||
Possible choices are:
|
||||
1. return the values untouched, which means the caller has to
|
||||
be aware of the s2.13 format. (BMP_CONV64_NONE)
|
||||
2. return the values as normal 16bit values, left in linear
|
||||
light (BMP_CONV64_16BIT)
|
||||
3. return the values as normal 16bit values, converted to sRGB
|
||||
gamma. (BMP_CONV64_16BIT_SRGB)
|
||||
be aware of the s2.13 format and linear gamma. (BMP_CONV64_NONE)
|
||||
2. return the values converted to 16-bit integers (or other selected
|
||||
number format), left in linear light (BMP_CONV64_LINEAR)
|
||||
3. return the values converted to 16-bit integers (or other selected
|
||||
numer format), converted to sRGB gamma. (BMP_CONV64_SRGB)
|
||||
|
||||
Choice 3 (16bit sRGB gamma) seems to be the most sensible default(and I made
|
||||
Choice 3 (16bit sRGB gamma) seems to be the most sensible default (and I made
|
||||
it the default), as it will work well for all callers which are not
|
||||
aware/don't care about 64bit BMPs and just want to use/diplay them like any
|
||||
other BMP. (Even though this goes against my original intent to not have
|
||||
bmplib do any color conversions.)
|
||||
|
||||
Note: the s2.13 format allows for negative values and values greater than 1!
|
||||
When converting to normal 16bit (BMP_CONV64_16BIT and BMP_CONV64_16BIT_SRGB),
|
||||
these values will be clipped to 0...1.
|
||||
When converting to 16bit integers, these values will be clipped to 0...1. In
|
||||
order to preserve the full possible range of 64bit BMP pixel values, the
|
||||
number format should be set to either BMP_FORMAT_FLOAT or BMP_FORMAT_S2_13.
|
||||
|
||||
In case the default (BMP_CONV64_16BIT_SRGB) doesn't work for you, bmplib now
|
||||
provides these two functions to check if a BMP is 64bit and to set the
|
||||
bmplib provides these functions to check if a BMP is 64bit and to set the
|
||||
conversion:
|
||||
- `bmpread_is_64bit()`
|
||||
- `bmpread_set_64bit_conv()`
|
||||
- `bmp_set_number_format()`
|
||||
|
||||
As to writing BMPs, by default bmplib will not write 64bit BMPs, as they are
|
||||
so exotic that only few applications will read them (other than native
|
||||
Microsoft tools, the new GIMP 3.0 is the only one I am aware of). Use
|
||||
`bmpwrite_set_64bit()` in order to write 64bit BMPs.
|
||||
|
||||
|
||||
## TODOs:
|
||||
### Definitely:
|
||||
|
||||
- [x] write indexed images.
|
||||
- [x] write RLE-compressed images (RLE4/RLE8 only. No OS/2 v2 BMPs).
|
||||
- [x] 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").
|
||||
- [x] 64-bits BMPs. (I changed my mind)
|
||||
|
||||
### Unclear:
|
||||
|
||||
@@ -157,19 +152,6 @@ conversion:
|
||||
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
|
||||
|
||||
264
bmp-common.c
264
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,10 @@ API const char* bmp_version(void)
|
||||
|
||||
API const char* bmp_errmsg(BMPHANDLE h)
|
||||
{
|
||||
if (!(h && (h->magic == HMAGIC_READ || h->magic == HMAGIC_WRITE)))
|
||||
if (!(h && (h->common.magic == HMAGIC_READ || h->common.magic == HMAGIC_WRITE)))
|
||||
return "BMPHANDLE is NULL or invalid";
|
||||
|
||||
return logmsg(h->log);
|
||||
|
||||
return logmsg(h->common.log);
|
||||
}
|
||||
|
||||
|
||||
@@ -75,17 +72,17 @@ API BMPRESULT bmp_set_number_format(BMPHANDLE h, enum BmpFormat format)
|
||||
if (!h)
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
switch (h->magic) {
|
||||
switch (h->common.magic) {
|
||||
case HMAGIC_READ:
|
||||
return br_set_number_format((BMPREAD)(void*)h, format);
|
||||
return br_set_number_format(&h->read, format);
|
||||
|
||||
case HMAGIC_WRITE:
|
||||
return bw_set_number_format((BMPWRITE)(void*)h, format);
|
||||
return bw_set_number_format(&h->write, format);
|
||||
|
||||
default:
|
||||
#ifdef DEBUG
|
||||
printf("bmp_set_number_format() called with invalid handle (0x%04x)\n",
|
||||
(unsigned int) h->magic);
|
||||
(unsigned int) h->common.magic);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@@ -94,6 +91,25 @@ API BMPRESULT bmp_set_number_format(BMPHANDLE h, enum BmpFormat format)
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* bmp_set_huffman_t4black_value
|
||||
*******************************************************/
|
||||
|
||||
API BMPRESULT bmp_set_huffman_t4black_value(BMPHANDLE h, int blackidx)
|
||||
{
|
||||
if (!h)
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
if (!(h->common.magic == HMAGIC_READ || h->common.magic == HMAGIC_WRITE))
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
h->common.huffman_black_is_zero = !blackidx;
|
||||
|
||||
return BMP_RESULT_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* bmp_free
|
||||
*******************************************************/
|
||||
@@ -103,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;
|
||||
}
|
||||
}
|
||||
@@ -123,30 +139,28 @@ API void bmp_free(BMPHANDLE h)
|
||||
|
||||
|
||||
/********************************************************
|
||||
* cm_check_is_read_handle
|
||||
* cm_read_handle
|
||||
*******************************************************/
|
||||
|
||||
int cm_check_is_read_handle(BMPHANDLE h)
|
||||
BMPREAD cm_read_handle(BMPHANDLE h)
|
||||
{
|
||||
BMPREAD rp = (BMPREAD)(void*)h;
|
||||
if (h && h->common.magic == HMAGIC_READ)
|
||||
return &h->read;
|
||||
|
||||
if (rp && rp->magic == HMAGIC_READ)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/********************************************************
|
||||
* bm_check_is_write_handle
|
||||
* cm_write_handle
|
||||
*******************************************************/
|
||||
|
||||
int cm_check_is_write_handle(BMPHANDLE h)
|
||||
BMPWRITE cm_write_handle(BMPHANDLE h)
|
||||
{
|
||||
BMPWRITE wp = (BMPWRITE)(void*)h;
|
||||
if (h && h->common.magic == HMAGIC_WRITE)
|
||||
return &h->write;
|
||||
|
||||
if (wp && wp->magic == HMAGIC_WRITE)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -154,7 +168,7 @@ int cm_check_is_write_handle(BMPHANDLE h)
|
||||
* cm_gobble_up
|
||||
*******************************************************/
|
||||
|
||||
int cm_gobble_up(BMPREAD_R rp, int count)
|
||||
bool cm_gobble_up(BMPREAD_R rp, int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -162,16 +176,15 @@ int cm_gobble_up(BMPREAD_R rp, int count)
|
||||
if (EOF == getc(rp->file)) {
|
||||
if (feof(rp->file)) {
|
||||
rp->lasterr = BMP_ERR_TRUNCATED;
|
||||
logerr(rp->log, "unexpected end of file");
|
||||
}
|
||||
else {
|
||||
logerr(rp->c.log, "unexpected end of file");
|
||||
} else {
|
||||
rp->lasterr = BMP_ERR_FILEIO;
|
||||
logsyserr(rp->log, "error reading from file");
|
||||
logsyserr(rp->c.log, "error reading from file");
|
||||
}
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -187,9 +200,6 @@ int cm_count_bits(unsigned long v)
|
||||
{
|
||||
int bits = 0;
|
||||
|
||||
if (v < 0)
|
||||
v = -v;
|
||||
|
||||
while (v) {
|
||||
bits++;
|
||||
v >>= 1;
|
||||
@@ -221,18 +231,19 @@ const char* cm_format_name(enum BmpFormat format)
|
||||
|
||||
|
||||
|
||||
int cm_all_lessoreq_int(int limit, int n, ...)
|
||||
bool cm_all_lessoreq_int(int limit, int n, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int i, ret = TRUE;
|
||||
int i;
|
||||
bool ret = true;
|
||||
|
||||
if (n < 1)
|
||||
return TRUE;
|
||||
return true;
|
||||
|
||||
va_start(ap, n);
|
||||
for (i = 0; i < n; i++) {
|
||||
if (va_arg(ap, int) > limit) {
|
||||
ret = FALSE;
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -242,19 +253,20 @@ int cm_all_lessoreq_int(int limit, int n, ...)
|
||||
}
|
||||
|
||||
|
||||
int cm_all_equal_int(int n, ...)
|
||||
bool cm_all_equal_int(int n, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int first, i, ret = TRUE;
|
||||
int first, i;
|
||||
bool ret = true;
|
||||
|
||||
if (n < 2)
|
||||
return TRUE;
|
||||
return true;
|
||||
|
||||
va_start(ap, n);
|
||||
first = va_arg(ap, int);
|
||||
for (i = 1; i < n; i++) {
|
||||
if (va_arg(ap, int) != first) {
|
||||
ret = FALSE;
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -264,18 +276,19 @@ int cm_all_equal_int(int n, ...)
|
||||
}
|
||||
|
||||
|
||||
int cm_all_positive_int(int n, ...)
|
||||
bool cm_all_positive_int(int n, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int i, ret = TRUE;
|
||||
int i;
|
||||
bool ret = true;
|
||||
|
||||
if (n < 1)
|
||||
return TRUE;
|
||||
return true;
|
||||
|
||||
va_start(ap, n);
|
||||
for (i = 0; i < n; i++) {
|
||||
if (va_arg(ap, int) < 0) {
|
||||
ret = FALSE;
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -285,18 +298,19 @@ int cm_all_positive_int(int n, ...)
|
||||
}
|
||||
|
||||
|
||||
int cm_is_one_of(int candidate, int n, ...)
|
||||
bool cm_is_one_of(int n, int candidate, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int i, ret = FALSE;
|
||||
int i;
|
||||
bool ret = false;
|
||||
|
||||
if (n < 1)
|
||||
return TRUE;
|
||||
return true;
|
||||
|
||||
va_start(ap, n);
|
||||
va_start(ap, candidate);
|
||||
for (i = 0; i < n; i++) {
|
||||
if (va_arg(ap, int) == candidate) {
|
||||
ret = TRUE;
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -306,109 +320,147 @@ int cm_is_one_of(int candidate, int n, ...)
|
||||
}
|
||||
|
||||
|
||||
|
||||
int cm_align4padding(unsigned long a)
|
||||
int cm_align4padding(unsigned long long a)
|
||||
{
|
||||
return cm_align4size(a) - a;
|
||||
return (int) (cm_align4size(a) - a);
|
||||
}
|
||||
|
||||
int cm_align2padding(unsigned long a)
|
||||
{
|
||||
return cm_align2size(a) - a;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*********************************************************
|
||||
* endianess-agnostic functions to read/write
|
||||
* 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;
|
||||
}
|
||||
|
||||
366
bmp-common.h
366
bmp-common.h
@@ -1,29 +1,23 @@
|
||||
/* 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))
|
||||
@@ -32,10 +26,13 @@
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define ATTR_CONST __attribute__((const))
|
||||
#define API __attribute__ ((visibility ("default")))
|
||||
#else
|
||||
#define ATTR_CONST
|
||||
#define API
|
||||
#endif
|
||||
|
||||
|
||||
union Pixel {
|
||||
unsigned int value[4];
|
||||
struct {
|
||||
@@ -48,7 +45,7 @@ union Pixel {
|
||||
|
||||
struct Colormask {
|
||||
union {
|
||||
unsigned long value[4];
|
||||
unsigned long long value[4];
|
||||
struct {
|
||||
unsigned long long red;
|
||||
unsigned long long green;
|
||||
@@ -57,21 +54,21 @@ struct Colormask {
|
||||
};
|
||||
} mask;
|
||||
union {
|
||||
unsigned long value[4];
|
||||
int value[4];
|
||||
struct {
|
||||
unsigned long red;
|
||||
unsigned long green;
|
||||
unsigned long blue;
|
||||
unsigned long alpha;
|
||||
int red;
|
||||
int green;
|
||||
int blue;
|
||||
int alpha;
|
||||
};
|
||||
} shift;
|
||||
union {
|
||||
int value[4];
|
||||
struct {
|
||||
int red;
|
||||
int green;
|
||||
int blue;
|
||||
int alpha;
|
||||
int red;
|
||||
int green;
|
||||
int blue;
|
||||
int alpha;
|
||||
};
|
||||
} bits;
|
||||
union {
|
||||
@@ -85,80 +82,103 @@ struct Colormask {
|
||||
} maxval;
|
||||
};
|
||||
|
||||
typedef struct Bmpread *BMPREAD;
|
||||
typedef struct Bmpwrite *BMPWRITE;
|
||||
typedef struct Bmpread * restrict BMPREAD_R;
|
||||
typedef struct Bmpwrite * restrict BMPWRITE_R;
|
||||
|
||||
struct Palette {
|
||||
int numcolors;
|
||||
union Pixel color[1];
|
||||
};
|
||||
|
||||
|
||||
struct Bmpcommon {
|
||||
uint32_t magic;
|
||||
LOG log;
|
||||
bool huffman_black_is_zero; /* defaults to false */
|
||||
};
|
||||
|
||||
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;
|
||||
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;
|
||||
enum BmpOrient orientation;
|
||||
int has_alpha; /* original BMP has alpha channel */
|
||||
bool is_icon;
|
||||
bool icon_is_mono;
|
||||
bool has_alpha; /* original BMP has alpha channel */
|
||||
enum BmpUndefined undefined_mode;
|
||||
int we_allocated_buffer;
|
||||
int line_by_line;
|
||||
bool we_allocated_buffer;
|
||||
struct Palette *palette;
|
||||
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 conv64_explicit;
|
||||
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;
|
||||
int result_format_explicit;
|
||||
size_t result_size;
|
||||
bool conv64_explicit;
|
||||
bool result_indexed;
|
||||
bool result_format_explicit;
|
||||
/* state */
|
||||
unsigned long lasterr;
|
||||
int getinfo_called;
|
||||
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_index;
|
||||
int invalid_delta;
|
||||
int invalid_overrun;
|
||||
int file_err;
|
||||
int file_eof;
|
||||
int panic;
|
||||
|
||||
uint32_t hufbuf;
|
||||
int hufbuf_len;
|
||||
bool truncated;
|
||||
bool invalid_index;
|
||||
bool invalid_delta;
|
||||
bool invalid_overrun;
|
||||
bool file_err;
|
||||
bool file_eof;
|
||||
bool panic;
|
||||
};
|
||||
|
||||
enum WriteState {
|
||||
WS_INIT,
|
||||
WS_DIMENSIONS_SET,
|
||||
WS_SAVE_STARTED,
|
||||
WS_SAVE_DONE,
|
||||
WS_FATAL,
|
||||
};
|
||||
|
||||
struct Bmpwrite {
|
||||
struct {
|
||||
uint32_t magic;
|
||||
LOG log;
|
||||
};
|
||||
struct Bmpcommon c;
|
||||
FILE *file;
|
||||
struct Bmpfile *fh;
|
||||
struct Bmpinfo *ih;
|
||||
@@ -168,143 +188,170 @@ struct Bmpwrite {
|
||||
int source_channels;
|
||||
int source_bitsperchannel;
|
||||
int source_bytes_per_pixel;
|
||||
int source_format;
|
||||
enum BmpFormat source_format;
|
||||
bool source_has_alpha;
|
||||
struct Palette *palette;
|
||||
int palette_size; /* sizeof palette in bytes */
|
||||
unsigned char *iccprofile;
|
||||
int iccprofile_size;
|
||||
/* output */
|
||||
size_t bytes_written;
|
||||
uint64_t bytes_written;
|
||||
size_t bytes_written_before_bitdata;
|
||||
int has_alpha;
|
||||
enum BmpOrient outorientation;
|
||||
bool huffman_fg_idx;
|
||||
struct Colormask cmask;
|
||||
int rle_requested;
|
||||
int rle;
|
||||
int allow_2bit; /* Windows CE, but many will not read it */
|
||||
int out64bit;
|
||||
enum BmpRLEtype rle_requested;
|
||||
int rle; /* 1, 4, 8, or 24 */
|
||||
bool allow_2bit; /* Windows CE */
|
||||
bool allow_huffman; /* OS/2 */
|
||||
bool allow_rle24; /* OS/2 */
|
||||
bool out64bit;
|
||||
int outbytes_per_pixel;
|
||||
int padding;
|
||||
int *group;
|
||||
int group_count;
|
||||
/* state */
|
||||
int outbits_set;
|
||||
int dimensions_set;
|
||||
int saveimage_done;
|
||||
int line_by_line;
|
||||
enum WriteState write_state;
|
||||
bool outbits_set;
|
||||
int lbl_y;
|
||||
uint32_t hufbuf;
|
||||
int hufbuf_len;
|
||||
};
|
||||
|
||||
|
||||
union Bmphandle {
|
||||
struct Bmpcommon common;
|
||||
struct Bmpread read;
|
||||
struct Bmpwrite write;
|
||||
};
|
||||
|
||||
int cm_all_lessoreq_int(int limit, int n, ...);
|
||||
int cm_all_equal_int(int n, ...);
|
||||
int cm_all_positive_int(int n, ...);
|
||||
int cm_is_one_of(int candidate, int n, ...);
|
||||
|
||||
#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);
|
||||
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);
|
||||
|
||||
int cm_gobble_up(BMPREAD_R rp, int count);
|
||||
int cm_check_is_read_handle(BMPHANDLE h);
|
||||
int cm_check_is_write_handle(BMPHANDLE h);
|
||||
bool cm_gobble_up(BMPREAD_R rp, int count);
|
||||
BMPREAD cm_read_handle(BMPHANDLE h);
|
||||
BMPWRITE cm_write_handle(BMPHANDLE h);
|
||||
|
||||
const char* cm_conv64_name(enum Bmpconv64 conv);
|
||||
const char* cm_format_name(enum BmpFormat format);
|
||||
|
||||
int write_u16_le(FILE *file, uint16_t val);
|
||||
int write_u32_le(FILE *file, uint32_t val);
|
||||
int read_u16_le(FILE *file, uint16_t *val);
|
||||
int read_u32_le(FILE *file, uint32_t *val);
|
||||
bool write_u16_le(FILE *file, uint16_t val);
|
||||
bool write_u32_le(FILE *file, uint32_t val);
|
||||
bool read_u16_le(FILE *file, uint16_t *val);
|
||||
bool read_u32_le(FILE *file, uint32_t *val);
|
||||
|
||||
int write_s16_le(FILE *file, int16_t val);
|
||||
int write_s32_le(FILE *file, int32_t val);
|
||||
int read_s16_le(FILE *file, int16_t *val);
|
||||
int read_s32_le(FILE *file, int32_t *val);
|
||||
bool write_s16_le(FILE *file, int16_t val);
|
||||
bool write_s32_le(FILE *file, int32_t val);
|
||||
bool read_s16_le(FILE *file, int16_t *val);
|
||||
bool read_s32_le(FILE *file, int32_t *val);
|
||||
|
||||
#define API __attribute__ ((visibility ("default")))
|
||||
uint32_t u32_from_le(const unsigned char *buf);
|
||||
int32_t s32_from_le(const unsigned char *buf);
|
||||
uint16_t u16_from_le(const unsigned char *buf);
|
||||
int16_t s16_from_le(const unsigned char *buf);
|
||||
|
||||
const char* cm_infoheader_name(enum BmpInfoVer infoversion);
|
||||
|
||||
|
||||
#define HMAGIC_READ (0x44414552UL)
|
||||
#define HMAGIC_WRITE (0x54495257UL)
|
||||
#define HMAGIC_READ 0x44414552UL
|
||||
#define HMAGIC_WRITE 0x54495257UL
|
||||
|
||||
#define BMPFILE_BM (0x4d42)
|
||||
#define BMPFILE_BA (0x4142)
|
||||
#define BMPFILE_CI (0x4943)
|
||||
#define BMPFILE_CP (0x5043)
|
||||
#define BMPFILE_IC (0x4349)
|
||||
#define BMPFILE_PT (0x5450)
|
||||
#define BMPFILE_BM 0x4d42
|
||||
#define BMPFILE_BA 0x4142
|
||||
#define BMPFILE_CI 0x4943
|
||||
#define BMPFILE_CP 0x5043
|
||||
#define BMPFILE_IC 0x4349
|
||||
#define BMPFILE_PT 0x5450
|
||||
|
||||
|
||||
#define BMPFHSIZE (14)
|
||||
#define BMPIHSIZE_V3 (40)
|
||||
#define BMPIHSIZE_V4 (108)
|
||||
|
||||
typedef uint16_t WORD;
|
||||
typedef uint32_t DWORD;
|
||||
typedef int32_t LONG;
|
||||
typedef uint8_t BYTE;
|
||||
#define BMPFHSIZE 14
|
||||
#define BMPIHSIZE_V3 40
|
||||
#define BMPIHSIZE_V4 108
|
||||
#define BMPIHSIZE_OS22 64
|
||||
#define BMPIHSIZE_V5 124
|
||||
|
||||
struct Bmpfile {
|
||||
WORD type; /* "BM" */
|
||||
DWORD size; /* bytes in file */
|
||||
WORD reserved1;
|
||||
WORD reserved2;
|
||||
DWORD offbits;
|
||||
uint16_t type; /* "BM" */
|
||||
uint32_t size; /* bytes in file */
|
||||
uint16_t reserved1;
|
||||
uint16_t reserved2;
|
||||
uint32_t offbits;
|
||||
};
|
||||
|
||||
struct Bmpinfo {
|
||||
/* BITMAPINFOHEADER (40 bytes) */
|
||||
DWORD size; /* sizof struct */
|
||||
LONG width;
|
||||
LONG height;
|
||||
WORD planes;
|
||||
WORD bitcount;
|
||||
DWORD compression;
|
||||
DWORD sizeimage; /* 0 ok for uncompressed */
|
||||
LONG xpelspermeter;
|
||||
LONG ypelspermeter;
|
||||
DWORD clrused;
|
||||
DWORD clrimportant;
|
||||
uint32_t size; /* sizof struct */
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
uint16_t planes;
|
||||
uint16_t bitcount;
|
||||
uint32_t compression;
|
||||
uint32_t sizeimage; /* 0 ok for uncompressed */
|
||||
int32_t xpelspermeter;
|
||||
int32_t ypelspermeter;
|
||||
uint32_t clrused;
|
||||
uint32_t clrimportant;
|
||||
/* BITMAPV4INFOHEADER (108 bytes) */
|
||||
DWORD redmask;
|
||||
DWORD greenmask;
|
||||
DWORD bluemask;
|
||||
DWORD alphamask;
|
||||
DWORD cstype;
|
||||
LONG redX;
|
||||
LONG redY;
|
||||
LONG redZ;
|
||||
LONG greenX;
|
||||
LONG greenY;
|
||||
LONG greenZ;
|
||||
LONG blueX;
|
||||
LONG blueY;
|
||||
LONG blueZ;
|
||||
DWORD gammared;
|
||||
DWORD gammagreen;
|
||||
DWORD gammablue;
|
||||
uint32_t redmask;
|
||||
uint32_t greenmask;
|
||||
uint32_t bluemask;
|
||||
uint32_t alphamask;
|
||||
uint32_t cstype;
|
||||
int32_t redX;
|
||||
int32_t redY;
|
||||
int32_t redZ;
|
||||
int32_t greenX;
|
||||
int32_t greenY;
|
||||
int32_t greenZ;
|
||||
int32_t blueX;
|
||||
int32_t blueY;
|
||||
int32_t blueZ;
|
||||
uint32_t gammared;
|
||||
uint32_t gammagreen;
|
||||
uint32_t gammablue;
|
||||
/* BITMAPV5INFOHEADER (124 bytes) */
|
||||
DWORD intent;
|
||||
DWORD profiledata;
|
||||
DWORD profilesize;
|
||||
DWORD reserved;
|
||||
uint32_t intent;
|
||||
uint32_t profiledata;
|
||||
uint32_t profilesize;
|
||||
uint32_t reserved;
|
||||
|
||||
/* OS22XBITMAPHEADER */
|
||||
WORD resolution; /* = 0 */
|
||||
WORD orientation; /* = 0 */
|
||||
WORD halftone_alg;
|
||||
DWORD halftone_parm1;
|
||||
DWORD halftone_parm2;
|
||||
DWORD color_encoding; /* = 0 (RGB) */
|
||||
DWORD app_id;
|
||||
uint16_t resolution; /* = 0 */
|
||||
uint16_t orientation; /* = 0 */
|
||||
uint16_t halftone_alg;
|
||||
uint32_t halftone_parm1;
|
||||
uint32_t halftone_parm2;
|
||||
uint32_t color_encoding; /* = 0 (RGB) */
|
||||
uint32_t app_id;
|
||||
|
||||
/* internal only, not from file: */
|
||||
enum BmpInfoVer version;
|
||||
/* internal only, not from file: */
|
||||
enum BmpInfoVer version;
|
||||
};
|
||||
|
||||
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
|
||||
@@ -324,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);
|
||||
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,29 +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->getinfo_called) {
|
||||
logerr(rp->log, "Must call bmpread_load_info() before loading palette");
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -102,12 +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->dim_queried_channels = FALSE;
|
||||
rp->result_indexed = true;
|
||||
rp->read_state = MIN(RS_HEADER_OK, rp->read_state);
|
||||
rp->dim_queried_channels = false;
|
||||
rp->result_channels = 1;
|
||||
if (!br_set_resultbits(rp))
|
||||
if (!br_set_resultbits(rp)) {
|
||||
rp->read_state = RS_FATAL;
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < rp->palette->numcolors; i++) {
|
||||
|
||||
1345
bmp-read.c
1345
bmp-read.c
File diff suppressed because it is too large
Load Diff
12
bmp-read.h
12
bmp-read.h
@@ -1,23 +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);
|
||||
int br_set_resultbits(BMPREAD_R rp);
|
||||
bool br_set_resultbits(BMPREAD_R rp);
|
||||
BMPRESULT br_set_number_format(BMPREAD_R rp, enum BmpFormat format);
|
||||
|
||||
1594
bmp-write.c
1594
bmp-write.c
File diff suppressed because it is too large
Load Diff
10
bmp-write.h
10
bmp-write.h
@@ -1,20 +1,20 @@
|
||||
/* 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/>
|
||||
*/
|
||||
|
||||
|
||||
289
bmplib.h
289
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,16 +22,27 @@
|
||||
#ifndef BMPLIB_H
|
||||
#define BMPLIB_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define DEPR(m) __attribute__ ((deprecated(m)))
|
||||
#define DEPR(m) __attribute__ ((deprecated(m)))
|
||||
#else
|
||||
#define DEPR(m)
|
||||
#define DEPR(m)
|
||||
#endif
|
||||
|
||||
typedef struct Bmphandle *BMPHANDLE;
|
||||
#if defined (WIN32) || defined (_WIN32)
|
||||
#ifdef BMPLIB_LIB
|
||||
#define APIDECL __declspec(dllexport)
|
||||
#else
|
||||
#define APIDECL __declspec(dllimport)
|
||||
#endif
|
||||
#else
|
||||
#define APIDECL
|
||||
#endif
|
||||
|
||||
|
||||
typedef union Bmphandle *BMPHANDLE;
|
||||
|
||||
|
||||
/*
|
||||
@@ -73,15 +84,19 @@ typedef struct Bmphandle *BMPHANDLE;
|
||||
* the image unless you first call
|
||||
* 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_INVALID,
|
||||
BMP_RESULT_TRUNCATED,
|
||||
BMP_RESULT_INSANE,
|
||||
BMP_RESULT_PNG,
|
||||
BMP_RESULT_JPEG,
|
||||
BMP_RESULT_ERROR,
|
||||
BMP_RESULT_OK = 0,
|
||||
BMP_RESULT_INVALID,
|
||||
BMP_RESULT_TRUNCATED,
|
||||
BMP_RESULT_INSANE,
|
||||
BMP_RESULT_PNG,
|
||||
BMP_RESULT_JPEG,
|
||||
BMP_RESULT_ERROR,
|
||||
BMP_RESULT_ARRAY
|
||||
};
|
||||
typedef enum Bmpresult BMPRESULT;
|
||||
|
||||
@@ -101,11 +116,9 @@ typedef enum Bmpresult BMPRESULT;
|
||||
* - bmp_set_number_format(BMP_FORMAT_S2_13).
|
||||
*/
|
||||
enum Bmpconv64 {
|
||||
BMP_CONV64_SRGB = 0, /* default */
|
||||
BMP_CONV64_LINEAR = 1,
|
||||
BMP_CONV64_16BIT_SRGB DEPR("use BMP_CONV64_SRGB instead") = 0,
|
||||
BMP_CONV64_16BIT DEPR("use BMP_CONV64_LINEAR instead") = 1,
|
||||
BMP_CONV64_NONE
|
||||
BMP_CONV64_SRGB = 0, /* default */
|
||||
BMP_CONV64_LINEAR,
|
||||
BMP_CONV64_NONE,
|
||||
};
|
||||
typedef enum Bmpconv64 BMPCONV64;
|
||||
|
||||
@@ -113,22 +126,20 @@ typedef enum Bmpconv64 BMPCONV64;
|
||||
/*
|
||||
* BMP info header versions
|
||||
*
|
||||
* There doesn't seem to be consensus on whether the
|
||||
* BITMAPINFOHEADER is version 1 (with the two Adobe
|
||||
* extensions being v2 and v3) or version 3 (with the
|
||||
* older BITMAPCIREHEADER and OS22XBITMAPHEADER being
|
||||
* v1 and v2).
|
||||
* I am going with BITMAPINFOHEADER = v3
|
||||
* There doesn't seem to be consensus on whether the BITMAPINFOHEADER is
|
||||
* version 1 (with the two Adobe extensions being v2 and v3) or version 3
|
||||
* (with the older BITMAPCOREHEADER and OS22XBITMAPHEADER being v1 and v2).
|
||||
* I am going with BITMAPINFOHEADER = v3.
|
||||
*/
|
||||
enum BmpInfoVer {
|
||||
BMPINFO_CORE_OS21 = 1, /* 12 bytes */
|
||||
BMPINFO_OS22, /* 16 / 40(!) / 64 bytes */
|
||||
BMPINFO_V3, /* 40 bytes */
|
||||
BMPINFO_V3_ADOBE1, /* 52 bytes, unofficial */
|
||||
BMPINFO_V3_ADOBE2, /* 56 bytes, unofficial */
|
||||
BMPINFO_V4, /* 108 bytes */
|
||||
BMPINFO_V5, /* 124 bytes */
|
||||
BMPINFO_FUTURE /* future versions, larger than 124 bytes */
|
||||
BMPINFO_CORE_OS21 = 1, /* 12 bytes */
|
||||
BMPINFO_OS22, /* 16 / 40(!) / 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;
|
||||
|
||||
@@ -145,9 +156,9 @@ typedef enum BmpInfoVer BMPINFOVER;
|
||||
* table size.
|
||||
*/
|
||||
enum BmpRLEtype {
|
||||
BMP_RLE_NONE,
|
||||
BMP_RLE_AUTO,
|
||||
BMP_RLE_RLE8
|
||||
BMP_RLE_NONE,
|
||||
BMP_RLE_AUTO,
|
||||
BMP_RLE_RLE8
|
||||
};
|
||||
typedef enum BmpRLEtype BMPRLETYPE;
|
||||
|
||||
@@ -155,8 +166,9 @@ typedef enum BmpRLEtype BMPRLETYPE;
|
||||
/*
|
||||
* undefined pixels in RLE images
|
||||
*
|
||||
* BMP_UNDEFINED_TO_ZERO set undefined pixels to 0 (= first
|
||||
* entry in color table).
|
||||
* 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
|
||||
@@ -164,9 +176,8 @@ typedef enum BmpRLEtype BMPRLETYPE;
|
||||
*
|
||||
*/
|
||||
enum BmpUndefined {
|
||||
BMP_UNDEFINED_LEAVE,
|
||||
BMP_UNDEFINED_TO_ZERO DEPR("use BMP_UNDEFINED_LEAVE instead") = 0,
|
||||
BMP_UNDEFINED_TO_ALPHA /* default */
|
||||
BMP_UNDEFINED_LEAVE,
|
||||
BMP_UNDEFINED_TO_ALPHA, /* default */
|
||||
};
|
||||
typedef enum BmpUndefined BMPUNDEFINED;
|
||||
|
||||
@@ -180,95 +191,158 @@ typedef enum BmpUndefined BMPUNDEFINED;
|
||||
* still gives the orientation of the BMP file.
|
||||
*/
|
||||
enum BmpOrient {
|
||||
BMP_ORIENT_BOTTOMUP,
|
||||
BMP_ORIENT_TOPDOWN
|
||||
BMP_ORIENT_BOTTOMUP,
|
||||
BMP_ORIENT_TOPDOWN
|
||||
};
|
||||
typedef enum BmpOrient BMPORIENT;
|
||||
|
||||
|
||||
/*
|
||||
* 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
|
||||
BMP_FORMAT_INT,
|
||||
BMP_FORMAT_FLOAT,
|
||||
BMP_FORMAT_S2_13
|
||||
};
|
||||
typedef enum BmpFormat BMPFORMAT;
|
||||
|
||||
|
||||
BMPHANDLE bmpread_new(FILE *file);
|
||||
|
||||
BMPRESULT bmpread_load_info(BMPHANDLE h);
|
||||
|
||||
BMPRESULT bmpread_dimensions(BMPHANDLE h,
|
||||
int *width,
|
||||
int *height,
|
||||
int *channels,
|
||||
int *bitsperchannel,
|
||||
BMPORIENT *orientation);
|
||||
|
||||
int bmpread_width(BMPHANDLE h);
|
||||
int bmpread_height(BMPHANDLE h);
|
||||
int bmpread_channels(BMPHANDLE h);
|
||||
int bmpread_bits_per_channel(BMPHANDLE h);
|
||||
BMPORIENT bmpread_orientation(BMPHANDLE h);
|
||||
|
||||
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);
|
||||
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;
|
||||
|
||||
|
||||
int bmpread_num_palette_colors(BMPHANDLE h);
|
||||
BMPRESULT bmpread_load_palette(BMPHANDLE h, unsigned char **palette);
|
||||
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' */
|
||||
};
|
||||
|
||||
|
||||
void bmpread_set_undefined(BMPHANDLE h, BMPUNDEFINED mode);
|
||||
void bmpread_set_insanity_limit(BMPHANDLE h, size_t limit);
|
||||
|
||||
int bmpread_is_64bit(BMPHANDLE h);
|
||||
BMPRESULT bmpread_set_64bit_conv(BMPHANDLE h, BMPCONV64 conv);
|
||||
APIDECL BMPHANDLE bmpread_new(FILE *file);
|
||||
|
||||
BMPINFOVER bmpread_info_header_version(BMPHANDLE h);
|
||||
int bmpread_info_header_size(BMPHANDLE h);
|
||||
int bmpread_info_compression(BMPHANDLE h);
|
||||
int bmpread_info_bitcount(BMPHANDLE h);
|
||||
const char* bmpread_info_header_name(BMPHANDLE h);
|
||||
const char* bmpread_info_compression_name(BMPHANDLE h);
|
||||
BMPRESULT bmpread_info_channel_bits(BMPHANDLE h, int *r, int *g, int *b, int *a);
|
||||
APIDECL 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);
|
||||
|
||||
|
||||
APIDECL int bmpread_array_num(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpread_array_info(BMPHANDLE h, struct BmpArrayInfo *ai, int idx);
|
||||
|
||||
|
||||
APIDECL BMPINFOVER bmpread_info_header_version(BMPHANDLE h);
|
||||
APIDECL const char* bmpread_info_header_name(BMPHANDLE h);
|
||||
APIDECL int bmpread_info_header_size(BMPHANDLE h);
|
||||
APIDECL int bmpread_info_compression(BMPHANDLE h);
|
||||
APIDECL const char* bmpread_info_compression_name(BMPHANDLE h);
|
||||
APIDECL int bmpread_info_bitcount(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpread_info_channel_bits(BMPHANDLE h, int *r, int *g, int *b, int *a);
|
||||
|
||||
|
||||
|
||||
|
||||
BMPHANDLE bmpwrite_new(FILE *file);
|
||||
APIDECL BMPHANDLE bmpwrite_new(FILE *file);
|
||||
|
||||
BMPRESULT bmpwrite_set_dimensions(BMPHANDLE h,
|
||||
unsigned width,
|
||||
unsigned height,
|
||||
unsigned channels,
|
||||
unsigned bits_per_channel);
|
||||
BMPRESULT bmpwrite_set_resolution(BMPHANDLE h, int xdpi, int ydpi);
|
||||
BMPRESULT bmpwrite_set_output_bits(BMPHANDLE h, int red, int green, int blue, int alpha);
|
||||
BMPRESULT bmpwrite_set_palette(BMPHANDLE h, int numcolors, const unsigned char *palette);
|
||||
BMPRESULT bmpwrite_allow_2bit(BMPHANDLE h);
|
||||
BMPRESULT bmpwrite_set_rle(BMPHANDLE h, BMPRLETYPE type);
|
||||
BMPRESULT bmpwrite_set_orientation(BMPHANDLE h, BMPORIENT orientation);
|
||||
BMPRESULT bmpwrite_set_64bit(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpwrite_set_dimensions(BMPHANDLE h,
|
||||
unsigned width,
|
||||
unsigned height,
|
||||
unsigned channels,
|
||||
unsigned bitsperchannel);
|
||||
APIDECL BMPRESULT bmpwrite_set_resolution(BMPHANDLE h, int xdpi, int ydpi);
|
||||
APIDECL BMPRESULT bmpwrite_set_output_bits(BMPHANDLE h, int red, int green, int blue, int alpha);
|
||||
APIDECL BMPRESULT bmpwrite_set_palette(BMPHANDLE h, int numcolors, const unsigned char *palette);
|
||||
APIDECL BMPRESULT bmpwrite_allow_2bit(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpwrite_allow_huffman(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpwrite_allow_rle24(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpwrite_set_rle(BMPHANDLE h, BMPRLETYPE type);
|
||||
APIDECL BMPRESULT bmpwrite_set_orientation(BMPHANDLE h, BMPORIENT orientation);
|
||||
APIDECL BMPRESULT bmpwrite_set_64bit(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpwrite_set_huffman_img_fg_idx(BMPHANDLE h, int idx);
|
||||
|
||||
BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image);
|
||||
BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line);
|
||||
APIDECL BMPRESULT bmpwrite_set_iccprofile(BMPHANDLE h, size_t size,
|
||||
const unsigned char *iccprofile);
|
||||
APIDECL BMPRESULT bmpwrite_set_rendering_intent(BMPHANDLE h, BMPINTENT intent);
|
||||
|
||||
APIDECL BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image);
|
||||
APIDECL BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line);
|
||||
|
||||
|
||||
BMPRESULT bmp_set_number_format(BMPHANDLE h, BMPFORMAT format);
|
||||
|
||||
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);
|
||||
|
||||
const char* bmp_version(void);
|
||||
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
|
||||
@@ -296,13 +370,6 @@ const char* bmp_version(void);
|
||||
|
||||
|
||||
|
||||
/* these functions are kept for binary compatibility and will be
|
||||
* removed from future versions:
|
||||
*/
|
||||
|
||||
int DEPR("use bmpread_orientation() instead") bmpread_topdown(BMPHANDLE h);
|
||||
void DEPR("use bmpread_set_undefined() instead") bmpread_set_undefined_to_alpha(BMPHANDLE h, int mode);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
30
logging.h
30
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
|
||||
|
||||
|
||||
|
||||
94
meson.build
94
meson.build
@@ -1,12 +1,23 @@
|
||||
project('bmplib', 'c', default_options: ['c_std=c11'], version: '1.4.7')
|
||||
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)
|
||||
@@ -26,7 +37,9 @@ 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']
|
||||
|
||||
|
||||
@@ -36,16 +49,85 @@ elif cc.sizeof('int') < 4
|
||||
error('sizeof(int) must be at least 32 bit.')
|
||||
endif
|
||||
|
||||
gen_huffman = executable('gen-huffman', 'gen-huffman.c')
|
||||
huff_codes = custom_target('huffman-codes.h',
|
||||
output: 'huffman-codes.h',
|
||||
command: [gen_huffman, '@OUTPUT@'],
|
||||
)
|
||||
|
||||
|
||||
bmplib = shared_library('bmp',
|
||||
bmplib_sources,
|
||||
[bmplib_sources, huff_codes[0]],
|
||||
version: meson.project_version(),
|
||||
install: true,
|
||||
dependencies: m_dep)
|
||||
dependencies: m_dep,
|
||||
)
|
||||
|
||||
pkg_mod = import('pkgconfig')
|
||||
pkg_mod.generate(libraries: bmplib,
|
||||
version: meson.project_version(),
|
||||
name: 'libbmp',
|
||||
filebase: 'libbmp',
|
||||
description: 'Library for reading/writing Windows BMP files.')
|
||||
description: 'Library for reading/writing Windows BMP files.',
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
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