mirror of
https://github.com/rupertwh/bmplib.git
synced 2026-04-14 18:22:42 +00:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
093c984b84 | ||
|
|
d5338751d6 | ||
|
|
e6a84d2a34 | ||
|
|
26edf63068 | ||
|
|
4c7f1881c8 | ||
|
|
0b12d525f9 | ||
|
|
1dc99397fe | ||
|
|
c490f79e76 | ||
|
|
55c7e8d24a | ||
|
|
e5c39138ae | ||
|
|
05e69a2f51 | ||
|
|
8223b8038e | ||
|
|
6db7f5ff5f | ||
|
|
99598632f2 | ||
|
|
7b3ca4cf47 | ||
|
|
bdb3482b89 | ||
|
|
056079ae80 | ||
|
|
393a02a67f | ||
|
|
da6905b6ee | ||
|
|
5e8db7a16c | ||
|
|
39ce995f05 | ||
|
|
bbabf02cd9 | ||
|
|
f65c0efca8 | ||
|
|
337cac7461 | ||
|
|
31a4b5464a | ||
|
|
47547159c2 | ||
|
|
b5a066a076 | ||
|
|
a98834f7e9 | ||
|
|
5b3988fbe9 | ||
|
|
f47fdcdd17 | ||
|
|
6cc3ef5e91 |
48
.clang-format
Normal file
48
.clang-format
Normal file
@@ -0,0 +1,48 @@
|
||||
# For more information, see:
|
||||
#
|
||||
# https://clang.llvm.org/docs/ClangFormat.html
|
||||
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
||||
#
|
||||
---
|
||||
|
||||
BasedOnStyle: WebKit
|
||||
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignArrayOfStructures: Right
|
||||
AlignConsecutiveAssignments:
|
||||
Enabled: true
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: true
|
||||
AlignCompound: true
|
||||
AlignFunctionPointers: false
|
||||
PadOperators: true
|
||||
AlignConsecutiveDeclarations: Consecutive
|
||||
AlignConsecutiveMacros: Consecutive
|
||||
AlignEscapedNewlines: Left
|
||||
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
BreakAfterReturnType: Automatic
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BreakBeforeBraces: Linux
|
||||
IndentWidth: 8
|
||||
PointerAlignment: Right
|
||||
UseTab: ForIndentation
|
||||
|
||||
SpaceBeforeParens: ControlStatementsExceptControlMacros
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterCStyleCast: false
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ColumnLimit: 80
|
||||
PenaltyBreakAssignment: 60
|
||||
PenaltyBreakBeforeFirstCallParameter: 100
|
||||
PenaltyBreakOpenParenthesis: 40
|
||||
PenaltyExcessCharacter: 1
|
||||
# Strings are more often longer by usage, so let's give these slightly
|
||||
# more space to breath.
|
||||
PenaltyBreakString: 60
|
||||
PenaltyReturnTypeOnItsOwnLine: 90
|
||||
740
API-full.md
Normal file
740
API-full.md
Normal file
@@ -0,0 +1,740 @@
|
||||
# Rupert's bmplib -- Full API Description
|
||||
|
||||
Refer to the *Quick Start Guide* (API-quick-start.md) for a quick intro to bmplib which describes only the minimal set of functions needed to read/write BMP files.
|
||||
|
||||
|
||||
## 1. Functions for reading BMP files
|
||||
|
||||
### Get a handle
|
||||
```
|
||||
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
|
||||
(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).
|
||||
|
||||
|
||||
### 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_bitsperchannel(BMPHANDLE h)
|
||||
BMPORIENT bmpread_orientation(BMPHANDLE h)
|
||||
|
||||
int bmpread_resolution_xdpi(BMPHANDLE h)
|
||||
int bmpread_resolution_ydpi(BMPHANDLE h)
|
||||
|
||||
```
|
||||
|
||||
#### top-down / bottom-up
|
||||
`orientation` is one of:
|
||||
- `BMPORIENT_BOTTOMUP`
|
||||
- `BMPORIENT_TOPDOWN`
|
||||
|
||||
`bmpread_orientation()` or the `orientation` value returned by
|
||||
`bmpread_dimensions()` **is only relevant if you load the BMP file
|
||||
line-by-line**. In line-by-line mode (using `bmpread_load_line()`), the
|
||||
image data is always delivered in the order it is in the BMP file. The
|
||||
`orientation` value will tell you if it's top-down or bottom-up. On the
|
||||
other hand, when the whole image is loaded at once (using `bmpread_load_image
|
||||
()`), bmplib will **always** return the image top-down, regardless of how
|
||||
the BMP file is oriented. The `orientation` value will still indicate the
|
||||
orientation of the original BMP.
|
||||
|
||||
#### 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, 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)
|
||||
```
|
||||
|
||||
`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.
|
||||
|
||||
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:
|
||||
|
||||
Options for `bmpread_set_64bit()` are:
|
||||
|
||||
- `BMP_CONV64_SRGB`: the default, original data is assumed to be s2.13
|
||||
fixed-point linear and converted to sRGB-gamma.
|
||||
- `BMP_CONV64_LINEAR`: no gamma-conversion is applied to the image data.
|
||||
- `BMP_CONV64_NONE`: this option is just a shorthand for setting
|
||||
BMP_CONV64_LINEAR *and* BMP_FORMAT_S2_13. Image values are returned exactly
|
||||
as they are in the BMP file, without any conversion or attempt at
|
||||
interpretation.
|
||||
|
||||
|
||||
### Setting a number format
|
||||
|
||||
By default, bmplib will always return the image data as 8-,16-, or 32-bit integer values. You can instead set the number format to floating point or fixed using:
|
||||
|
||||
```
|
||||
BMPRESULT bmp_set_number_format(BMPHANDLE h, BMPFORMAT format)
|
||||
```
|
||||
|
||||
(see below, *3. General functions for both reading/writing BMPs*)
|
||||
|
||||
|
||||
|
||||
### Huge files: bmpread_set_insanity_limit()
|
||||
|
||||
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 *
|
||||
bitsperchannel / 8.
|
||||
|
||||
```
|
||||
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)
|
||||
|
||||
|
||||
### 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)
|
||||
```
|
||||
|
||||
|
||||
### Release the handle
|
||||
|
||||
```
|
||||
void bmp_free(BMPHANDLE h)
|
||||
```
|
||||
|
||||
Frees all resources associated with the handle `h`. **Image data is not
|
||||
affected**, so you can call bmp_free() immediately after `bmpread_load_image
|
||||
()` and still use the returned image data.
|
||||
|
||||
Note: Any error message strings returned by `bmp_errmsg()` are invalidated by
|
||||
`bmp_free()` and must not be used anymore!
|
||||
|
||||
|
||||
|
||||
|
||||
## 2. Functions for writing BMP files
|
||||
|
||||
### Get a handle
|
||||
```
|
||||
BMPHANDLE bmpwrite_new(FILE *file)
|
||||
```
|
||||
|
||||
### Set image dimensions
|
||||
```
|
||||
BMPRESULT bmpwrite_set_dimensions(BMPHANDLE h,
|
||||
unsigned width,
|
||||
unsigned height,
|
||||
unsigned channels,
|
||||
unsigned bitsperchannel)
|
||||
|
||||
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.
|
||||
|
||||
|
||||
### 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.
|
||||
|
||||
|
||||
|
||||
|
||||
### 64-bit RGBA BMPs
|
||||
|
||||
By default, bmplib will not write 64-bit BMPs because they are rather exotic and hardly any
|
||||
software can open them.
|
||||
|
||||
If you do want to write 64-bit BMPs, call
|
||||
|
||||
```
|
||||
BMPRESULT bmpwrite_set_64bit(BMPHANDLE h)
|
||||
```
|
||||
|
||||
In order to make use of the extended range available in 64-bit BMPs (-4.0 to +3.999...), you will probably want to provide the image buffer either as 32-bit float or as 16-bit s2.13 (and call `bmp_set_number_format()` accordingly).
|
||||
|
||||
Note: 64-bit BMPs store pixel values in *linear light*. Unlike when *reading* 64-bit BMPs, bmplib will not make any gamma/linear conversion while writing BMPs. You have to provide the proper linear values in the image buffer.
|
||||
|
||||
|
||||
|
||||
|
||||
### Write the image
|
||||
|
||||
```
|
||||
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_set_number_format()
|
||||
|
||||
```
|
||||
BMPRESULT bmp_set_number_format(BMPHANDLE h, BMPFORMAT format)
|
||||
```
|
||||
|
||||
sets the number format of the image buffer received from / passed to bmplib. `format` can be one of
|
||||
|
||||
- `BMP_FORMAT_INT` image buffer values are expected/returned as 8-, 16-, or 32-bit integers. (this is the default)
|
||||
- `BMP_FORMAT_FLOAT` image buffer values are expected/returned as 32-bit floating point numbers (C `float`).
|
||||
- `BMP_FORMAT_S2_13` image buffer values are expected/returned as s2.13 fixed point numbers. s2.13 is a 16-bit format with one sign bit, 2 integer bits, and 13 bits for the fractional part. Range is from -4.0 to +3.999...
|
||||
|
||||
For indexed images, `BMP_FORMAT_INT` is the only valid format.
|
||||
|
||||
|
||||
### bmp_version()
|
||||
|
||||
```
|
||||
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 values are:
|
||||
- `BMP_CONV64_SRGB` (default)
|
||||
- `BMP_CONV64_LINEAR`
|
||||
- `BMP_CONV64_NONE`
|
||||
|
||||
Can safely be cast from/to int.
|
||||
|
||||
#### `BMPFORMAT`
|
||||
|
||||
Used in `bmp_set_number_format()`. Possible values are:
|
||||
- `BMP_FORMAT_INT` (default)
|
||||
- `BMP_FORMAT_FLOAT` 32-bit floating point
|
||||
- `BMP_FORMAT_S2_13` s2.13 fixed point
|
||||
|
||||
|
||||
|
||||
## 5. Sample code
|
||||
|
||||
### Reading BMPs
|
||||
|
||||
```
|
||||
/* (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.
|
||||
*/
|
||||
```
|
||||
|
||||
|
||||
### 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);
|
||||
```
|
||||
281
API-quick-start.md
Normal file
281
API-quick-start.md
Normal file
@@ -0,0 +1,281 @@
|
||||
# Rupert's bmplib -- Quick Start Guide
|
||||
|
||||
The API has grown quite a bit more than I had originally anticipated. But most
|
||||
of the provided functions are optional.
|
||||
|
||||
So, to not scare you off right away, I wrote this quick start guide version of
|
||||
the API description which only covers the basic functions. These basic
|
||||
functions will be sufficient for many/most use cases. (see also the two
|
||||
examples at the end of this document):
|
||||
|
||||
For the complete API, refer to the *Full API Description* (API-full.md).
|
||||
|
||||
## 1. Reading BMP files:
|
||||
|
||||
```
|
||||
bmpread_new()
|
||||
bmpread_dimensions()
|
||||
bmpread_load_image()
|
||||
bmp_free()
|
||||
```
|
||||
|
||||
### Get a handle
|
||||
```
|
||||
BMPHANDLE bmpread_new(FILE *file)
|
||||
```
|
||||
|
||||
`bmpread_new()` returns a handle which is used for all subsequent calls to
|
||||
bmplib. When you are done with the file, call `bmp_free()` to release this
|
||||
handle.
|
||||
|
||||
The handle cannot be reused to read multiple files.
|
||||
|
||||
### Get image dimensions
|
||||
|
||||
```
|
||||
BMPRESULT bmpread_dimensions(BMPHANDLE h,
|
||||
int *width,
|
||||
int *height,
|
||||
int *channels,
|
||||
int *bitsperchannel,
|
||||
BMPORIENT *orientation)
|
||||
```
|
||||
|
||||
Use `bmpread_dimensions()` to get all dimensions with one call. The return
|
||||
value will be the same as for `bmpread_load_info()` (see "3. Result codes"
|
||||
below and *Full API Description*).
|
||||
|
||||
The dimensions describe the image returned by bmplib, *not* necessarily the
|
||||
original BMP file.
|
||||
|
||||
`orientation` can be ignored, it is only relevant when loading the image
|
||||
line-by-line (see *Full API Description*)
|
||||
|
||||
|
||||
### Load the image
|
||||
|
||||
```
|
||||
BMPRESULT bmpread_load_image(BMPHANDLE h, unsigned char **pbuffer)
|
||||
```
|
||||
|
||||
Loads the complete image from the BMP file into the buffer pointed to by
|
||||
`pbuffer`. You can either allocate a buffer yourself or let `pbuffer` point
|
||||
to a NULL-pointer in which case bmplib will allocate an appropriate buffer.
|
||||
In the latter case, you will have to `free()` the buffer, once you are done
|
||||
with it.
|
||||
|
||||
If you allocate the buffer yourself, the buffer must be at least as large as
|
||||
the size returned by `bmpread_buffersize()` (see *Full API description*).
|
||||
|
||||
```
|
||||
unsigned char *buffer;
|
||||
|
||||
/* either: */
|
||||
buffer = NULL;
|
||||
bmpread_load_image(h, &buffer); /* bmplib will allocate the buffer */
|
||||
|
||||
/* or: */
|
||||
buffer = malloc(bmpread_buffersize(h));
|
||||
bmpread_load_image(h, &buffer); /* bmplib will use the provided buffer */
|
||||
|
||||
```
|
||||
|
||||
The image data is written to the buffer according to the returned dimensions
|
||||
(see above). 16-bit and 32-bit values are always written in host byte order,
|
||||
in the order R-G-B or R-G-B-A. The returned image is always top-down, i.e.
|
||||
data starts in the top left corner. Unlike BMPs which are (almost always)
|
||||
bottom-up.
|
||||
|
||||
If `bmpread_load_image()` returns BMP_RESULT_TRUNCATED or BMP_RESULT_INVALID,
|
||||
the file may have been damaged or simply contains invalid image data. Image
|
||||
data is loaded anyway as far as possible and may be partially usable.
|
||||
|
||||
|
||||
|
||||
### Release the handle
|
||||
|
||||
```
|
||||
void bmp_free(BMPHANDLE h)
|
||||
```
|
||||
|
||||
Frees all resources associated with the handle `h`. **Image data is not
|
||||
affected**, so you can call bmp_free() immediately after `bmpread_load_image
|
||||
()` and still use the returned image data.
|
||||
|
||||
Note: Any error message strings returned by `bmp_errmsg()` are invalidated by
|
||||
`bmp_free()` and must not be used anymore!
|
||||
|
||||
|
||||
|
||||
## 2. Writing BMP files:
|
||||
|
||||
```
|
||||
bmpwrite_new()
|
||||
bmpwrite_set_dimensions()
|
||||
bmpwrite_save_image()
|
||||
bmp_free()
|
||||
```
|
||||
|
||||
### Get a handle
|
||||
```
|
||||
BMPHANDLE bmpwrite_new(FILE *file)
|
||||
```
|
||||
|
||||
### Set image dimensions
|
||||
```
|
||||
BMPRESULT bmpwrite_set_dimensions(BMPHANDLE h,
|
||||
unsigned width,
|
||||
unsigned height,
|
||||
unsigned channels,
|
||||
unsigned bitsperchannel)
|
||||
```
|
||||
|
||||
Note: the dimensions set with `bmpwrite_set_dimensions()` describe the source
|
||||
data that you pass to bmplib, *not* the output BMP format. Use
|
||||
`bmpwrite_set_output_bits()`, `bmpwrite_set_palette()`, and
|
||||
`bmpwrite_set_64bit()` to modify the format written to the BMP file. (see *Full
|
||||
API description*)
|
||||
|
||||
|
||||
|
||||
### Write the image
|
||||
|
||||
```
|
||||
BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image)
|
||||
```
|
||||
|
||||
Write the whole image at once with `bmpwrite_save_image()`.
|
||||
|
||||
The image data pointed to by `image` must be in the format described
|
||||
by `bmpwrite_set_dimensions()`. Multi-byte values (16 or 32 bit) are expected
|
||||
in host byte order, the channels in the order R-G-B-(A).
|
||||
|
||||
Important: Image data must be provided top-down. (Even though the created BMP
|
||||
file will be bottom-up.)
|
||||
|
||||
### bmp_free()
|
||||
|
||||
```
|
||||
void bmp_free(BMPHANDLE h)
|
||||
```
|
||||
|
||||
Frees all resources associated with the handle `h`.
|
||||
|
||||
Note: Any error messages returned by `bmp_errmsg()` are invalidated by
|
||||
`bmp_free()` and cannot be used anymore.
|
||||
|
||||
|
||||
|
||||
|
||||
## 3. Result codes
|
||||
|
||||
#### `BMPRESULT`
|
||||
|
||||
Many bmplib functions return the success/failure of an operation as a
|
||||
`BMPRESULT`. It can have one of the following values:
|
||||
|
||||
- `BMP_RESULT_OK`
|
||||
- `BMP_RESULT_INVALID`
|
||||
- `BMP_RESULT_TRUNCATED`
|
||||
- `BMP_RESULT_INSANE`
|
||||
- `BMP_RESULT_PNG`
|
||||
- `BMP_RESULT_JPEG`
|
||||
- `BMP_RESULT_ERROR`
|
||||
|
||||
Can safely be cast from/to int. BMP_RESULT_OK is guaranteed to have the value 0.
|
||||
|
||||
|
||||
|
||||
## 5. Sample code
|
||||
|
||||
### Reading BMPs
|
||||
|
||||
```
|
||||
/* (all error checking left out for clarity) */
|
||||
|
||||
BMPHANDLE h;
|
||||
FILE *file;
|
||||
int width, height, channels, bitsperchannel, orientation;
|
||||
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.
|
||||
*/
|
||||
|
||||
bmpread_dimensions(h, &width, &height, &channels, &bitsperchannel, &orientation);
|
||||
|
||||
|
||||
/* load the image and clean up: */
|
||||
|
||||
image_buffer = NULL;
|
||||
bmpread_load_image(h, &image_buffer);
|
||||
|
||||
bmp_free(h);
|
||||
fclose(file);
|
||||
|
||||
|
||||
/* ready to use the image written to image_buffer */
|
||||
|
||||
/* image data is always returned in host byte order as
|
||||
* 8, 16, or 32 bits per channel RGB or RGBA data.
|
||||
* No padding.
|
||||
*/
|
||||
```
|
||||
|
||||
|
||||
### Writing BMPs
|
||||
|
||||
```
|
||||
/* (all error checking left out for clarity) */
|
||||
|
||||
BMPHANDLE h;
|
||||
FILE *file;
|
||||
unsigned char *image_buffer;
|
||||
int width, height, channels, bitsperchannel;
|
||||
|
||||
/* 'image_buffer' contains the image to be saved as either
|
||||
* 8, 16, or 32 bits per channel RGB or RGBA data in
|
||||
* host byte order without any padding
|
||||
*/
|
||||
|
||||
|
||||
/* open a file for writing and get a BMPHANDLE */
|
||||
|
||||
file = fopen("image.bmp", "wb");
|
||||
h = bmpwrite_new(file);
|
||||
|
||||
|
||||
/* inform bmplib of the image dimensions.
|
||||
* The color information (channels, bits) refer to the format
|
||||
* your image buffer is in, not the format the BMP file should
|
||||
* be written in.
|
||||
*/
|
||||
|
||||
bmpwrite_set_dimensions(h, width, height, channels, bitsperchannel);
|
||||
|
||||
|
||||
/* Optional: choose bit-depths (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);
|
||||
```
|
||||
489
API.md
489
API.md
@@ -1,489 +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_TO_ZERO`. In that case, the returned image will have no alpha channel, and undefined pixels will be set to zero. This function has no effect on non-RLE BMPs.
|
||||
```
|
||||
void bmpread_set_undefined(BMPHANDLE h, BMPUNDEFINED mode)
|
||||
```
|
||||
`mode` can be one of:
|
||||
- `BMPUNDEFINED_TO_ZERO`
|
||||
- `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 set to zero! (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.
|
||||
|
||||
|
||||
### 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 maybe 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 enty will be set to zero, and attempts to point outside the image will be ignored.
|
||||
In both cases, `bmpread_load_image()` and `bmpread_load_line()` will return BMP_RESULT_INVALID, unless the image is also truncated, then BMP_RESULT_TRUNCATED is returned.
|
||||
|
||||
|
||||
### Query info about the BMP file
|
||||
Note: these functions return information about the original BMP file being read. They do *not* describe the format of the returned image data, which may be different!
|
||||
```
|
||||
int bmpread_info_header_version(BMPHANDLE h)
|
||||
int bmpread_info_header_size(BMPHANDLE h)
|
||||
int bmpread_info_compression(BMPHANDLE h)
|
||||
int bmpread_info_bitcount(BMPHANDLE h)
|
||||
const char* bmpread_info_header_name(BMPHANDLE h)
|
||||
const char* bmpread_info_compression_name(BMPHANDLE h)
|
||||
BMPRESULT bmpread_info_channel_bits(BMPHANDLE h, int *r, int *g, int *b, int *a)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
## 2. Functions for writing BMP files
|
||||
|
||||
### 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
|
||||
|
||||
|
||||
### Write the image
|
||||
```
|
||||
BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image)
|
||||
BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line)
|
||||
```
|
||||
Write either the whole image at once with `bmpwrite_save_image()` or one line at a time with `bmpwrite_save_line()`.
|
||||
The image data pointed to by `image` or `line` must be in the format described by `bmpwrite_set_dimensions()`. Multi-byte values (16 or 32 bit) are expected in host byte order, the channels in the order R-G-B-(A). Indexed data must be supplied as 8 bit per pixel, even when writing lower bit (1/2/4) BMPs (see above).
|
||||
Important: When writing the whole image at once using `bmpwrite_save_image()`, the image data must be provided top-down (same as is returned by `bmpread_load_image()`). When using `bmpwrite_save_line()` to write the image line-by-line, the image data must be provided bottom-up (i.e. starting with the bottom-most line).
|
||||
In both cases, the actual BMP will be written bottom-up, which is the only 'officially' correct orientation.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 3. 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.
|
||||
|
||||
|
||||
### 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);
|
||||
```
|
||||
|
||||
115
README.md
115
README.md
@@ -6,13 +6,18 @@
|
||||
- Write any sensible BMP
|
||||
- Robustness! Don't let malformed BMPs bother us
|
||||
|
||||
## Current status (v1.4.4):
|
||||
Download [bmplib on github](https://github.com/rupertwh/bmplib).
|
||||
|
||||
## Current status (v1.6.0):
|
||||
### Reading BMP files:
|
||||
- 16/24/32 bit RGB(A) with any bits/channel combination (BI_RGB, BI_BITFIELDS, BI_ALPHABITFIELDS).
|
||||
- 16/24/32 bit RGB(A) with any bits/channel combination
|
||||
(BI_RGB, BI_BITFIELDS, BI_ALPHABITFIELDS).
|
||||
- 64 bit RGBA (caveat see below)
|
||||
- 1/2/4/8 bit indexed (palette), including RLE4 and RLE8 compressed.
|
||||
- RLE24 compressed (OS/2).
|
||||
- optional line-by-line reading of BMPs, even RLE.
|
||||
- Huffman encoded (OS/2).
|
||||
- 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,27 +27,30 @@
|
||||
|
||||
Questionable files that failed:
|
||||
- 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.
|
||||
BMP_RESULT_PNG and leave the file pointer in the correct state to be
|
||||
passed on to either libpng or libjpeg. Works as designed. Don't want to
|
||||
create dependency on those libs.
|
||||
- We currently ignore icc-profiles and chromaticity/gamma values. 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.
|
||||
- write BI_RGB when possible, BI_BITFIELDS only when
|
||||
necessary.
|
||||
- optional line-by-line writing of BMPs.
|
||||
- optionally supply image data as float or s2.13 fixed point.
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
### Download and compile bmplib library
|
||||
To install the latest development version of the library under the default `/usr/local` prefix:
|
||||
|
||||
To install the latest development version of the library under the default
|
||||
`/usr/local` prefix on debian-like Linux:
|
||||
|
||||
```
|
||||
sudo apt install build-essential git meson pkg-config
|
||||
git clone https://github.com/rupertwh/bmplib.git
|
||||
@@ -63,13 +71,17 @@ ninja install
|
||||
|
||||
|
||||
### Use bmplib in your program
|
||||
|
||||
A minimalistic `meson.build` for a program that uses bmplib:
|
||||
|
||||
```
|
||||
project('mytest', 'c')
|
||||
bmpdep = dependency('libbmp')
|
||||
executable('mytest', 'main.c', dependencies: [bmpdep])
|
||||
```
|
||||
|
||||
Includes:
|
||||
|
||||
```
|
||||
#include <bmplib.h>
|
||||
```
|
||||
@@ -78,48 +90,51 @@ see API.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 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.
|
||||
|
||||
64bit BMPs are a special breed. First of all, there is very little information
|
||||
about the format out there, let alone an 'official' spec from MS. It seems
|
||||
that the RGBA components are stored as s2.13 fixed-point numbers. And
|
||||
according to Jason Summers' BMP Suite the RGB components are encoded in
|
||||
linear light. As that's the only sample of a 64-bit BMP I have, that's what I
|
||||
am going with for now. But that also means that there is no one obvious
|
||||
format in which to return the data.
|
||||
|
||||
Possible choices are:
|
||||
1. return the values untouched, which means the caller has to
|
||||
be aware of the s2.13 format. (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 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.)
|
||||
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.
|
||||
Note: the s2.13 format allows for negative values and values greater than 1!
|
||||
When converting to 16bit integers, these values will be clipped to 0...1. In
|
||||
order to preserve the full possible range of 64bit BMP pixel values, the
|
||||
number format should be set to either BMP_FORMAT_FLOAT or BMP_FORMAT_S2_13.
|
||||
|
||||
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 conversion:
|
||||
bmplib provides these functions to check if a BMP is 64bit and to set the
|
||||
conversion:
|
||||
- `bmpread_is_64bit()`
|
||||
- `bmpread_set_64bit_conv()`
|
||||
- `bmp_set_number_format()`
|
||||
|
||||
As to writing BMPs, by default bmplib will not write 64bit BMPs, as they are so exotic that only few applications will read them (other than native Microsoft tools, the new GIMP 3.0 is the only one I am aware of).
|
||||
Use `bmpwrite_set_64bit()` in order to write 64bit BMPs.
|
||||
|
||||
|
||||
## TODOs:
|
||||
### Definitely:
|
||||
|
||||
- [x] write indexed images.
|
||||
- [x] write RLE-compressed images (RLE4/RLE8 only. No OS/2 v2 BMPs).
|
||||
- [x] write RLE-compressed images ~~(RLE4/RLE8 only. No OS/2 v2 BMPs)~~.
|
||||
- [x] read RLE24-encoded BMPs.
|
||||
- [ ] read Huffman-encoded BMPs.
|
||||
- [x] read Huffman-encoded BMPs. (Still haven't found any real-life examples)
|
||||
- [x] line-by-line reading/writing. ~~Right now, the image can only be
|
||||
passed as a whole to/from bmplib.~~
|
||||
- [ ] read/write icc-profile and chromaticity/gamma values
|
||||
@@ -129,25 +144,29 @@ a BMP is 64bit and to set the conversion:
|
||||
|
||||
|
||||
### Maybe:
|
||||
- [x] passing indexed data and palette to user (optionally) instead of RGB-data.
|
||||
|
||||
- [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.
|
||||
(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.
|
||||
(which is the 'official' default) or let user pick one/multiple/all to
|
||||
be read in sequence.
|
||||
- [ ] Add a 'not-a-BMP-file' return type instead of just returning error.
|
||||
- [ ] icon- and pointer-files ("CI", "CP", "IC", "PT").
|
||||
- ~~[ ] icon- and pointer-files ("CI", "CP", "IC", "PT").~~
|
||||
- [x] 64-bits BMPs. (I changed my mind)
|
||||
|
||||
### Unclear:
|
||||
|
||||
- [ ] platforms: I am writing bmplib on Linux/intel (Ubuntu) using meson.
|
||||
Suggestions welcome on what is necessary to build on other
|
||||
platforms/cpus. And Windows?
|
||||
|
||||
|
||||
### Non-feature (internal):
|
||||
- [x] complete API description (see API.md)
|
||||
|
||||
- [x] complete API description (see API-full.md and API-quick-start.md)
|
||||
- [x] bmp-read.c is getting too big, split into several files
|
||||
|
||||
|
||||
@@ -164,5 +183,5 @@ Rupert
|
||||
|
||||
bmplibinfo@gmail.com
|
||||
|
||||
(be sure to include 'BMP' anywhere in the subject line, otherwise mail will
|
||||
go straight into spam folder)
|
||||
(be sure to include 'BMP' anywhere in the subject line, otherwise mail will go
|
||||
straight into spam folder)
|
||||
|
||||
104
bmp-common.c
104
bmp-common.c
@@ -29,6 +29,7 @@
|
||||
#include "logging.h"
|
||||
#include "bmp-common.h"
|
||||
#include "bmp-read.h"
|
||||
#include "huffman.h"
|
||||
#include "bmp-write.h"
|
||||
|
||||
|
||||
@@ -66,6 +67,34 @@ API const char* bmp_errmsg(BMPHANDLE h)
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* bmp_set_number_format
|
||||
*******************************************************/
|
||||
|
||||
API BMPRESULT bmp_set_number_format(BMPHANDLE h, enum BmpFormat format)
|
||||
{
|
||||
if (!h)
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
switch (h->magic) {
|
||||
case HMAGIC_READ:
|
||||
return br_set_number_format((BMPREAD)(void*)h, format);
|
||||
|
||||
case HMAGIC_WRITE:
|
||||
return bw_set_number_format((BMPWRITE)(void*)h, format);
|
||||
|
||||
default:
|
||||
#ifdef DEBUG
|
||||
printf("bmp_set_number_format() called with invalid handle (0x%04x)\n",
|
||||
(unsigned int) h->magic);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* bmp_free
|
||||
*******************************************************/
|
||||
@@ -108,21 +137,37 @@ int cm_check_is_read_handle(BMPHANDLE h)
|
||||
}
|
||||
|
||||
|
||||
/********************************************************
|
||||
* bm_check_is_write_handle
|
||||
*******************************************************/
|
||||
|
||||
int cm_check_is_write_handle(BMPHANDLE h)
|
||||
{
|
||||
BMPWRITE wp = (BMPWRITE)(void*)h;
|
||||
|
||||
if (wp && wp->magic == HMAGIC_WRITE)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/********************************************************
|
||||
* cm_gobble_up
|
||||
*******************************************************/
|
||||
|
||||
int cm_gobble_up(FILE *file, int count, LOG log)
|
||||
int cm_gobble_up(BMPREAD_R rp, int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (EOF == getc(file)) {
|
||||
if (feof(file))
|
||||
logerr(log, "unexpected end of file");
|
||||
else
|
||||
logsyserr(log, "error reading from file");
|
||||
if (EOF == getc(rp->file)) {
|
||||
if (feof(rp->file)) {
|
||||
rp->lasterr = BMP_ERR_TRUNCATED;
|
||||
logerr(rp->log, "unexpected end of file");
|
||||
} else {
|
||||
rp->lasterr = BMP_ERR_FILEIO;
|
||||
logsyserr(rp->log, "error reading from file");
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
@@ -154,6 +199,28 @@ int cm_count_bits(unsigned long v)
|
||||
|
||||
|
||||
|
||||
const char* cm_conv64_name(enum Bmpconv64 conv)
|
||||
{
|
||||
switch (conv) {
|
||||
case BMP_CONV64_SRGB : return "BMP_CONV64_SRGB";
|
||||
case BMP_CONV64_LINEAR: return "BMP_CONV64_LINEAR";
|
||||
case BMP_CONV64_NONE : return "BMP_CONV64_NONE";}
|
||||
return "(invalid)";
|
||||
}
|
||||
|
||||
|
||||
const char* cm_format_name(enum BmpFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case BMP_FORMAT_INT : return "BMP_FORMAT_INT";
|
||||
case BMP_FORMAT_FLOAT: return "BMP_FORMAT_FLOAT";
|
||||
case BMP_FORMAT_S2_13: return "BMP_FORMAT_S2_13";
|
||||
}
|
||||
return "(invalid)";
|
||||
}
|
||||
|
||||
|
||||
|
||||
int cm_all_lessoreq_int(int limit, int n, ...)
|
||||
{
|
||||
va_list ap;
|
||||
@@ -218,13 +285,34 @@ int cm_all_positive_int(int n, ...)
|
||||
}
|
||||
|
||||
|
||||
int cm_is_one_of(int candidate, int n, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int i, ret = FALSE;
|
||||
|
||||
int cm_align4padding(unsigned long a)
|
||||
if (n < 1)
|
||||
return TRUE;
|
||||
|
||||
va_start(ap, n);
|
||||
for (i = 0; i < n; i++) {
|
||||
if (va_arg(ap, int) == candidate) {
|
||||
ret = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int cm_align4padding(unsigned long long a)
|
||||
{
|
||||
return cm_align4size(a) - a;
|
||||
}
|
||||
|
||||
int cm_align2padding(unsigned long a)
|
||||
int cm_align2padding(unsigned long long a)
|
||||
{
|
||||
return cm_align2size(a) - a;
|
||||
}
|
||||
|
||||
94
bmp-common.h
94
bmp-common.h
@@ -30,6 +30,11 @@
|
||||
#define MIN(a,b) ((a)<(b)?(a):(b))
|
||||
#define ARR_SIZE(a) ((int) (sizeof a / sizeof (a)[0]))
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define ATTR_CONST __attribute__((const))
|
||||
#else
|
||||
#define ATTR_CONST
|
||||
#endif
|
||||
|
||||
union Pixel {
|
||||
unsigned int value[4];
|
||||
@@ -69,9 +74,21 @@ struct Colormask {
|
||||
int alpha;
|
||||
};
|
||||
} bits;
|
||||
union {
|
||||
double val[4];
|
||||
struct {
|
||||
double red;
|
||||
double green;
|
||||
double blue;
|
||||
double alpha;
|
||||
};
|
||||
} 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;
|
||||
@@ -89,23 +106,27 @@ struct Bmpread {
|
||||
struct Bmpinfo *ih;
|
||||
unsigned int insanity_limit;
|
||||
int width;
|
||||
int height;
|
||||
unsigned height;
|
||||
enum BmpOrient orientation;
|
||||
int has_alpha; /* original BMP has alpha channel */
|
||||
enum BmpUndefined undefined_to_alpha;
|
||||
enum BmpUndefined undefined_mode;
|
||||
int we_allocated_buffer;
|
||||
int line_by_line;
|
||||
struct Palette *palette;
|
||||
struct Colormask colormask;
|
||||
struct Colormask cmask;
|
||||
/* 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;
|
||||
/* state */
|
||||
unsigned long lasterr;
|
||||
int getinfo_called;
|
||||
int getinfo_return;
|
||||
int jpeg;
|
||||
@@ -114,7 +135,7 @@ struct Bmpread {
|
||||
int dim_queried_width;
|
||||
int dim_queried_height;
|
||||
int dim_queried_channels;
|
||||
int dim_queried_bits_per_channel;
|
||||
int dim_queried_bitsperchannel;
|
||||
int image_loaded;
|
||||
int rle;
|
||||
int rle_eol;
|
||||
@@ -122,26 +143,77 @@ struct Bmpread {
|
||||
int lbl_x; /* remember where we are in the image */
|
||||
int lbl_y; /* for line by line reading */
|
||||
int lbl_file_y; /* RLE files may be ahead of the image y */
|
||||
uint32_t hufbuf;
|
||||
int hufbuf_len;
|
||||
int truncated;
|
||||
int invalid_pixels;
|
||||
int invalid_index;
|
||||
int invalid_delta;
|
||||
int invalid_overrun;
|
||||
int file_err;
|
||||
int file_eof;
|
||||
int panic;
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct Bmpwrite {
|
||||
struct {
|
||||
uint32_t magic;
|
||||
LOG log;
|
||||
};
|
||||
FILE *file;
|
||||
struct Bmpfile *fh;
|
||||
struct Bmpinfo *ih;
|
||||
int width;
|
||||
int height;
|
||||
/* input */
|
||||
int source_channels;
|
||||
int source_bitsperchannel;
|
||||
int source_bytes_per_pixel;
|
||||
int source_format;
|
||||
struct Palette *palette;
|
||||
int palette_size; /* sizeof palette in bytes */
|
||||
/* output */
|
||||
size_t bytes_written;
|
||||
size_t bytes_written_before_bitdata;
|
||||
int has_alpha;
|
||||
enum BmpOrient outorientation;
|
||||
struct Colormask cmask;
|
||||
int rle_requested;
|
||||
int rle;
|
||||
int allow_2bit; /* Windows CE, but many will not read it */
|
||||
int out64bit;
|
||||
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;
|
||||
int lbl_y;
|
||||
};
|
||||
|
||||
|
||||
|
||||
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);
|
||||
int cm_align4padding(unsigned long long a);
|
||||
int cm_align2padding(unsigned long long a);
|
||||
int cm_count_bits(unsigned long v);
|
||||
|
||||
int cm_gobble_up(FILE *file, int count, LOG log);
|
||||
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);
|
||||
|
||||
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);
|
||||
@@ -159,10 +231,6 @@ int read_s32_le(FILE *file, int32_t *val);
|
||||
#define HMAGIC_READ (0x44414552UL)
|
||||
#define HMAGIC_WRITE (0x54495257UL)
|
||||
|
||||
typedef struct Bmpread *BMPREAD;
|
||||
typedef struct Bmpwrite *BMPWRITE;
|
||||
|
||||
|
||||
#define BMPFILE_BM (0x4d42)
|
||||
#define BMPFILE_BA (0x4142)
|
||||
#define BMPFILE_CI (0x4943)
|
||||
|
||||
@@ -28,16 +28,49 @@
|
||||
#include "bmplib.h"
|
||||
#include "logging.h"
|
||||
#include "bmp-common.h"
|
||||
#include "huffman.h"
|
||||
#include "bmp-read.h"
|
||||
#include "reversebits.h"
|
||||
|
||||
|
||||
static inline unsigned long s_scaleint(unsigned long val, int frombits, int tobits) __attribute__((const));
|
||||
/*
|
||||
* Rough overview over top-level functions defined in this file:
|
||||
*
|
||||
*
|
||||
* API entry points bmpread_load_image() bmpread_load_line()
|
||||
* \ /
|
||||
* \ /
|
||||
* common prep work, \ /
|
||||
* buffer allocation, s_load_image_or_line()
|
||||
* sanity checks, etc. / \
|
||||
* / \
|
||||
* / \
|
||||
* 'supervision' s_read_whole_image() s_read_one_line()
|
||||
* \ /
|
||||
* \ /
|
||||
* \ /
|
||||
* s_read_rgb_line()
|
||||
* 'grunt work' s_read_indexed_line()
|
||||
* s_read_rle_line()
|
||||
*/
|
||||
|
||||
static inline unsigned long s_scaleint(unsigned long val, int frombits, int tobits) ATTR_CONST;
|
||||
static void s_set_file_error(BMPREAD_R rp);
|
||||
static void s_log_error_from_state(BMPREAD_R rp);
|
||||
static int s_cont_error(BMPREAD_R rp);
|
||||
static int s_stopping_error(BMPREAD_R rp);
|
||||
static inline int s_read_one_byte(BMPREAD_R rp);
|
||||
static inline void s_int_to_result_format(BMPREAD_R rp, int frombits, unsigned char *restrict px);
|
||||
|
||||
static BMPRESULT s_load_image(BMPREAD_R rp, unsigned char **restrict buffer, int line_by_line);
|
||||
static BMPRESULT s_load_image_or_line(BMPREAD_R rp, unsigned char **restrict buffer, int line_by_line);
|
||||
static int s_read_rgb_line(BMPREAD_R rp, unsigned char *restrict line);
|
||||
static void s_read_indexed_line(BMPREAD_R rp, unsigned char *restrict line);
|
||||
static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
|
||||
int *restrict x, int *restrict yoff);
|
||||
static void s_read_huffman_line(BMPREAD_R rp, unsigned char *restrict line);
|
||||
|
||||
_Static_assert(sizeof(float) == 4, "sizeof(float) must be 4. Cannot build bmplib.");
|
||||
_Static_assert(sizeof(int) >= 4, "int must be at least 32bit. Cannot build bmplib.");
|
||||
|
||||
|
||||
/********************************************************
|
||||
@@ -52,7 +85,7 @@ API BMPRESULT bmpread_load_image(BMPHANDLE h, unsigned char **restrict buffer)
|
||||
return BMP_RESULT_ERROR;
|
||||
rp = (BMPREAD)(void*)h;
|
||||
|
||||
return s_load_image(rp, buffer, FALSE);
|
||||
return s_load_image_or_line(rp, buffer, FALSE);
|
||||
}
|
||||
|
||||
|
||||
@@ -69,23 +102,22 @@ API BMPRESULT bmpread_load_line(BMPHANDLE h, unsigned char **restrict buffer)
|
||||
return BMP_RESULT_ERROR;
|
||||
rp = (BMPREAD)(void*)h;
|
||||
|
||||
logreset(rp->log); /* otherwise we might accumulate thousands */
|
||||
/* of log entries with large corrupted */
|
||||
/* images */
|
||||
logreset(rp->log); /* otherwise we might accumulate thousands */
|
||||
/* of log entries with large corrupt images */
|
||||
|
||||
return s_load_image(rp, buffer, TRUE);
|
||||
return s_load_image_or_line(rp, buffer, TRUE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* s_load_image
|
||||
* s_load_image_or_line
|
||||
*******************************************************/
|
||||
|
||||
static void s_read_whole_image(BMPREAD_R rp, unsigned char *restrict image);
|
||||
static void s_read_one_line(BMPREAD_R rp, unsigned char *restrict image);
|
||||
|
||||
static BMPRESULT s_load_image(BMPREAD_R rp, unsigned char **restrict buffer, int line_by_line)
|
||||
static BMPRESULT s_load_image_or_line(BMPREAD_R rp, unsigned char **restrict buffer, int line_by_line)
|
||||
{
|
||||
size_t buffer_size;
|
||||
|
||||
@@ -120,7 +152,7 @@ static BMPRESULT s_load_image(BMPREAD_R rp, unsigned char **restrict buffer, int
|
||||
}
|
||||
|
||||
if (line_by_line)
|
||||
buffer_size = rp->width * rp->result_bytes_per_pixel;
|
||||
buffer_size = (size_t) rp->width * rp->result_bytes_per_pixel;
|
||||
else
|
||||
buffer_size = rp->result_size;
|
||||
if (!*buffer) { /* no buffer supplied, we will allocate one */
|
||||
@@ -129,33 +161,34 @@ static BMPRESULT s_load_image(BMPREAD_R rp, unsigned char **restrict buffer, int
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
rp->we_allocated_buffer = TRUE;
|
||||
} else {
|
||||
rp->we_allocated_buffer = FALSE;
|
||||
}
|
||||
else rp->we_allocated_buffer = FALSE;
|
||||
|
||||
memset(*buffer, 0, buffer_size);
|
||||
if (rp->we_allocated_buffer || (rp->rle && (rp->undefined_mode == BMP_UNDEFINED_TO_ALPHA)))
|
||||
memset(*buffer, 0, buffer_size);
|
||||
|
||||
if (!line_by_line)
|
||||
rp->image_loaded = TRUE; /* point of no return */
|
||||
|
||||
if (!rp->line_by_line) { /* either whole image ot first line */
|
||||
|
||||
if (!rp->line_by_line) { /* either whole image or first line */
|
||||
if (rp->bytes_read > rp->fh->offbits) {
|
||||
logerr(rp->log, "Panic! Corrupt file?");
|
||||
logerr(rp->log, "Corrupt file");
|
||||
goto abort;
|
||||
}
|
||||
/* skip to actual bitmap data: */
|
||||
if (!cm_gobble_up(rp->file, rp->fh->offbits - rp->bytes_read, rp->log)) {
|
||||
if (!cm_gobble_up(rp, rp->fh->offbits - rp->bytes_read)) {
|
||||
logerr(rp->log, "while seeking start of bitmap data");
|
||||
goto abort;
|
||||
}
|
||||
rp->bytes_read += rp->fh->offbits - rp->bytes_read;
|
||||
}
|
||||
|
||||
if (line_by_line) {
|
||||
rp->line_by_line = TRUE; /* don't set this earlier, or we won't */
|
||||
/* be able to identify first line */
|
||||
s_read_one_line(rp, *buffer);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
s_read_whole_image(rp, *buffer);
|
||||
}
|
||||
|
||||
@@ -164,8 +197,7 @@ static BMPRESULT s_load_image(BMPREAD_R rp, unsigned char **restrict buffer, int
|
||||
rp->truncated = TRUE;
|
||||
rp->image_loaded = TRUE;
|
||||
return BMP_RESULT_TRUNCATED;
|
||||
}
|
||||
else if (rp->invalid_pixels)
|
||||
} else if (s_cont_error(rp))
|
||||
return BMP_RESULT_INVALID;
|
||||
|
||||
return BMP_RESULT_OK;
|
||||
@@ -184,15 +216,35 @@ abort:
|
||||
/********************************************************
|
||||
* s_read_whole_image
|
||||
*******************************************************/
|
||||
static void s_read_rgb_image(BMPREAD_R rp, unsigned char *restrict image);
|
||||
static void s_read_indexed_or_rle_image(BMPREAD_R rp, unsigned char *restrict image);
|
||||
|
||||
static void s_read_whole_image(BMPREAD_R rp, unsigned char *restrict image)
|
||||
{
|
||||
if (rp->ih->bitcount <= 8 || rp->rle)
|
||||
s_read_indexed_or_rle_image(rp, image);
|
||||
else
|
||||
s_read_rgb_image(rp, image);
|
||||
int x = 0, y, yoff = 1;
|
||||
size_t linesize, real_y;
|
||||
|
||||
linesize = (size_t) rp->width * rp->result_bytes_per_pixel;
|
||||
|
||||
for (y = 0; y < rp->height; y += yoff) {
|
||||
real_y = (rp->orientation == BMP_ORIENT_TOPDOWN) ? y : rp->height-1-y;
|
||||
if (rp->rle) {
|
||||
s_read_rle_line(rp, image + real_y * linesize, &x, &yoff);
|
||||
if (x >= rp->width)
|
||||
x = 0;
|
||||
} else if (rp->ih->bitcount <= 8) {
|
||||
if (rp->ih->compression == BI_OS2_HUFFMAN)
|
||||
s_read_huffman_line(rp, image + real_y * linesize);
|
||||
else
|
||||
s_read_indexed_line(rp, image + real_y * linesize);
|
||||
} else {
|
||||
s_read_rgb_line(rp, image + real_y * linesize);
|
||||
}
|
||||
if (rp->rle_eof || s_stopping_error(rp))
|
||||
break;
|
||||
if (yoff > rp->height - y) {
|
||||
logerr(rp->log, "RLE delta beyond image dimensions");
|
||||
rp->invalid_delta = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -201,11 +253,6 @@ static void s_read_whole_image(BMPREAD_R rp, unsigned char *restrict image)
|
||||
* s_read_one_line
|
||||
*******************************************************/
|
||||
|
||||
static int s_read_rgb_line(BMPREAD_R rp, unsigned char *restrict line);
|
||||
static void s_read_indexed_line(BMPREAD_R rp, unsigned char *restrict line);
|
||||
static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
|
||||
int *restrict x, int *restrict yoff);
|
||||
|
||||
static void s_read_one_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
{
|
||||
int yoff = 1;
|
||||
@@ -217,29 +264,28 @@ static void s_read_one_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
|
||||
if (rp->lbl_file_y > rp->lbl_y) {
|
||||
; /* nothing to do, RLE skipped line */
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (rp->rle) {
|
||||
s_read_rle_line(rp, line, &rp->lbl_x, &yoff);
|
||||
}
|
||||
else {
|
||||
s_read_indexed_line(rp, line);
|
||||
}
|
||||
|
||||
if (!(rp->rle_eof || s_stopping_error(rp))) {
|
||||
rp->lbl_file_y += yoff;
|
||||
|
||||
if (rp->lbl_file_y > rp->height) {
|
||||
rp->invalid_delta = TRUE;
|
||||
} else {
|
||||
if (rp->ih->compression == BI_OS2_HUFFMAN) {
|
||||
s_read_huffman_line(rp, line);
|
||||
} else {
|
||||
s_read_indexed_line(rp, line);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(rp->rle_eof || s_stopping_error(rp))) {
|
||||
if (yoff > rp->height - rp->lbl_file_y) {
|
||||
rp->invalid_delta = TRUE;
|
||||
}
|
||||
rp->lbl_file_y += yoff;
|
||||
|
||||
}
|
||||
if (rp->rle_eof)
|
||||
rp->lbl_file_y = rp->height;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
s_read_rgb_line(rp, line);
|
||||
}
|
||||
|
||||
@@ -251,104 +297,134 @@ static void s_read_one_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* s_read_rgb_image
|
||||
*******************************************************/
|
||||
|
||||
static void s_read_rgb_image(BMPREAD_R rp, unsigned char *restrict image)
|
||||
{
|
||||
int y;
|
||||
size_t linelength, offs, real_y;
|
||||
|
||||
linelength = (size_t) rp->width * rp->result_bytes_per_pixel;
|
||||
|
||||
for (y = 0; y < rp->height; y++) {
|
||||
real_y = (rp->orientation == BMP_ORIENT_TOPDOWN) ? y : rp->height-1-y;
|
||||
offs = linelength * real_y;
|
||||
if (!s_read_rgb_line(rp, image + offs))
|
||||
rp->truncated = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* s_read_rgb_line
|
||||
*******************************************************/
|
||||
static inline int s_read_rgb_pixel(BMPREAD_R rp, union Pixel *restrict px);
|
||||
static inline void s_convert64(uint16_t *val64);
|
||||
static inline void s_convert64srgb(uint16_t *val64);
|
||||
|
||||
static inline int s_read_rgb_pixel(BMPREAD_R rp, union Pixel *restrict px);
|
||||
static inline double s_s2_13_to_float(uint16_t s2_13);
|
||||
static inline double s_int_to_float(unsigned long ul, int bits);
|
||||
static inline void s_convert64(uint16_t *val64);
|
||||
static inline void s_convert64srgb(uint16_t *val64);
|
||||
static inline double s_srgb_gamma_float(double d);
|
||||
static inline uint16_t s_srgb_gamma_s2_13(uint16_t s2_13);
|
||||
|
||||
static int s_read_rgb_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
{
|
||||
int x, padding;
|
||||
union Pixel pixel;
|
||||
int i, x, padding;
|
||||
union Pixel px;
|
||||
size_t offs;
|
||||
int bits = rp->result_bitsperchannel;
|
||||
uint32_t pxval;
|
||||
double d;
|
||||
uint16_t s2_13;
|
||||
|
||||
for (x = 0; x < rp->width; x++) {
|
||||
|
||||
if (!s_read_rgb_pixel(rp, &pixel)) {
|
||||
if (!s_read_rgb_pixel(rp, &px)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
offs = x * rp->result_channels;
|
||||
|
||||
switch (rp->result_bits_per_channel) {
|
||||
case 8:
|
||||
((unsigned char*)line)[offs] = pixel.red;
|
||||
((unsigned char*)line)[offs+1] = pixel.green;
|
||||
((unsigned char*)line)[offs+2] = pixel.blue;
|
||||
if (rp->has_alpha)
|
||||
((unsigned char*)line)[offs+3] = pixel.alpha;
|
||||
break;
|
||||
case 16:
|
||||
((uint16_t*)line)[offs] = (pixel.red);
|
||||
((uint16_t*)line)[offs+1] = (pixel.green);
|
||||
((uint16_t*)line)[offs+2] = (pixel.blue);
|
||||
if (rp->has_alpha)
|
||||
((uint16_t*)line)[offs+3] = (pixel.alpha);
|
||||
|
||||
switch (rp->result_format) {
|
||||
case BMP_FORMAT_INT:
|
||||
for (i = 0; i < rp->result_channels; i++) {
|
||||
pxval = s_scaleint(px.value[i], rp->cmask.bits.value[i], bits);
|
||||
switch(bits) {
|
||||
case 8:
|
||||
((unsigned char*)line)[offs + i] = pxval;
|
||||
break;
|
||||
case 16:
|
||||
((uint16_t*)line)[offs + i] = pxval;
|
||||
break;
|
||||
case 32:
|
||||
((uint32_t*)line)[offs + i] = pxval;
|
||||
break;
|
||||
default:
|
||||
logerr(rp->log, "Waaaaaaaaaaaaaah!");
|
||||
rp->panic = TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
if (rp->ih->bitcount == 64) {
|
||||
switch (rp->conv64) {
|
||||
case BMP_CONV64_16BIT_SRGB:
|
||||
case BMP_CONV64_SRGB:
|
||||
s_convert64srgb(&((uint16_t*)line)[offs]);
|
||||
break;
|
||||
case BMP_CONV64_16BIT:
|
||||
case BMP_CONV64_LINEAR:
|
||||
s_convert64(&((uint16_t*)line)[offs]);
|
||||
break;
|
||||
case BMP_CONV64_NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 32:
|
||||
((uint32_t*)line)[offs] = pixel.red;
|
||||
((uint32_t*)line)[offs+1] = pixel.green;
|
||||
((uint32_t*)line)[offs+2] = pixel.blue;
|
||||
if (rp->has_alpha)
|
||||
((uint32_t*)line)[offs+3] = pixel.alpha;
|
||||
|
||||
case BMP_FORMAT_FLOAT:
|
||||
if (rp->ih->bitcount == 64) {
|
||||
for (i = 0; i < rp->result_channels; i++) {
|
||||
d = s_s2_13_to_float(px.value[i]);
|
||||
if (i < 3 && rp->conv64 == BMP_CONV64_SRGB)
|
||||
d = s_srgb_gamma_float(d);
|
||||
((float*)line)[offs+i] = d;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < rp->result_channels; i++) {
|
||||
d = s_int_to_float(px.value[i], rp->cmask.bits.value[i]);
|
||||
((float*)line)[offs + i] = d;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case BMP_FORMAT_S2_13:
|
||||
if (rp->ih->bitcount == 64) {
|
||||
for (i = 0; i < rp->result_channels; i++) {
|
||||
s2_13 = px.value[i];
|
||||
if (i < 3 && rp->conv64 == BMP_CONV64_SRGB)
|
||||
s2_13 = s_srgb_gamma_s2_13(s2_13);
|
||||
((uint16_t*)line)[offs+i] = s2_13;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < rp->result_channels; i++) {
|
||||
d = s_int_to_float(px.value[i], rp->cmask.bits.value[i]);
|
||||
d = d * 8192.0 + 0.5;
|
||||
((uint16_t*)line)[offs+i] = (uint16_t) d;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
logerr(rp->log, "Waaaaaaaaaaaaaah!");
|
||||
logerr(rp->log, "Unknown format");
|
||||
rp->panic = TRUE;
|
||||
return FALSE;
|
||||
|
||||
}
|
||||
}
|
||||
padding = cm_align4padding((rp->width * rp->ih->bitcount + 7) / 8);
|
||||
if (!cm_gobble_up(rp->file, padding, rp->log)) {
|
||||
padding = cm_align4padding(((uint64_t)rp->width * rp->ih->bitcount + 7) / 8);
|
||||
if (!cm_gobble_up(rp, padding)) {
|
||||
s_set_file_error(rp);
|
||||
return FALSE;
|
||||
}
|
||||
rp->bytes_read += padding;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static inline double s_s2_13_to_float(uint16_t s2_13)
|
||||
{
|
||||
return ((double)((int16_t)s2_13)) / 8192.0;
|
||||
}
|
||||
|
||||
|
||||
static inline double s_int_to_float(unsigned long ul, int bits)
|
||||
{
|
||||
return (double) ul / (double) ((1ULL<<bits)-1);
|
||||
}
|
||||
|
||||
|
||||
static inline void s_convert64(uint16_t *val64)
|
||||
{
|
||||
int i;
|
||||
int i;
|
||||
int32_t s;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
@@ -374,13 +450,9 @@ static inline void s_convert64srgb(uint16_t *val64)
|
||||
s = s << 16 >> 16; /* propagate sign bit */
|
||||
if (i < 3) {
|
||||
v = (double) s / (1<<13);
|
||||
if (v <= 0.0031308)
|
||||
v = 12.92 * v;
|
||||
else
|
||||
v = 1.055 * pow(v, 1.0/2.4) - 0.055;
|
||||
v = s_srgb_gamma_float(v);
|
||||
s = (int32_t) (v * (double) 0xffff);
|
||||
}
|
||||
else { /* don't apply gamma to alpha channel */
|
||||
} else { /* don't apply gamma to alpha channel */
|
||||
s *= 0xffff;
|
||||
s >>= 13;
|
||||
}
|
||||
@@ -392,11 +464,29 @@ static inline void s_convert64srgb(uint16_t *val64)
|
||||
|
||||
|
||||
|
||||
static inline double s_srgb_gamma_float(double d)
|
||||
{
|
||||
if (d <= 0.0031308)
|
||||
d = 12.92 * d;
|
||||
else
|
||||
d = 1.055 * pow(d, 1.0/2.4) - 0.055;
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
static inline uint16_t s_srgb_gamma_s2_13(uint16_t s2_13)
|
||||
{
|
||||
double d;
|
||||
|
||||
d = (double) ((int16_t)s2_13) / 8192.0;
|
||||
d = s_srgb_gamma_float(d);
|
||||
return (uint16_t) (((int)(d * 8192.0 + 0.5)) & 0xffff);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* s_read_rgb_pixel
|
||||
*
|
||||
* read RGB image pixels.
|
||||
* works with up to 32bits and any RGBA-masks
|
||||
*******************************************************/
|
||||
|
||||
static inline int s_read_rgb_pixel(BMPREAD_R rp, union Pixel *restrict px)
|
||||
@@ -406,88 +496,40 @@ static inline int s_read_rgb_pixel(BMPREAD_R rp, union Pixel *restrict px)
|
||||
|
||||
v = 0;
|
||||
for (i = 0; i < rp->ih->bitcount; i+=8 ) {
|
||||
if (EOF == (byte = getc(rp->file))) {
|
||||
if (EOF == (byte = s_read_one_byte(rp))) {
|
||||
s_set_file_error(rp);
|
||||
return FALSE;
|
||||
}
|
||||
v |= ((unsigned long long)byte) << i;
|
||||
}
|
||||
|
||||
px->red = (v & rp->colormask.mask.red) >> rp->colormask.shift.red;
|
||||
px->green = (v & rp->colormask.mask.green) >> rp->colormask.shift.green;
|
||||
px->blue = (v & rp->colormask.mask.blue) >> rp->colormask.shift.blue;
|
||||
|
||||
px->red = s_scaleint(px->red, rp->colormask.bits.red, rp->result_bits_per_channel);
|
||||
px->green = s_scaleint(px->green, rp->colormask.bits.green, rp->result_bits_per_channel);
|
||||
px->blue = s_scaleint(px->blue, rp->colormask.bits.blue, rp->result_bits_per_channel);
|
||||
|
||||
if (rp->has_alpha) {
|
||||
px->alpha = (v & rp->colormask.mask.alpha) >> rp->colormask.shift.alpha;
|
||||
px->alpha = s_scaleint(px->alpha, rp->colormask.bits.alpha, rp->result_bits_per_channel);
|
||||
}
|
||||
px->red = (v & rp->cmask.mask.red) >> rp->cmask.shift.red;
|
||||
px->green = (v & rp->cmask.mask.green) >> rp->cmask.shift.green;
|
||||
px->blue = (v & rp->cmask.mask.blue) >> rp->cmask.shift.blue;
|
||||
if (rp->has_alpha)
|
||||
px->alpha = (v & rp->cmask.mask.alpha) >> rp->cmask.shift.alpha;
|
||||
else
|
||||
px->alpha = (1<<rp->result_bits_per_channel) - 1;
|
||||
px->alpha = (1<<rp->result_bitsperchannel) - 1;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* s_read_indexed_or_rle_image
|
||||
* - 4/8/24 bit RLE
|
||||
* - 1/2/4/8 bits non-RLE indexed
|
||||
*******************************************************/
|
||||
|
||||
static void s_read_indexed_or_rle_image(BMPREAD_R rp, unsigned char *restrict image)
|
||||
{
|
||||
int x = 0, y = 0, yoff = 1;
|
||||
size_t linesize, real_y;
|
||||
|
||||
linesize = (size_t) rp->width * (size_t) rp->result_bytes_per_pixel;
|
||||
|
||||
while (y < rp->height) {
|
||||
real_y = (rp->orientation == BMP_ORIENT_TOPDOWN) ? y : rp->height-1-y;
|
||||
if (rp->rle) {
|
||||
s_read_rle_line(rp, (unsigned char*)image +
|
||||
real_y * linesize, &x, &yoff);
|
||||
}
|
||||
else {
|
||||
s_read_indexed_line(rp, (unsigned char*)image + real_y*linesize);
|
||||
}
|
||||
if (rp->rle_eof || s_stopping_error(rp))
|
||||
break;
|
||||
|
||||
y += yoff;
|
||||
|
||||
if (y > rp->height) {
|
||||
logerr(rp->log, "RLE delta beyond image dimensions");
|
||||
rp->invalid_delta = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* only relevant for RLE-images: */
|
||||
if (x >= rp->width)
|
||||
x = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* s_read_indexed_line
|
||||
* - 1/2/4/8 bits non-RLE indexed
|
||||
*******************************************************/
|
||||
static inline int s_read_n_bytes(BMPREAD_R rp, int n, unsigned int *restrict buff);
|
||||
static inline unsigned int s_bits_from_buffer(unsigned int buf, int size,
|
||||
static inline int s_read_n_bytes(BMPREAD_R rp, int n, unsigned long *restrict buff);
|
||||
static inline unsigned long s_bits_from_buffer(unsigned long buf, int size,
|
||||
int nbits, int used_bits);
|
||||
|
||||
static void s_read_indexed_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
{
|
||||
int bits_used, buffer_size, x = 0, v;
|
||||
int done = FALSE;
|
||||
unsigned int buffer;
|
||||
size_t offs;
|
||||
int bits_used, buffer_size, x = 0, v;
|
||||
int done = FALSE;
|
||||
unsigned long buffer;
|
||||
size_t offs;
|
||||
|
||||
/* setting the buffer size to the alignment takes care of padding bytes */
|
||||
buffer_size = 32;
|
||||
@@ -497,26 +539,24 @@ static void s_read_indexed_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
bits_used = 0;
|
||||
while (bits_used < buffer_size) {
|
||||
|
||||
/* mask out the relevant bits for current pixel */
|
||||
v = (int) s_bits_from_buffer(buffer, buffer_size, rp->ih->bitcount, bits_used);
|
||||
/* mask out the relevant bits for current pixel */
|
||||
v = (int) s_bits_from_buffer(buffer, buffer_size,
|
||||
rp->ih->bitcount, bits_used);
|
||||
bits_used += rp->ih->bitcount;
|
||||
|
||||
if (v >= rp->palette->numcolors) {
|
||||
rp->invalid_pixels = TRUE;
|
||||
v = rp->palette->numcolors - 1;
|
||||
rp->invalid_index = TRUE;
|
||||
}
|
||||
else {
|
||||
offs = (size_t) x * (size_t) rp->result_bytes_per_pixel;
|
||||
if (rp->result_indexed) {
|
||||
line[offs] = v;
|
||||
}
|
||||
else {
|
||||
line[offs] = rp->palette->color[v].red;
|
||||
line[offs+1] = rp->palette->color[v].green;
|
||||
line[offs+2] = rp->palette->color[v].blue;
|
||||
if (rp->rle && (rp->undefined_to_alpha == BMP_UNDEFINED_TO_ALPHA))
|
||||
line[offs+3] = 0xff; /* set alpha to 1.0
|
||||
for defined pixels */
|
||||
}
|
||||
|
||||
offs = (size_t) x * rp->result_bytes_per_pixel;
|
||||
if (rp->result_indexed) {
|
||||
line[offs] = v;
|
||||
} else {
|
||||
line[offs] = rp->palette->color[v].red;
|
||||
line[offs+1] = rp->palette->color[v].green;
|
||||
line[offs+2] = rp->palette->color[v].blue;
|
||||
s_int_to_result_format(rp, 8, line + offs);
|
||||
}
|
||||
if (++x == rp->width) {
|
||||
done = TRUE;
|
||||
@@ -532,25 +572,16 @@ static void s_read_indexed_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
* s_read_n_bytes
|
||||
*******************************************************/
|
||||
|
||||
static inline int s_read_n_bytes(BMPREAD_R rp, int n, unsigned int *restrict buff)
|
||||
static inline int s_read_n_bytes(BMPREAD_R rp, int n, unsigned long *restrict buff)
|
||||
{
|
||||
int byte;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (n > sizeof *buff) {
|
||||
logerr(rp->log, "Nooooo! Trying to read more than fits into buffer");
|
||||
rp->panic = TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
*buff = 0;
|
||||
while (n--) {
|
||||
if (EOF == (byte = getc(rp->file))) {
|
||||
if (EOF == (byte = s_read_one_byte(rp))) {
|
||||
s_set_file_error(rp);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*buff <<= 8;
|
||||
*buff |= byte;
|
||||
}
|
||||
@@ -563,7 +594,7 @@ static inline int s_read_n_bytes(BMPREAD_R rp, int n, unsigned int *restrict buf
|
||||
* s_bits_from_buffer
|
||||
*******************************************************/
|
||||
|
||||
static inline unsigned int s_bits_from_buffer(unsigned int buf, int size,
|
||||
static inline unsigned long s_bits_from_buffer(unsigned long buf, int size,
|
||||
int nbits, int used_bits)
|
||||
{
|
||||
unsigned long mask;
|
||||
@@ -592,7 +623,7 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
|
||||
{
|
||||
int repeat = FALSE, left_in_run = 0;
|
||||
int right, up;
|
||||
int padding = FALSE, odd = FALSE, v, r, g, b = 0;
|
||||
int padding = FALSE, odd = FALSE, v, r = 0, g = 0, b = 0;
|
||||
size_t offs;
|
||||
int bits = rp->ih->bitcount;
|
||||
|
||||
@@ -611,26 +642,29 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
|
||||
/* for 24-bit RLE, b holds blue value, */
|
||||
/* for 4/8-bit RLE, b holds index value(s) */
|
||||
/* 4-bit RLE only needs new byte every other time */
|
||||
if (EOF == (b = getc(rp->file)) ||
|
||||
(bits == 24 && EOF == (g = getc(rp->file))) ||
|
||||
(bits == 24 && EOF == (r = getc(rp->file)))) {
|
||||
if (EOF == (b = s_read_one_byte(rp)) ||
|
||||
(bits == 24 && EOF == (g = s_read_one_byte(rp))) ||
|
||||
(bits == 24 && EOF == (r = s_read_one_byte(rp)))) {
|
||||
s_set_file_error(rp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (left_in_run == 0 && padding) {
|
||||
if (EOF == getc(rp->file)) {
|
||||
if (EOF == s_read_one_byte(rp)) {
|
||||
s_set_file_error(rp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
offs = (size_t) *x * (size_t) rp->result_bytes_per_pixel;
|
||||
offs = (size_t) *x * rp->result_bytes_per_pixel;
|
||||
if ((rp->undefined_mode == BMP_UNDEFINED_TO_ALPHA) && !rp->result_indexed)
|
||||
line[offs+3] = 0xff; /* set alpha to 1.0 for defined pixels */
|
||||
switch (bits) {
|
||||
case 24:
|
||||
line[offs] = r;
|
||||
line[offs+1] = g;
|
||||
line[offs+2] = b;
|
||||
s_int_to_result_format(rp, 8, line + offs);
|
||||
break;
|
||||
case 4:
|
||||
case 8:
|
||||
@@ -641,28 +675,25 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
|
||||
odd = !odd;
|
||||
}
|
||||
if (v >= rp->palette->numcolors) {
|
||||
rp->invalid_pixels = TRUE;
|
||||
v = rp->palette->numcolors - 1;
|
||||
rp->invalid_index = TRUE;
|
||||
}
|
||||
else {
|
||||
if (rp->result_indexed) {
|
||||
line[offs] = v;
|
||||
}
|
||||
else {
|
||||
line[offs] = rp->palette->color[v].red;
|
||||
line[offs+1] = rp->palette->color[v].green;
|
||||
line[offs+2] = rp->palette->color[v].blue;
|
||||
}
|
||||
if (rp->result_indexed) {
|
||||
line[offs] = v;
|
||||
} else {
|
||||
line[offs] = rp->palette->color[v].red;
|
||||
line[offs+1] = rp->palette->color[v].green;
|
||||
line[offs+2] = rp->palette->color[v].blue;
|
||||
s_int_to_result_format(rp, 8, line + offs);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if ((rp->undefined_to_alpha == BMP_UNDEFINED_TO_ALPHA) && !rp->result_indexed)
|
||||
line[offs+3] = 0xff; /* set alpha to 1.0 for defined pixels */
|
||||
|
||||
*x += 1;
|
||||
if (*x >= rp->width) {
|
||||
rp->rle_eol = FALSE; /* EOL detected by width, not by RLE-code */
|
||||
if (left_in_run) {
|
||||
rp->invalid_pixels = TRUE;
|
||||
rp->invalid_overrun = TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -670,16 +701,16 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
|
||||
}
|
||||
|
||||
/* not in a literal or RLE run, start afresh */
|
||||
if (EOF == (v = getc(rp->file))) {
|
||||
if (EOF == (v = s_read_one_byte(rp))) {
|
||||
s_set_file_error(rp);
|
||||
break;
|
||||
}
|
||||
|
||||
/* start RLE run */
|
||||
if (v > 0) {
|
||||
if (EOF == (b = getc(rp->file)) ||
|
||||
(bits == 24 && EOF == (g = getc(rp->file))) ||
|
||||
(bits == 24 && EOF == (r = getc(rp->file)))) {
|
||||
if (EOF == (b = s_read_one_byte(rp)) ||
|
||||
(bits == 24 && EOF == (g = s_read_one_byte(rp))) ||
|
||||
(bits == 24 && EOF == (r = s_read_one_byte(rp)))) {
|
||||
s_set_file_error(rp);
|
||||
break;
|
||||
}
|
||||
@@ -691,7 +722,7 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
|
||||
}
|
||||
|
||||
/* v == 0: escape, look at next byte */
|
||||
if (EOF == (v = getc(rp->file))) {
|
||||
if (EOF == (v = s_read_one_byte(rp))) {
|
||||
s_set_file_error(rp);
|
||||
break;
|
||||
}
|
||||
@@ -735,7 +766,7 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
|
||||
|
||||
/* delta. */
|
||||
if (v == 2) {
|
||||
if (EOF == (right = getc(rp->file)) || EOF == (up = getc(rp->file))) {
|
||||
if (EOF == (right = s_read_one_byte(rp)) || EOF == (up = s_read_one_byte(rp))) {
|
||||
s_set_file_error(rp);
|
||||
break;
|
||||
}
|
||||
@@ -758,6 +789,131 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
|
||||
}
|
||||
|
||||
|
||||
/********************************************************
|
||||
* s_read_huffman_line
|
||||
*******************************************************/
|
||||
|
||||
static void s_read_huffman_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
{
|
||||
int count;
|
||||
size_t x = 0, offs;
|
||||
int byte, eol = FALSE, ndecoded;
|
||||
int black = 0;
|
||||
|
||||
while(1) {
|
||||
while (rp->hufbuf_len <= 24) {
|
||||
if (EOF == (byte = s_read_one_byte(rp)))
|
||||
break;
|
||||
byte = reversebits[byte];
|
||||
rp->hufbuf |= ((uint32_t)byte) << rp->hufbuf_len;
|
||||
rp->hufbuf_len += 8;
|
||||
}
|
||||
if (rp->hufbuf_len == 0)
|
||||
break;
|
||||
|
||||
if (eol || ((rp->hufbuf & 0x00ff) == 0)) {
|
||||
/* EOL, look for next 1, ignore any number of 0's */
|
||||
if (rp->hufbuf == 0) {
|
||||
eol = TRUE;
|
||||
rp->hufbuf_len = 0;
|
||||
continue;
|
||||
}
|
||||
while ((rp->hufbuf & 0x0001) == 0) {
|
||||
rp->hufbuf >>= 1;
|
||||
rp->hufbuf_len--;
|
||||
}
|
||||
rp->hufbuf >>= 1;
|
||||
rp->hufbuf_len--;
|
||||
if (x == 0) /* ignore eol at start of line */
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
ndecoded = huff_decode(&count, rp->hufbuf, rp->hufbuf_len, black);
|
||||
if (ndecoded == 0) {
|
||||
/* code was invalid, throw away one bit and try again */
|
||||
rp->hufbuf >>= 1;
|
||||
rp->hufbuf_len--;
|
||||
continue;
|
||||
}
|
||||
rp->hufbuf >>= ndecoded;
|
||||
rp->hufbuf_len -= ndecoded;
|
||||
|
||||
count = MIN(count, rp->width - x);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
offs = x * rp->result_bytes_per_pixel;
|
||||
if (rp->result_indexed) {
|
||||
line[offs] = black;
|
||||
} else {
|
||||
line[offs] = rp->palette->color[black].red;
|
||||
line[offs+1] = rp->palette->color[black].green;
|
||||
line[offs+2] = rp->palette->color[black].blue;
|
||||
s_int_to_result_format(rp, 8, line + x);
|
||||
}
|
||||
x++;
|
||||
}
|
||||
black = !black;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* s_int_to_result_format
|
||||
* convert integer values in image buffer
|
||||
* to selected number format.
|
||||
*******************************************************/
|
||||
|
||||
static inline void s_int_to_result_format(BMPREAD_R rp, int frombits, unsigned char *restrict px)
|
||||
{
|
||||
int c;
|
||||
uint32_t v;
|
||||
|
||||
if (rp->result_format == BMP_FORMAT_INT)
|
||||
return;
|
||||
#ifdef DEBUG
|
||||
if (frombits > rp->result_bitsperchannel) {
|
||||
printf("This is bad, frombits must be <= result_bitsperchannel");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
for (c = rp->result_channels - 1; c >= 0; c--) {
|
||||
switch (frombits) {
|
||||
case 8:
|
||||
v = px[c];
|
||||
break;
|
||||
case 16:
|
||||
v = ((uint16_t*)px)[c];
|
||||
break;
|
||||
case 32:
|
||||
v = ((uint32_t*)px)[c];
|
||||
break;
|
||||
default:
|
||||
#ifdef DEBUG
|
||||
printf("Invalid pixel int size %d!!!", frombits);
|
||||
exit(1);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
switch (rp->result_format) {
|
||||
case BMP_FORMAT_FLOAT:
|
||||
((float*)px)[c] = (double) v / ((1ULL<<frombits)-1);
|
||||
break;
|
||||
case BMP_FORMAT_S2_13:
|
||||
((uint16_t*)px)[c] = (uint16_t) ((double) v / ((1ULL<<frombits)-1) * 8192.0 + 0.5);
|
||||
break;
|
||||
default:
|
||||
#ifdef DEBUG
|
||||
logerr(rp->log, "Unexpected result format %d", rp->result_format);
|
||||
exit(1);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* s_set_file_error
|
||||
@@ -785,16 +941,33 @@ static void s_log_error_from_state(BMPREAD_R rp)
|
||||
logerr(rp->log, "Unexpected end of file.");
|
||||
if (rp->file_err)
|
||||
logsyserr(rp->log, "While reading file");
|
||||
if (rp->invalid_pixels)
|
||||
logerr(rp->log, "File contained invalid data.");
|
||||
if (rp->invalid_index)
|
||||
logerr(rp->log, "File contained invalid color index.");
|
||||
if (rp->invalid_delta)
|
||||
logerr(rp->log, "Invalid delta pointing outside image area.");
|
||||
if (rp->invalid_overrun)
|
||||
logerr(rp->log, "RLE data overrunning image area.");
|
||||
if (rp->truncated)
|
||||
logerr(rp->log, "Image was truncated.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* s_cont_error
|
||||
*******************************************************/
|
||||
|
||||
static int s_cont_error(BMPREAD_R rp)
|
||||
{
|
||||
if (rp->invalid_index ||
|
||||
rp->invalid_overrun) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* s_stopping_error
|
||||
*******************************************************/
|
||||
@@ -808,39 +981,30 @@ static int s_stopping_error(BMPREAD_R rp)
|
||||
rp->panic) {
|
||||
return TRUE;
|
||||
}
|
||||
/* ok to continue reading BMP */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* s_read_one_byte
|
||||
*******************************************************/
|
||||
|
||||
static inline int s_read_one_byte(BMPREAD_R rp)
|
||||
{
|
||||
int byte;
|
||||
if (EOF != (byte = getc(rp->file)))
|
||||
rp->bytes_read++;
|
||||
return byte;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* s_scaleint
|
||||
*******************************************************/
|
||||
|
||||
static inline unsigned long s_scaleint(unsigned long val, int frombits, int tobits)
|
||||
{
|
||||
unsigned long result;
|
||||
int spaceleft;
|
||||
|
||||
/* scaling down, easy */
|
||||
if (frombits >= tobits)
|
||||
return val >> (frombits - tobits);
|
||||
|
||||
if (frombits < 1)
|
||||
return 0UL;
|
||||
|
||||
/* scaling up */
|
||||
result = val << (tobits - frombits);
|
||||
spaceleft = tobits - frombits;
|
||||
|
||||
while (spaceleft > 0) {
|
||||
if (spaceleft >= frombits)
|
||||
result |= val << (spaceleft - frombits);
|
||||
else
|
||||
result |= val >> (frombits - spaceleft);
|
||||
spaceleft -= frombits;
|
||||
}
|
||||
|
||||
return result;
|
||||
return (unsigned long) ((double) val * ((1ULL<<tobits)-1) / ((1ULL<<frombits)-1) + 0.5);
|
||||
}
|
||||
|
||||
@@ -77,6 +77,10 @@ API BMPRESULT bmpread_load_palette(BMPHANDLE h, unsigned char **palette)
|
||||
return 0;
|
||||
rp = (BMPREAD)(void*)h;
|
||||
|
||||
if (!rp->getinfo_called) {
|
||||
logerr(rp->log, "Must call bmpread_load_info() before loading palette");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
if (!rp->palette) {
|
||||
logerr(rp->log, "Image has no palette");
|
||||
return BMP_RESULT_ERROR;
|
||||
@@ -100,12 +104,10 @@ API BMPRESULT bmpread_load_palette(BMPHANDLE h, unsigned char **palette)
|
||||
if (!rp->result_indexed) {
|
||||
rp->result_indexed = TRUE;
|
||||
rp->dimensions_queried = FALSE;
|
||||
|
||||
rp->dim_queried_channels = FALSE;
|
||||
rp->result_channels = 1;
|
||||
rp->result_bits_per_pixel = 8;
|
||||
rp->result_bytes_per_pixel = 1;
|
||||
rp->result_bits_per_channel = 8;
|
||||
rp->result_size = (size_t) rp->width * (size_t) rp->height;
|
||||
if (!br_set_resultbits(rp))
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
for (i = 0; i < rp->palette->numcolors; i++) {
|
||||
|
||||
672
bmp-read.c
672
bmp-read.c
File diff suppressed because it is too large
Load Diff
@@ -19,3 +19,5 @@
|
||||
*/
|
||||
|
||||
void br_free(BMPREAD rp);
|
||||
int br_set_resultbits(BMPREAD_R rp);
|
||||
BMPRESULT br_set_number_format(BMPREAD_R rp, enum BmpFormat format);
|
||||
|
||||
961
bmp-write.c
961
bmp-write.c
File diff suppressed because it is too large
Load Diff
@@ -20,3 +20,4 @@
|
||||
|
||||
|
||||
void bw_free(BMPWRITE wp);
|
||||
BMPRESULT bw_set_number_format(BMPWRITE wp, enum BmpFormat format);
|
||||
|
||||
93
bmplib.h
93
bmplib.h
@@ -25,6 +25,12 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define DEPR(m) __attribute__ ((deprecated(m)))
|
||||
#else
|
||||
#define DEPR(m)
|
||||
#endif
|
||||
|
||||
typedef struct Bmphandle *BMPHANDLE;
|
||||
|
||||
|
||||
@@ -83,19 +89,22 @@ typedef enum Bmpresult BMPRESULT;
|
||||
/*
|
||||
* 64-bit BMPs: conversion of RGBA (16bit) values
|
||||
*
|
||||
* BMP_CONV64_16BIT_SRGB (default) convert components to 'normal'
|
||||
* 16bit with sRGB gamma. Assumes components
|
||||
* are stored as s2.13 linear.
|
||||
* BMP_CONV64_SRGB (default) Assume components are
|
||||
* stored in linear light and convert
|
||||
* to sRGB gamma.
|
||||
*
|
||||
* BMP_CONV64_16BIT convert components from s2.13 to
|
||||
* 'normal' 16bit. No gamma conversion.
|
||||
* BMP_CONV64_LINEAR No gamma conversion.
|
||||
*
|
||||
* BMP_CONV64_NONE Leave components as they are, which
|
||||
* might always(?) be s2.13 linear.
|
||||
* BMP_CONV64_NONE Leave components as they are. This is
|
||||
* a shortcut for the combination
|
||||
* - bmpread_set_64bit_conv(BMP_CONV_LINEAR) and
|
||||
* - bmp_set_number_format(BMP_FORMAT_S2_13).
|
||||
*/
|
||||
enum Bmpconv64 {
|
||||
BMP_CONV64_16BIT_SRGB, /* default */
|
||||
BMP_CONV64_16BIT,
|
||||
BMP_CONV64_SRGB = 0, /* default */
|
||||
BMP_CONV64_LINEAR = 1,
|
||||
BMP_CONV64_16BIT_SRGB DEPR("use BMP_CONV64_SRGB instead") = 0,
|
||||
BMP_CONV64_16BIT DEPR("use BMP_CONV64_LINEAR instead") = 1,
|
||||
BMP_CONV64_NONE
|
||||
};
|
||||
typedef enum Bmpconv64 BMPCONV64;
|
||||
@@ -155,7 +164,8 @@ typedef enum BmpRLEtype BMPRLETYPE;
|
||||
*
|
||||
*/
|
||||
enum BmpUndefined {
|
||||
BMP_UNDEFINED_TO_ZERO,
|
||||
BMP_UNDEFINED_LEAVE,
|
||||
BMP_UNDEFINED_TO_ZERO DEPR("use BMP_UNDEFINED_LEAVE instead") = 0,
|
||||
BMP_UNDEFINED_TO_ALPHA /* default */
|
||||
};
|
||||
typedef enum BmpUndefined BMPUNDEFINED;
|
||||
@@ -176,6 +186,14 @@ enum BmpOrient {
|
||||
typedef enum BmpOrient BMPORIENT;
|
||||
|
||||
|
||||
enum BmpFormat {
|
||||
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);
|
||||
@@ -190,7 +208,7 @@ BMPRESULT bmpread_dimensions(BMPHANDLE h,
|
||||
int bmpread_width(BMPHANDLE h);
|
||||
int bmpread_height(BMPHANDLE h);
|
||||
int bmpread_channels(BMPHANDLE h);
|
||||
int bmpread_bits_per_channel(BMPHANDLE h);
|
||||
int bmpread_bitsperchannel(BMPHANDLE h);
|
||||
BMPORIENT bmpread_orientation(BMPHANDLE h);
|
||||
|
||||
int bmpread_resolution_xdpi(BMPHANDLE h);
|
||||
@@ -209,15 +227,15 @@ BMPRESULT bmpread_load_palette(BMPHANDLE h, unsigned char **palette);
|
||||
void bmpread_set_undefined(BMPHANDLE h, BMPUNDEFINED mode);
|
||||
void bmpread_set_insanity_limit(BMPHANDLE h, size_t limit);
|
||||
|
||||
int bmpread_is_64bit(BMPHANDLE h);
|
||||
int bmpread_is_64bit(BMPHANDLE h);
|
||||
BMPRESULT bmpread_set_64bit_conv(BMPHANDLE h, BMPCONV64 conv);
|
||||
|
||||
BMPINFOVER bmpread_info_header_version(BMPHANDLE h);
|
||||
const char* bmpread_info_header_name(BMPHANDLE h);
|
||||
int bmpread_info_header_size(BMPHANDLE h);
|
||||
int bmpread_info_compression(BMPHANDLE h);
|
||||
int bmpread_info_bitcount(BMPHANDLE h);
|
||||
const char* bmpread_info_header_name(BMPHANDLE h);
|
||||
const char* bmpread_info_compression_name(BMPHANDLE h);
|
||||
int bmpread_info_bitcount(BMPHANDLE h);
|
||||
BMPRESULT bmpread_info_channel_bits(BMPHANDLE h, int *r, int *g, int *b, int *a);
|
||||
|
||||
|
||||
@@ -229,17 +247,20 @@ BMPRESULT bmpwrite_set_dimensions(BMPHANDLE h,
|
||||
unsigned width,
|
||||
unsigned height,
|
||||
unsigned channels,
|
||||
unsigned bits_per_channel);
|
||||
unsigned bitsperchannel);
|
||||
BMPRESULT bmpwrite_set_resolution(BMPHANDLE h, int xdpi, int ydpi);
|
||||
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);
|
||||
|
||||
BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image);
|
||||
BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line);
|
||||
|
||||
|
||||
BMPRESULT bmp_set_number_format(BMPHANDLE h, BMPFORMAT format);
|
||||
|
||||
void bmp_free(BMPHANDLE h);
|
||||
|
||||
@@ -248,19 +269,43 @@ const char* bmp_errmsg(BMPHANDLE h);
|
||||
const char* bmp_version(void);
|
||||
|
||||
|
||||
/* these errorcodes aren't part of the API yet, but will be.
|
||||
* currently only used internally by bmplib.
|
||||
*/
|
||||
|
||||
#define BMP_ERRTYPE_HARD 0x0000000f
|
||||
#define BMP_ERR_FILEIO 0x00000001
|
||||
#define BMP_ERR_MEMORY 0x00000002
|
||||
#define BMP_ERR_INTERNAL 0x00000004
|
||||
|
||||
#define BMP_ERRTYPE_DATA 0x0000fff0
|
||||
#define BMP_ERR_PIXEL 0x00000010
|
||||
#define BMP_ERR_TRUNCATED 0x00000020
|
||||
#define BMP_ERR_HEADER 0x00000040
|
||||
#define BMP_ERR_INSANE 0x00000080
|
||||
#define BMP_ERR_UNSUPPORTED 0x00000100
|
||||
#define BMP_ERR_JPEG 0x00000200
|
||||
#define BMP_ERR_PNG 0x00000400
|
||||
#define BMP_ERR_DIMENSIONS 0x00000800
|
||||
#define BMP_ERR_INVALID 0x00001000
|
||||
|
||||
#define BMP_ERRTYPE_USER 0x0fff0000
|
||||
#define BMP_ERR_CONV64 0x00010000
|
||||
#define BMP_ERR_FORMAT 0x00020000
|
||||
#define BMP_ERR_NULL 0x00040000
|
||||
#define BMP_ERR_PALETTE 0x00080000
|
||||
#define BMP_ERR_NOINFO 0x00100000
|
||||
#define BMP_ERR_UNDEFMODE 0x00200000
|
||||
|
||||
|
||||
|
||||
/* these functions are kept for binary compatibility and will be
|
||||
* removed from future versions:
|
||||
*/
|
||||
#if defined(__GNUC__)
|
||||
#define DEPR __attribute__ ((deprecated))
|
||||
#else
|
||||
#define DEPR
|
||||
#endif
|
||||
|
||||
int DEPR bmpread_topdown(BMPHANDLE h); /* use bmpread_orientation() instead */
|
||||
|
||||
void DEPR bmpread_set_undefined_to_alpha(BMPHANDLE h, int mode); /* use bmpread_set_undefined instead */
|
||||
|
||||
int DEPR("use bmpread_orientation() instead") bmpread_topdown(BMPHANDLE h);
|
||||
void DEPR("use bmpread_set_undefined() instead") bmpread_set_undefined_to_alpha(BMPHANDLE h, int mode);
|
||||
int DEPR("use bmpread_bitsperchannel() instead") bmpread_bits_per_channel(BMPHANDLE h);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
1
google0f997b088a966545.html
Normal file
1
google0f997b088a966545.html
Normal file
@@ -0,0 +1 @@
|
||||
google-site-verification: google0f997b088a966545.html
|
||||
279
huffman-codes.h
Normal file
279
huffman-codes.h
Normal file
@@ -0,0 +1,279 @@
|
||||
/* bmplib - huffman-codes.h
|
||||
*
|
||||
* Copyright (c) 2024, Rupert Weber, Luong Chi Mai.
|
||||
*
|
||||
* This file is part of bmplib.
|
||||
* bmplib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
* If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Modified Huffman terminating and makeup codes
|
||||
*
|
||||
* Source:
|
||||
*
|
||||
* INTRODUCTION TO COMPUTER VISION
|
||||
* AND IMAGE PROCESSING
|
||||
* by Luong Chi Mai
|
||||
* Department of Pattern Recognition and Knowledge Engineering
|
||||
* Institute of Information Technology, Hanoi, Vietnam
|
||||
* E-mail: lcmai@ioit.ncst.ac.vn
|
||||
*
|
||||
* http://www.netnam.vn:80/unescocourse/computervision/104.htm
|
||||
* (via Wayback machine, original site seems defunct)
|
||||
*
|
||||
* Excerpt:
|
||||
*
|
||||
* [...] Each line is coded as a series of alternating runs of white and black
|
||||
* bits. Runs of 63 or less are coded with a terminating code. Runs of 64
|
||||
* or greater require that a makeup code prefix the terminating code. The
|
||||
* makeup codes are used to describe runs in multiples of 64 from 64 to
|
||||
* 2560.
|
||||
*
|
||||
* [...] The protocol also assumes that the line begins with a run of white
|
||||
* bits. If it doesn't, a run of white bits of 0 length must begin the
|
||||
* encoded line. The encoding then alternates between black bits and white
|
||||
* bits to the end of the line. Each scan line ends with a special EOL
|
||||
* (end of line) character consisting of eleven zeros and a 1
|
||||
* (000000000001).
|
||||
*
|
||||
* [...] a decoder seeing eight [zeroes] will recognize the end of line and
|
||||
* continue scanning for a 1. Upon receiving the 1, it will then start a
|
||||
* new line.
|
||||
*
|
||||
*/
|
||||
|
||||
struct Huffcode {
|
||||
int number;
|
||||
const char *bits;
|
||||
};
|
||||
|
||||
static struct Huffcode huff_term_white[] = { { 0, "00110101" },
|
||||
{ 1, "000111" },
|
||||
{ 2, "0111" },
|
||||
{ 3, "1000" },
|
||||
{ 4, "1011" },
|
||||
{ 5, "1100" },
|
||||
{ 6, "1110" },
|
||||
{ 7, "1111" },
|
||||
{ 8, "10011" },
|
||||
{ 9, "10100" },
|
||||
{ 10, "00111" },
|
||||
{ 11, "01000" },
|
||||
{ 12, "001000" },
|
||||
{ 13, "000011" },
|
||||
{ 14, "110100" },
|
||||
{ 15, "110101" },
|
||||
{ 16, "101010" },
|
||||
{ 17, "101011" },
|
||||
{ 18, "0100111" },
|
||||
{ 19, "0001100" },
|
||||
{ 20, "0001000" },
|
||||
{ 21, "0010111" },
|
||||
{ 22, "0000011" },
|
||||
{ 23, "0000100" },
|
||||
{ 24, "0101000" },
|
||||
{ 25, "0101011" },
|
||||
{ 26, "0010011" },
|
||||
{ 27, "0100100" },
|
||||
{ 28, "0011000" },
|
||||
{ 29, "00000010" },
|
||||
{ 30, "00000011" },
|
||||
{ 31, "00011010" },
|
||||
{ 32, "00011011" },
|
||||
{ 33, "00010010" },
|
||||
{ 34, "00010011" },
|
||||
{ 35, "00010100" },
|
||||
{ 36, "00010101" },
|
||||
{ 37, "00010110" }, /* 00010110 ? orig: 00001110 */
|
||||
{ 38, "00010111" },
|
||||
{ 39, "00101000" },
|
||||
{ 40, "00101001" },
|
||||
{ 41, "00101010" },
|
||||
{ 42, "00101011" },
|
||||
{ 43, "00101100" },
|
||||
{ 44, "00101101" },
|
||||
{ 45, "00000100" },
|
||||
{ 46, "00000101" },
|
||||
{ 47, "00001010" },
|
||||
{ 48, "00001011" },
|
||||
{ 49, "01010010" },
|
||||
{ 50, "01010011" },
|
||||
{ 51, "01010100" },
|
||||
{ 52, "01010101" },
|
||||
{ 53, "00100100" },
|
||||
{ 54, "00100101" },
|
||||
{ 55, "01011000" },
|
||||
{ 56, "01011001" },
|
||||
{ 57, "01011010" },
|
||||
{ 58, "01011011" },
|
||||
{ 59, "01001010" },
|
||||
{ 60, "01001011" },
|
||||
{ 61, "00110010" },
|
||||
{ 62, "00110011" }, /* 001110011 */
|
||||
{ 63, "00110100" },
|
||||
};
|
||||
|
||||
static struct Huffcode huff_term_black[] = { { 0, "0000110111" },
|
||||
{ 1, "010" },
|
||||
{ 2, "11" },
|
||||
{ 3, "10" },
|
||||
{ 4, "011" },
|
||||
{ 5, "0011" },
|
||||
{ 6, "0010" },
|
||||
{ 7, "00011" },
|
||||
{ 8, "000101" },
|
||||
{ 9, "000100" },
|
||||
{ 10, "0000100" },
|
||||
{ 11, "0000101" },
|
||||
{ 12, "0000111" },
|
||||
{ 13, "00000100" },
|
||||
{ 14, "00000111" },
|
||||
{ 15, "000011000" },
|
||||
{ 16, "0000010111" },
|
||||
{ 17, "0000011000" },
|
||||
{ 18, "0000001000" },
|
||||
{ 19, "00001100111" },
|
||||
{ 20, "00001101000" },
|
||||
{ 21, "00001101100" },
|
||||
{ 22, "00000110111" },
|
||||
{ 23, "00000101000" },
|
||||
{ 24, "00000010111" },
|
||||
{ 25, "00000011000" },
|
||||
{ 26, "000011001010" },
|
||||
{ 27, "000011001011" },
|
||||
{ 28, "000011001100" },
|
||||
{ 29, "000011001101" },
|
||||
{ 30, "000001101000" },
|
||||
{ 31, "000001101001" },
|
||||
{ 32, "000001101010" },
|
||||
{ 33, "000001101011" },
|
||||
{ 34, "000011010010" },
|
||||
{ 35, "000011010011" },
|
||||
{ 36, "000011010100" },
|
||||
{ 37, "000011010101" },
|
||||
{ 38, "000011010110" },
|
||||
{ 39, "000011010111" },
|
||||
{ 40, "000001101100" },
|
||||
{ 41, "000001101101" },
|
||||
{ 42, "000011011010" },
|
||||
{ 43, "000011011011" },
|
||||
{ 44, "000001010100" },
|
||||
{ 45, "000001010101" },
|
||||
{ 46, "000001010110" },
|
||||
{ 47, "000001010111" },
|
||||
{ 48, "000001100100" },
|
||||
{ 49, "000001100101" },
|
||||
{ 50, "000001010010" },
|
||||
{ 51, "000001010011" },
|
||||
{ 52, "000000100100" },
|
||||
{ 53, "000000110111" },
|
||||
{ 54, "000000111000" },
|
||||
{ 55, "000000100111" },
|
||||
{ 56, "000000101000" },
|
||||
{ 57, "000001011000" },
|
||||
{ 58, "000001011001" },
|
||||
{ 59, "000000101011" },
|
||||
{ 60, "000000101100" },
|
||||
{ 61, "000001011010" },
|
||||
{ 62, "000001100110" },
|
||||
{ 63, "000001100111" },
|
||||
};
|
||||
|
||||
static struct Huffcode huff_makeup_white[] = { { 64, "11011" },
|
||||
{ 128, "10010" },
|
||||
{ 192, "010111" },
|
||||
{ 256, "0110111" },
|
||||
{ 320, "00110110" },
|
||||
{ 384, "00110111" },
|
||||
{ 448, "01100100" },
|
||||
{ 512, "01100101" },
|
||||
{ 576, "01101000" },
|
||||
{ 640, "01100111" },
|
||||
{ 704, "011001100" },
|
||||
{ 768, "011001101" },
|
||||
{ 832, "011010010" },
|
||||
{ 896, "011010011" }, /* original 101010011 */
|
||||
{ 960, "011010100" },
|
||||
{ 1024, "011010101" },
|
||||
{ 1088, "011010110" },
|
||||
{ 1152, "011010111" },
|
||||
{ 1216, "011011000" },
|
||||
{ 1280, "011011001" },
|
||||
{ 1344, "011011010" },
|
||||
{ 1408, "011011011" },
|
||||
{ 1472, "010011000" },
|
||||
{ 1536, "010011001" },
|
||||
{ 1600, "010011010" },
|
||||
{ 1664, "011000" },
|
||||
{ 1728, "010011011" },
|
||||
{ 1792, "00000001000" },
|
||||
{ 1856, "00000001100" },
|
||||
{ 1920, "00000001101" },
|
||||
{ 1984, "000000010010" },
|
||||
{ 2048, "000000010011" },
|
||||
{ 2112, "000000010100" },
|
||||
{ 2170, "000000010101" },
|
||||
{ 2240, "000000010110" },
|
||||
{ 2304, "000000010111" },
|
||||
{ 2368, "000000011100" },
|
||||
{ 2432, "000000011101" },
|
||||
{ 2496, "000000011110" },
|
||||
{ 2560, "000000011111" },
|
||||
/* { EOL, "000000000001" }, */
|
||||
};
|
||||
|
||||
static struct Huffcode huff_makeup_black[] = { { 64, "0000001111" }, /* 000000111*/
|
||||
{ 128, "000011001000" }, /* 00011001000 */
|
||||
{ 192, "000011001001" },
|
||||
{ 256, "000001011011" },
|
||||
{ 320, "000000110011" },
|
||||
{ 384, "000000110100" },
|
||||
{ 448, "000000110101" },
|
||||
{ 512, "0000001101100" },
|
||||
{ 576, "0000001101101" },
|
||||
{ 640, "0000001001010" },
|
||||
{ 704, "0000001001011" },
|
||||
{ 768, "0000001001100" },
|
||||
{ 832, "0000001001101" },
|
||||
{ 896, "0000001110010" },
|
||||
{ 960, "0000001110011" },
|
||||
{ 1024, "0000001110100" },
|
||||
{ 1088, "0000001110101" },
|
||||
{ 1152, "0000001110110" },
|
||||
{ 1216, "0000001110111" },
|
||||
{ 1280, "0000001010010" },
|
||||
{ 1344, "0000001010011" },
|
||||
{ 1408, "0000001010100" },
|
||||
{ 1472, "0000001010101" },
|
||||
{ 1536, "0000001011010" },
|
||||
{ 1600, "0000001011011" },
|
||||
{ 1664, "0000001100100" },
|
||||
{ 1728, "0000001100101" },
|
||||
{ 1792, "00000001000" },
|
||||
{ 1856, "00000001100" },
|
||||
{ 1920, "00000001101" },
|
||||
{ 1984, "000000010010" },
|
||||
{ 2048, "000000010011" },
|
||||
{ 2112, "000000010100" },
|
||||
{ 2170, "000000010101" },
|
||||
{ 2240, "000000010110" },
|
||||
{ 2304, "000000010111" },
|
||||
{ 2368, "000000011100" },
|
||||
{ 2432, "000000011101" },
|
||||
{ 2496, "000000011110" },
|
||||
{ 2560, "000000011111" },
|
||||
/* { EOL, "000000000001" }, */
|
||||
};
|
||||
223
huffman.c
Normal file
223
huffman.c
Normal file
@@ -0,0 +1,223 @@
|
||||
/* bmplib - huffman.c
|
||||
*
|
||||
* Copyright (c) 2024, Rupert Weber.
|
||||
*
|
||||
* This file is part of bmplib.
|
||||
* bmplib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
* If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#ifndef __STDC_NO_THREADS__
|
||||
#include <threads.h>
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
#include "bmplib.h"
|
||||
#include "logging.h"
|
||||
#include "bmp-common.h"
|
||||
#include "huffman-codes.h"
|
||||
#include "huffman.h"
|
||||
|
||||
|
||||
|
||||
struct Node {
|
||||
struct Node *l;
|
||||
struct Node *r;
|
||||
int value;
|
||||
int terminal;
|
||||
int makeup;
|
||||
};
|
||||
|
||||
static int nnodes = 0;
|
||||
static struct Node nodebuffer[2 * (ARR_SIZE(huff_term_black) + ARR_SIZE(huff_makeup_black)) + 1
|
||||
+ 2 * (ARR_SIZE(huff_term_white) + ARR_SIZE(huff_makeup_white)) + 1];
|
||||
|
||||
static struct Node *black_tree;
|
||||
static struct Node *white_tree;
|
||||
|
||||
|
||||
static void s_buildtree(void);
|
||||
static void add_node(struct Node **node, const char *bits, int value, int makeup);
|
||||
|
||||
|
||||
#ifndef __STDC_NO_THREADS__
|
||||
static once_flag build_once = ONCE_FLAG_INIT;
|
||||
#else
|
||||
static int initialized = FALSE;
|
||||
#endif
|
||||
|
||||
|
||||
int huff_decode(int *result, uint32_t bits, int nbits, int black)
|
||||
{
|
||||
struct Node *node;
|
||||
int bits_used = 0, sub, subresult;
|
||||
|
||||
#ifndef __STDC_NO_THREADS__
|
||||
call_once(&build_once, s_buildtree);
|
||||
#else
|
||||
if (!initialized) {
|
||||
s_buildtree();
|
||||
initialized = TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
node = black ? black_tree : white_tree;
|
||||
|
||||
while (node && !node->terminal && bits_used < nbits) {
|
||||
if (bits & 1)
|
||||
node = node->r;
|
||||
else
|
||||
node = node->l;
|
||||
bits_used++;
|
||||
bits >>= 1;
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
/* invalid code */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (node->makeup) {
|
||||
sub = huff_decode(&subresult, bits, nbits - bits_used, black);
|
||||
if (!sub) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
*result = node->value + subresult;
|
||||
return bits_used + sub;
|
||||
}
|
||||
} else {
|
||||
*result = node->value;
|
||||
return bits_used;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void s_buildtree(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(nodebuffer, 0, sizeof nodebuffer);
|
||||
|
||||
for (i = 0; i < ARR_SIZE(huff_term_black); i++) {
|
||||
add_node(&black_tree, huff_term_black[i].bits,
|
||||
huff_term_black[i].number, FALSE);
|
||||
}
|
||||
for (i = 0; i < ARR_SIZE(huff_makeup_black); i++) {
|
||||
add_node(&black_tree, huff_makeup_black[i].bits,
|
||||
huff_makeup_black[i].number, TRUE);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARR_SIZE(huff_term_white); i++) {
|
||||
add_node(&white_tree, huff_term_white[i].bits,
|
||||
huff_term_white[i].number, FALSE);
|
||||
}
|
||||
for (i = 0; i < ARR_SIZE(huff_makeup_white); i++) {
|
||||
add_node(&white_tree, huff_makeup_white[i].bits,
|
||||
huff_makeup_white[i].number, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void add_node(struct Node **node, const char *bits, int value, int makeup)
|
||||
{
|
||||
if (!*node) {
|
||||
*node = &nodebuffer[nnodes++];
|
||||
}
|
||||
|
||||
if (!*bits) {
|
||||
/* we are on the final bit of the sequence */
|
||||
(*node)->value = value;
|
||||
(*node)->terminal = TRUE;
|
||||
(*node)->makeup = makeup;
|
||||
} else {
|
||||
switch (*bits) {
|
||||
case '0':
|
||||
add_node(&((*node)->l), bits+1, value, makeup);
|
||||
break;
|
||||
case '1':
|
||||
add_node(&((*node)->r), bits+1, value, makeup);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef NEVER
|
||||
static void print_table(struct Huffcode *table, int size)
|
||||
{
|
||||
int i;
|
||||
unsigned long buf;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
buf = 0;
|
||||
for (int j = 0; j < strlen(table[i].bits); j++) {
|
||||
buf <<= 1;
|
||||
buf |= table[i].bits[j] == '1' ? 1 : 0;
|
||||
}
|
||||
printf ("%02d: 0x%02x, %d, %s\n", i, (int) buf, (int) strlen(table[i].bits), table[i].bits);
|
||||
}
|
||||
}
|
||||
|
||||
static int depth = 0;
|
||||
|
||||
static void find_empty_nodes(struct Node *node)
|
||||
{
|
||||
static char str[30];
|
||||
|
||||
str[depth] = 0;
|
||||
if (!node->terminal) {
|
||||
if (!(node->l && node->r)) {
|
||||
str[depth] = 0;
|
||||
printf("node %s not full. l=%p r=%p\n", str, (void*)node->l, (void*)node->r);
|
||||
}
|
||||
if (node->l) {
|
||||
str[depth++] = '0';
|
||||
find_empty_nodes(node->l);
|
||||
}
|
||||
if (node->r) {
|
||||
str[depth++] = '1';
|
||||
find_empty_nodes(node->r);
|
||||
}
|
||||
} else {
|
||||
//printf("%d: %s\n", node->value, str);
|
||||
}
|
||||
depth--;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
21
huffman.h
Normal file
21
huffman.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/* bmplib - huffman.h
|
||||
*
|
||||
* Copyright (c) 2024, Rupert Weber.
|
||||
*
|
||||
* This file is part of bmplib.
|
||||
* bmplib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
* If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
int huff_decode(int *result, uint32_t bits, int nbits, int black);
|
||||
@@ -231,8 +231,7 @@ static int s_allocate(LOG log, size_t add_chars)
|
||||
if (log->size == 0)
|
||||
log->buffer[0] = 0;
|
||||
log->size = newsize;
|
||||
}
|
||||
else
|
||||
} else
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
|
||||
11
meson.build
11
meson.build
@@ -1,4 +1,4 @@
|
||||
project('bmplib', 'c', default_options: ['c_std=c11'], version: '1.4.4')
|
||||
project('bmplib', 'c', default_options: ['c_std=c11'], version: '1.6.0')
|
||||
|
||||
cc = meson.get_compiler('c')
|
||||
|
||||
@@ -27,8 +27,17 @@ bmplib_sources = ['bmp-read.c',
|
||||
'bmp-read-loadimage.c',
|
||||
'bmp-read-loadindexed.c',
|
||||
'bmp-common.c',
|
||||
'huffman.c',
|
||||
'logging.c']
|
||||
|
||||
|
||||
if cc.sizeof('float') != 4
|
||||
error('sizeof(float) must be 4 bytes.')
|
||||
elif cc.sizeof('int') < 4
|
||||
error('sizeof(int) must be at least 32 bit.')
|
||||
endif
|
||||
|
||||
|
||||
bmplib = shared_library('bmp',
|
||||
bmplib_sources,
|
||||
version: meson.project_version(),
|
||||
|
||||
1
meson_options.txt
Symbolic link
1
meson_options.txt
Symbolic link
@@ -0,0 +1 @@
|
||||
meson.options
|
||||
56
reversebits.h
Normal file
56
reversebits.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/* bmplib - reversebits.h
|
||||
*
|
||||
* Copyright (c) 2024, Rupert Weber.
|
||||
*
|
||||
* This file is part of bmplib.
|
||||
* bmplib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
* If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
|
||||
|
||||
static int reversebits[] = {
|
||||
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
|
||||
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
|
||||
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
|
||||
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
|
||||
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
|
||||
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
|
||||
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
|
||||
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
|
||||
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
|
||||
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
|
||||
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
|
||||
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
|
||||
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
|
||||
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
|
||||
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
|
||||
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
|
||||
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
|
||||
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
|
||||
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
|
||||
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
|
||||
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
|
||||
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
|
||||
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
|
||||
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
|
||||
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
|
||||
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
|
||||
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
|
||||
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
|
||||
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
|
||||
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
|
||||
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
|
||||
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
|
||||
};
|
||||
Reference in New Issue
Block a user