mirror of
https://github.com/rupertwh/bmplib.git
synced 2026-04-15 18:52:39 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b3c8a1081 | ||
|
|
1ede644b71 | ||
|
|
1b9c8af659 | ||
|
|
a4800cb684 | ||
|
|
ed07c4f618 | ||
|
|
9297d88483 | ||
|
|
c0c2513c7d | ||
|
|
352a231632 | ||
|
|
72f8085c41 |
113
API-full.md
113
API-full.md
@@ -2,10 +2,10 @@
|
||||
|
||||
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)
|
||||
```
|
||||
@@ -16,14 +16,14 @@ 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
|
||||
@@ -42,8 +42,8 @@ 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).
|
||||
|
||||
|
||||
### Get image dimensions
|
||||
|
||||
```
|
||||
BMPRESULT bmpread_dimensions(BMPHANDLE h,
|
||||
int *width,
|
||||
@@ -77,30 +77,32 @@ 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:
|
||||
- `BMP_ORIENT_BOTTOMUP`
|
||||
- `BMP_ORIENT_TOPDOWN`
|
||||
|
||||
`bmpread_orientation()` or the `orientation` value returned by
|
||||
`bmpread_dimensions()` **is only relevant if you load the BMP file
|
||||
line-by-line**. In line-by-line mode (using `bmpread_load_line()`), the
|
||||
image data is always delivered in the order it is in the BMP file. The
|
||||
`orientation` value will tell you if it's top-down or bottom-up. On the
|
||||
other hand, when the whole image is loaded at once (using `bmpread_load_image
|
||||
()`), bmplib will **always** return the image top-down, regardless of how
|
||||
the BMP file is oriented. The `orientation` value will still indicate the
|
||||
orientation of the original BMP.
|
||||
`*orientation` is one of:
|
||||
|
||||
- `BMP_ORIENT_BOTTOMUP`
|
||||
- `BMP_ORIENT_TOPDOWN`
|
||||
|
||||
`bmpread_orientation()` or the `orientation` value returned by
|
||||
`bmpread_dimensions()` **is only relevant if you load the BMP file
|
||||
line-by-line**. In line-by-line mode (using `bmpread_load_line()`), the
|
||||
image data is always delivered in the order it is in the BMP file. The
|
||||
`orientation` value will tell you if it's top-down or bottom-up. On the
|
||||
other hand, when the whole image is loaded at once (using `bmpread_load_image
|
||||
()`), bmplib will **always** return the image top-down, regardless of how
|
||||
the BMP file is oriented. The `orientation` value will still indicate the
|
||||
orientation of the original BMP.
|
||||
|
||||
#### 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.
|
||||
|
||||
Returns the buffer size you have to allocate for the whole image.
|
||||
|
||||
### Indexed BMPs
|
||||
|
||||
@@ -141,7 +143,6 @@ bmpread_load_palette(h, &palette); /* bmplib will allocate the palette
|
||||
/* 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
|
||||
@@ -175,6 +176,7 @@ void bmpread_set_undefined(BMPHANDLE h, BMPUNDEFINED mode)
|
||||
```
|
||||
|
||||
`mode` can be one of:
|
||||
|
||||
- `BMP_UNDEFINED_LEAVE`
|
||||
- `BMP_UNDEFINED_TO_ALPHA` (default)
|
||||
|
||||
@@ -182,7 +184,6 @@ 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
|
||||
|
||||
```
|
||||
@@ -207,7 +208,6 @@ Options for `bmpread_set_64bit()` are:
|
||||
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:
|
||||
@@ -218,8 +218,6 @@ 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
|
||||
@@ -232,8 +230,8 @@ void
|
||||
bmpread_set_insanity_limit(BMPHANDLE h, size_t limit)
|
||||
```
|
||||
|
||||
|
||||
### Load the image
|
||||
|
||||
#### bmpread_load_image()
|
||||
|
||||
```
|
||||
@@ -259,7 +257,6 @@ 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
|
||||
@@ -273,6 +270,7 @@ 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)
|
||||
```
|
||||
@@ -303,7 +301,6 @@ 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
|
||||
@@ -316,7 +313,6 @@ 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
|
||||
@@ -333,7 +329,6 @@ 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
|
||||
|
||||
```
|
||||
@@ -347,17 +342,16 @@ affected**, so you can call bmp_free() immediately after `bmpread_load_image
|
||||
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,
|
||||
@@ -366,7 +360,6 @@ BMPRESULT bmpwrite_set_dimensions(BMPHANDLE h,
|
||||
unsigned bitsperchannel)
|
||||
|
||||
BMPRESULT bmpwrite_set_resolution(BMPHANDLE h, int xdpi, int ydpi)
|
||||
|
||||
```
|
||||
|
||||
Note: the dimensions set with `bmpwrite_set_dimensions()` describe the source
|
||||
@@ -374,7 +367,6 @@ 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
|
||||
@@ -415,6 +407,7 @@ BMP for 3- or 4-color images, call `bmpwrite_allow_2bit()` before calling
|
||||
```
|
||||
BMPRESULT bmpwrite_set_rle(BMPHANDLE h, BMPRLETYPE type)
|
||||
BMPRESULT bmpwrite_allow_huffman(BMPHANDLE h)
|
||||
BMPRESULT bmpwrite_set_huffman_img_fg_idx(BMPHANDLE h, int idx)
|
||||
```
|
||||
|
||||
Indexed images may optionally be written run-lenght-encoded (RLE) bitmaps.
|
||||
@@ -427,16 +420,30 @@ only after explicitly allowing it by calling `bmpwrite_allow_huffman()`
|
||||
|
||||
To activate RLE compression, call `bmpwrite_set_rle()` with `type` set to one
|
||||
of the following values:
|
||||
|
||||
- `BMP_RLE_NONE` no RLE compression, same as not calling `bmpwrite_set_rle()`
|
||||
at all
|
||||
- `BMP_RLE_AUTO` choose RLE4, RLE8, or 1-D Huffman based on number of colors
|
||||
in palette
|
||||
- `BMP_RLE_RLE8` use RLE8, regardless of number of colors in palette
|
||||
|
||||
In order to write 1-D Huffman encoded bitmpas, the provided palette must have
|
||||
2 colors, RLE type must be set to `BMP_RLE_AUTO`, and `bmpwrite_allow_huffman
|
||||
()` must be called. Be aware that *very* few programs will be able to read
|
||||
Huffman encoded BMPs!
|
||||
|
||||
#### 1-D Huffman encoding
|
||||
In order to write 1-D Huffman encoded bitmpas,
|
||||
|
||||
- the provided palette must have 2 colors,
|
||||
- RLE type must be set to `BMP_RLE_AUTO`,
|
||||
- and `bmpwrite_allow_huffman()` must be called.
|
||||
|
||||
Be aware that *very* few programs will be able to read Huffman encoded BMPs!
|
||||
|
||||
In order to get the best compression result, you should also call
|
||||
`bmpwrite_set_huffman_img_fg_idx()` to specify which color index (0 or 1) in
|
||||
the image corresponds to the foreground color. Huffman compression is
|
||||
optimized for scanned text, meaning short runs of foreground color and long
|
||||
(er) runs of background color. This will not change the appearance of the
|
||||
image, but setting it correctly will result in better compression.
|
||||
|
||||
|
||||
#### RLE24
|
||||
|
||||
@@ -449,7 +456,6 @@ In order to save an image as RLE24, the data must be provided as 8 bits per
|
||||
channel RGB (no alpha channel). Call `bmpwrite_set_rle()` with type set to
|
||||
`BMP_RLE_AUTO` and also call `bmpwrite_allow_rle24()` (in any order).
|
||||
|
||||
|
||||
### top-down / bottom-up
|
||||
|
||||
By default, bmplib will write BMP files bottom-up, which is how BMP files are
|
||||
@@ -463,8 +469,9 @@ BMPRESULT bmpwrite_set_orientation(BMPHANDLE h, BMPORIENT orientation)
|
||||
```
|
||||
|
||||
with `orientation` set to one of the following values:
|
||||
- `BMPORIENT_BOTTOMUP`
|
||||
- `BMPORIENT_TOPDOWN`
|
||||
|
||||
- `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,
|
||||
@@ -474,9 +481,6 @@ 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
|
||||
@@ -492,9 +496,6 @@ In order to make use of the extended range available in 64-bit BMPs (-4.0 to +3.
|
||||
|
||||
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
|
||||
|
||||
```
|
||||
@@ -517,11 +518,6 @@ Important: When writing the whole image at once using `bmpwrite_save_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()
|
||||
@@ -536,7 +532,6 @@ affected, so you can call bmp_free() immediately after bmpread_load_image
|
||||
by `bmp_errmsg()` are invalidated by `bmp_free()` and cannot be used
|
||||
anymore.
|
||||
|
||||
|
||||
### bmp_errmsg()
|
||||
|
||||
```
|
||||
@@ -547,7 +542,6 @@ 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()
|
||||
|
||||
```
|
||||
@@ -562,7 +556,6 @@ sets the number format of the image buffer received from / passed to bmplib. `fo
|
||||
|
||||
For indexed images, `BMP_FORMAT_INT` is the only valid format.
|
||||
|
||||
|
||||
### bmp_version()
|
||||
|
||||
```
|
||||
@@ -571,9 +564,6 @@ const char* bmp_version(void)
|
||||
|
||||
Returns a zero-terminated character string containing the version of bmplib.
|
||||
|
||||
|
||||
|
||||
|
||||
## 4. Data types and constants
|
||||
|
||||
#### `BMPHANDLE`
|
||||
@@ -608,10 +598,10 @@ else {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### `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)
|
||||
@@ -627,6 +617,7 @@ 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
|
||||
@@ -636,6 +627,7 @@ 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`
|
||||
|
||||
@@ -644,6 +636,7 @@ 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`
|
||||
@@ -653,12 +646,11 @@ 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
|
||||
@@ -713,7 +705,6 @@ Used in `bmp_set_number_format()`. Possible values are:
|
||||
*/
|
||||
```
|
||||
|
||||
|
||||
### Writing BMPs
|
||||
|
||||
```
|
||||
|
||||
106
bmp-common.h
106
bmp-common.h
@@ -175,6 +175,7 @@ struct Bmpwrite {
|
||||
size_t bytes_written_before_bitdata;
|
||||
bool has_alpha;
|
||||
enum BmpOrient outorientation;
|
||||
bool huffman_fg_idx;
|
||||
struct Colormask cmask;
|
||||
enum BmpRLEtype rle_requested;
|
||||
int rle; /* 1, 4, or 8 */
|
||||
@@ -243,72 +244,67 @@ int16_t s16_from_le(const unsigned char *buf);
|
||||
#define BMPFILE_PT 0x5450
|
||||
|
||||
|
||||
#define BMPFHSIZE 14
|
||||
#define BMPIHSIZE_V3 40
|
||||
#define BMPIHSIZE_V4 108
|
||||
#define BMPIHSIZE_OS22 64
|
||||
|
||||
typedef uint16_t WORD;
|
||||
typedef uint32_t DWORD;
|
||||
typedef int32_t LONG;
|
||||
typedef uint8_t BYTE;
|
||||
#define BMPFHSIZE 14
|
||||
#define BMPIHSIZE_V3 40
|
||||
#define BMPIHSIZE_V4 108
|
||||
#define BMPIHSIZE_OS22 64
|
||||
|
||||
struct Bmpfile {
|
||||
WORD type; /* "BM" */
|
||||
DWORD size; /* bytes in file */
|
||||
WORD reserved1;
|
||||
WORD reserved2;
|
||||
DWORD offbits;
|
||||
uint16_t type; /* "BM" */
|
||||
uint32_t size; /* bytes in file */
|
||||
uint16_t reserved1;
|
||||
uint16_t reserved2;
|
||||
uint32_t offbits;
|
||||
};
|
||||
|
||||
struct Bmpinfo {
|
||||
/* BITMAPINFOHEADER (40 bytes) */
|
||||
DWORD size; /* sizof struct */
|
||||
LONG width;
|
||||
LONG height;
|
||||
WORD planes;
|
||||
WORD bitcount;
|
||||
DWORD compression;
|
||||
DWORD sizeimage; /* 0 ok for uncompressed */
|
||||
LONG xpelspermeter;
|
||||
LONG ypelspermeter;
|
||||
DWORD clrused;
|
||||
DWORD clrimportant;
|
||||
uint32_t size; /* sizof struct */
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
uint16_t planes;
|
||||
uint16_t bitcount;
|
||||
uint32_t compression;
|
||||
uint32_t sizeimage; /* 0 ok for uncompressed */
|
||||
int32_t xpelspermeter;
|
||||
int32_t ypelspermeter;
|
||||
uint32_t clrused;
|
||||
uint32_t clrimportant;
|
||||
/* BITMAPV4INFOHEADER (108 bytes) */
|
||||
DWORD redmask;
|
||||
DWORD greenmask;
|
||||
DWORD bluemask;
|
||||
DWORD alphamask;
|
||||
DWORD cstype;
|
||||
LONG redX;
|
||||
LONG redY;
|
||||
LONG redZ;
|
||||
LONG greenX;
|
||||
LONG greenY;
|
||||
LONG greenZ;
|
||||
LONG blueX;
|
||||
LONG blueY;
|
||||
LONG blueZ;
|
||||
DWORD gammared;
|
||||
DWORD gammagreen;
|
||||
DWORD gammablue;
|
||||
uint32_t redmask;
|
||||
uint32_t greenmask;
|
||||
uint32_t bluemask;
|
||||
uint32_t alphamask;
|
||||
uint32_t cstype;
|
||||
int32_t redX;
|
||||
int32_t redY;
|
||||
int32_t redZ;
|
||||
int32_t greenX;
|
||||
int32_t greenY;
|
||||
int32_t greenZ;
|
||||
int32_t blueX;
|
||||
int32_t blueY;
|
||||
int32_t blueZ;
|
||||
uint32_t gammared;
|
||||
uint32_t gammagreen;
|
||||
uint32_t gammablue;
|
||||
/* BITMAPV5INFOHEADER (124 bytes) */
|
||||
DWORD intent;
|
||||
DWORD profiledata;
|
||||
DWORD profilesize;
|
||||
DWORD reserved;
|
||||
uint32_t intent;
|
||||
uint32_t profiledata;
|
||||
uint32_t profilesize;
|
||||
uint32_t reserved;
|
||||
|
||||
/* OS22XBITMAPHEADER */
|
||||
WORD resolution; /* = 0 */
|
||||
WORD orientation; /* = 0 */
|
||||
WORD halftone_alg;
|
||||
DWORD halftone_parm1;
|
||||
DWORD halftone_parm2;
|
||||
DWORD color_encoding; /* = 0 (RGB) */
|
||||
DWORD app_id;
|
||||
uint16_t resolution; /* = 0 */
|
||||
uint16_t orientation; /* = 0 */
|
||||
uint16_t halftone_alg;
|
||||
uint32_t halftone_parm1;
|
||||
uint32_t halftone_parm2;
|
||||
uint32_t color_encoding; /* = 0 (RGB) */
|
||||
uint32_t app_id;
|
||||
|
||||
/* internal only, not from file: */
|
||||
enum BmpInfoVer version;
|
||||
/* internal only, not from file: */
|
||||
enum BmpInfoVer version;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
|
||||
#define BMPLIB_LIB
|
||||
@@ -33,7 +34,6 @@
|
||||
#include "bmp-common.h"
|
||||
#include "huffman.h"
|
||||
#include "bmp-read.h"
|
||||
#include "reversebits.h"
|
||||
|
||||
|
||||
/*
|
||||
@@ -499,29 +499,58 @@ static inline bool s_read_rgb_pixel(BMPREAD_R rp, union Pixel *restrict px)
|
||||
* s_read_indexed_line
|
||||
* - 1/2/4/8 bits non-RLE indexed
|
||||
*******************************************************/
|
||||
static inline bool 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);
|
||||
struct Buffer32 {
|
||||
uint32_t buffer;
|
||||
int n;
|
||||
};
|
||||
|
||||
static inline bool s_buffer32_fill(BMPREAD_R rp, struct Buffer32 *restrict buf)
|
||||
{
|
||||
int byte;
|
||||
|
||||
memset(buf, 0, sizeof *buf);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (EOF == (byte = s_read_one_byte(rp))) {
|
||||
s_set_file_error(rp);
|
||||
return false;
|
||||
}
|
||||
buf->buffer <<= 8;
|
||||
buf->buffer |= byte;
|
||||
}
|
||||
buf->n = 32;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static inline uint32_t s_buffer32_bits(struct Buffer32 *restrict buf, int nbits)
|
||||
{
|
||||
uint32_t result;
|
||||
|
||||
assert(nbits < 32);
|
||||
|
||||
result = buf->buffer >> (32 - nbits);
|
||||
buf->buffer = (buf->buffer << nbits) & 0xffffffffUL;
|
||||
buf->n -= nbits;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void s_read_indexed_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
{
|
||||
int bits_used, buffer_size, x = 0, v;
|
||||
bool done = false;
|
||||
unsigned long buffer;
|
||||
size_t offs;
|
||||
int x = 0, v;
|
||||
bool done = false;
|
||||
struct Buffer32 buffer;
|
||||
size_t offs;
|
||||
|
||||
/* setting the buffer size to the alignment takes care of padding bytes */
|
||||
buffer_size = 32;
|
||||
/* the buffer size of 32 bits takes care of padding bytes */
|
||||
|
||||
while (!done && s_read_n_bytes(rp, buffer_size / 8, &buffer)) {
|
||||
while (!done && s_buffer32_fill(rp, &buffer)) {
|
||||
|
||||
bits_used = 0;
|
||||
while (bits_used < buffer_size) {
|
||||
while (buffer.n >= rp->ih->bitcount) {
|
||||
|
||||
/* 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;
|
||||
v = (int) s_buffer32_bits(&buffer, rp->ih->bitcount);
|
||||
|
||||
if (v >= rp->palette->numcolors) {
|
||||
v = rp->palette->numcolors - 1;
|
||||
@@ -537,6 +566,7 @@ static void s_read_indexed_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
line[offs+2] = rp->palette->color[v].blue;
|
||||
s_int_to_result_format(rp, 8, line + offs);
|
||||
}
|
||||
|
||||
if (++x == rp->width) {
|
||||
done = true;
|
||||
break; /* discarding rest of buffer == padding */
|
||||
@@ -547,51 +577,6 @@ static void s_read_indexed_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* s_read_n_bytes
|
||||
*******************************************************/
|
||||
|
||||
static inline bool s_read_n_bytes(BMPREAD_R rp, int n, unsigned long *restrict buff)
|
||||
{
|
||||
int byte;
|
||||
|
||||
*buff = 0;
|
||||
while (n--) {
|
||||
if (EOF == (byte = s_read_one_byte(rp))) {
|
||||
s_set_file_error(rp);
|
||||
return false;
|
||||
}
|
||||
*buff <<= 8;
|
||||
*buff |= byte;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* s_bits_from_buffer
|
||||
*******************************************************/
|
||||
|
||||
static inline unsigned long s_bits_from_buffer(unsigned long buf, int size,
|
||||
int nbits, int used_bits)
|
||||
{
|
||||
unsigned long mask;
|
||||
int shift;
|
||||
|
||||
shift = size - (nbits + used_bits);
|
||||
|
||||
mask = (1U << nbits) - 1;
|
||||
mask <<= shift;
|
||||
|
||||
buf &= mask;
|
||||
buf >>= shift;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* s_read_rle_line
|
||||
* - 4/8/24 bit RLE
|
||||
@@ -787,7 +772,7 @@ static void s_read_huffman_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
if (rp->hufbuf_len == 0)
|
||||
break;
|
||||
|
||||
if ((rp->hufbuf & 0x00ff) == 0) {
|
||||
if ((rp->hufbuf & 0xff000000UL) == 0) {
|
||||
if (!s_huff_skip_eol(rp)) {
|
||||
rp->truncated = true;
|
||||
break;
|
||||
@@ -837,11 +822,11 @@ static bool s_huff_skip_eol(BMPREAD_R rp)
|
||||
huff_fillbuf(rp);
|
||||
continue;
|
||||
}
|
||||
while ((rp->hufbuf & 0x0001) == 0) {
|
||||
rp->hufbuf >>= 1;
|
||||
while ((rp->hufbuf & 0x80000000UL) == 0) {
|
||||
rp->hufbuf <<= 1;
|
||||
rp->hufbuf_len--;
|
||||
}
|
||||
rp->hufbuf >>= 1;
|
||||
rp->hufbuf <<= 1;
|
||||
rp->hufbuf_len--;
|
||||
return true;
|
||||
}
|
||||
@@ -858,12 +843,12 @@ static bool s_huff_find_eol(BMPREAD_R rp)
|
||||
huff_fillbuf (rp);
|
||||
while (rp->hufbuf_len > 11)
|
||||
{
|
||||
if ((rp->hufbuf & 0x07ff) == 0) {
|
||||
rp->hufbuf >>= 11;
|
||||
if ((rp->hufbuf & 0xffe00000UL) == 0) {
|
||||
rp->hufbuf <<= 11;
|
||||
rp->hufbuf_len -= 11;
|
||||
return s_huff_skip_eol (rp);
|
||||
}
|
||||
rp->hufbuf >>= 1;
|
||||
rp->hufbuf <<= 1;
|
||||
rp->hufbuf_len -= 1;
|
||||
if (rp->hufbuf_len < 12)
|
||||
huff_fillbuf (rp);
|
||||
|
||||
211
bmp-read.c
211
bmp-read.c
@@ -95,7 +95,6 @@ static bool s_read_info_header(BMPREAD_R rp);
|
||||
static bool s_is_bmptype_supported(BMPREAD_R rp);
|
||||
static struct Palette* s_read_palette(BMPREAD_R rp);
|
||||
static bool s_read_colormasks(BMPREAD_R rp);
|
||||
static bool s_check_dimensions(BMPREAD_R rp);
|
||||
|
||||
API BMPRESULT bmpread_load_info(BMPHANDLE h)
|
||||
{
|
||||
@@ -107,7 +106,6 @@ API BMPRESULT bmpread_load_info(BMPHANDLE h)
|
||||
if (rp->getinfo_called)
|
||||
return rp->getinfo_return;
|
||||
|
||||
|
||||
if (!s_read_file_header(rp))
|
||||
goto abort;
|
||||
|
||||
@@ -135,14 +133,16 @@ API BMPRESULT bmpread_load_info(BMPHANDLE h)
|
||||
goto abort;
|
||||
|
||||
rp->width = (int) rp->ih->width;
|
||||
rp->height = (unsigned) rp->ih->height;
|
||||
|
||||
/* negative height flips the image vertically */
|
||||
if (rp->ih->height < 0) {
|
||||
rp->orientation = BMP_ORIENT_TOPDOWN;
|
||||
rp->height = - (int64_t) rp->ih->height;
|
||||
rp->height = (unsigned) (-(int64_t)rp->ih->height);
|
||||
} else {
|
||||
rp->height = (unsigned) rp->ih->height;
|
||||
}
|
||||
|
||||
|
||||
if (rp->ih->compression == BI_RLE4 ||
|
||||
rp->ih->compression == BI_RLE8 ||
|
||||
rp->ih->compression == BI_OS2_RLE24)
|
||||
@@ -176,7 +176,6 @@ API BMPRESULT bmpread_load_info(BMPHANDLE h)
|
||||
if (!(rp->palette = s_read_palette(rp)))
|
||||
goto abort;
|
||||
} else if (!rp->rle) { /* RGB */
|
||||
memset(&rp->cmask, 0, sizeof rp->cmask);
|
||||
if (!s_read_colormasks(rp))
|
||||
goto abort;
|
||||
|
||||
@@ -192,9 +191,6 @@ API BMPRESULT bmpread_load_info(BMPHANDLE h)
|
||||
if (!br_set_resultbits(rp))
|
||||
goto abort;
|
||||
|
||||
if (!s_check_dimensions(rp))
|
||||
goto abort;
|
||||
|
||||
if (rp->insanity_limit &&
|
||||
rp->result_size > rp->insanity_limit) {
|
||||
logerr(rp->log, "file is insanely large");
|
||||
@@ -704,30 +700,6 @@ static bool s_is_bmptype_supported_indexed(BMPREAD_R rp)
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* s_check_dimensions
|
||||
*****************************************************************************/
|
||||
|
||||
static bool s_check_dimensions(BMPREAD_R rp)
|
||||
{
|
||||
uint64_t npixels;
|
||||
size_t maxpixels;
|
||||
|
||||
npixels = (uint64_t) rp->width * rp->height;
|
||||
maxpixels = SIZE_MAX / rp->result_bytes_per_pixel;
|
||||
|
||||
if (npixels > maxpixels || rp->width < 1 || rp->height < 1 ||
|
||||
rp->height > INT32_MAX) {
|
||||
logerr(rp->log, "Invalid BMP dimensions (%dx%d)",
|
||||
(int) rp->ih->width, (int) rp->ih->height);
|
||||
rp->lasterr = BMP_ERR_DIMENSIONS;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* s_read_palette
|
||||
*****************************************************************************/
|
||||
@@ -826,6 +798,107 @@ static struct Palette* s_read_palette(BMPREAD_R rp)
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* br_set_resultbits
|
||||
*****************************************************************************/
|
||||
static bool s_check_dimensions(BMPREAD_R rp);
|
||||
|
||||
bool br_set_resultbits(BMPREAD_R rp)
|
||||
{
|
||||
int newbits, max_bits = 0, i;
|
||||
|
||||
if (!rp->ih->bitcount)
|
||||
return true;
|
||||
|
||||
switch (rp->result_format) {
|
||||
case BMP_FORMAT_FLOAT:
|
||||
if (rp->result_indexed) {
|
||||
logerr(rp->log, "Float is invalid number format for indexed image\n");
|
||||
rp->lasterr = BMP_ERR_FORMAT;
|
||||
return false;
|
||||
}
|
||||
newbits = 8 * sizeof (float);
|
||||
break;
|
||||
|
||||
case BMP_FORMAT_S2_13:
|
||||
if (rp->result_indexed) {
|
||||
logerr(rp->log, "s2.13 is invalid number format for indexed image\n");
|
||||
rp->lasterr = BMP_ERR_FORMAT;
|
||||
return false;
|
||||
}
|
||||
newbits = 16;
|
||||
break;
|
||||
|
||||
case BMP_FORMAT_INT:
|
||||
if (rp->ih->bitcount <= 8 || rp->rle)
|
||||
newbits = 8;
|
||||
else { /* RGB */
|
||||
for (i = 0; i < 4; i++) {
|
||||
max_bits = MAX(max_bits, rp->cmask.bits.value[i]);
|
||||
}
|
||||
newbits = 8;
|
||||
while (newbits < max_bits && newbits < 32) {
|
||||
newbits *= 2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logerr(rp->log, "Invalid number format %d\n", rp->result_format);
|
||||
rp->lasterr = BMP_ERR_FORMAT;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
if (newbits != rp->result_bitsperchannel) {
|
||||
rp->dim_queried_bitsperchannel = false;
|
||||
rp->dimensions_queried = false;
|
||||
}
|
||||
rp->result_bitsperchannel = newbits;
|
||||
rp->result_bits_per_pixel = rp->result_bitsperchannel * rp->result_channels;
|
||||
rp->result_bytes_per_pixel = rp->result_bits_per_pixel / 8;
|
||||
|
||||
if (!s_check_dimensions(rp))
|
||||
return false;
|
||||
|
||||
rp->result_size = (size_t) rp->width * rp->height * rp->result_bytes_per_pixel;
|
||||
|
||||
if (rp->getinfo_called) {
|
||||
if (rp->insanity_limit && rp->result_size > rp->insanity_limit) {
|
||||
if (rp->getinfo_return == BMP_RESULT_OK) {
|
||||
logerr(rp->log, "file is insanely large");
|
||||
rp->lasterr = BMP_ERR_INSANE;
|
||||
rp->getinfo_return = BMP_RESULT_INSANE;
|
||||
}
|
||||
} else if (rp->getinfo_return == BMP_RESULT_INSANE)
|
||||
rp->getinfo_return = BMP_RESULT_OK;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* s_check_dimensions
|
||||
*****************************************************************************/
|
||||
|
||||
static bool s_check_dimensions(BMPREAD_R rp)
|
||||
{
|
||||
uint64_t npixels;
|
||||
size_t maxpixels;
|
||||
|
||||
npixels = (uint64_t) rp->width * rp->height;
|
||||
maxpixels = SIZE_MAX / rp->result_bytes_per_pixel;
|
||||
|
||||
if (npixels > maxpixels || rp->width < 1 || rp->height < 1 || rp->height > INT32_MAX) {
|
||||
logerr(rp->log, "Invalid BMP dimensions (%dx%u)", rp->width, rp->height);
|
||||
rp->lasterr = BMP_ERR_DIMENSIONS;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* s_read_colormasks
|
||||
*****************************************************************************/
|
||||
@@ -892,80 +965,6 @@ static bool s_read_colormasks(BMPREAD_R rp)
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* br_set_resultbits
|
||||
*****************************************************************************/
|
||||
|
||||
bool br_set_resultbits(BMPREAD_R rp)
|
||||
{
|
||||
int newbits, max_bits = 0, i;
|
||||
|
||||
if (!rp->ih->bitcount)
|
||||
return true;
|
||||
|
||||
switch (rp->result_format) {
|
||||
case BMP_FORMAT_FLOAT:
|
||||
if (rp->result_indexed) {
|
||||
logerr(rp->log, "Float is invalid number format for indexed image\n");
|
||||
rp->lasterr = BMP_ERR_FORMAT;
|
||||
return false;
|
||||
}
|
||||
newbits = 8 * sizeof (float);
|
||||
break;
|
||||
|
||||
case BMP_FORMAT_S2_13:
|
||||
if (rp->result_indexed) {
|
||||
logerr(rp->log, "s2.13 is invalid number format for indexed image\n");
|
||||
rp->lasterr = BMP_ERR_FORMAT;
|
||||
return false;
|
||||
}
|
||||
newbits = 16;
|
||||
break;
|
||||
|
||||
case BMP_FORMAT_INT:
|
||||
if (rp->ih->bitcount <= 8 || rp->rle)
|
||||
newbits = 8;
|
||||
else { /* RGB */
|
||||
for (i = 0; i < 4; i++) {
|
||||
max_bits = MAX(max_bits, rp->cmask.bits.value[i]);
|
||||
}
|
||||
newbits = 8;
|
||||
while (newbits < max_bits && newbits < 32) {
|
||||
newbits *= 2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logerr(rp->log, "Invalid number format %d\n", rp->result_format);
|
||||
rp->lasterr = BMP_ERR_FORMAT;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
if (newbits != rp->result_bitsperchannel) {
|
||||
rp->dim_queried_bitsperchannel = false;
|
||||
rp->dimensions_queried = false;
|
||||
}
|
||||
rp->result_bitsperchannel = newbits;
|
||||
rp->result_bits_per_pixel = rp->result_bitsperchannel * rp->result_channels;
|
||||
rp->result_bytes_per_pixel = rp->result_bits_per_pixel / 8;
|
||||
|
||||
rp->result_size = (size_t) rp->width * rp->height * rp->result_bytes_per_pixel;
|
||||
if (rp->getinfo_called) {
|
||||
if (rp->insanity_limit && rp->result_size > rp->insanity_limit) {
|
||||
if (rp->getinfo_return == BMP_RESULT_OK) {
|
||||
logerr(rp->log, "file is insanely large");
|
||||
rp->lasterr = BMP_ERR_INSANE;
|
||||
rp->getinfo_return = BMP_RESULT_INSANE;
|
||||
}
|
||||
} else if (rp->getinfo_return == BMP_RESULT_INSANE)
|
||||
rp->getinfo_return = BMP_RESULT_OK;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* s_read_masks_from_bitfields
|
||||
*****************************************************************************/
|
||||
|
||||
57
bmp-write.c
57
bmp-write.c
@@ -67,6 +67,7 @@ API BMPHANDLE bmpwrite_new(FILE *file)
|
||||
wp->rle_requested = BMP_RLE_NONE;
|
||||
wp->outorientation = BMP_ORIENT_BOTTOMUP;
|
||||
wp->source_format = BMP_FORMAT_INT;
|
||||
wp->huffman_fg_idx = 1;
|
||||
|
||||
if (!(wp->log = logcreate()))
|
||||
goto abort;
|
||||
@@ -346,8 +347,8 @@ API BMPRESULT bmpwrite_set_resolution(BMPHANDLE h, int xdpi, int ydpi)
|
||||
if (s_check_already_saved(wp))
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
wp->ih->xpelspermeter = (LONG) (100.0 / 2.54 * xdpi + 0.5);
|
||||
wp->ih->ypelspermeter = (LONG) (100.0 / 2.54 * ydpi + 0.5);
|
||||
wp->ih->xpelspermeter = (int32_t) (100.0 / 2.54 * xdpi + 0.5);
|
||||
wp->ih->ypelspermeter = (int32_t) (100.0 / 2.54 * ydpi + 0.5);
|
||||
|
||||
return BMP_RESULT_OK;
|
||||
}
|
||||
@@ -439,6 +440,25 @@ API BMPRESULT bmpwrite_set_64bit(BMPHANDLE h)
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* bmpwrite_set_huffman_img_fg_idx
|
||||
*****************************************************************************/
|
||||
|
||||
API BMPRESULT bmpwrite_set_huffman_img_fg_idx(BMPHANDLE h, int idx)
|
||||
{
|
||||
BMPWRITE wp;
|
||||
|
||||
if (!(wp = cm_write_handle(h)))
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
if (s_check_already_saved(wp))
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
wp->huffman_fg_idx = !!idx;
|
||||
|
||||
return BMP_RESULT_OK;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* s_check_already_saved
|
||||
@@ -690,10 +710,10 @@ static void s_decide_outformat(BMPWRITE_R wp)
|
||||
wp->outbytes_per_pixel = wp->ih->bitcount / 8;
|
||||
|
||||
if (wp->ih->version >= BMPINFO_V4 && !wp->out64bit) {
|
||||
wp->ih->redmask = (DWORD) (wp->cmask.mask.red << wp->cmask.shift.red);
|
||||
wp->ih->greenmask = (DWORD) (wp->cmask.mask.green << wp->cmask.shift.green);
|
||||
wp->ih->bluemask = (DWORD) (wp->cmask.mask.blue << wp->cmask.shift.blue);
|
||||
wp->ih->alphamask = (DWORD) (wp->cmask.mask.alpha << wp->cmask.shift.alpha);
|
||||
wp->ih->redmask = (uint32_t) (wp->cmask.mask.red << wp->cmask.shift.red);
|
||||
wp->ih->greenmask = (uint32_t) (wp->cmask.mask.green << wp->cmask.shift.green);
|
||||
wp->ih->bluemask = (uint32_t) (wp->cmask.mask.blue << wp->cmask.shift.blue);
|
||||
wp->ih->alphamask = (uint32_t) (wp->cmask.mask.alpha << wp->cmask.shift.alpha);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -703,7 +723,7 @@ static void s_decide_outformat(BMPWRITE_R wp)
|
||||
filesize = bitmapsize + BMPFHSIZE + wp->ih->size + wp->palette_size;
|
||||
|
||||
wp->fh->type = 0x4d42; /* "BM" */
|
||||
wp->fh->size = (DWORD) ((wp->rle || filesize > UINT32_MAX) ? 0 : filesize);
|
||||
wp->fh->size = (uint32_t) ((wp->rle || filesize > UINT32_MAX) ? 0 : filesize);
|
||||
wp->fh->offbits = BMPFHSIZE + wp->ih->size + wp->palette_size;
|
||||
|
||||
wp->ih->width = wp->width;
|
||||
@@ -712,7 +732,7 @@ static void s_decide_outformat(BMPWRITE_R wp)
|
||||
else
|
||||
wp->ih->height = -wp->height;
|
||||
wp->ih->planes = 1;
|
||||
wp->ih->sizeimage = (DWORD) ((wp->rle || bitmapsize > UINT32_MAX) ? 0 : bitmapsize);
|
||||
wp->ih->sizeimage = (uint32_t) ((wp->rle || bitmapsize > UINT32_MAX) ? 0 : bitmapsize);
|
||||
}
|
||||
|
||||
|
||||
@@ -908,7 +928,7 @@ static bool s_save_header(BMPWRITE_R wp)
|
||||
* We ignore any errors quietly, as there's nothing we
|
||||
* can do and most (all?) readers ignore those sizes in
|
||||
* the header, anyway. Same goes for file/bitmap sizes
|
||||
* which are too big for the respective fields.
|
||||
* when they are too big for the respective fields.
|
||||
*****************************************************************************/
|
||||
|
||||
static bool s_try_saving_image_size(BMPWRITE_R wp)
|
||||
@@ -1023,7 +1043,7 @@ static bool s_save_line_rle(BMPWRITE_R wp, const unsigned char *line)
|
||||
{
|
||||
int i, j, k, x, l, dx, outbyte = 0;
|
||||
bool even;
|
||||
int small_number, minlen = 0;
|
||||
int small_number = 0, minlen = 0;
|
||||
|
||||
switch (wp->rle) {
|
||||
case 4:
|
||||
@@ -1220,7 +1240,7 @@ static bool s_save_line_huff(BMPWRITE_R wp, const unsigned char *line)
|
||||
|
||||
while (x < wp->width) {
|
||||
len = 0;
|
||||
while ((len < wp->width - x) && ((!!line[x + len]) == black))
|
||||
while ((len < wp->width - x) && ((!!line[x + len]) == (black ^ !wp->huffman_fg_idx)))
|
||||
len++;
|
||||
if (!huff_encode(wp, len, black))
|
||||
goto abort;
|
||||
@@ -1395,9 +1415,9 @@ static inline uint16_t float_to_s2_13(double d)
|
||||
|
||||
d = round(d * 8192.0);
|
||||
|
||||
if (d >= 32768.0)
|
||||
if (d >= 32767.0)
|
||||
s2_13 = 0x7fff; /* max positive value */
|
||||
else if (d < -32768.0)
|
||||
else if (d <= -32768.0)
|
||||
s2_13 = 0x8000; /* min negative value */
|
||||
else
|
||||
s2_13 = (uint16_t) (0xffff & (int)d);
|
||||
@@ -1413,11 +1433,16 @@ static inline uint16_t float_to_s2_13(double d)
|
||||
|
||||
static bool s_write_palette(BMPWRITE_R wp)
|
||||
{
|
||||
int i, c;
|
||||
int i, c;
|
||||
bool reverse = false;
|
||||
|
||||
if (wp->rle == 1)
|
||||
reverse = !wp->huffman_fg_idx;
|
||||
|
||||
for (i = 0; i < wp->palette->numcolors; i++) {
|
||||
int idx = reverse ? wp->palette->numcolors - i - 1 : i;
|
||||
for (c = 0; c < 3; c++) {
|
||||
if (EOF == s_write_one_byte(wp->palette->color[i].value[2-c], wp))
|
||||
if (EOF == s_write_one_byte(wp->palette->color[idx].value[2-c], wp))
|
||||
return false;
|
||||
}
|
||||
if (EOF == s_write_one_byte(0, wp))
|
||||
@@ -1493,7 +1518,7 @@ static bool s_write_bmp_info_header(BMPWRITE_R wp)
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
for (int i = 0; (DWORD) i < wp->ih->size - 40; i++) {
|
||||
for (int i = 0; (uint32_t) i < wp->ih->size - 40; i++) {
|
||||
if (EOF == putc(0, wp->file))
|
||||
return false;
|
||||
wp->bytes_written++;
|
||||
|
||||
11
bmplib.h
11
bmplib.h
@@ -124,12 +124,10 @@ typedef enum Bmpconv64 BMPCONV64;
|
||||
/*
|
||||
* BMP info header versions
|
||||
*
|
||||
* There doesn't seem to be consensus on whether the
|
||||
* BITMAPINFOHEADER is version 1 (with the two Adobe
|
||||
* extensions being v2 and v3) or version 3 (with the
|
||||
* older BITMAPCIREHEADER and OS22XBITMAPHEADER being
|
||||
* v1 and v2).
|
||||
* I am going with BITMAPINFOHEADER = v3
|
||||
* There doesn't seem to be consensus on whether the BITMAPINFOHEADER is
|
||||
* version 1 (with the two Adobe extensions being v2 and v3) or version 3
|
||||
* (with the older BITMAPCOREHEADER and OS22XBITMAPHEADER being v1 and v2).
|
||||
* I am going with BITMAPINFOHEADER = v3.
|
||||
*/
|
||||
enum BmpInfoVer {
|
||||
BMPINFO_CORE_OS21 = 1, /* 12 bytes */
|
||||
@@ -268,6 +266,7 @@ APIDECL BMPRESULT bmpwrite_allow_rle24(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpwrite_set_rle(BMPHANDLE h, BMPRLETYPE type);
|
||||
APIDECL BMPRESULT bmpwrite_set_orientation(BMPHANDLE h, BMPORIENT orientation);
|
||||
APIDECL BMPRESULT bmpwrite_set_64bit(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpwrite_set_huffman_img_fg_idx(BMPHANDLE h, int idx);
|
||||
|
||||
APIDECL BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image);
|
||||
APIDECL BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line);
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
/* bmplib - gen-reversebits.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 <stdio.h>
|
||||
|
||||
static int reverse(int val, int bits)
|
||||
{
|
||||
int mask;
|
||||
|
||||
bits /= 2;
|
||||
if (bits == 0)
|
||||
return val;
|
||||
mask = (1 << bits) - 1;
|
||||
return (reverse(val & mask, bits) << bits) | reverse(val >> bits, bits);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int reversed, i;
|
||||
FILE *file;
|
||||
const char *src_name = "reversebits.h";
|
||||
const char *this_name = "gen-reversebits.c";
|
||||
|
||||
if (argc == 2) {
|
||||
if (!(file = fopen(argv[1], "w"))) {
|
||||
perror(argv[1]);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
file = stdout;
|
||||
}
|
||||
|
||||
fprintf(file, "/* bmplib - %s\n", src_name);
|
||||
fprintf(file, " *\n"
|
||||
" * Copyright (c) 2024, Rupert Weber.\n"
|
||||
" *\n"
|
||||
" * This file is part of bmplib.\n"
|
||||
" * bmplib is free software: you can redistribute it and/or modify\n"
|
||||
" * it under the terms of the GNU Lesser General Public License as\n"
|
||||
" * published by the Free Software Foundation, either version 3 of\n"
|
||||
" * the License, or (at your option) any later version.\n"
|
||||
" *\n");
|
||||
fprintf(file, " * This program is distributed in the hope that it will be useful,\n"
|
||||
" * but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
|
||||
" * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
|
||||
" * GNU Lesser General Public License for more details.\n"
|
||||
" *\n"
|
||||
" * You should have received a copy of the GNU Lesser General Public\n"
|
||||
" * License along with this library.\n"
|
||||
" * If not, see <https://www.gnu.org/licenses/>\n"
|
||||
" */\n\n");
|
||||
fprintf(file, "/* This file is auto-generated by %s */\n\n\n", this_name);
|
||||
|
||||
fprintf(file, "static const unsigned char reversebits[] = {\n\t");
|
||||
for (i = 0; i < 256; i++) {
|
||||
reversed = reverse(i, 8);
|
||||
fprintf(file, "0x%02x, ", reversed);
|
||||
if ((i + 1) % 8 == 0 && i < 255)
|
||||
fprintf(file, "\n\t");
|
||||
}
|
||||
fprintf(file, "\n};\n");
|
||||
return 0;
|
||||
}
|
||||
10
huffman.c
10
huffman.c
@@ -29,7 +29,6 @@
|
||||
#include "bmplib.h"
|
||||
#include "logging.h"
|
||||
#include "bmp-common.h"
|
||||
#include "reversebits.h"
|
||||
#include "huffman.h"
|
||||
#include "huffman-codes.h"
|
||||
|
||||
@@ -63,7 +62,7 @@ int huff_decode(BMPREAD_R rp, int black)
|
||||
}
|
||||
|
||||
result += nodebuffer[idx].value;
|
||||
rp->hufbuf >>= bits_used;
|
||||
rp->hufbuf <<= bits_used;
|
||||
rp->hufbuf_len -= bits_used;
|
||||
|
||||
} while (nodebuffer[idx].makeup && result < INT_MAX - 2560);
|
||||
@@ -85,12 +84,12 @@ static int s_findnode(uint32_t bits, int nbits, bool black, int *found)
|
||||
idx = black ? blackroot : whiteroot;
|
||||
|
||||
while (idx != -1 && !nodebuffer[idx].terminal && bits_used < nbits) {
|
||||
if (bits & 1)
|
||||
if (bits & 0x80000000UL)
|
||||
idx = nodebuffer[idx].r;
|
||||
else
|
||||
idx = nodebuffer[idx].l;
|
||||
bits_used++;
|
||||
bits >>= 1;
|
||||
bits <<= 1;
|
||||
}
|
||||
*found = idx;
|
||||
return idx != -1 ? bits_used : 0;
|
||||
@@ -110,8 +109,7 @@ void huff_fillbuf(BMPREAD_R rp)
|
||||
if (EOF == (byte = getc(rp->file)))
|
||||
break;
|
||||
rp->bytes_read++;
|
||||
byte = reversebits[byte];
|
||||
rp->hufbuf |= ((uint32_t)byte) << rp->hufbuf_len;
|
||||
rp->hufbuf |= (uint32_t)byte << (24 - rp->hufbuf_len);
|
||||
rp->hufbuf_len += 8;
|
||||
}
|
||||
}
|
||||
|
||||
14
meson.build
14
meson.build
@@ -1,4 +1,4 @@
|
||||
project('bmplib', 'c', default_options: ['c_std=c11'], version: '1.7.3')
|
||||
project('bmplib', 'c', default_options: ['c_std=c11'], version: '1.7.4')
|
||||
|
||||
cc = meson.get_compiler('c')
|
||||
|
||||
@@ -7,6 +7,8 @@ add_project_arguments('-pedantic', language : 'c')
|
||||
add_project_arguments('-fvisibility=hidden', language: 'c')
|
||||
if get_option('buildtype') == 'debug'
|
||||
add_project_arguments('-DDEBUG', language: 'c')
|
||||
elif get_option('buildtype') == 'release'
|
||||
add_project_arguments('-DNDEBUG', language: 'c')
|
||||
endif
|
||||
|
||||
m_dep = cc.find_library('m', required : false)
|
||||
@@ -37,22 +39,15 @@ elif cc.sizeof('int') < 4
|
||||
error('sizeof(int) must be at least 32 bit.')
|
||||
endif
|
||||
|
||||
|
||||
gen_huffman = executable('gen-huffman', 'gen-huffman.c')
|
||||
huff_codes = custom_target('huffman-codes.h',
|
||||
output: 'huffman-codes.h',
|
||||
command: [gen_huffman, '@OUTPUT@'],
|
||||
)
|
||||
|
||||
gen_reversebits = executable('gen-reversebits', 'gen-reversebits.c')
|
||||
reversebits = custom_target('reversebits.h',
|
||||
output: 'reversebits.h',
|
||||
command: [gen_reversebits, '@OUTPUT@'],
|
||||
)
|
||||
|
||||
|
||||
bmplib = shared_library('bmp',
|
||||
[bmplib_sources, huff_codes[0], reversebits[0]],
|
||||
[bmplib_sources, huff_codes[0]],
|
||||
version: meson.project_version(),
|
||||
install: true,
|
||||
dependencies: m_dep,
|
||||
@@ -65,4 +60,3 @@ pkg_mod.generate(libraries: bmplib,
|
||||
filebase: 'libbmp',
|
||||
description: 'Library for reading/writing Windows BMP files.',
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user