11 Commits
icc ... v1.7.7

Author SHA1 Message Date
Rupert
b43fd940e6 bump version to 1.7.7 2025-04-18 15:53:08 +02:00
Rupert
61a4caf6ef fix line-by-line writing bug, refactor write state
bug introduced by 142427e17: line-by-line writing of RLE files resulted
in corrupted files.
Found while refactoring write state
2025-04-18 15:51:09 +02:00
Rupert
4942795e14 don't require explicitly calling load_info() before reading ICC profile 2025-04-17 23:48:19 +02:00
Rupert
52801cd5d5 cleanup s_decide_outformat() 2025-04-17 23:47:24 +02:00
Rupert
0e4e611955 add semicolons in doc for consistent syntax highlighter 2025-04-16 23:50:00 +02:00
Rupert
58ebfc8dc7 doc update, bump version to v1.7.6 2025-04-16 23:43:45 +02:00
Rupert
1e3f4abe16 add support for setting rendering intent
new function: bmpwrite_set_rendering_intent()
2025-04-16 23:08:14 +02:00
Rupert
be9882e04f documentation update
- add documentation for ICC profile functions
- add 'c' to code snippets to enable syntax highlighting
- minor corrections
2025-04-16 13:11:37 +02:00
Rupert
3218cd6e65 small fix in RLE24 preconditions 2025-04-15 22:13:26 +02:00
Rupert
142427e179 add ICC profile writing support
new function bmpwrite_set_iccprofile()
2025-04-15 22:13:26 +02:00
Rupert
0580565bf3 add ICC profile reading support
- new functions:
 - bmpread_iccprofile_size()
 - bmpread_load_iccprofile()
- only pass on profile as is, profile is not interpreted or applied
2025-04-15 22:10:13 +02:00
9 changed files with 794 additions and 311 deletions

View File

