1 Commits

Author SHA1 Message Date
Rupert
cc85e1994a Slight API changes
- replace bmpread_topdown() and bmpread_set_undefined_to_alpha() by
  bmpread_orientation() and bmpread_set_undefined(), keeping former
  as deprecated for now.
- use enums/typedefs for parameters where appropriate
- update docs accordingly
- restructure API.md for more consistency
- some minor cleanups
2024-10-01 15:45:32 +02:00
8 changed files with 327 additions and 234 deletions

258
API.md
View File

@@ -1,9 +1,24 @@
# 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
### Getting a handle: bmpread_new()
### Get a handle
```
BMPHANDLE bmpread_new(FILE *file)
```
@@ -11,7 +26,8 @@ BMPHANDLE bmpread_new(FILE *file)
The handle cannot be reused to read multiple files.
### Read the file header: bmpread_load_info()
### Read the file header
```
BMPRESULT bmpread_load_info(BMPHANDLE h)
```
@@ -24,18 +40,19 @@ bmplib reads the file header and checks validity. Possible return values are:
Calling `bmpread_load_info()` is optional when you use `bmpread_dimensions()` (see below).
### Getting information about image dimensions: bmpread_dimensions() et al.
### Get image dimensions
```
BMPRESULT bmpread_dimensions(BMPHANDLE h,
int *width,
int *height,
int *channels,
int *bitsperchannel,
int *topdown)
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* necesarily the original BMP file.
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.
@@ -46,78 +63,26 @@ int bmpread_width(BMPHANDLE h)
int bmpread_height(BMPHANDLE h)
int bmpread_channels(BMPHANDLE h)
int bmpread_bits_per_channel(BMPHANDLE h)
int bmpread_topdown(BMPHANDLE h)
BMPORIENT bmpread_orientation(BMPHANDLE h)
int bmpread_resolution_xdpi(BMPHANDLE h)
int bmpread_resolution_ydpi(BMPHANDLE h)
```
#### top-down / bottom-up
`bmpread_topdown()` or the topdown value returned by `bmpread_dimensions()` is only relevant if you load the BMP file line-by-line. In line-by-line mode (using `bmpread_load_line()`), the image data is always delivered in the order it is in the BMP file. The topdown value will tell you if it's top-down or bottom-up. On the other hand, when the whole image is loaded at once (using `bmpread_load_image()`), bmplib will *always* return the image top-down, regardless of how the BMP file is oriented. The topdown value will still indicate the orientation of the original BMP.
### Required size for buffer to receive image: bmpread_buffersize():
#### 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.
### Optional settings for 64bit BMPs
```
int bmpread_is_64bit(BMPHANDLE h)
BMPRESULT bmpread_set_64bit_conv(BMPHANDLE h, enum Bmp64bitconv 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.
### bmpread_load_image()
```
BMPRESULT bmpread_load_image(BMPHANDLE h, unsigned char **pbuffer)
```
Loads the complete image from the BMP file into the buffer pointed to by `pbuffer`.
You can either allocate a buffer yourself or let `pbuffer` point to a NULL-pointer in which case bmplib will allocate an appropriate buffer. In the latter case, you will have to `free()` the buffer, once you are done with it.
If you allocate the buffer yourself, the buffer must be at least as large as the size returned by `bmpread_buffersize()`.
```
unsigned char *buffer;
/* either: */
buffer = NULL;
bmpread_load_image(h, &buffer); /* bmplib will allocate the buffer */
/* or: */
buffer = malloc(bmpread_buffersize(h));
bmpread_load_image(h, &buffer); /* bmplib will use the provided buffer */
```
The image data is written to the buffer according to the returned dimensions (see above). Multi-byte values are always written in host byte order, in the order R-G-B or R-G-B-A. The returned image is always top-down, i.e. data starts in the top left corner. Unlike BMPs which are (almost always) bottom-up. (See above, "Getting information...")
If bmpread_load_image() return BMP_RESULT_TRUNCATED or BMP_RESULT_INVALID, the file may have been damaged or simply contains invalid image data. Image data is loaded anyway as far as possible and maybe partially usable.
### bmpread_load_line()
```
BMPRESULT bmpread_load_line(BMPHANDLE h, unsigned char **bufferp)
```
Loads a single scan line from the BMP file into the buffer pointed to by `bufferp`.
You can either allocate a buffer yourself or let `bufferp` point to a NULL-pointer in which case bmplib will allocate an appropriate buffer. In the latter case, you will have to call `free()` on the buffer, once you are done with it.
To determine the required buffer size, either divide the value from `bmpread_buffersize()` by the number of scanlines (= `bmpread_height()`), or calculate from the image dimensions returned by bmplib as width * channels * bits_per_channel / 8.
```
single_line_buffersize = bmpread_buffersize(h) / bmpread_height(h);
/* or */
single_line_buffersize = bmpread_width(h) * bmpread_channels(h) * bmpread_bits_per_channel(h) / 8;
```
Repeated calls to `bmpread_load_line()` will return each scan line, one after the other.
Important: when reading the image this way, line by line, the orientation (`bmpread_topdown()`) of the original BMP matters! The lines will be returned in whichever order they are stored in the BMP. Use the value returned by `bmpread_topdown()` to determine if it is top-down or bottom-up. Almost all BMPs will be bottom-up. (see above)
### Indexed BMPs
By default, bmplib will interpret indexed (color palette) BMPs and return the image as 24-bit RGB data.
@@ -149,8 +114,36 @@ bmpread_load_palette(h, &palette); /* bmplib will use the provided buff
```
Note: Once you have called `bmpread_load_palette()`, both `bmpread_load_image()` and `bmpread_load_line()` will return the image as 8-bit indexes into the palette, you *cannot go back* to the default of loading the image as 24-bit RGB data. After you loaded the palette, calls to `bmpread_dimensions()`, `bmpread_buffersize()`, etc. will reflect that change.
Also, `bmpread_set_undefined_to_alpha()` will have no effect.
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()
@@ -160,20 +153,56 @@ calling `bmpread_load_image()`. `limit` is the new allowed size in bytes. (not M
void bmpread_set_insanity_limit(BMPHANDLE h, size_t limit)
```
### Undefined and invalid pixels: bmpread_set_undefined_to_alpha()
#### Undefined pixels
RLE-encoded BMP files may have undefined pixels, either by using early end-of-line or end-of-file codes, or by using delta codes to skip part of the image. That is not an error, but a feature of RLE. bmplib default is to make such pixels transparent. RLE-encoded BMPs will therefore always be returned with an alpha channel by default, whether the file has such undefined pixels or not (because bmplib doesn't know beforehand if there will be any undefined pixels). You can change this behaviour by calling
`bmpread_set_undefined_to_alpha()`, with the second argument `yes` set to 0. In that case, the returned image will have no alpha channel, and undefined pixels will be set to zero. This function has no effect on non-RLE BMPs.
### Load the image
#### bmpread_load_image()
```
void bmpread_set_undefined_to_alpha(BMPHANDLE h, int yes)
BMPRESULT bmpread_load_image(BMPHANDLE h, unsigned char **pbuffer)
```
Note: this setting will have no effect if you use `bmpread_load_palette()` to switch to loading the indexed data! (see above)
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;
#### Invalid pixels
/* 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!
```
@@ -238,7 +267,7 @@ written as either RLE4 or RLE8 (default is RLE4), images with more than 16 color
To activate RLE compression, call
```
BMPRESULT bmpwrite_set_rle(BMPHANDLE h, enum BmpRLEtype type)
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
@@ -261,7 +290,7 @@ In both cases, the actual BMP will be written bottom-up, which is the only 'offi
## 3. Functions for both reading/writing BMPs
## 3. General functions for both reading/writing BMPs
### bmp_free()
```
@@ -282,35 +311,78 @@ Returns a zero-terminated character string containing the last error description
```
const char* bmp_version(void)
```
Returns a zero-terminated character string containing the version of bmplib.
## 4. Data types and constants
`BMPHANDLE`
#### `BMPHANDLE`
Returned by `bmpread_new()` and `bmpwrite_new()`.
Identifies the current operation for all subsequent
calls to bmplib-functions.
`BMPRESULT`
#### `BMPRESULT`
Many bmplib functions return the success/failure of an operation as a `BMPRESULT`. It can have one
of the following values:
- `BMP_RESULT_OK`
- `BMP_RESULT_ERROR`
- `BMP_RESULT_TRUNCATED`
- `BMP_RESULT_INVALID`
- `BMP_RESULT_TRUNCATED`
- `BMP_RESULT_INSANE`
- `BMP_RESULT_PNG`
- `BMP_RESULT_JPEG`
- `BMP_RESULT_INSANE`
- `BMP_RESULT_ERROR`
`BMP_RESULT_OK` will always have the vaue 0, all other result codes should only be referred to by name, their values might change in the future.
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
@@ -321,7 +393,7 @@ of the following values:
BMPHANDLE h;
FILE *file;
int width, height, channels, bitsperchannel, topdown;
int width, height, channels, bitsperchannel, orientation;
uint8_t *image_buffer;
@@ -353,7 +425,7 @@ of the following values:
/* load the image and clean up: */
bmpread_load_image(h, image_buffer);
bmpread_load_image(h, &image_buffer);
bmp_free(h);
fclose(file);
@@ -375,7 +447,7 @@ of the following values:
BMPHANDLE h;
FILE *file;
uint8_t *image_buffer;
int width, height, channel, bitsperchannel;
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
@@ -395,7 +467,7 @@ of the following values:
* be written in.
*/
bmpwrite_set_dimensions(h, width, height, channels, bits_per_channel);
bmpwrite_set_dimensions(h, width, height, channels, bitsperchannel);

View File

@@ -6,7 +6,7 @@
- Write any sensible BMP
- Robustness! Don't let malformed BMPs bother us
## Current status (v1.4.3):
## Current status (v1.4.4):
### Reading BMP files:
- 16/24/32 bit RGB(A) with any bits/channel combination (BI_RGB, BI_BITFIELDS, BI_ALPHABITFIELDS).
- 64 bit RGBA (caveat see below)

View File

@@ -84,22 +84,21 @@ struct Bmpread {
LOG log;
};
FILE *file;
unsigned long bytes_read; /* number of bytes we have read from the file */
size_t bytes_read; /* number of bytes we have read from the file */
struct Bmpfile *fh;
struct Bmpinfo *ih;
unsigned int insanity_limit;
int width;
int height;
int topdown;
enum BmpOrient orientation;
int has_alpha; /* original BMP has alpha channel */
int undefined_to_alpha;
int wipe_buffer;
enum BmpUndefined undefined_to_alpha;
int we_allocated_buffer;
int line_by_line;
struct Palette *palette;
struct Colormask colormask;
/* result image dimensions */
enum Bmp64bitconv conv64;
enum Bmpconv64 conv64;
int result_channels;
int result_indexed;
int result_bits_per_pixel;
@@ -164,19 +163,6 @@ typedef struct Bmpread *BMPREAD;
typedef struct Bmpwrite *BMPWRITE;
/* There seems to be ambiguity about whether the 40-byte
* BITMAPINFOHEADER is version 1 or version 3. Both make
* sense, depending on whether you consider the
* BITMAPCORE/OS2 versions v1 and v2, or if you consider
* the Adobe-extensions (supposedly at one time
* MS-'official') v2 and v3.
* I am going with BITMAPINFOHEADER = v3.
*/
#define BMPFILE_BM (0x4d42)
#define BMPFILE_BA (0x4142)
#define BMPFILE_CI (0x4943)

View File

@@ -109,12 +109,10 @@ static BMPRESULT s_load_image(BMPREAD_R rp, unsigned char **restrict buffer, int
return BMP_RESULT_ERROR;
}
#ifdef NEVER /* gets too complicated with querying single dim values */
if (!rp->dimensions_queried) {
logerr(rp->log, "must query dimensions before loading image");
return BMP_RESULT_ERROR;
}
#endif
if (!buffer) {
logerr(rp->log, "buffer pointer is NULL");
@@ -134,8 +132,7 @@ static BMPRESULT s_load_image(BMPREAD_R rp, unsigned char **restrict buffer, int
}
else rp->we_allocated_buffer = FALSE;
if (rp->wipe_buffer || rp->undefined_to_alpha || rp->we_allocated_buffer)
memset(*buffer, 0, buffer_size);
memset(*buffer, 0, buffer_size);
if (!line_by_line)
rp->image_loaded = TRUE; /* point of no return */
@@ -266,7 +263,7 @@ static void s_read_rgb_image(BMPREAD_R rp, unsigned char *restrict image)
linelength = (size_t) rp->width * rp->result_bytes_per_pixel;
for (y = 0; y < rp->height; y++) {
real_y = rp->topdown ? y : rp->height-1-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;
@@ -450,7 +447,7 @@ static void s_read_indexed_or_rle_image(BMPREAD_R rp, unsigned char *restrict im
linesize = (size_t) rp->width * (size_t) rp->result_bytes_per_pixel;
while (y < rp->height) {
real_y = rp->topdown ? y : rp->height-1-y;
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);
@@ -516,7 +513,7 @@ static void s_read_indexed_line(BMPREAD_R rp, unsigned char *restrict line)
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)
if (rp->rle && (rp->undefined_to_alpha == BMP_UNDEFINED_TO_ALPHA))
line[offs+3] = 0xff; /* set alpha to 1.0
for defined pixels */
}
@@ -658,7 +655,7 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
}
break;
}
if (rp->undefined_to_alpha && !rp->result_indexed)
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;
@@ -710,7 +707,7 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
padding = v & 0x01 ? TRUE : FALSE;
break;
case 4:
if (v%4 == 1 || v%4 == 2)
if ((v+1)%4 >= 2)
padding = TRUE;
else
padding = FALSE;

