mirror of
https://github.com/rupertwh/bmplib.git
synced 2026-04-22 14:13:07 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7910ac36b | ||
|
|
7ece494541 | ||
|
|
c8ead48e33 | ||
|
|
8b38742ea3 | ||
|
|
3a673da403 | ||
|
|
fabbe4f8af |
58
API-full.md
58
API-full.md
@@ -104,7 +104,30 @@ size_t bmpread_buffersize(BMPHANDLE h);
|
||||
|
||||
Returns the buffer size you have to allocate for the whole image.
|
||||
|
||||
### 1.5. Indexed BMPs
|
||||
### 1.5. Image type
|
||||
|
||||
```c
|
||||
BMPIMAGETYPE bmpread_image_type(BMPHANDLE h);
|
||||
```
|
||||
|
||||
Returns one of
|
||||
- `BMP_IMAGETYPE_NONE` type hasn't been determined (yet). Either
|
||||
`bmpread_load_info()` hasn't been called yet, or the file is not a valid
|
||||
BMP file.
|
||||
- `BMP_IMAGETYPE_BM` bitmap (normal image)
|
||||
- `BMP_IMAGETYPE_BA` bitmap array
|
||||
- `BMP_IMAGETYPE_CI` color icon
|
||||
- `BMP_IMAGETYPE_CP` color pointer
|
||||
- `BMP_IMAGETYPE_IC` icon (monochrome)
|
||||
- `BMP_IMAGETYPE_PT` pointer (monochrome)
|
||||
|
||||
Other than bitmap arrays (in which case `bmpread_load_info()` will have
|
||||
returned `BMP_RESULT_ARRAY`) and the limitations that apply to icon and
|
||||
pointer files (see below) the returned type is purely informational and no
|
||||
special actions need to be taken.
|
||||
|
||||
|
||||
### 1.6. Indexed BMPs
|
||||
|
||||
By default, bmplib will interpret indexed (color palette) BMPs and return the
|
||||
image as 24-bit RGB data, same as non-indexed (RGB) BMPs.
|
||||
@@ -153,7 +176,7 @@ RGB data. After you loaded the palette, calls to `bmpread_dimensions
|
||||
`bmpread_set_undefined()` will have no effect, as indexed images cannot have
|
||||
an alpha channel (see below).
|
||||
|
||||
#### 1.5.1. Undefined pixels
|
||||
#### 1.6.1. Undefined pixels
|
||||
|
||||
RLE-encoded BMP files may have undefined pixels, either by using early
|
||||
end-of-line or end-of-file codes, or by using delta codes to skip part of the
|
||||
@@ -184,7 +207,7 @@ Note: If you use `bmpread_load_palette()` to switch to loading the index data
|
||||
instead of RGB data, this setting will have no effect and undefined pixels
|
||||
will always be left alone! (see above)
|
||||
|
||||
### 1.6. ICC color profiles
|
||||
### 1.7. ICC color profiles
|
||||
|
||||
```c
|
||||
size_t bmpread_iccprofile_size(BMPHANDLE h);
|
||||
@@ -210,7 +233,7 @@ if (bmpread_iccprofile_size(h) > 0)
|
||||
```
|
||||
|
||||
|
||||
### 1.7. Optional settings for 64bit BMPs
|
||||
### 1.8. Optional settings for 64bit BMPs
|
||||
|
||||
```c
|
||||
int bmpread_is_64bit(BMPHANDLE h);
|
||||
@@ -238,7 +261,7 @@ Options for `bmpread_set_64bit()` are:
|
||||
`BMP_FORMAT_S2_13`. Image values are returned exactly as they are in the BMP
|
||||
file, without any conversion or attempt at interpretation.
|
||||
|
||||
### 1.8. Setting a number format
|
||||
### 1.9. Setting a number format
|
||||
|
||||
By default, bmplib will always return the image data as 8-,16-, or 32-bit
|
||||
integer values. You can instead set the number format to floating point or
|
||||
@@ -250,7 +273,7 @@ BMPRESULT bmp_set_number_format(BMPHANDLE h, BMPFORMAT format);
|
||||
|
||||
(see below, *3. General functions for both reading/writing BMPs*)
|
||||
|
||||
### 1.9. Huge files: bmpread_set_insanity_limit()
|
||||
### 1.10. Huge files: bmpread_set_insanity_limit()
|
||||
|
||||
bmplib will refuse to load images beyond a certain size (default 500MB) and
|
||||
instead return `BMP_RESULT_INSANE`. If you want to load the image anyway, call
|
||||
@@ -261,7 +284,7 @@ instead return `BMP_RESULT_INSANE`. If you want to load the image anyway, call
|
||||
void bmpread_set_insanity_limit(BMPHANDLE h, size_t limit);
|
||||
```
|
||||
|
||||
### 1.10. OS/2 bitmap arrays (type 'BA')
|
||||
### 1.11. OS/2 bitmap arrays (type 'BA')
|
||||
|
||||
Bitmap arrays are meant to contain several versions (with different
|
||||
resolutions and color depths) of the same image. They are not meant to
|
||||
@@ -314,16 +337,19 @@ First completely read one image before proceeding to the next one.
|
||||
OS/2 icons and pointers are often contained in bitmap arrays.
|
||||
|
||||
Some limitations apply for loading icons and pointers:
|
||||
- The image is always returned as RGBA, loading index values and palette does
|
||||
not work (yet).
|
||||
- The image is always returned as 8 bit per channel RGBA, loading index values
|
||||
and palette is not supported.
|
||||
- Maximum supported size is 512x512. This shouldn't be limiting for actual
|
||||
legacy icons and pointers.
|
||||
- RLE is not supported (yet)
|
||||
- For RLE images, undefined mode is always assumed to be
|
||||
`BMP_UNDEFINED_LEAVE`, regardless of what `bmpread_set_undefined()` was set
|
||||
to. (Because icons and pointers have their own mask-based transparency
|
||||
scheme.)
|
||||
|
||||
|
||||
### 1.11. Load the image
|
||||
### 1.12. Load the image
|
||||
|
||||
#### 1.11.1. bmpread_load_image()
|
||||
#### 1.12.1. bmpread_load_image()
|
||||
|
||||
```c
|
||||
BMPRESULT bmpread_load_image(BMPHANDLE h, unsigned char **pbuffer);
|
||||
@@ -360,7 +386,7 @@ If `bmpread_load_image()` returns `BMP_RESULT_TRUNCATED` or `BMP_RESULT_INVALID`
|
||||
the file may have been damaged or simply contains invalid image data. Image
|
||||
data is loaded anyway as far as possible and may be partially usable.
|
||||
|
||||
#### 1.11.2. bmpread_load_line()
|
||||
#### 1.12.2. bmpread_load_line()
|
||||
|
||||
```c
|
||||
BMPRESULT bmpread_load_line(BMPHANDLE h, unsigned char **pbuffer);
|
||||
@@ -392,7 +418,7 @@ returned in whichever order they are stored in the BMP. Use the value
|
||||
returned by `bmpread_orientation()` to determine if it is top-down or
|
||||
bottom-up. Almost all BMPs will be bottom-up. (see above)
|
||||
|
||||
### 1.12. Invalid pixels
|
||||
### 1.13. Invalid pixels
|
||||
|
||||
Invalid pixels may occur in indexed BMPs, both RLE and non-RLE. Invalid pixels
|
||||
either point beyond the given color palette, or they try to set pixels
|
||||
@@ -404,7 +430,7 @@ In both cases, `bmpread_load_image()` and `bmpread_load_line()` will return
|
||||
`BMP_RESULT_INVALID`, unless the image is also truncated, then
|
||||
`BMP_RESULT_TRUNCATED` is returned.
|
||||
|
||||
### 1.13. Query info about the BMP file
|
||||
### 1.14. Query info about the BMP file
|
||||
|
||||
Note: these functions return information about the original BMP file being
|
||||
read. They do *not* describe the format of the returned image data, which may
|
||||
@@ -420,7 +446,7 @@ const char* bmpread_info_compression_name(BMPHANDLE h);
|
||||
BMPRESULT bmpread_info_channel_bits(BMPHANDLE h, int *r, int *g, int *b, int *a);
|
||||
```
|
||||
|
||||
### 1.14. Release the handle
|
||||
### 1.15. Release the handle
|
||||
|
||||
```c
|
||||
void bmp_free(BMPHANDLE h);
|
||||
|
||||
34
README.md
34
README.md
@@ -32,7 +32,7 @@ Download [bmplib on github](https://github.com/rupertwh/bmplib).
|
||||
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.
|
||||
- We currently ignore chromaticity/gamma values from V4+ headers. See TODO.
|
||||
|
||||
|
||||
### Writing BMP files:
|
||||
@@ -136,31 +136,14 @@ Microsoft tools, the new GIMP 3.0 is the only one I am aware of). Use
|
||||
## TODOs:
|
||||
### Definitely:
|
||||
|
||||
- [x] write indexed images.
|
||||
- [x] write RLE-compressed images ~~(RLE4/RLE8 only. No OS/2 v2 BMPs)~~.
|
||||
- [x] read RLE24-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.~~
|
||||
- [x] read/write icc-profile and chromaticity/gamma values
|
||||
- [x] sanity checks for size of of image / palette. Require confirmation
|
||||
above a certain size (~ 500MB?)
|
||||
- [x] store undefined pixels (RLE delta and early EOL/EOF) as alpha
|
||||
- [ ] read/write chromaticity/gamma values
|
||||
|
||||
|
||||
### Maybe:
|
||||
|
||||
- [x] passing indexed data and palette to user (optionally) instead of
|
||||
RGB-data.
|
||||
- [ ] interpret icc-profile, to enable giving at least sRGB/not-sRGB info.
|
||||
(Like sRGB / probably-sRGB / maybe-sRGB). Torn on that one, would need
|
||||
dependency on liblcms2.
|
||||
- [x] "BA"-files (bitmap-arrays). Either return the first bitmap only
|
||||
(which is the 'official' default) or let user pick one/multiple/all to
|
||||
be read in sequence.
|
||||
- [ ] Add a 'not-a-BMP-file' return type instead of just returning error.
|
||||
- [x] icon- and pointer-files ("CI", "CP", "IC", "PT").
|
||||
- [x] 64-bits BMPs. (I changed my mind)
|
||||
|
||||
### Unclear:
|
||||
|
||||
@@ -169,19 +152,6 @@ Microsoft tools, the new GIMP 3.0 is the only one I am aware of). Use
|
||||
platforms/cpus. And Windows?
|
||||
|
||||
|
||||
### Non-feature (internal):
|
||||
|
||||
- [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
|
||||
|
||||
|
||||
|
||||
|
||||
## Misc:
|
||||
- [x] License: probably LPGL3? That's what I'm going with for now.
|
||||
|
||||
|
||||
|
||||
Cheers,
|
||||
|
||||
Rupert
|
||||
|
||||
@@ -125,8 +125,12 @@ bool icon_read_array(BMPREAD_R rp)
|
||||
s_array_header_from_file_header(&ah, rp->fh);
|
||||
|
||||
while (n < nmax) {
|
||||
if (ah.type != BMPFILE_BA)
|
||||
if (ah.type != BMPFILE_BA) {
|
||||
logerr(rp->c.log, "Invalid BMP type (0x%04x), expected 'BA'", (unsigned) ah.type);
|
||||
invalid = true;
|
||||
rp->lasterr = BMP_ERR_HEADER;
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(&imgs[n].ah, &ah, sizeof ah);
|
||||
|
||||
@@ -139,10 +143,13 @@ bool icon_read_array(BMPREAD_R rp)
|
||||
bmp_free(imgs[n].handle);
|
||||
invalid = true;
|
||||
rp->lasterr = BMP_ERR_HEADER;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
logerr(rp->c.log, "Failed to create handle for array image");
|
||||
invalid = true;
|
||||
rp->lasterr = BMP_ERR_HEADER;
|
||||
rp->lasterr = BMP_ERR_MEMORY;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ah.offsetnext)
|
||||
|
||||
@@ -74,6 +74,7 @@ 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 bool s_are_settings_icon_compatible(BMPREAD_R rp);
|
||||
|
||||
static_assert(sizeof(float) == 4, "sizeof(float) must be 4. Cannot build bmplib.");
|
||||
static_assert(sizeof(int) * CHAR_BIT >= 32, "int must be at least 32bit. Cannot build bmplib.");
|
||||
@@ -156,6 +157,15 @@ static BMPRESULT s_load_image_or_line(BMPREAD_R rp, unsigned char **restrict buf
|
||||
return BMP_RESULT_INSANE;
|
||||
}
|
||||
|
||||
if (rp->read_state < RS_LOAD_STARTED && rp->is_icon) {
|
||||
if (!s_are_settings_icon_compatible(rp)) {
|
||||
logerr(rp->c.log, "Panic! Trying to load icon/pointer with incompatibele settings.\n");
|
||||
rp->read_state = RS_FATAL;
|
||||
rp->lasterr = BMP_ERR_INTERNAL;
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (!buffer) {
|
||||
logerr(rp->c.log, "Buffer pointer is NULL. (It may point to a NULL pointer, but must not itself be NULL)");
|
||||
return BMP_RESULT_ERROR;
|
||||
@@ -217,7 +227,27 @@ abort:
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
static bool s_are_settings_icon_compatible(BMPREAD_R rp)
|
||||
{
|
||||
/* some catch-all sanity checks for icons/pointers. Strictly, these
|
||||
* shouldn't be necessary, as they should have been caught already.
|
||||
* If any of these fail, there is a bug somewhere else.
|
||||
*/
|
||||
|
||||
if (rp->result_channels != 4 || rp->result_bitsperchannel != 8)
|
||||
return false;
|
||||
|
||||
if (rp->result_format != BMP_FORMAT_INT)
|
||||
return false;
|
||||
|
||||
if (rp->rle && (rp->undefined_mode != BMP_UNDEFINED_LEAVE))
|
||||
return false;
|
||||
|
||||
if (!(rp->rle || (rp->ih->compression == BI_RGB)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/********************************************************
|
||||
* apply_icon_alpha
|
||||
@@ -225,9 +255,6 @@ abort:
|
||||
|
||||
static void apply_icon_alpha(BMPREAD_R rp, int y, unsigned char *restrict line)
|
||||
{
|
||||
if (!(rp->result_channels == 4 && rp->result_bitsperchannel == 8))
|
||||
return;
|
||||
|
||||
for (int x = 0; x < rp->width; x++) {
|
||||
line[x * 4 + 3] = rp->icon_mono_and[(rp->height - y - 1) * rp->width + x];
|
||||
}
|
||||
|
||||
71
bmp-read.c
71
bmp-read.c
@@ -122,21 +122,26 @@ API BMPRESULT bmpread_load_info(BMPHANDLE h)
|
||||
case BMPFILE_CP:
|
||||
case BMPFILE_IC:
|
||||
case BMPFILE_PT:
|
||||
if (rp->read_state < RS_EXPECT_ICON_MASK) {
|
||||
if (rp->read_state != RS_EXPECT_ICON_MASK) {
|
||||
pos = icon_load_masks(rp);
|
||||
if (pos < 0)
|
||||
goto abort;
|
||||
|
||||
/* re-read the file header, because for color icons/pointers
|
||||
* we started with the monochrome headers, and icon_load_masks()
|
||||
* returned the pos of the actual color headers.
|
||||
*/
|
||||
rp->bytes_read = 0;
|
||||
if (fseek(rp->file, pos, SEEK_SET)) {
|
||||
logsyserr(rp->c.log, "Setting file position");
|
||||
goto abort;
|
||||
}
|
||||
|
||||
if (!s_read_file_header(rp))
|
||||
goto abort;
|
||||
|
||||
if (rp->fh->type != type) {
|
||||
logerr(rp->c.log, "Filetype mismatch: have %x, expected %x",
|
||||
logerr(rp->c.log, "Filetype mismatch: have 0x%04x, expected 0x%04x",
|
||||
(unsigned)rp->fh->type, type);
|
||||
goto abort;
|
||||
}
|
||||
@@ -146,9 +151,16 @@ API BMPRESULT bmpread_load_info(BMPHANDLE h)
|
||||
rp->icon_is_mono = false;
|
||||
else
|
||||
rp->icon_is_mono = true;
|
||||
|
||||
rp->undefined_mode = BMP_UNDEFINED_LEAVE;
|
||||
}
|
||||
|
||||
/* otherwise we read the AND/XOR masks as a normal image */
|
||||
/* otherwise, state is RS_EXPECT_ICON_MASK, which means that
|
||||
* icon_load_masks() is reading the AND/XOR masks (under a
|
||||
* separate BMPHANDLE). We don't have to do anything special,
|
||||
* just treat it as a normal BMP image, not as an icon/pointer
|
||||
*/
|
||||
|
||||
break;
|
||||
|
||||
case BMPFILE_BA:
|
||||
@@ -161,9 +173,9 @@ API BMPRESULT bmpread_load_info(BMPHANDLE h)
|
||||
goto abort;
|
||||
}
|
||||
|
||||
rp->read_state = RS_ARRAY;
|
||||
return BMP_RESULT_ARRAY;
|
||||
|
||||
rp->read_state = RS_ARRAY;
|
||||
rp->getinfo_return = BMP_RESULT_ARRAY;
|
||||
return rp->getinfo_return;
|
||||
|
||||
default:
|
||||
logerr(rp->c.log, "Unkown BMP type 0x%04x\n", (unsigned int) rp->fh->type);
|
||||
@@ -272,6 +284,36 @@ abort:
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* bmpread_image_type
|
||||
*****************************************************************************/
|
||||
|
||||
API BMPIMAGETYPE bmpread_image_type(BMPHANDLE h)
|
||||
{
|
||||
BMPREAD rp;
|
||||
|
||||
if (!(rp = cm_read_handle(h)))
|
||||
return BMP_IMAGETYPE_NONE;
|
||||
|
||||
if (rp->read_state < RS_HEADER_OK) {
|
||||
logerr(rp->c.log, "Must load info, first.");
|
||||
return BMP_IMAGETYPE_NONE;
|
||||
}
|
||||
|
||||
switch (rp->fh->type) {
|
||||
case BMPFILE_BM: return BMP_IMAGETYPE_BM;
|
||||
case BMPFILE_BA: return BMP_IMAGETYPE_BA;
|
||||
case BMPFILE_IC: return BMP_IMAGETYPE_IC;
|
||||
case BMPFILE_PT: return BMP_IMAGETYPE_PT;
|
||||
case BMPFILE_CI: return BMP_IMAGETYPE_CI;
|
||||
case BMPFILE_CP: return BMP_IMAGETYPE_CP;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return BMP_IMAGETYPE_NONE;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* bmpread_set_64bit_conv
|
||||
*****************************************************************************/
|
||||
@@ -717,6 +759,12 @@ API void bmpread_set_undefined(BMPHANDLE h, enum BmpUndefined mode)
|
||||
if (!(rp = cm_read_handle(h)))
|
||||
return;
|
||||
|
||||
if (rp->is_icon && mode != BMP_UNDEFINED_LEAVE) {
|
||||
logerr(rp->c.log, "For icons/pointers, only BMP_UNDEFINED_LEAVE is valid.");
|
||||
rp->lasterr = BMP_ERR_UNDEFMODE;
|
||||
mode = BMP_UNDEFINED_LEAVE;
|
||||
}
|
||||
|
||||
if (mode == rp->undefined_mode)
|
||||
return;
|
||||
|
||||
@@ -792,29 +840,36 @@ static bool s_is_bmptype_supported(BMPREAD_R rp)
|
||||
{
|
||||
if (rp->ih->planes != 1) {
|
||||
logerr(rp->c.log, "Unsupported number of planes (%d). Must be 1.", (int) rp->ih->planes);
|
||||
rp->lasterr = BMP_ERR_HEADER;
|
||||
rp->lasterr = BMP_ERR_UNSUPPORTED;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rp->is_icon) {
|
||||
if (rp->ih->compression != BI_RGB) {
|
||||
if (rp->ih->compression != BI_RGB &&
|
||||
rp->ih->compression != BI_RLE4 &&
|
||||
rp->ih->compression != BI_RLE8 &&
|
||||
rp->ih->compression != BI_OS2_RLE24) {
|
||||
logerr(rp->c.log, "Unsupported compression %s for icon/pointer",
|
||||
s_compression_name(rp->ih->compression));
|
||||
rp->lasterr = BMP_ERR_UNSUPPORTED;
|
||||
return false;
|
||||
}
|
||||
if (rp->ih->bitcount > 32) {
|
||||
logerr(rp->c.log, "Unsupported bitcount %d for icon/pointer",
|
||||
(int) rp->ih->bitcount);
|
||||
rp->lasterr = BMP_ERR_UNSUPPORTED;
|
||||
return false;
|
||||
}
|
||||
if (rp->ih->version > BMPINFO_OS22) {
|
||||
logerr(rp->c.log, "Unsupported header version %s for icon/pointer",
|
||||
cm_infoheader_name(rp->ih->version));
|
||||
rp->lasterr = BMP_ERR_UNSUPPORTED;
|
||||
return false;
|
||||
}
|
||||
if (rp->result_format != BMP_FORMAT_INT) {
|
||||
logerr(rp->c.log, "Chosen number format %s is incompatible with icon/pointer",
|
||||
cm_format_name(rp->result_format));
|
||||
rp->lasterr = BMP_ERR_UNSUPPORTED;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user