@@ -1,4 +1,4 @@
# Rupert's bmplib -- Full API Description (v1.7.5)
# Rupert's bmplib -- Full API Description (v1.7.7)
Refer to the *Quick Start Guide* (API-quick-start.md) for a quick intro to bmplib which describes only the minimal set of functions needed to read/write BMP files.
@@ -6,7 +6,7 @@ Refer to the *Quick Start Guide* (API-quick-start.md) for a quick intro to bmpli
### Get a handle
```
```c
BMPHANDLE bmpread_new(FILE *file)
```
@@ -18,7 +18,7 @@ The handle cannot be reused to read multiple files.
### Read the file header
```
```c
BMPRESULT bmpread_load_info(BMPHANDLE h)
```
@@ -31,7 +31,7 @@ bmplib reads the file header and checks validity. Possible return values are:
`bmpread_set_insanity_limit()` to increase the allowed file size.
Otherwise, `bmpread_load_image()` will refuse to load the image. (You can
build the library with a different default limit, using the meson
option '-Dinsanity_limit_mb=nnn')
option `-Dinsanity_limit_mb=nnn`)
- `BMP_RESULT_PNG` / `BMP_RESULT_JPEG`: It's not really a BMP file, but a
wrapped PNG or JPEG. The file pointer is left in the correct state to be
passed on to e.g. libpng or libjpeg.
@@ -44,13 +44,13 @@ Calling `bmpread_load_info()` is optional when you use `bmpread_dimensions()`
### Get image dimensions
```
```c
BMPRESULT bmpread_dimensions(BMPHANDLE h,
int *width,
int *height,
int *channels,
int *bitsperchannel,
BMPORIENT *orientation)
BMPORIENT *orientation);
```
Use `bmpread_dimensions()` to get all dimensions with one call. It is not
@@ -68,15 +68,15 @@ Note, in order to use these functions, -- unlike with `bmpread_dimensions
()` -- you must first (successfully) call `bmpread_load_info()`, otherwise
they will all return 0!
```
int bmpread_width(BMPHANDLE h)
int bmpread_height(BMPHANDLE h)
int bmpread_channels(BMPHANDLE h)
int bmpread_bitsperchannel(BMPHANDLE h)
BMPORIENT bmpread_orientation(BMPHANDLE h)
```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)
int bmpread_resolution_xdpi(BMPHANDLE h);
int bmpread_resolution_ydpi(BMPHANDLE h);
```
#### top-down / bottom-up
@@ -98,8 +98,8 @@ orientation of the original BMP.
#### Required size for buffer to receive image
```
size_t bmpread_buffersize(BMPHANDLE h)
```c
size_t bmpread_buffersize(BMPHANDLE h);
```
Returns the buffer size you have to allocate for the whole image.
@@ -112,9 +112,9 @@ image as 24-bit RGB data, same as non-indexed (RGB) BMPs.
If instead you want to keep the image as indexed, you have the option do so
with these two functions:
```
int bmpread_num_palette_colors(BMPHANDLE h)
BMPRESULT bmpread_load_palette(BMPHANDLE h, unsigned char **palette)
```c
int bmpread_num_palette_colors(BMPHANDLE h);
BMPRESULT bmpread_load_palette(BMPHANDLE h, unsigned char **palette);
```
`bmpread_num_palette_colors()` will return 0 for non-indexed images, otherwise
@@ -130,7 +130,7 @@ the palette buffer will contain "rgb0rgb0rgb0...".
As with the main image buffer, you can either provide one for the palette or
let bmplib allocate it for you (and then `free()` it, once you are done):
```
```c
unsigned char *palette;
int numcolors;
@@ -171,8 +171,8 @@ untouched by undefined pixels. (Note: if you let bmplib allocate the image
buffer, it will always be initialized to zero before loading the image). This
function has no effect on non-RLE BMPs.
```
void bmpread_set_undefined(BMPHANDLE h, BMPUNDEFINED mode)
```c
void bmpread_set_undefined(BMPHANDLE h, BMPUNDEFINED mode);
```
`mode` can be one of:
@@ -184,11 +184,37 @@ Note: If you use `bmpread_load_palette()` to switch to loading the index data
instead of RGB data, this setting will have no effect and undefined pixels
will always be left alone! (see above)
### ICC color profiles
```c
size_t bmpread_iccprofile_size(BMPHANDLE h);
BMPRESULT bmpread_load_iccprofile(BMPHANDLE h, unsigned char **pprofile);
```
Use `bmpread_iccprofile_size()` to query the size (or existence) of an
embedded color profile. If the BMP file doesn't contain a profile, the return
value is 0.
bmplib does not interpret or apply embedded ICC color profiles. The profile is
simply returned 'as is', image data is not afected in any way.
`bmpread_load_iccprofile()` loads the profile into the buffer pointed to by
`*pprofile`. As with loading image and palette data, you can either allocate
the buffer yourself or pass a pointer to a NULL-pointer and let bmplib
allocate an appropriate buffer, e.g.:
```c
unsigned char *profile = NULL;
if (bmpread_iccprofile_size(h) > 0)
bmpread_load_iccprofile(h, &profile);
```
### Optional settings for 64bit BMPs
```
int bmpread_is_64bit(BMPHANDLE h)
BMPRESULT bmpread_set_64bit_conv(BMPHANDLE h, BMPCONV64 conv)
```c
int bmpread_is_64bit(BMPHANDLE h);
BMPRESULT bmpread_set_64bit_conv(BMPHANDLE h, BMPCONV64 conv);
```
If you don't do anything, 64bit BMPs will be read like any other BMP and the
@@ -196,7 +222,11 @@ data will be returned as 16bit/channel sRGB RGBA.
But if you want to access the original s2.13 fixed-point components, or you
don't want the linear-to-sRGB conversion, you can use `bmpread_set_64bit_conv
()` and `bmp_set_number_format()` to control how the image is returned:
()` and `bmp_set_number_format()` to control how the image is returned.
64bit BMP pixel values are in the [-4...4) range, beyond the usual
[0...1]. Unless you load the image as `BMP_FORMAT_S2_13` or `BMP_FORMAT_FLOAT`,
the values will be clipped to [0...1].
Options for `bmpread_set_64bit()` are:
@@ -204,16 +234,18 @@ Options for `bmpread_set_64bit()` are:
fixed-point linear and converted to sRGB-gamma.
- `BMP_CONV64_LINEAR`: no gamma-conversion is applied to the image data.
- `BMP_CONV64_NONE`: this option is just a shorthand for setting
BMP_CONV64_LINEAR *and* BMP_FORMAT_S2_13. Image values are returned exactly
as they are in the BMP file, without any conversion or attempt at
interpretation.
`BMP_CONV64_LINEAR` *and also* calling `bmp_set_number_format()` with
`BMP_FORMAT_S2_13`. Image values are returned exactly as they are in the BMP
file, without any conversion or attempt at interpretation.
### Setting a number format
By default, bmplib will always return the image data as 8-,16-, or 32-bit integer values. You can instead set the number format to floating point or fixed using:
By default, bmplib will always return the image data as 8-,16-, or 32-bit
integer values. You can instead set the number format to floating point or
fixed using:
```
BMPRESULT bmp_set_number_format(BMPHANDLE h, BMPFORMAT format)
```c
BMPRESULT bmp_set_number_format(BMPHANDLE h, BMPFORMAT format);
```
(see below, *3. General functions for both reading/writing BMPs*)
@@ -221,21 +253,20 @@ BMPRESULT bmp_set_number_format(BMPHANDLE h, BMPFORMAT format)
### Huge files: bmpread_set_insanity_limit()
bmplib will refuse to load images beyond a certain size (default 500MB) and
instead return BMP_RESULT_INSANE. If you want to load the image anyway, call
`bmpread_set_insanity_limit()` at any time before calling `bmpread_load_image
()`. `limit` is the new allowed size in bytes. (not MB!)
instead return `BMP_RESULT_INSANE`. If you want to load the image anyway, call
`bmpread_set_insanity_limit()` at any time before calling `bmpread_load_image()`.
`limit` is the new allowed size in bytes (not MB!).
```
void
bmpread_set_insanity_limit(BMPHANDLE h, size_t limit)
```c
void bmpread_set_insanity_limit(BMPHANDLE h, size_t limit);
```
### Load the image
#### bmpread_load_image()
```
BMPRESULT bmpread_load_image(BMPHANDLE h, unsigned char **pbuffer)
```c
BMPRESULT bmpread_load_image(BMPHANDLE h, unsigned char **pbuffer);
```
Loads the complete image from the BMP file into the buffer pointed to by
@@ -247,7 +278,7 @@ with it.
If you allocate the buffer yourself, the buffer must be at least as large as
the size returned by `bmpread_buffersize()`.
```
```c
unsigned char *buffer;
/* either: */
@@ -265,14 +296,14 @@ order R-G-B or R-G-B-A. The returned image is always top-down, i.e. data
starts in the top left corner. Unlike BMPs which are (almost always)
bottom-up. (See above, "Getting information...")
If `bmpread_load_image()` returns BMP_RESULT_TRUNCATED or BMP_RESULT_INVALID,
If `bmpread_load_image()` returns `BMP_RESULT_TRUNCATED` or `BMP_RESULT_INVALID`,
the file may have been damaged or simply contains invalid image data. Image
data is loaded anyway as far as possible and may be partially usable.
#### bmpread_load_line()
```
BMPRESULT bmpread_load_line(BMPHANDLE h, unsigned char **pbuffer)
```c
BMPRESULT bmpread_load_line(BMPHANDLE h, unsigned char **pbuffer);
```
Loads a single scan line from the BMP file into the buffer pointed to by
@@ -286,7 +317,7 @@ To determine the required buffer size, either divide the value from
calculate from the image dimensions returned by bmplib as width * channels *
bitsperchannel / 8.
```
```c
single_line_buffersize = bmpread_buffersize(h) / bmpread_height(h);
/* or */
single_line_buffersize = bmpread_width(h) * bmpread_channels(h) * bmpread_bitsperchannel(h) / 8;
@@ -310,8 +341,8 @@ be set to the maximum allowed value, and attempts to point outside the image
will be ignored.
In both cases, `bmpread_load_image()` and `bmpread_load_line()` will return
BMP_RESULT_INVALID, unless the image is also truncated, then
BMP_RESULT_TRUNCATED is returned.
`BMP_RESULT_INVALID`, unless the image is also truncated, then
`BMP_RESULT_TRUNCATED` is returned.
### Query info about the BMP file
@@ -319,25 +350,25 @@ 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)
```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);
```
### Release the handle
```
void bmp_free(BMPHANDLE h)
```c
void bmp_free(BMPHANDLE h);
```
Frees all resources associated with the handle `h`. **Image data is not
affected**, so you can call bmp_free() immediately after `bmpread_load_image
()` and still use the returned image data.
affected**, so you can call `bmp_free()` immediately after `bmpread_load_image()`
and still use the returned image data.
Note: Any error message strings returned by `bmp_errmsg()` are invalidated by
`bmp_free()` and must not be used anymore!
@@ -346,20 +377,20 @@ Note: Any error message strings returned by `bmp_errmsg()` are invalidated by
### Get a handle
```
BMPHANDLE bmpwrite_new(FILE *file)
```c
BMPHANDLE bmpwrite_new(FILE *file);
```
### Set image dimensions
```
```c
BMPRESULT bmpwrite_set_dimensions(BMPHANDLE h,
unsigned width,
unsigned height,
unsigned channels,
unsigned bitsperchannel)
unsigned bitsperchannel);
BMPRESULT bmpwrite_set_resolution(BMPHANDLE h, int xdpi, int ydpi)
BMPRESULT bmpwrite_set_resolution(BMPHANDLE h, int xdpi, int ydpi);
```
Note: the dimensions set with `bmpwrite_set_dimensions()` describe the source
@@ -374,15 +405,15 @@ choose appropriate bit-depths for your image. The bit-depth per channel can
be anywhere between 0 and 32, inclusive. In sum, the bits must be at least 1
and must not exceed 32.
```
BMPRESULT bmpwrite_set_output_bits(BMPHANDLE h, int red, int green, int blue, int alpha)
```c
BMPRESULT bmpwrite_set_output_bits(BMPHANDLE h, int red, int green, int blue, int alpha);
```
### Indexed images
```
BMPRESULT bmpwrite_set_palette(BMPHANDLE h, int numcolors, unsigned char *palette)
BMPRESULT bmpwrite_allow_2bit(BMPHANDLE h)
```c
BMPRESULT bmpwrite_set_palette(BMPHANDLE h, int numcolors, unsigned char *palette);
BMPRESULT bmpwrite_allow_2bit(BMPHANDLE h);
```
You can write 1/2/4/8-bit indexed images by providing a color palette with
@@ -404,13 +435,13 @@ BMP for 3- or 4-color images, call `bmpwrite_allow_2bit()` before calling
#### RLE
```
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)
```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 run-lenght-encoded (RLE) bitmaps.
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.
@@ -440,8 +471,8 @@ 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
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.
@@ -464,8 +495,8 @@ usually orientated.
For non-RLE files, you have the option to change the orientation to top-down.
(RLE files always have to be written in the default bottom-up orientation.)
```
BMPRESULT bmpwrite_set_orientation(BMPHANDLE h, BMPORIENT orientation)
```c
BMPRESULT bmpwrite_set_orientation(BMPHANDLE h, BMPORIENT orientation);
```
with `orientation` set to one of the following values:
@@ -481,26 +512,58 @@ 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.
### ICC color profiles
```c
BMPRESULT bmpwrite_set_iccprofile(BMPHANDLE h, size_t size,
const unsigned char *iccprofile);
BMPRESULT bmpwrite_set_rendering_intent(BMPHANDLE h, BMPINTENT intent);
```
Use `bmpwrite_set_iccprofile()` to write an embedded ICC color profile to the
BMP file.
bmplib will not interpret or validate the supplied profile in any way.
Setting a color profile or rendering intent will disable Huffman and RLE24 encodings.
(color profiles require a BITMAPV5HEADER, but those encodings would require
an older OS/2 info header, instead.)
You can optionally specify a rendering intent with `bmpwrite_set_rendering_intent()`,
where `intent` is one of:
- `BMP_INTENT_NONE`
- `BMP_INTENT_BUSINESS` (= saturation)
- `BMP_INTENT_GRAPHICS` (= relative colorimetric)
- `BMP_INTENT_IMAGES` (= perceptive)
- `BMP_INTENT_ABS_COLORIMETRIC` (= absolute colorimetric)
### 64-bit RGBA BMPs
By default, bmplib will not write 64-bit BMPs because they are rather exotic and hardly any
software can open them.
By default, bmplib will not write 64-bit BMPs because they are rather exotic
and hardly any software can open them.
If you do want to write 64-bit BMPs, call
```
BMPRESULT bmpwrite_set_64bit(BMPHANDLE h)
```c
BMPRESULT bmpwrite_set_64bit(BMPHANDLE h);
```
In order to make use of the extended range available in 64-bit BMPs (-4.0 to +3.999...), you will probably want to provide the image buffer either as 32-bit float or as 16-bit s2.13 (and call `bmp_set_number_format()` accordingly).
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.
Note: 64-bit BMPs store pixel values in *linear light*. Unlike when *reading*
64-bit BMPs, bmplib will not make any gamma/linear conversion while writing
BMPs. You have to provide the proper linear values in the image buffer.
### Write the image
```
BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image)
BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line)
```c
BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image);
BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line);
```
Write either the whole image at once with `bmpwrite_save_image()` or one line
@@ -512,8 +575,8 @@ in host byte order, the channels in the order R-G-B-(A). Indexed data must be
supplied as 8 bit per pixel, even when writing lower bit (1/2/4) BMPs
(see above).
Important: When writing the whole image at once using `bmpwrite_save_image
()`, the image data must be provided top-down (same as is returned by
Important: When writing the whole image at once using `bmpwrite_save_image()`,
the image data must be provided top-down (same as is returned by
`bmpread_load_image()`). When using `bmpwrite_save_line()` to write the image
line-by-line, the image data must be provided according to the orientation
set with `bmpwrite_set_orientation()` (see above).
@@ -522,52 +585,56 @@ set with `bmpwrite_set_orientation()` (see above).
### bmp_free()
```
void bmp_free(BMPHANDLE h)
```c
void bmp_free(BMPHANDLE h);
```
Frees all resources associated with the handle `h`. Image data is not
affected, so you can call bmp_free() immediately after bmpread_load_image
() and still use the returned image data. Note: Any error messages returned
affected, so you can call `bmp_free()` immediately after `bmpread_load_image()`
and still use the returned image data. Note: Any error messages returned
by `bmp_errmsg()` are invalidated by `bmp_free()` and cannot be used
anymore.
anymore!
### bmp_errmsg()
```
const char* bmp_errmsg(BMPHANDLE h)
```c
const char* bmp_errmsg(BMPHANDLE h);
```
Returns a zero-terminated character string containing the last error
description(s). The returned string is safe to use until any other
bmplib-function is called with the same handle.
bmplib-function is called with the same handle or the handle is freed with
`bmp_free()`.
### bmp_set_number_format()
```
BMPRESULT bmp_set_number_format(BMPHANDLE h, BMPFORMAT format)
```c
BMPRESULT bmp_set_number_format(BMPHANDLE h, BMPFORMAT format);
```
sets the number format of the image buffer received from / passed to bmplib. `format` can be one of
- `BMP_FORMAT_INT` image buffer values are expected/returned as 8-, 16-, or 32-bit integers. (this is the default)
- `BMP_FORMAT_INT` image buffer values are expected/returned as 8-, 16-, or
32-bit integers. (this is the default)
- `BMP_FORMAT_FLOAT` image buffer values are expected/returned as 32-bit floating point numbers (C `float`).
- `BMP_FORMAT_S2_13` image buffer values are expected/returned as s2.13 fixed point numbers. s2.13 is a 16-bit format with one sign bit, 2 integer bits, and 13 bits for the fractional part. Range is from -4.0 to +3.999...
- `BMP_FORMAT_S2_13` image buffer values are expected/returned as s2.13 fixed
point numbers. s2.13 is a 16-bit format with one sign bit, 2 integer bits,
and 13 bits for the fractional part. Range is from -4.0 to +3.999...
For indexed images, `BMP_FORMAT_INT` is the only valid format.
### bmp_version()
```
const char* bmp_version(void)
```c
const char* bmp_version(void);
```
Returns a zero-terminated character string containing the version of bmplib.
### bmp_set_huffman_t4black_value()
```
BMPRESULT bmp_set_huffman_t4black_value(BMPHANDLE h, int blackidx)
```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
@@ -592,8 +659,8 @@ Changing this value will invert the image colors!
Reasons to use this function:
- You know that bmplib's default of 'black'=1 is wrong, and you want to set it
to 0. (In that case, please also drop a note on github.)
- 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.
@@ -604,9 +671,8 @@ Reasons to use this function:
#### `BMPHANDLE`
Returned by `bmpread_new()` and `bmpwrite_new()`.
Identifies the current operation for all subsequent
calls to bmplib-functions.
Returned by `bmpread_new()` and `bmpwrite_new()`. Identifies the current
operation for all subsequent calls to bmplib-functions.
#### `BMPRESULT`
@@ -639,7 +705,7 @@ else {
Returned by `bmpread_info_header_version()`. Possible values are:
- `BMPINFO_CORE_OS21` BITMAPCOREHEADER aka OS21XBITMAPHEADER (12 bytes)
- `BMPINFO_OS22` OS22XBITMAPHEADER (16/40/64 bytes)
- `BMPINFO_OS22` OS22XBITMAPHEADER (16-64 bytes)
- `BMPINFO_V3` BITMAPINFOHEADER (40 bytes)
- `BMPINFO_V3_ADOBE1` BITMAPINFOHEADER with additional RGB masks (52 bytes)
- `BMPINFO_V3_ADOBE2` BITMAPINFOHEADER with additional RGBA masks (56 bytes)
@@ -665,7 +731,7 @@ Can safely be cast from/to int.
Used in `bmpread_set_undefined()`. Possible values are:
- `BMP_UNDEFINED_TO_ALPHA` (default)
- `BMP_UNDEFINED_TO_ZERO`
- `BMP_UNDEFINED_LEAVE`
Can safely be cast from/to int.
@@ -691,7 +757,7 @@ Used in `bmp_set_number_format()`. Possible values are:
### Reading BMPs
```
```c
/* (all error checking left out for clarity) */
BMPHANDLE h;
@@ -743,7 +809,7 @@ Used in `bmp_set_number_format()`. Possible values are:
### Writing BMPs
```
```c
/* (all error checking left out for clarity) */
BMPHANDLE h;

View File

@@ -12,15 +12,16 @@ For the complete API, refer to the *Full API Description* (API-full.md).
## 1. Reading BMP files:
```
bmpread_new()
bmpread_dimensions()
bmpread_load_image()
bmp_free()
```c
bmpread_new();
bmpread_dimensions();
bmpread_load_image();
bmp_free();
```
### Get a handle
```
```c
BMPHANDLE bmpread_new(FILE *file)
```
@@ -32,13 +33,13 @@ The handle cannot be reused to read multiple files.
### Get image dimensions
```
```c
BMPRESULT bmpread_dimensions(BMPHANDLE h,
int *width,
int *height,
int *channels,
int *bitsperchannel,
BMPORIENT *orientation)
BMPORIENT *orientation);
```
Use `bmpread_dimensions()` to get all dimensions with one call. The return
@@ -54,8 +55,8 @@ line-by-line. Can be set to NULL. (see *Full API Description*)
### Load the image
```
BMPRESULT bmpread_load_image(BMPHANDLE h, unsigned char **pbuffer)
```c
BMPRESULT bmpread_load_image(BMPHANDLE h, unsigned char **pbuffer);
```
Loads the complete image from the BMP file into the buffer pointed to by
@@ -67,7 +68,7 @@ with it.
If you allocate the buffer yourself, the buffer must be at least as large as
the size returned by `bmpread_buffersize()` (see *Full API description*).
```
```c
unsigned char *buffer;
/* either: */
@@ -86,7 +87,7 @@ in the order R-G-B or R-G-B-A. The returned image is always top-down, i.e.
data starts in the top left corner. Unlike BMPs which are (almost always)
bottom-up.
If `bmpread_load_image()` returns BMP_RESULT_TRUNCATED or BMP_RESULT_INVALID,
If `bmpread_load_image()` returns `BMP_RESULT_TRUNCATED` or `BMP_RESULT_INVALID`,
the file may have been damaged or simply contains invalid image data. Image
data is loaded anyway as far as possible and may be partially usable.
@@ -94,13 +95,13 @@ data is loaded anyway as far as possible and may be partially usable.
### Release the handle
```
void bmp_free(BMPHANDLE h)
```c
void bmp_free(BMPHANDLE h);
```
Frees all resources associated with the handle `h`. **Image data is not
affected**, so you can call bmp_free() immediately after `bmpread_load_image
()` and still use the returned image data.
affected**, so you can call `bmp_free()` immediately after `bmpread_load_image()`
and still use the returned image data.
Note: Any error message strings returned by `bmp_errmsg()` are invalidated by
`bmp_free()` and must not be used anymore!
@@ -109,25 +110,27 @@ Note: Any error message strings returned by `bmp_errmsg()` are invalidated by
## 2. Writing BMP files:
```
bmpwrite_new()
bmpwrite_set_dimensions()
bmpwrite_save_image()
bmp_free()
```c
bmpwrite_new();
bmpwrite_set_dimensions();
bmpwrite_save_image();
bmp_free();
```
### Get a handle
```
BMPHANDLE bmpwrite_new(FILE *file)
```c
BMPHANDLE bmpwrite_new(FILE *file);
```
### Set image dimensions
```
```c
BMPRESULT bmpwrite_set_dimensions(BMPHANDLE h,
unsigned width,
unsigned height,
unsigned channels,
unsigned bitsperchannel)
unsigned bitsperchannel);
```
Note: the dimensions set with `bmpwrite_set_dimensions()` describe the source
@@ -140,8 +143,8 @@ API description*)
### Write the image
```
BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image)
```c
BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image);
```
Write the whole image at once with `bmpwrite_save_image()`.
@@ -155,8 +158,8 @@ file will be bottom-up.)
### bmp_free()
```
void bmp_free(BMPHANDLE h)
```c
void bmp_free(BMPHANDLE h);
```
Frees all resources associated with the handle `h`.
@@ -182,7 +185,7 @@ Many bmplib functions return the success/failure of an operation as a
- `BMP_RESULT_JPEG`
- `BMP_RESULT_ERROR`
Can safely be cast from/to int. BMP_RESULT_OK is guaranteed to have the value 0.
Can safely be cast from/to int. `BMP_RESULT_OK` is guaranteed to have the value 0.
@@ -190,7 +193,7 @@ Can safely be cast from/to int. BMP_RESULT_OK is guaranteed to have the value 0.
### Reading BMPs
```
```c
/* (all error checking left out for clarity) */
BMPHANDLE h;
@@ -236,7 +239,7 @@ Can safely be cast from/to int. BMP_RESULT_OK is guaranteed to have the value 0.
### Writing BMPs
```
```c
/* (all error checking left out for clarity) */
BMPHANDLE h;