View File

@@ -50,8 +50,8 @@ API BMPHANDLE bmpread_new(FILE *file)
}
memset(rp, 0, sizeof *rp);
rp->magic = HMAGIC_READ;
rp->undefined_to_alpha = TRUE;
rp->wipe_buffer = TRUE;
rp->undefined_to_alpha = BMP_UNDEFINED_TO_ALPHA;
rp->orientation = BMP_ORIENT_BOTTOMUP;
rp->conv64 = BMP_CONV64_16BIT_SRGB;
if (!(rp->log = logcreate()))
@@ -111,12 +111,8 @@ API BMPRESULT bmpread_load_info(BMPHANDLE h)
return BMP_RESULT_ERROR;
rp = (BMPREAD)(void*)h;
if (rp->getinfo_called) {
#ifdef DEBUG
printf("getinfo() had already been called");
#endif
if (rp->getinfo_called)
return rp->getinfo_return;
}
rp->getinfo_called = TRUE;
if (!s_read_file_header(rp))
@@ -148,7 +144,7 @@ API BMPRESULT bmpread_load_info(BMPHANDLE h)
/* negative height flips the image vertically */
if (rp->height < 0) {
rp->topdown = TRUE;
rp->orientation = BMP_ORIENT_TOPDOWN;
rp->height = -rp->height;
}
@@ -199,10 +195,10 @@ API BMPRESULT bmpread_load_info(BMPHANDLE h)
/* add alpha channel for undefined pixels in RLE bitmaps */
if (rp->rle) {
rp->result_bits_per_pixel = rp->undefined_to_alpha ? 32 : 24;
rp->result_bytes_per_pixel = rp->undefined_to_alpha ? 4 : 3;
rp->result_bits_per_pixel = (rp->undefined_to_alpha == BMP_UNDEFINED_TO_ALPHA) ? 32 : 24;
rp->result_bytes_per_pixel = (rp->undefined_to_alpha == BMP_UNDEFINED_TO_ALPHA) ? 4 : 3;
rp->result_bits_per_channel = 8;
rp->result_channels = rp->undefined_to_alpha ? 4 : 3;
rp->result_channels = (rp->undefined_to_alpha == BMP_UNDEFINED_TO_ALPHA) ? 4 : 3;
}
@@ -235,7 +231,7 @@ abort:
* bmpread_set_64bit_conv
*******************************************************/
API BMPRESULT bmpread_set_64bit_conv(BMPHANDLE h, enum Bmp64bitconv conv)
API BMPRESULT bmpread_set_64bit_conv(BMPHANDLE h, enum Bmpconv64 conv)
{
BMPREAD rp;
@@ -289,10 +285,10 @@ API int bmpread_is_64bit(BMPHANDLE h)
*******************************************************/
API BMPRESULT bmpread_dimensions(BMPHANDLE h, int* restrict width,
int* restrict height,
int* restrict channels,
int* restrict bitsperchannel,
int* restrict topdown)
int* restrict height,
int* restrict channels,
int* restrict bitsperchannel,
enum BmpOrient* restrict orientation)
{
BMPREAD rp;
@@ -311,7 +307,7 @@ API BMPRESULT bmpread_dimensions(BMPHANDLE h, int* restrict width,
if (height) *height = rp->height;
if (channels) *channels = rp->result_channels;
if (bitsperchannel) *bitsperchannel = rp->result_bits_per_channel;
if (topdown) *topdown = rp->topdown;
if (orientation) *orientation = rp->orientation;
rp->dimensions_queried = TRUE;
return rp->getinfo_return;
@@ -328,7 +324,7 @@ enum Dimint {
DIM_HEIGHT,
DIM_CHANNELS,
DIM_BITS_PER_CHANNEL,
DIM_TOPDOWN,
DIM_ORIENTATION,
DIM_XDPI,
DIM_YDPI,
};
@@ -348,7 +344,10 @@ API int bmpread_bits_per_channel(BMPHANDLE h)
{ return s_single_dim_val(h, DIM_BITS_PER_CHANNEL); }
API int bmpread_topdown(BMPHANDLE h)
{ return s_single_dim_val(h, DIM_TOPDOWN); }
{ return s_single_dim_val(h, DIM_ORIENTATION); }
API enum BmpOrient bmpread_orientation(BMPHANDLE h)
{ return (enum BmpOrient) s_single_dim_val(h, DIM_ORIENTATION); }
API int bmpread_resolution_xdpi(BMPHANDLE h)
{ return s_single_dim_val(h, DIM_XDPI); }
@@ -386,8 +385,8 @@ static int s_single_dim_val(BMPHANDLE h, enum Dimint dim)
case DIM_BITS_PER_CHANNEL:
rp->dim_queried_bits_per_channel = TRUE;
return rp->result_bits_per_channel;
case DIM_TOPDOWN:
return rp->topdown;
case DIM_ORIENTATION:
return (int) rp->orientation;
case DIM_XDPI:
return rp->ih->xpelspermeter / 39.37 + 0.5;
case DIM_YDPI:
@@ -449,10 +448,12 @@ API void bmpread_set_insanity_limit(BMPHANDLE h, size_t limit)
/********************************************************
* bmpread_set_undefined_to_alpha
* bmpread_set_undefined
*******************************************************/
API void bmpread_set_undefined_to_alpha(BMPHANDLE h, int mode)
{ bmpread_set_undefined(h, (enum BmpUndefined) mode); }
API void bmpread_set_undefined_to_alpha(BMPHANDLE h, int yes)
API void bmpread_set_undefined(BMPHANDLE h, enum BmpUndefined mode)
{
BMPREAD rp;
@@ -460,26 +461,30 @@ API void bmpread_set_undefined_to_alpha(BMPHANDLE h, int yes)
return;
rp = (BMPREAD)(void*)h;
if (!rp->undefined_to_alpha == !yes)
if (mode == rp->undefined_to_alpha)
return;
if (mode != BMP_UNDEFINED_TO_ALPHA && mode != BMP_UNDEFINED_TO_ZERO) {
logerr(rp->log, "Invalid undefined-mode selected");
return;
}
rp->undefined_to_alpha = mode;
if (!rp->getinfo_called || (rp->getinfo_called != BMP_RESULT_OK &&
rp->getinfo_called != BMP_RESULT_INSANE)) {
rp->undefined_to_alpha = !!yes;
return;
}
/* we are changing the setting after dimensions have */
/* been established. Only relevant for RLE-encoding */
rp->undefined_to_alpha = !!yes;
if (!rp->rle)
return;
rp->result_bytes_per_pixel = yes ? 4 : 3;
rp->result_bits_per_pixel = yes ? 32 : 24;
rp->result_channels = yes ? 4 : 3;
rp->result_bytes_per_pixel = (mode == BMP_UNDEFINED_TO_ALPHA) ? 4 : 3;
rp->result_bits_per_pixel = (mode == BMP_UNDEFINED_TO_ALPHA) ? 32 : 24;
rp->result_channels = (mode == BMP_UNDEFINED_TO_ALPHA) ? 4 : 3;
rp->result_size = (size_t) rp->width *
(size_t) rp->height *
@@ -1234,8 +1239,8 @@ enum Infoint {
static int s_info_int(BMPHANDLE h, enum Infoint info);
API int bmpread_info_header_version(BMPHANDLE h)
{ return s_info_int(h, INFO_INT_HEADER_VERSION); }
API enum BmpInfoVer bmpread_info_header_version(BMPHANDLE h)
{ return (enum BmpInfoVer) s_info_int(h, INFO_INT_HEADER_VERSION); }
API int bmpread_info_header_size(BMPHANDLE h)
{ return s_info_int(h, INFO_INT_HEADER_SIZE); }
API int bmpread_info_compression(BMPHANDLE h)
@@ -1260,7 +1265,7 @@ static int s_info_int(BMPHANDLE h, enum Infoint info)
switch (info) {
case INFO_INT_HEADER_VERSION:
return rp->ih->version;
return (int) rp->ih->version;
case INFO_INT_HEADER_SIZE:
return (int) rp->ih->size;
case INFO_INT_COMPRESSION:

View File

@@ -560,15 +560,6 @@ static int s_save_info(BMPWRITE_R wp)
}
}
#ifdef DEBUG
printf("RGB format: %d-%d-%d - %d\n", wp->colormask.bits.red, wp->colormask.bits.green,
wp->colormask.bits.blue, wp->colormask.bits.alpha);
printf("masks: 0x%04llx 0x%04llx 0x%04llx \nshift: 0x%04lx 0x%04lx 0x%04lx \n",
wp->colormask.mask.red, wp->colormask.mask.green, wp->colormask.mask.blue,
wp->colormask.shift.red, wp->colormask.shift.green, wp->colormask.shift.blue);
printf("bmpinfo: %d\nBits: %d\n", wp->ih->version,
(int) wp->ih->bitcount);
#endif
return TRUE;
}

182
bmplib.h
View File

@@ -22,14 +22,16 @@
#ifndef BMPLIB_H
#define BMPLIB_H
#ifdef __cplusplus
extern "C" {
extern "C" {
#endif
typedef struct Bmphandle *BMPHANDLE;
/****************************************************************
/*
* result codes
*
* BMP_RESULT_OK all is good, proceed
* BMP_RESULT_OK all is good, proceed.
*
* BMP_RESULT_ERROR something went wrong, image wasn't read
* from / written to file.
@@ -45,7 +47,8 @@ extern "C" {
* BMP_RESULT_INVALID Some or all of the pixel values were
* invalid. This happens when indexed
* images point to colors beyond the given
* palette size. If the image is also
* palette size or to pixels outside of the
* image dimensions. If the image is also
* truncated, BMP_RESULT_TRUNCATED will be
* returned instead.
*
@@ -64,72 +67,50 @@ extern "C" {
* the image unless you first call
* bmpread_set_insanity_limit() to set a new
* sufficiently high limit.
*
****************************************************************/
*/
enum Bmpresult {
BMP_RESULT_OK = 0,
BMP_RESULT_ERROR,
BMP_RESULT_TRUNCATED,
BMP_RESULT_INVALID,
BMP_RESULT_TRUNCATED,
BMP_RESULT_INSANE,
BMP_RESULT_PNG,
BMP_RESULT_JPEG,
BMP_RESULT_INSANE,
BMP_RESULT_ERROR,
};
typedef enum Bmpresult BMPRESULT;
typedef struct Bmphandle *BMPHANDLE;
typedef enum Bmpresult BMPRESULT;
/****************************************************************
* conversion of 64bit BMPs
/*
* 64-bit BMPs: conversion of RGBA (16bit) values
*
* I have very little information on 64bit BMPs. 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.
* Possible choices are:
* 1. return the values untouched, which means the caller has to
* be aware of the s2.13 format. (BMP_64CONV_NONE)
* 2. return the values as normal 16bit values, left in linear
* light (BMP_64CONV_16BIT)
* 3. return the values as normal 16bit values, converted to sRGB
* gamma. (BMP_64CONV_16BIT_SRGB)
* BMP_CONV64_16BIT_SRGB (default) convert components to 'normal'
* 16bit with sRGB gamma. Assumes components
* are stored as s2.13 linear.
*
* Choice 3 (16bit sRGB gamma) seems to be the most sensible 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)
* BMP_CONV64_16BIT convert components from s2.13 to
* 'normal' 16bit. No gamma conversion.
*
* Note: the s2.13 format allows for negative values and values
* greater than 1! When converting to 16bit, these values will be
* clipped to 0...1.
*
* In any case, we'll need to provide functions to check if a BMP is
* 64bit and to set the conversion:
* bmpread_is_64bit()
* bmpread_set_64bit_conv()
*******************************************************************/
enum Bmp64bitconv {
BMP_CONV64_16BIT_SRGB, /* default */
* BMP_CONV64_NONE Leave components as they are, which
* might always(?) be s2.13 linear.
*/
enum Bmpconv64 {
BMP_CONV64_16BIT_SRGB, /* default */
BMP_CONV64_16BIT,
BMP_CONV64_NONE
};
int bmpread_is_64bit(BMPHANDLE h);
BMPRESULT bmpread_set_64bit_conv(BMPHANDLE h, enum Bmp64bitconv conv);
typedef enum Bmpconv64 BMPCONV64;
/*
* BMP info header versions
*
* There doesn't seem to be consensus on whether the
* BITMAPINFOHEADER is version 1 (with the two Adobe
* extensions being v2 and v3) or version 3 (with the
* older BITMAPCIREHEADER and OS22XBITMAPHEADER being
* v1 and v2).
* I am going with BITMAPINFOHEADER = v3
*/
enum BmpInfoVer {
BMPINFO_CORE_OS21 = 1, /* 12 bytes */
BMPINFO_OS22, /* 16 / 40(!) / 64 bytes */
@@ -140,31 +121,77 @@ enum BmpInfoVer {
BMPINFO_V5, /* 124 bytes */
BMPINFO_FUTURE /* future versions, larger than 124 bytes */
};
typedef enum BmpInfoVer BMPINFOVER;
/*
* RLE type
*
* BMP_RLE_NONE no RLE
*
* BMP_RLE_AUTO RLE4 for color tables with 16 or fewer
* colors.
*
* BMP_RLE_RLE8 always use RLE8, regardless of color
* table size.
*/
enum BmpRLEtype {
BMP_RLE_NONE = 0,
BMP_RLE_NONE,
BMP_RLE_AUTO,
BMP_RLE_RLE8
};
typedef enum BmpRLEtype BMPRLETYPE;
/*
* undefined pixels in RLE images
*
* BMP_UNDEFINED_TO_ZERO set undefined pixels to 0 (= first
* entry in color table).
*
* BMP_UNDEFINED_TO_ALPHA (default) make undefined pixels
* transparent. Always adds an alpha
* channel to the result.
*
*/
enum BmpUndefined {
BMP_UNDEFINED_TO_ZERO,
BMP_UNDEFINED_TO_ALPHA /* default */
};
typedef enum BmpUndefined BMPUNDEFINED;
/*
* orientation
*
* Only relevant when reading the image line-by-line.
* When reading the image as a whole, it will *always*
* be returned in top-down orientation. bmpread_orientation()
* still gives the orientation of the BMP file.
*/
enum BmpOrient {
BMP_ORIENT_BOTTOMUP,
BMP_ORIENT_TOPDOWN
};
typedef enum BmpOrient BMPORIENT;
BMPHANDLE bmpread_new(FILE *file);
BMPRESULT bmpread_load_info(BMPHANDLE h);
BMPRESULT bmpread_dimensions(BMPHANDLE h,
int *width,
int *height,
int *channels,
int *bitsperchannel,
int *topdown);
BMPRESULT bmpread_dimensions(BMPHANDLE h,
int *width,
int *height,
int *channels,
int *bitsperchannel,
BMPORIENT *orientation);
int bmpread_width(BMPHANDLE h);
int bmpread_height(BMPHANDLE h);
int bmpread_channels(BMPHANDLE h);
int bmpread_bits_per_channel(BMPHANDLE h);
int bmpread_topdown(BMPHANDLE h);
BMPORIENT bmpread_orientation(BMPHANDLE h);
int bmpread_resolution_xdpi(BMPHANDLE h);
int bmpread_resolution_ydpi(BMPHANDLE h);
@@ -179,18 +206,19 @@ int bmpread_num_palette_colors(BMPHANDLE h);
BMPRESULT bmpread_load_palette(BMPHANDLE h, unsigned char **palette);
void bmpread_set_undefined_to_alpha(BMPHANDLE h, int yes);
void bmpread_set_undefined(BMPHANDLE h, BMPUNDEFINED mode);
void bmpread_set_insanity_limit(BMPHANDLE h, size_t limit);
int bmpread_is_64bit(BMPHANDLE h);
BMPRESULT bmpread_set_64bit_conv(BMPHANDLE h, BMPCONV64 conv);
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);
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);
BMPRESULT bmpread_info_channel_bits(BMPHANDLE h, int *r, int *g, int *b, int *a);
@@ -206,7 +234,7 @@ 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, enum BmpRLEtype type);
BMPRESULT bmpwrite_set_rle(BMPHANDLE h, BMPRLETYPE type);
BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image);
BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line);
@@ -220,6 +248,20 @@ const char* bmp_errmsg(BMPHANDLE h);
const char* bmp_version(void);
/* 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 */
#ifdef __cplusplus
}
#endif

View File

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