View File

@@ -8,7 +8,7 @@
Download [bmplib on github](https://github.com/rupertwh/bmplib).
## Current status (v1.7.5):
## Current status (v1.7.7):
### Reading BMP files:
- 16/24/32 bit RGB(A) with any bits/channel combination
(BI_RGB, BI_BITFIELDS, BI_ALPHABITFIELDS).
@@ -25,7 +25,7 @@ Download [bmplib on github](https://github.com/rupertwh/bmplib).
- most 'questionable' files (see below)
- some 'bad' files
Questionable files that failed:
Questionable files that fail:
- embedded JPEG and PNG. Not really a fail. We return BMP_RESULT_JPEG or
BMP_RESULT_PNG and leave the file pointer in the correct state to be
passed on to either libpng or libjpeg. Works as designed. Don't want to
@@ -125,8 +125,10 @@ conversion:
- `bmpread_set_64bit_conv()`
- `bmp_set_number_format()`
As to writing BMPs, by default bmplib will not write 64bit BMPs, as they are so exotic that only few applications will read them (other than native Microsoft tools, the new GIMP 3.0 is the only one I am aware of).
Use `bmpwrite_set_64bit()` in order to write 64bit BMPs.
As to writing BMPs, by default bmplib will not write 64bit BMPs, as they are
so exotic that only few applications will read them (other than native
Microsoft tools, the new GIMP 3.0 is the only one I am aware of). Use
`bmpwrite_set_64bit()` in order to write 64bit BMPs.
## TODOs:
@@ -138,7 +140,7 @@ Use `bmpwrite_set_64bit()` in order to write 64bit BMPs.
- [x] read Huffman-encoded BMPs. (Still haven't found any real-life examples)
- [x] line-by-line reading/writing. ~~Right now, the image can only be
passed as a whole to/from bmplib.~~
- [ ] read/write icc-profile and chromaticity/gamma values
- [x] read/write icc-profile and chromaticity/gamma values
- [x] sanity checks for size of of image / palette. Require confirmation
above a certain size (~ 500MB?)
- [x] store undefined pixels (RLE delta and early EOL/EOF) as alpha

View File

@@ -441,3 +441,27 @@ int16_t s16_from_le(const unsigned char *buf)
{
return (int16_t)u16_from_le(buf);
}
/*****************************************************************************
* 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";
}
}

View File

@@ -154,6 +154,14 @@ struct Bmpread {
};
enum WriteState {
WS_INIT,
WS_DIMENSIONS_SET,
WS_SAVE_STARTED,
WS_SAVE_DONE,
WS_FATAL,
};
struct Bmpwrite {
struct Bmpcommon c;
FILE *file;
@@ -166,30 +174,30 @@ struct Bmpwrite {
int source_bitsperchannel;
int source_bytes_per_pixel;
enum BmpFormat source_format;
bool source_has_alpha;
struct Palette *palette;
int palette_size; /* sizeof palette in bytes */
unsigned char *iccprofile;
int iccprofile_size;
/* output */
size_t bytes_written;
uint64_t bytes_written;
size_t bytes_written_before_bitdata;
bool has_alpha;
enum BmpOrient outorientation;
bool huffman_fg_idx;
struct Colormask cmask;
enum BmpRLEtype rle_requested;
int rle; /* 1, 4, or 8 */
bool allow_2bit; /* Windows CE, but many will not read it */
bool allow_huffman;
bool allow_rle24;
int rle; /* 1, 4, 8, or 24 */
bool allow_2bit; /* Windows CE */
bool allow_huffman; /* OS/2 */
bool allow_rle24; /* OS/2 */
bool out64bit;
int outbytes_per_pixel;
int padding;
int *group;
int group_count;
/* state */
enum WriteState write_state;
bool outbits_set;
bool dimensions_set;
bool saveimage_done;
bool line_by_line;
int lbl_y;
uint32_t hufbuf;
int hufbuf_len;
@@ -242,6 +250,8 @@ 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
@@ -258,6 +268,7 @@ int16_t s16_from_le(const unsigned char *buf);
#define BMPIHSIZE_V3 40
#define BMPIHSIZE_V4 108
#define BMPIHSIZE_OS22 64
#define BMPIHSIZE_V5 124
struct Bmpfile {
uint16_t type; /* "BM" */
@@ -317,7 +328,9 @@ struct Bmpinfo {
enum BmpInfoVer version;
};
#define IH_PROFILEDATA_OFFSET (14L + 112L)
#define MAX_ICCPROFILE_SIZE (1UL << 20)
#define BI_RGB 0
@@ -344,3 +357,9 @@ struct Bmpinfo {
#define LCS_WINDOWS_COLOR_SPACE 0x57696e20 /* 'Win ' */
#define PROFILE_LINKED 0x4c494e4b /* 'LINK' */
#define PROFILE_EMBEDDED 0x4d424544 /* 'MBED' */
#define LCS_GM_BUSINESS 1
#define LCS_GM_GRAPHICS 2
#define LCS_GM_IMAGES 4
#define LCS_GM_ABS_COLORIMETRIC 8

View File

@@ -35,7 +35,6 @@
#include "bmp-read.h"
const char* s_infoheader_name(int infoversion);
const char* s_compression_name(int compression);
@@ -267,6 +266,13 @@ API int bmpread_is_64bit(BMPHANDLE h)
if (!(rp = cm_read_handle(h)))
return 0;
if (!rp->getinfo_called)
bmpread_load_info((BMPHANDLE)(void*)rp);
if (rp->getinfo_return != BMP_RESULT_OK && rp->getinfo_return != BMP_RESULT_INSANE) {
return 0;
}
if (rp->ih->bitcount == 64)
return 1;
return 0;
@@ -274,6 +280,121 @@ API int bmpread_is_64bit(BMPHANDLE h)
/*****************************************************************************
* bmpread_iccprofile_size
*****************************************************************************/
API size_t bmpread_iccprofile_size(BMPHANDLE h)
{
BMPREAD rp;
if (!(rp = cm_read_handle(h)))
return 0;
if (!rp->getinfo_called)
return 0;
if (rp->getinfo_return != BMP_RESULT_OK && rp->getinfo_return != BMP_RESULT_INSANE) {
return 0;
}
if (rp->ih->cstype == PROFILE_EMBEDDED && rp->ih->profilesize <= MAX_ICCPROFILE_SIZE)
return (size_t)rp->ih->profilesize;
return 0;
}
/********************************************************
* bmpread_load_iccprofile
*******************************************************/
API BMPRESULT bmpread_load_iccprofile(BMPHANDLE h, unsigned char **profile)
{
BMPREAD rp;
size_t memsize;
long pos;
bool we_allocated = false;
bool file_messed_up = false;
if (!(rp = cm_read_handle(h)))
goto abort;
if (!rp->getinfo_called)
bmpread_load_info((BMPHANDLE)(void*)rp);
if (rp->getinfo_return != BMP_RESULT_OK && rp->getinfo_return != BMP_RESULT_INSANE) {
goto abort;
}
if (rp->ih->cstype != PROFILE_EMBEDDED) {
logerr(rp->c.log, "Image has no ICC profile");
goto abort;
}
if (rp->ih->profilesize > MAX_ICCPROFILE_SIZE) {
logerr(rp->c.log, "ICC profile is too large (%lu). Max is %lu",
(unsigned long) rp->ih->profilesize,
(unsigned long) MAX_ICCPROFILE_SIZE);
goto abort;
}
if (!profile) {
logerr(rp->c.log, "profile is NULL");
goto abort;
}
memsize = rp->ih->profilesize;
if (!*profile) {
if (!(*profile = malloc(memsize))) {
logsyserr(rp->c.log, "allocating ICC profile");
goto abort;
}
we_allocated = true;
}
memset(*profile, 0, memsize);
if (-1 == (pos = ftell(rp->file))) {
logsyserr(rp->c.log, "reading current file position");
goto abort;
}
if (fseek(rp->file, rp->ih->profiledata + 14, SEEK_SET)) {
logsyserr(rp->c.log, "seeking ICC profile in file");
goto abort;
}
/* Any failure from here on out cannot be reasonably recovered from, as
* the file position will be messed up! */
file_messed_up = true;
if (memsize != fread(*profile, 1, memsize, rp->file)) {
if (feof(rp->file))
logerr(rp->c.log, "EOF while reading ICC profile");
else
logsyserr(rp->c.log, "reading ICC profile");
goto abort;
}
if (fseek(rp->file, pos, SEEK_SET)) {
logsyserr(rp->c.log, "failed to reset file position after reading ICC profile");
goto abort;
}
return BMP_RESULT_OK;
abort:
if (profile && *profile && we_allocated) {
free(*profile);
*profile = NULL;
}
if (file_messed_up)
rp->getinfo_return = BMP_RESULT_ERROR;
return BMP_RESULT_ERROR;
}
/*****************************************************************************
* bmpread_dimensions
*****************************************************************************/
@@ -1410,7 +1531,7 @@ static const char* s_info_str(BMPHANDLE h, enum Infostr info)
switch (info) {
case INFO_STR_HEADER_NAME:
return s_infoheader_name(rp->ih->version);
return cm_infoheader_name(rp->ih->version);
case INFO_STR_COMPRESSION_NAME:
return s_compression_name(rp->ih->compression);
@@ -1455,28 +1576,6 @@ API BMPRESULT bmpread_info_channel_bits(BMPHANDLE h, int *r, int *g, int *b, int
/*****************************************************************************
* s_infoheader_name
*****************************************************************************/
const char* s_infoheader_name(int 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";
}
}
/*****************************************************************************
* s_compression_name
*****************************************************************************/

View File

@@ -37,16 +37,21 @@
#include "huffman.h"
#include "bmp-write.h"
static void s_decide_outformat(BMPWRITE_R wp);
static bool s_decide_outformat(BMPWRITE_R wp);
static bool s_write_palette(BMPWRITE_R wp);
static bool s_write_bmp_file_header(BMPWRITE_R wp);
static bool s_write_bmp_info_header(BMPWRITE_R wp);
static bool s_write_iccprofile(BMPWRITE_R wp);
static inline int s_write_one_byte(int byte, BMPWRITE_R wp);
static bool s_save_header(BMPWRITE_R wp);
static bool s_try_saving_image_size(BMPWRITE_R wp);
static bool s_try_saving_image_size(BMPWRITE_R wp, uint64_t file_size, uint64_t image_size);
static bool s_finalize_file(BMPWRITE_R wp);
static int s_calc_mask_values(BMPWRITE_R wp);
static bool s_is_setting_compatible(BMPWRITE_R wp, const char *setting, ...);
static bool s_check_already_saved(BMPWRITE_R wp);
static bool s_check_save_started(BMPWRITE_R wp);
static bool s_ready_to_save(BMPWRITE_R wp);
@@ -68,6 +73,7 @@ API BMPHANDLE bmpwrite_new(FILE *file)
wp->outorientation = BMP_ORIENT_BOTTOMUP;
wp->source_format = BMP_FORMAT_INT;
wp->huffman_fg_idx = 1;
wp->write_state = WS_INIT;
if (!(wp->c.log = logcreate()))
goto abort;
@@ -118,21 +124,25 @@ API BMPRESULT bmpwrite_set_dimensions(BMPHANDLE h,
if (!(wp = cm_write_handle(h)))
return BMP_RESULT_ERROR;
if (s_check_already_saved(wp))
if (s_check_save_started(wp))
return BMP_RESULT_ERROR;
if (!(s_is_setting_compatible(wp, "srcchannels", source_channels) &&
s_is_setting_compatible(wp, "srcbits", source_bitsperchannel)))
s_is_setting_compatible(wp, "srcbits", source_bitsperchannel))) {
wp->write_state = WS_INIT;
return BMP_RESULT_ERROR;
}
if (!cm_is_one_of(3, source_bitsperchannel, 8, 16, 32)) {
logerr(wp->c.log, "Invalid number of bits per channel: %d",
(int) source_bitsperchannel);
wp->write_state = WS_INIT;
return BMP_RESULT_ERROR;
}
if (!cm_is_one_of(4, source_channels, 3, 4, 1, 2)) {
logerr(wp->c.log, "Invalid number of channels: %d", (int) source_channels);
wp->write_state = WS_INIT;
return BMP_RESULT_ERROR;
}
@@ -144,6 +154,7 @@ API BMPRESULT bmpwrite_set_dimensions(BMPHANDLE h,
logerr(wp->c.log, "Invalid dimensions %ux%ux%u @ %ubits",
width, height, source_channels,
source_bitsperchannel);
wp->write_state = WS_INIT;
return BMP_RESULT_ERROR;
}
@@ -151,7 +162,7 @@ API BMPRESULT bmpwrite_set_dimensions(BMPHANDLE h,
wp->height = (int) height;
wp->source_channels = (int) source_channels;
wp->source_bitsperchannel = (int) source_bitsperchannel;
wp->dimensions_set = true;
wp->write_state = WS_DIMENSIONS_SET;
return BMP_RESULT_OK;
}
@@ -167,7 +178,7 @@ BMPRESULT bw_set_number_format(BMPWRITE_R wp, enum BmpFormat format)
if (format == wp->source_format)
return BMP_RESULT_OK;
if (s_check_already_saved(wp))
if (s_check_save_started(wp))
return BMP_RESULT_ERROR;
if (!s_is_setting_compatible(wp, "format", format))
@@ -190,11 +201,13 @@ API BMPRESULT bmpwrite_set_output_bits(BMPHANDLE h, int red, int green, int blue
if (!(wp = cm_write_handle(h)))
return BMP_RESULT_ERROR;
if (s_check_already_saved(wp))
if (s_check_save_started(wp))
return BMP_RESULT_ERROR;
if (!s_is_setting_compatible(wp, "outbits"))
if (!s_is_setting_compatible(wp, "outbits")) {
wp->outbits_set = false;
return BMP_RESULT_ERROR;
}
if (!(cm_all_positive_int(4, red, green, blue, alpha) &&
cm_all_lessoreq_int(32, 4, red, green, blue, alpha) &&
@@ -232,7 +245,7 @@ API BMPRESULT bmpwrite_set_palette(BMPHANDLE h, int numcolors,
if (!(wp = cm_write_handle(h)))
return BMP_RESULT_ERROR;
if (s_check_already_saved(wp))
if (s_check_save_started(wp))
return BMP_RESULT_ERROR;
if (wp->palette) {
@@ -268,6 +281,90 @@ API BMPRESULT bmpwrite_set_palette(BMPHANDLE h, int numcolors,
/*****************************************************************************
* bmpwrite_set_iccprofile
*****************************************************************************/
API BMPRESULT bmpwrite_set_iccprofile(BMPHANDLE h, size_t size,
const unsigned char *iccprofile)
{
BMPWRITE wp;
assert(MAX_ICCPROFILE_SIZE < INT_MAX);
if (!(wp = cm_write_handle(h)))
return BMP_RESULT_ERROR;
if (s_check_save_started(wp))
return BMP_RESULT_ERROR;
if (wp->iccprofile) {
free(wp->iccprofile);
wp->iccprofile = NULL;
wp->iccprofile_size = 0;
wp->ih->profilesize = 0;
wp->ih->cstype = LCS_WINDOWS_COLOR_SPACE;
}
if (size > MAX_ICCPROFILE_SIZE) {
logerr(wp->c.log, "ICC profile is too large (%zuMB). Max is %luMB.",
size >> 20, (unsigned long)(MAX_ICCPROFILE_SIZE >> 20));
return BMP_RESULT_ERROR;
}
if (!(wp->iccprofile = malloc(size))) {
logsyserr(wp->c.log, "Allocating ICC profile");
return BMP_RESULT_ERROR;
}
memcpy(wp->iccprofile, iccprofile, size);
wp->iccprofile_size = (int)size;
wp->ih->profilesize = size;
wp->ih->cstype = PROFILE_EMBEDDED;
return BMP_RESULT_OK;
}
/*****************************************************************************
* bmpwrite_set_rendering_intent
*****************************************************************************/
API BMPRESULT bmpwrite_set_rendering_intent(BMPHANDLE h, BMPINTENT intent)
{
BMPWRITE wp;
if (!(wp = cm_write_handle(h)))
return BMP_RESULT_ERROR;
if (s_check_save_started(wp))
return BMP_RESULT_ERROR;
switch(intent) {
case BMP_INTENT_NONE:
wp->ih->intent = 0;
break;
case BMP_INTENT_BUSINESS:
wp->ih->intent = LCS_GM_BUSINESS;
break;
case BMP_INTENT_GRAPHICS:
wp->ih->intent = LCS_GM_GRAPHICS;
break;
case BMP_INTENT_IMAGES:
wp->ih->intent = LCS_GM_IMAGES;
break;
case BMP_INTENT_ABS_COLORIMETRIC:
wp->ih->intent = LCS_GM_ABS_COLORIMETRIC;
break;
default:
logerr(wp->c.log, "Invalid redering intent: %d", (int) intent);
return BMP_RESULT_ERROR;
}
return BMP_RESULT_OK;
}
/*****************************************************************************
* bmpwrite_set_orientation
*****************************************************************************/
@@ -279,7 +376,7 @@ API BMPRESULT bmpwrite_set_orientation(BMPHANDLE h, enum BmpOrient orientation)
if (!(wp = cm_write_handle(h)))
return BMP_RESULT_ERROR;
if (s_check_already_saved(wp))
if (s_check_save_started(wp))
return BMP_RESULT_ERROR;
switch (orientation) {
@@ -316,7 +413,7 @@ API BMPRESULT bmpwrite_set_rle(BMPHANDLE h, enum BmpRLEtype type)
if (!(wp = cm_write_handle(h)))
return BMP_RESULT_ERROR;
if (s_check_already_saved(wp))
if (s_check_save_started(wp))
return BMP_RESULT_ERROR;
if (!s_is_setting_compatible(wp, "rle", type))
@@ -344,7 +441,7 @@ API BMPRESULT bmpwrite_set_resolution(BMPHANDLE h, int xdpi, int ydpi)
if (!(wp = cm_write_handle(h)))
return BMP_RESULT_ERROR;
if (s_check_already_saved(wp))
if (s_check_save_started(wp))
return BMP_RESULT_ERROR;
wp->ih->xpelspermeter = (int32_t) (100.0 / 2.54 * xdpi + 0.5);
@@ -366,7 +463,7 @@ API BMPRESULT bmpwrite_allow_2bit(BMPHANDLE h)
if (!(wp = cm_write_handle(h)))
return BMP_RESULT_ERROR;
if (s_check_already_saved(wp))
if (s_check_save_started(wp))
return BMP_RESULT_ERROR;
wp->allow_2bit = true;
@@ -387,7 +484,7 @@ API BMPRESULT bmpwrite_allow_huffman(BMPHANDLE h)
if (!(wp = cm_write_handle(h)))
return BMP_RESULT_ERROR;
if (s_check_already_saved(wp))
if (s_check_save_started(wp))
return BMP_RESULT_ERROR;
wp->allow_huffman = true;
@@ -408,7 +505,7 @@ API BMPRESULT bmpwrite_allow_rle24(BMPHANDLE h)
if (!(wp = cm_write_handle(h)))
return BMP_RESULT_ERROR;
if (s_check_already_saved(wp))
if (s_check_save_started(wp))
return BMP_RESULT_ERROR;
wp->allow_rle24 = true;
@@ -429,7 +526,7 @@ API BMPRESULT bmpwrite_set_64bit(BMPHANDLE h)
if (!(wp = cm_write_handle(h)))
return BMP_RESULT_ERROR;
if (s_check_already_saved(wp))
if (s_check_save_started(wp))
return BMP_RESULT_ERROR;
if (!s_is_setting_compatible(wp, "64bit"))
@@ -451,7 +548,7 @@ API BMPRESULT bmpwrite_set_huffman_img_fg_idx(BMPHANDLE h, int idx)
if (!(wp = cm_write_handle(h)))
return BMP_RESULT_ERROR;
if (s_check_already_saved(wp))
if (s_check_save_started(wp))
return BMP_RESULT_ERROR;
wp->huffman_fg_idx = !!idx;
@@ -466,7 +563,7 @@ API BMPRESULT bmpwrite_set_huffman_img_fg_idx(BMPHANDLE h, int idx)
static bool s_check_already_saved(BMPWRITE_R wp)
{
if (wp->saveimage_done) {
if (wp->write_state >= WS_SAVE_DONE) {
logerr(wp->c.log, "Image already saved.");
return true;
}
@@ -474,11 +571,25 @@ static bool s_check_already_saved(BMPWRITE_R wp)
}
/*****************************************************************************
* s_check_save_started
*****************************************************************************/
static bool s_check_save_started(BMPWRITE_R wp)
{
if (wp->write_state >= WS_SAVE_STARTED) {
logerr(wp->c.log, "Image save already started.");
return true;
}
return false;
}
/*****************************************************************************
* s_is_setting_compatible
*
* setting: "outbits", "srcbits", "srcchannels",
* setting: "outbits", "srcbits", "srcchannels", "indexed",
* "format", "indexed", "64bit", "rle"
*****************************************************************************/
@@ -535,7 +646,7 @@ static bool s_is_setting_compatible(BMPWRITE_R wp, const char *setting, ...)
cm_format_name(wp->source_format));
ret = false;
}
if (wp->dimensions_set) {
if (wp->write_state >= WS_DIMENSIONS_SET) {
if (!(wp->source_channels == 1 && wp->source_bitsperchannel == 8)) {
logerr (wp->c.log, "Indexed images must be 1 channel, 8 bits");
ret = false;
@@ -545,7 +656,7 @@ static bool s_is_setting_compatible(BMPWRITE_R wp, const char *setting, ...)
format = va_arg(args, enum BmpFormat);
switch (format) {
case BMP_FORMAT_FLOAT:
if (wp->dimensions_set && wp->source_bitsperchannel != 32) {
if (wp->write_state >= WS_DIMENSIONS_SET && wp->source_bitsperchannel != 32) {
logerr(wp->c.log, "float cannot be %d bits per pixel",
wp->source_bitsperchannel);
ret = false;
@@ -556,7 +667,7 @@ static bool s_is_setting_compatible(BMPWRITE_R wp, const char *setting, ...)
}
break;
case BMP_FORMAT_S2_13:
if (wp->dimensions_set && wp->source_bitsperchannel != 16) {
if (wp->write_state >= WS_DIMENSIONS_SET && wp->source_bitsperchannel != 16) {
logerr(wp->c.log, "s2.13 cannot be %d bits per pixel",
wp->source_bitsperchannel);
ret = false;
@@ -572,12 +683,13 @@ static bool s_is_setting_compatible(BMPWRITE_R wp, const char *setting, ...)
}
} else if (!strcmp(setting, "rle")) {
rle = va_arg(args, enum BmpRLEtype);
if (rle == BMP_RLE_AUTO || rle == BMP_RLE_RLE8) {
if (rle != BMP_RLE_NONE) {
if (wp->outorientation != BMP_ORIENT_BOTTOMUP) {
logerr(wp->c.log, "RLE is invalid with top-down BMPs");
ret = false;
}
}
} else if (!strcmp(setting, "orientation")) {
orientation = va_arg(args, enum BmpOrient);
if (orientation == BMP_ORIENT_TOPDOWN) {
@@ -591,6 +703,13 @@ static bool s_is_setting_compatible(BMPWRITE_R wp, const char *setting, ...)
logerr(wp->c.log, "Indexed images cannot be 64bit");
ret = false;
}
if (wp->outbits_set) {
logerr(wp->c.log, "BMPs with specified channel bits cannot be 64bit");
ret = false;
}
} else {
logerr(wp->c.log, "Panic, invalid setting check for '%s'", setting);
ret = false;
}
va_end(args);
@@ -599,59 +718,82 @@ static bool s_is_setting_compatible(BMPWRITE_R wp, const char *setting, ...)
/*****************************************************************************
* s_infoheader_size
*****************************************************************************/
static uint32_t s_infoheader_size(enum BmpInfoVer version)
{
switch(version) {
case BMPINFO_CORE_OS21: return 12;
case BMPINFO_OS22: return 64;
case BMPINFO_V3: return 40;
case BMPINFO_V3_ADOBE1: return 52;
case BMPINFO_V3_ADOBE2: return 56;
case BMPINFO_V4: return 108;
case BMPINFO_V5: return 124;
default:
return -1;
}
}
/*****************************************************************************
* s_decide_outformat
*****************************************************************************/
static void s_decide_outformat(BMPWRITE_R wp)
static bool s_decide_outformat(BMPWRITE_R wp)
{
int bitsum;
uint64_t bitmapsize, filesize, bytes_per_line;
int bitsum = 0;
uint64_t bitmapsize, filesize, bytes_per_line;
enum BmpInfoVer version = BMPINFO_OS22, maxversion = BMPINFO_V5;
if ((wp->source_channels == 4 || wp->source_channels == 2) &&
((wp->outbits_set && wp->cmask.bits.alpha) || !wp->outbits_set) ) {
wp->has_alpha = true;
} else {
wp->cmask.bits.alpha = 0;
wp->has_alpha = false;
}
if (wp->iccprofile || (wp->ih->intent != 0))
version = MAX(BMPINFO_V5, version);
if (!wp->outbits_set) {
if (wp->source_channels == 4 || wp->source_channels == 2)
wp->source_has_alpha = true;
else
wp->source_has_alpha = false;
if (!wp->outbits_set && !wp->palette) {
if (wp->out64bit) {
wp->cmask.bits.red = 16;
wp->cmask.bits.green = 16;
wp->cmask.bits.blue = 16;
wp->cmask.bits.alpha = 16; /* 64bit always has alpha channel */
version = MAX(BMPINFO_V3, version);
} else {
wp->cmask.bits.red = wp->cmask.bits.green = wp->cmask.bits.blue = 8;
if (wp->has_alpha)
if (wp->source_has_alpha)
wp->cmask.bits.alpha = 8;
}
}
bitsum = s_calc_mask_values(wp);
if (wp->palette) {
wp->ih->version = BMPINFO_V3;
wp->ih->size = BMPIHSIZE_V3;
if (wp->source_channels > 1) {
logerr(wp->c.log, "Panic! Palette set with %d source channels",
wp->source_channels);
return false;
}
if (wp->rle_requested != BMP_RLE_NONE) {
if (wp->palette->numcolors > 16 ||
wp->rle_requested == BMP_RLE_RLE8) {
if (wp->palette->numcolors > 16 || wp->rle_requested == BMP_RLE_RLE8) {
wp->rle = 8;
wp->ih->compression = BI_RLE8;
wp->ih->bitcount = 8;
version = MAX(BMPINFO_V3, version);
} else if (wp->palette->numcolors > 2 || !wp->allow_huffman) {
} else if (wp->palette->numcolors > 2 || !wp->allow_huffman || version > BMPINFO_OS22) {
wp->rle = 4;
wp->ih->compression = BI_RLE4;
wp->ih->bitcount = 4;
version = MAX(BMPINFO_V3, version);
} else {
wp->rle = 1;
wp->ih->compression = BI_OS2_HUFFMAN;
wp->ih->bitcount = 1;
wp->ih->version = BMPINFO_OS22;
wp->ih->size = BMPIHSIZE_OS22;
version = MAX(BMPINFO_OS22, version);
maxversion = BMPINFO_OS22;
}
} else {
@@ -663,47 +805,62 @@ static void s_decide_outformat(BMPWRITE_R wp)
wp->ih->bitcount = 4;
}
} else if (wp->allow_rle24 && wp->source_channels == 3 &&
wp->source_bitsperchannel && wp->rle_requested == BMP_RLE_AUTO) {
} else if (wp->allow_rle24 && wp->source_channels == 3 && wp->source_bitsperchannel == 8 &&
wp->rle_requested == BMP_RLE_AUTO && version <= BMPINFO_OS22) {
wp->rle = 24;
wp->ih->compression = BI_OS2_RLE24;
wp->ih->bitcount = 24;
wp->ih->version = BMPINFO_OS22;
wp->ih->size = BMPIHSIZE_OS22;
version = MAX(BMPINFO_OS22, version);
maxversion = BMPINFO_OS22;
} else if (bitsum < 64 && (!cm_all_equal_int(3, (int) wp->cmask.bits.red,
(int) wp->cmask.bits.green,
(int) wp->cmask.bits.blue) ||
wp->has_alpha ||
(wp->cmask.bits.red > 0 &&
wp->cmask.bits.red != 5 &&
wp->cmask.bits.red != 8 ) )) {
/* we need BI_BITFIELDS if any of the following is true and we are not
* writing a 64bit BMP:
* - not all RGB-components have the same bitlength
* - we are writing an alpha-channel
* - bits per component are not either 5 or 8 (which have
* known RI_RGB representation)
*/
wp->ih->version = BMPINFO_V4;
wp->ih->size = BMPIHSIZE_V4;
wp->ih->compression = BI_BITFIELDS; /* do we need BI_ALPHABITFIELDS when alpha is present */
/* or will that just confuse other readers? !!!!! */
if (bitsum <= 16)
wp->ih->bitcount = 16;
else
wp->ih->bitcount = 32;
} else {
/* otherwise, use BI_RGB with either 5 or 8 bits per component
* resulting in bitcount of 16 or 24, or a 64bit BMP with 16 bits/comp.
*/
wp->ih->version = BMPINFO_V3;
wp->ih->size = BMPIHSIZE_V3;
wp->ih->compression = BI_RGB;
wp->ih->bitcount = (bitsum + 7) / 8 * 8;
/* RGB */
bitsum = s_calc_mask_values(wp);
if (bitsum < 64 && (!cm_all_equal_int(3, (int) wp->cmask.bits.red,
(int) wp->cmask.bits.green,
(int) wp->cmask.bits.blue) ||
wp->source_has_alpha ||
(wp->cmask.bits.red > 0 &&
wp->cmask.bits.red != 5 &&
wp->cmask.bits.red != 8 ) )) {
/* we need BI_BITFIELDS if any of the following is true and we are not
* writing a 64bit BMP:
* - not all RGB-components have the same bitlength
* - we are writing an alpha-channel
* - bits per component are not either 5 or 8 (which have
* known RI_RGB representation)
*/
version = MAX(BMPINFO_V4, version);
wp->ih->compression = BI_BITFIELDS;
if (bitsum <= 16)
wp->ih->bitcount = 16;
else
wp->ih->bitcount = 32;
} else {
/* otherwise, use BI_RGB with either 5 or 8 bits per component
* resulting in bitcount of 16 or 24, or a 64bit BMP with 16 bits/comp.
*/
wp->ih->compression = BI_RGB;
wp->ih->bitcount = (bitsum + 7) / 8 * 8;
}
}
if (version > maxversion) {
logerr(wp->c.log, "Panic! Info header version conflict. Have %s, need %s",
cm_infoheader_name(version), cm_infoheader_name(maxversion));
return false;
}
/* always use at least V3, unless a smaller version is required */
version = MAX(MIN(BMPINFO_V3, maxversion), version);
wp->ih->version = version;
wp->ih->size = s_infoheader_size(version);
if (wp->palette) {
wp->ih->clrused = wp->palette->numcolors;
} else {
@@ -720,9 +877,9 @@ static void s_decide_outformat(BMPWRITE_R wp)
bytes_per_line = ((uint64_t) wp->width * wp->ih->bitcount + 7) / 8;
wp->padding = cm_align4padding(bytes_per_line);
bitmapsize = (bytes_per_line + wp->padding) * wp->height;
filesize = bitmapsize + BMPFHSIZE + wp->ih->size + wp->palette_size;
filesize = bitmapsize + BMPFHSIZE + wp->ih->size + wp->palette_size + wp->iccprofile_size;
wp->fh->type = 0x4d42; /* "BM" */
wp->fh->type = BMPFILE_BM;
wp->fh->size = (uint32_t) ((wp->rle || filesize > UINT32_MAX) ? 0 : filesize);
wp->fh->offbits = BMPFHSIZE + wp->ih->size + wp->palette_size;
@@ -733,6 +890,13 @@ static void s_decide_outformat(BMPWRITE_R wp)
wp->ih->height = -wp->height;
wp->ih->planes = 1;
wp->ih->sizeimage = (uint32_t) ((wp->rle || bitmapsize > UINT32_MAX) ? 0 : bitmapsize);
if (wp->iccprofile) {
uint64_t profileoffset = bitmapsize + wp->ih->size + wp->palette_size;
wp->ih->profiledata = (uint32_t) ((wp->rle || profileoffset > UINT32_MAX) ? 0 : profileoffset);
}
return true;
}
@@ -753,18 +917,17 @@ API BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image)
if (!(wp = cm_write_handle(h)))
return BMP_RESULT_ERROR;
if (s_check_already_saved(wp))
if (s_check_save_started(wp))
return BMP_RESULT_ERROR;
if (wp->line_by_line) {
logerr(wp->c.log, "Cannot switch from line-by-line to saving full image");
if (!s_ready_to_save(wp))
return BMP_RESULT_ERROR;
if (!s_save_header(wp)) {
goto fatal;
}
if (!s_save_header(wp))
return BMP_RESULT_ERROR;
wp->saveimage_done = true;
wp->write_state = WS_SAVE_STARTED;
wp->bytes_written_before_bitdata = wp->bytes_written;
linesize = (size_t) wp->width * wp->source_bytes_per_pixel;
@@ -786,7 +949,7 @@ API BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image)
}
if (!res) {
logerr(wp->c.log, "failed saving line %d", y);
return BMP_RESULT_ERROR;
goto fatal;
}
}
if (wp->rle) {
@@ -794,19 +957,26 @@ API BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image)
if (EOF == s_write_one_byte(0, wp) ||
EOF == s_write_one_byte(1, wp)) {
logsyserr(wp->c.log, "Writing RLE end-of-file marker");
return BMP_RESULT_ERROR;
goto fatal;
}
}
else {
if (!(huff_encode_rtc(wp) && huff_flush(wp))) {
logsyserr(wp->c.log, "Writing RTC end-of-file marker");
return BMP_RESULT_ERROR;
goto fatal;
}
}
s_try_saving_image_size(wp);
}
if (!s_finalize_file(wp))
goto fatal;
wp->write_state = WS_SAVE_DONE;
return BMP_RESULT_OK;
fatal:
wp->write_state = WS_FATAL;
return BMP_RESULT_ERROR;
}
@@ -826,11 +996,15 @@ API BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line)
if (s_check_already_saved(wp))
return BMP_RESULT_ERROR;
if (!wp->line_by_line) { /* first line */
if (!s_ready_to_save(wp))
return BMP_RESULT_ERROR;
if (wp->write_state < WS_SAVE_STARTED) {
if (!s_save_header(wp))
goto abort;
goto fatal;
wp->write_state = WS_SAVE_STARTED;
wp->bytes_written_before_bitdata = wp->bytes_written;
wp->line_by_line = true;
}
switch (wp->rle) {
@@ -848,7 +1022,7 @@ API BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line)
}
if (!res)
goto abort;
goto fatal;
if (++wp->lbl_y >= wp->height) {
if (wp->rle) {
@@ -856,44 +1030,51 @@ API BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line)
if (EOF == s_write_one_byte(0, wp) ||
EOF == s_write_one_byte(1, wp)) {
logsyserr(wp->c.log, "Writing RLE end-of-file marker");
goto abort;
goto fatal;
}
} else {
if (!(huff_encode_rtc(wp) && huff_flush(wp))) {
logsyserr(wp->c.log, "Writing RTC end-of-file marker");
goto abort;
goto fatal;
}
}
s_try_saving_image_size(wp);
}
wp->saveimage_done = true;
}
if (!s_finalize_file(wp))
goto fatal;
wp->write_state = WS_SAVE_DONE;
}
return BMP_RESULT_OK;
abort:
wp->saveimage_done = true;
fatal:
wp->write_state = WS_FATAL;
return BMP_RESULT_ERROR;
}
/*****************************************************************************
* s_ready_to_save
*****************************************************************************/
static bool s_ready_to_save(BMPWRITE_R wp)
{
if (wp->write_state < WS_DIMENSIONS_SET) {
logerr(wp->c.log, "Must set dimensions before saving");
return false;
}
return true;
}
/*****************************************************************************
* s_save_header
*****************************************************************************/
static bool s_save_header(BMPWRITE_R wp)
{
if (wp->saveimage_done || wp->line_by_line) {
logerr(wp->c.log, "Image already saved.");
if (!s_decide_outformat(wp))
return false;
}
if (!wp->dimensions_set) {
logerr(wp->c.log, "Must set dimensions before saving");
return false;
}
s_decide_outformat(wp);
if (!s_write_bmp_file_header(wp)) {
logsyserr(wp->c.log, "Writing BMP file header");
@@ -917,6 +1098,35 @@ static bool s_save_header(BMPWRITE_R wp)
/*****************************************************************************
* s_finalize_file
*
* write anything that has to be written after the bitdata has been written
*****************************************************************************/
static bool s_finalize_file(BMPWRITE_R wp)
{
uint64_t file_size, img_size;
file_size = wp->bytes_written;
img_size = file_size - wp->bytes_written_before_bitdata;
if (wp->iccprofile) {
if (!s_write_iccprofile(wp))
return false;
file_size += wp->iccprofile_size;
}
if (wp->rle) {
if (!s_try_saving_image_size(wp, file_size, img_size))
return false;
}
return true;
}
/*****************************************************************************
* s_try_saving_image_size
*
@@ -931,13 +1141,8 @@ static bool s_save_header(BMPWRITE_R wp)
* when they are too big for the respective fields.
*****************************************************************************/
static bool s_try_saving_image_size(BMPWRITE_R wp)
static bool s_try_saving_image_size(BMPWRITE_R wp, uint64_t file_size, uint64_t image_size)
{
uint64_t image_size, file_size;
image_size = wp->bytes_written - wp->bytes_written_before_bitdata;
file_size = wp->bytes_written;
if (fseek(wp->file, 2, SEEK_SET)) /* file header -> bfSize */
return false;
if (file_size <= UINT32_MAX && !write_u32_le(wp->file, (uint32_t) file_size))
@@ -1250,8 +1455,8 @@ static bool s_save_line_huff(BMPWRITE_R wp, const unsigned char *line)
x += len;
}
return true;
abort:
logsyserr(wp->c.log, "Writing 1-D Huffman data to BMP file");
return false;
}
@@ -1301,7 +1506,7 @@ static inline unsigned long long s_imgrgb_to_outbytes(BMPWRITE_R wp,
if (wp->source_channels < 3)
rgb = false; /* grayscale */
if (wp->has_alpha) {
if (wp->source_has_alpha) {
alpha_offs = rgb ? 3 : 1;
outchannels = 4;
} else
@@ -1315,14 +1520,14 @@ static inline unsigned long long s_imgrgb_to_outbytes(BMPWRITE_R wp,
comp[0] = imgpx[0];
comp[1] = rgb ? imgpx[1] : comp[0];
comp[2] = rgb ? imgpx[2] : comp[0];
if (wp->has_alpha)
if (wp->source_has_alpha)
comp[3] = imgpx[alpha_offs];
break;
case 16:
comp[0] = ((const uint16_t*)imgpx)[0];
comp[1] = rgb ? ((const uint16_t*)imgpx)[1] : comp[0];
comp[2] = rgb ? ((const uint16_t*)imgpx)[2] : comp[0];
if (wp->has_alpha)
if (wp->source_has_alpha)
comp[3] = ((const uint16_t*)imgpx)[alpha_offs];
break;
@@ -1330,7 +1535,7 @@ static inline unsigned long long s_imgrgb_to_outbytes(BMPWRITE_R wp,
comp[0] = ((const uint32_t*)imgpx)[0];
comp[1] = rgb ? ((const uint32_t*)imgpx)[1] : comp[0];
comp[2] = rgb ? ((const uint32_t*)imgpx)[2] : comp[0];
if (wp->has_alpha)
if (wp->source_has_alpha)
comp[3] = ((const uint32_t*)imgpx)[alpha_offs];
break;
@@ -1350,7 +1555,7 @@ static inline unsigned long long s_imgrgb_to_outbytes(BMPWRITE_R wp,
dcomp[0] = ((const float*)imgpx)[0];
dcomp[1] = rgb ? ((const float*)imgpx)[1] : dcomp[0];
dcomp[2] = rgb ? ((const float*)imgpx)[2] : dcomp[0];
if (wp->has_alpha)
if (wp->source_has_alpha)
dcomp[3] = ((const float*)imgpx)[alpha_offs];
if (wp->out64bit) {
@@ -1373,7 +1578,7 @@ static inline unsigned long long s_imgrgb_to_outbytes(BMPWRITE_R wp,
comp[0] = ((const uint16_t*)imgpx)[0];
comp[1] = rgb ? ((const uint16_t*)imgpx)[1] : comp[0];
comp[2] = rgb ? ((const uint16_t*)imgpx)[2] : comp[0];
if (wp->has_alpha)
if (wp->source_has_alpha)
comp[3] = ((const uint16_t*)imgpx)[alpha_offs];
if (wp->out64bit) {
@@ -1399,7 +1604,7 @@ static inline unsigned long long s_imgrgb_to_outbytes(BMPWRITE_R wp,
for (i = 0, bytes = 0; i < outchannels; i++) {
bytes |= ((unsigned long long)comp[i] & wp->cmask.mask.value[i]) << wp->cmask.shift.value[i];
}
if (!wp->has_alpha && wp->out64bit)
if (!wp->source_has_alpha && wp->out64bit)
bytes |= 8192ULL << wp->cmask.shift.alpha;
return bytes;
@@ -1549,6 +1754,53 @@ static bool s_write_bmp_info_header(BMPWRITE_R wp)
}
wp->bytes_written += 68;
if (wp->ih->version == BMPINFO_V4)
return true;
if (!(write_u32_le(wp->file, wp->ih->intent) &&
write_u32_le(wp->file, wp->ih->profiledata) &&
write_u32_le(wp->file, wp->ih->profilesize) &&
write_u32_le(wp->file, wp->ih->reserved)))
return false;
wp->bytes_written += 16;
return true;
}
/*****************************************************************************
* s_write_iccprofile
*****************************************************************************/
static bool s_write_iccprofile(BMPWRITE_R wp)
{
if (wp->ih->version < BMPINFO_V5 || !wp->iccprofile)
return false;
uint64_t pos = wp->bytes_written;
if (wp->iccprofile_size != (int) fwrite(wp->iccprofile, 1, wp->iccprofile_size, wp->file)) {
logsyserr(wp->c.log, "Error writing ICC profile to file");
return false;
}
wp->bytes_written += wp->iccprofile_size;
if (wp->rle) {
if (fseek(wp->file, IH_PROFILEDATA_OFFSET, SEEK_SET)) {
logsyserr(wp->c.log, "Error writing ICC profile to file");
return false;
}
if (!write_u32_le(wp->file, pos - 14))
return false;
if (wp->bytes_written < (uint64_t) LONG_MAX)
fseek(wp->file, wp->bytes_written, SEEK_SET);
}
return true;
}
@@ -1582,6 +1834,8 @@ void bw_free(BMPWRITE wp)
free(wp->group);
if (wp->palette)
free(wp->palette);
if (wp->iccprofile)
free(wp->iccprofile);
if (wp->ih)
free(wp->ih);
if (wp->fh)

View File

@@ -203,6 +203,15 @@ enum BmpFormat {
typedef enum BmpFormat BMPFORMAT;
enum BmpIntent {
BMP_INTENT_NONE,
BMP_INTENT_BUSINESS, /* saturation */
BMP_INTENT_GRAPHICS, /* relative colorimetric */
BMP_INTENT_IMAGES, /* perceptive */
BMP_INTENT_ABS_COLORIMETRIC /* absolute colorimetric */
};
typedef enum BmpIntent BMPINTENT;
APIDECL BMPHANDLE bmpread_new(FILE *file);
APIDECL BMPRESULT bmpread_load_info(BMPHANDLE h);
@@ -239,6 +248,9 @@ 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 BMPINFOVER bmpread_info_header_version(BMPHANDLE h);
APIDECL const char* bmpread_info_header_name(BMPHANDLE h);
APIDECL int bmpread_info_header_size(BMPHANDLE h);
@@ -268,6 +280,10 @@ 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);
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);

View File

@@ -1,4 +1,4 @@
project('bmplib', 'c', default_options: ['c_std=c11', 'warning_level=3'], version: '1.7.5')
project('bmplib', 'c', default_options: ['c_std=c11', 'warning_level=3'], version: '1.7.7')
cc = meson.get_compiler('c')