mirror of
https://github.com/rupertwh/bmplib.git
synced 2026-04-10 16:22:40 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91066fc509 | ||
|
|
de7004a158 | ||
|
|
7523138fbb | ||
|
|
592605e06f | ||
|
|
eeae2205c5 | ||
|
|
2d7763de7f | ||
|
|
7513352a7b | ||
|
|
ea7b93ce64 |
38
API-full.md
38
API-full.md
@@ -1,4 +1,4 @@
|
||||
# Rupert's bmplib -- Full API Description (v1.7.1)
|
||||
# Rupert's bmplib -- Full API Description (v1.7.5)
|
||||
|
||||
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.
|
||||
|
||||
@@ -564,6 +564,42 @@ const char* bmp_version(void)
|
||||
|
||||
Returns a zero-terminated character string containing the version of bmplib.
|
||||
|
||||
### bmp_set_huffman_t4black_value()
|
||||
|
||||
```
|
||||
BMPRESULT bmp_set_huffman_t4black_value(BMPHANDLE h, int blackidx)
|
||||
```
|
||||
|
||||
(not to be confused with `bmpwrite_set_huffman_img_fg_idx()`, which serves an
|
||||
entirely different purpose, see above.)
|
||||
|
||||
ITU-T T.4 defines 'black' and 'white' pixel sequences (referring to fore- and
|
||||
background, respectively), but it doesn't prescribe which of those is
|
||||
represented by 0 or 1. That would have to be defined by a BMP specification,
|
||||
but documentation on Huffman BMPs is close to non-existent.
|
||||
|
||||
Current consensus seems to be that 'black' is 1, i.e. indexing the second
|
||||
color in the palette, and 'white' is 0, i.e. indexing the first color. This
|
||||
is the default for bmplib.
|
||||
|
||||
In case that's wrong (in fact it's not even clear if there is a right and a
|
||||
wrong), `bmp_set_huffman_t4black_value()` can be used to set the pixel value
|
||||
of 'black' to either 0 or 1 (and white to the respective opposite).
|
||||
|
||||
Can be used both for reading and writing BMPs.
|
||||
|
||||
Changing this value will invert the image colors!
|
||||
|
||||
Reasons to use this function:
|
||||
|
||||
- You know that bmplib's default of 'black'=1 is wrong, and you want to set it
|
||||
to 0. (In that case, please also drop a note on github.)
|
||||
- You don't care either way, but you want to be sure to get consistent
|
||||
behaviour, in case bmplib's default is ever changed in light of new
|
||||
information/documentation.
|
||||
- You need to interface with other software that you know assumes 'black'=0.
|
||||
|
||||
|
||||
## 4. Data types and constants
|
||||
|
||||
#### `BMPHANDLE`
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
Download [bmplib on github](https://github.com/rupertwh/bmplib).
|
||||
|
||||
## Current status (v1.7.1):
|
||||
## Current status (v1.7.5):
|
||||
### Reading BMP files:
|
||||
- 16/24/32 bit RGB(A) with any bits/channel combination
|
||||
(BI_RGB, BI_BITFIELDS, BI_ALPHABITFIELDS).
|
||||
|
||||
62
bmp-common.c
62
bmp-common.c
@@ -36,13 +36,6 @@
|
||||
#include "bmp-write.h"
|
||||
|
||||
|
||||
struct Bmphandle {
|
||||
struct {
|
||||
uint32_t magic;
|
||||
LOG log;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
@@ -62,10 +55,10 @@ API const char* bmp_version(void)
|
||||
|
||||
API const char* bmp_errmsg(BMPHANDLE h)
|
||||
{
|
||||
if (!(h && (h->magic == HMAGIC_READ || h->magic == HMAGIC_WRITE)))
|
||||
if (!(h && (h->common.magic == HMAGIC_READ || h->common.magic == HMAGIC_WRITE)))
|
||||
return "BMPHANDLE is NULL or invalid";
|
||||
|
||||
return logmsg(h->log);
|
||||
return logmsg(h->common.log);
|
||||
}
|
||||
|
||||
|
||||
@@ -79,17 +72,17 @@ API BMPRESULT bmp_set_number_format(BMPHANDLE h, enum BmpFormat format)
|
||||
if (!h)
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
switch (h->magic) {
|
||||
switch (h->common.magic) {
|
||||
case HMAGIC_READ:
|
||||
return br_set_number_format((BMPREAD)(void*)h, format);
|
||||
return br_set_number_format(&h->read, format);
|
||||
|
||||
case HMAGIC_WRITE:
|
||||
return bw_set_number_format((BMPWRITE)(void*)h, format);
|
||||
return bw_set_number_format(&h->write, format);
|
||||
|
||||
default:
|
||||
#ifdef DEBUG
|
||||
printf("bmp_set_number_format() called with invalid handle (0x%04x)\n",
|
||||
(unsigned int) h->magic);
|
||||
(unsigned int) h->common.magic);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@@ -98,6 +91,25 @@ API BMPRESULT bmp_set_number_format(BMPHANDLE h, enum BmpFormat format)
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* bmp_set_huffman_t4black_value
|
||||
*******************************************************/
|
||||
|
||||
API BMPRESULT bmp_set_huffman_t4black_value(BMPHANDLE h, int blackidx)
|
||||
{
|
||||
if (!h)
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
if (!(h->common.magic == HMAGIC_READ || h->common.magic == HMAGIC_WRITE))
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
h->common.huffman_black_is_zero = !blackidx;
|
||||
|
||||
return BMP_RESULT_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************************************
|
||||
* bmp_free
|
||||
*******************************************************/
|
||||
@@ -107,18 +119,18 @@ API void bmp_free(BMPHANDLE h)
|
||||
if (!h)
|
||||
return;
|
||||
|
||||
switch (h->magic) {
|
||||
switch (h->common.magic) {
|
||||
case HMAGIC_READ:
|
||||
br_free((BMPREAD)(void*)h);
|
||||
br_free(&h->read);
|
||||
break;
|
||||
case HMAGIC_WRITE:
|
||||
bw_free((BMPWRITE)(void*)h);
|
||||
bw_free(&h->write);
|
||||
break;
|
||||
|
||||
default:
|
||||
#ifdef DEBUG
|
||||
printf("bmp_free() called with invalid handle (0x%04x)\n",
|
||||
(unsigned int) h->magic);
|
||||
(unsigned int) h->common.magic);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@@ -132,10 +144,8 @@ API void bmp_free(BMPHANDLE h)
|
||||
|
||||
BMPREAD cm_read_handle(BMPHANDLE h)
|
||||
{
|
||||
BMPREAD rp = (BMPREAD)(void*)h;
|
||||
|
||||
if (rp && rp->magic == HMAGIC_READ)
|
||||
return rp;
|
||||
if (h && h->common.magic == HMAGIC_READ)
|
||||
return &h->read;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -147,10 +157,8 @@ BMPREAD cm_read_handle(BMPHANDLE h)
|
||||
|
||||
BMPWRITE cm_write_handle(BMPHANDLE h)
|
||||
{
|
||||
BMPWRITE wp = (BMPWRITE)(void*)h;
|
||||
|
||||
if (wp && wp->magic == HMAGIC_WRITE)
|
||||
return wp;
|
||||
if (h && h->common.magic == HMAGIC_WRITE)
|
||||
return &h->write;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -168,10 +176,10 @@ bool cm_gobble_up(BMPREAD_R rp, int count)
|
||||
if (EOF == getc(rp->file)) {
|
||||
if (feof(rp->file)) {
|
||||
rp->lasterr = BMP_ERR_TRUNCATED;
|
||||
logerr(rp->log, "unexpected end of file");
|
||||
logerr(rp->c.log, "unexpected end of file");
|
||||
} else {
|
||||
rp->lasterr = BMP_ERR_FILEIO;
|
||||
logsyserr(rp->log, "error reading from file");
|
||||
logsyserr(rp->c.log, "error reading from file");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
49
bmp-common.h
49
bmp-common.h
@@ -82,28 +82,29 @@ struct Colormask {
|
||||
} maxval;
|
||||
};
|
||||
|
||||
typedef struct Bmpread *BMPREAD;
|
||||
typedef struct Bmpwrite *BMPWRITE;
|
||||
typedef struct Bmpread *restrict BMPREAD_R;
|
||||
typedef struct Bmpwrite *restrict BMPWRITE_R;
|
||||
|
||||
struct Palette {
|
||||
int numcolors;
|
||||
union Pixel color[1];
|
||||
};
|
||||
|
||||
|
||||
struct Bmpcommon {
|
||||
uint32_t magic;
|
||||
LOG log;
|
||||
bool huffman_black_is_zero; /* defaults to false */
|
||||
};
|
||||
|
||||
|
||||
struct Bmpread {
|
||||
struct {
|
||||
uint32_t magic;
|
||||
LOG log;
|
||||
};
|
||||
struct Bmpcommon c;
|
||||
FILE *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;
|
||||
unsigned height;
|
||||
int height;
|
||||
enum BmpOrient orientation;
|
||||
bool has_alpha; /* original BMP has alpha channel */
|
||||
enum BmpUndefined undefined_mode;
|
||||
@@ -154,10 +155,7 @@ struct Bmpread {
|
||||
|
||||
|
||||
struct Bmpwrite {
|
||||
struct {
|
||||
uint32_t magic;
|
||||
LOG log;
|
||||
};
|
||||
struct Bmpcommon c;
|
||||
FILE *file;
|
||||
struct Bmpfile *fh;
|
||||
struct Bmpinfo *ih;
|
||||
@@ -167,9 +165,11 @@ struct Bmpwrite {
|
||||
int source_channels;
|
||||
int source_bitsperchannel;
|
||||
int source_bytes_per_pixel;
|
||||
int source_format;
|
||||
enum BmpFormat source_format;
|
||||
struct Palette *palette;
|
||||
int palette_size; /* sizeof palette in bytes */
|
||||
unsigned char *iccprofile;
|
||||
int iccprofile_size;
|
||||
/* output */
|
||||
size_t bytes_written;
|
||||
size_t bytes_written_before_bitdata;
|
||||
@@ -198,6 +198,18 @@ struct Bmpwrite {
|
||||
};
|
||||
|
||||
|
||||
union Bmphandle {
|
||||
struct Bmpcommon common;
|
||||
struct Bmpread read;
|
||||
struct Bmpwrite write;
|
||||
};
|
||||
|
||||
|
||||
typedef struct Bmpread *BMPREAD;
|
||||
typedef struct Bmpwrite *BMPWRITE;
|
||||
typedef struct Bmpread *restrict BMPREAD_R;
|
||||
typedef struct Bmpwrite *restrict BMPWRITE_R;
|
||||
|
||||
|
||||
bool cm_all_lessoreq_int(int limit, int n, ...);
|
||||
bool cm_all_equal_int(int n, ...);
|
||||
@@ -248,6 +260,7 @@ int16_t s16_from_le(const unsigned char *buf);
|
||||
#define BMPIHSIZE_V3 40
|
||||
#define BMPIHSIZE_V4 108
|
||||
#define BMPIHSIZE_OS22 64
|
||||
#define BMPIHSIZE_V5 124
|
||||
|
||||
struct Bmpfile {
|
||||
uint16_t type; /* "BM" */
|
||||
@@ -307,7 +320,9 @@ struct Bmpinfo {
|
||||
enum BmpInfoVer version;
|
||||
};
|
||||
|
||||
#define IH_PROFILEDATA_OFFSET (14L + 112L)
|
||||
|
||||
#define MAX_ICCPROFILE_SIZE (1UL << 20)
|
||||
|
||||
|
||||
#define BI_RGB 0
|
||||
@@ -334,3 +349,9 @@ struct Bmpinfo {
|
||||
#define LCS_WINDOWS_COLOR_SPACE 0x57696e20 /* 'Win ' */
|
||||
#define PROFILE_LINKED 0x4c494e4b /* 'LINK' */
|
||||
#define PROFILE_EMBEDDED 0x4d424544 /* 'MBED' */
|
||||
|
||||
|
||||
#define LCS_GM_ABS_COLORIMETRIC 8
|
||||
#define LCS_GM_BUSINESS 1
|
||||
#define LCS_GM_GRAPHICS 2
|
||||
#define LCS_GM_IMAGES 3
|
||||
|
||||
@@ -105,7 +105,7 @@ API BMPRESULT bmpread_load_line(BMPHANDLE h, unsigned char **restrict buffer)
|
||||
if (!(rp = cm_read_handle(h)))
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
logreset(rp->log); /* otherwise we might accumulate thousands */
|
||||
logreset(rp->c.log); /* otherwise we might accumulate thousands */
|
||||
/* of log entries with large corrupt images */
|
||||
|
||||
return s_load_image_or_line(rp, buffer, true);
|
||||
@@ -126,31 +126,31 @@ static BMPRESULT s_load_image_or_line(BMPREAD_R rp, unsigned char **restrict buf
|
||||
|
||||
if (!(rp->getinfo_called && (rp->getinfo_return == BMP_RESULT_OK))) {
|
||||
if (rp->getinfo_return == BMP_RESULT_INSANE) {
|
||||
logerr(rp->log, "trying to load insanley large image");
|
||||
logerr(rp->c.log, "trying to load insanley large image");
|
||||
return BMP_RESULT_INSANE;
|
||||
}
|
||||
logerr(rp->log, "getinfo had failed, cannot load image");
|
||||
logerr(rp->c.log, "getinfo had failed, cannot load image");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
if (rp->image_loaded) {
|
||||
logerr(rp->log, "Cannot load image more than once!");
|
||||
logerr(rp->c.log, "Cannot load image more than once!");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
if (rp->line_by_line && !line_by_line) {
|
||||
logerr(rp->log, "Image is being loaded line-by-line. "
|
||||
logerr(rp->c.log, "Image is being loaded line-by-line. "
|
||||
"Cannot switch to full image.");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
if (!rp->dimensions_queried) {
|
||||
logerr(rp->log, "must query dimensions before loading image");
|
||||
logerr(rp->c.log, "must query dimensions before loading image");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
if (!buffer) {
|
||||
logerr(rp->log, "buffer pointer is NULL");
|
||||
logerr(rp->c.log, "buffer pointer is NULL");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ static BMPRESULT s_load_image_or_line(BMPREAD_R rp, unsigned char **restrict buf
|
||||
buffer_size = rp->result_size;
|
||||
if (!*buffer) { /* no buffer supplied, we will allocate one */
|
||||
if (!(*buffer = malloc(buffer_size))) {
|
||||
logsyserr(rp->log, "allocating result buffer");
|
||||
logsyserr(rp->c.log, "allocating result buffer");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
rp->we_allocated_buffer = true;
|
||||
@@ -176,12 +176,12 @@ static BMPRESULT s_load_image_or_line(BMPREAD_R rp, unsigned char **restrict buf
|
||||
|
||||
if (!rp->line_by_line) { /* either whole image or first line */
|
||||
if (rp->bytes_read > rp->fh->offbits) {
|
||||
logerr(rp->log, "Corrupt file");
|
||||
logerr(rp->c.log, "Corrupt file");
|
||||
goto abort;
|
||||
}
|
||||
/* skip to actual bitmap data: */
|
||||
if (!cm_gobble_up(rp, rp->fh->offbits - rp->bytes_read)) {
|
||||
logerr(rp->log, "while seeking start of bitmap data");
|
||||
logerr(rp->c.log, "while seeking start of bitmap data");
|
||||
goto abort;
|
||||
}
|
||||
rp->bytes_read += rp->fh->offbits - rp->bytes_read;
|
||||
@@ -227,7 +227,7 @@ static void s_read_whole_image(BMPREAD_R rp, unsigned char *restrict image)
|
||||
|
||||
linesize = (size_t) rp->width * rp->result_bytes_per_pixel;
|
||||
|
||||
for (y = 0; y < (int) rp->height; y += yoff) {
|
||||
for (y = 0; y < rp->height; y += yoff) {
|
||||
real_y = (rp->orientation == BMP_ORIENT_TOPDOWN) ? y : rp->height-1-y;
|
||||
s_read_one_line(rp, image + real_y * linesize);
|
||||
if (rp->rle_eof || s_stopping_error(rp))
|
||||
@@ -264,7 +264,7 @@ static void s_read_one_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
}
|
||||
|
||||
if (!(rp->rle_eof || s_stopping_error(rp))) {
|
||||
if (yoff > (int) rp->height - rp->lbl_file_y) {
|
||||
if (yoff > rp->height - rp->lbl_file_y) {
|
||||
rp->invalid_delta = true;
|
||||
}
|
||||
rp->lbl_file_y += yoff;
|
||||
@@ -278,7 +278,7 @@ static void s_read_one_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
}
|
||||
|
||||
rp->lbl_y++;
|
||||
if (rp->lbl_y >= (int) rp->height) {
|
||||
if (rp->lbl_y >= rp->height) {
|
||||
rp->image_loaded = true;
|
||||
}
|
||||
}
|
||||
@@ -330,7 +330,7 @@ static void s_read_rgb_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
((uint32_t*)line)[offs + i] = pxval;
|
||||
break;
|
||||
default:
|
||||
logerr(rp->log, "Waaaaaaaaaaaaaah!");
|
||||
logerr(rp->c.log, "Waaaaaaaaaaaaaah!");
|
||||
rp->panic = true;
|
||||
return;
|
||||
}
|
||||
@@ -383,7 +383,7 @@ static void s_read_rgb_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
break;
|
||||
|
||||
default:
|
||||
logerr(rp->log, "Unknown format");
|
||||
logerr(rp->c.log, "Unknown format");
|
||||
rp->panic = true;
|
||||
return;
|
||||
}
|
||||
@@ -747,7 +747,7 @@ static void s_read_rle_line(BMPREAD_R rp, unsigned char *restrict line,
|
||||
continue;
|
||||
}
|
||||
|
||||
logerr(rp->log, "Should never get here! (x=%d, byte=%d)", (int) *x, (int) v);
|
||||
logerr(rp->c.log, "Should never get here! (x=%d, byte=%d)", (int) *x, (int) v);
|
||||
rp->panic = true;
|
||||
break;
|
||||
}
|
||||
@@ -800,11 +800,11 @@ static void s_read_huffman_line(BMPREAD_R rp, unsigned char *restrict line)
|
||||
for (int i = 0; i < runlen; i++, x++) {
|
||||
offs = (size_t) x * rp->result_bytes_per_pixel;
|
||||
if (rp->result_indexed) {
|
||||
line[offs] = black;
|
||||
line[offs] = black ^ rp->c.huffman_black_is_zero;
|
||||
} else {
|
||||
line[offs] = rp->palette->color[black].red;
|
||||
line[offs+1] = rp->palette->color[black].green;
|
||||
line[offs+2] = rp->palette->color[black].blue;
|
||||
line[offs] = rp->palette->color[black ^ rp->c.huffman_black_is_zero].red;
|
||||
line[offs+1] = rp->palette->color[black ^ rp->c.huffman_black_is_zero].green;
|
||||
line[offs+2] = rp->palette->color[black ^ rp->c.huffman_black_is_zero].blue;
|
||||
s_int_to_result_format(rp, 8, line + offs);
|
||||
}
|
||||
}
|
||||
@@ -907,7 +907,7 @@ static inline void s_int_to_result_format(BMPREAD_R rp, int frombits, unsigned c
|
||||
break;
|
||||
default:
|
||||
#ifdef DEBUG
|
||||
logerr(rp->log, "Unexpected result format %d", rp->result_format);
|
||||
logerr(rp->c.log, "Unexpected result format %d", rp->result_format);
|
||||
exit(1);
|
||||
#endif
|
||||
break;
|
||||
@@ -938,19 +938,19 @@ static void s_set_file_error(BMPREAD_R rp)
|
||||
static void s_log_error_from_state(BMPREAD_R rp)
|
||||
{
|
||||
if (rp->panic)
|
||||
logerr(rp->log, "An internal error occured.");
|
||||
logerr(rp->c.log, "An internal error occured.");
|
||||
if (rp->file_eof)
|
||||
logerr(rp->log, "Unexpected end of file.");
|
||||
logerr(rp->c.log, "Unexpected end of file.");
|
||||
if (rp->file_err)
|
||||
logsyserr(rp->log, "While reading file");
|
||||
logsyserr(rp->c.log, "While reading file");
|
||||
if (rp->invalid_index)
|
||||
logerr(rp->log, "File contained invalid color index.");
|
||||
logerr(rp->c.log, "File contained invalid color index.");
|
||||
if (rp->invalid_delta)
|
||||
logerr(rp->log, "Invalid delta pointing outside image area.");
|
||||
logerr(rp->c.log, "Invalid delta pointing outside image area.");
|
||||
if (rp->invalid_overrun)
|
||||
logerr(rp->log, "RLE data overrunning image area.");
|
||||
logerr(rp->c.log, "RLE data overrunning image area.");
|
||||
if (rp->truncated)
|
||||
logerr(rp->log, "Image was truncated.");
|
||||
logerr(rp->c.log, "Image was truncated.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -78,23 +78,23 @@ API BMPRESULT bmpread_load_palette(BMPHANDLE h, unsigned char **palette)
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
if (!rp->getinfo_called) {
|
||||
logerr(rp->log, "Must call bmpread_load_info() before loading palette");
|
||||
logerr(rp->c.log, "Must call bmpread_load_info() before loading palette");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
if (!rp->palette) {
|
||||
logerr(rp->log, "Image has no palette");
|
||||
logerr(rp->c.log, "Image has no palette");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
if (!palette) {
|
||||
logerr(rp->log, "palette is NULL");
|
||||
logerr(rp->c.log, "palette is NULL");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
memsize = rp->palette->numcolors * 4;
|
||||
if (!*palette) {
|
||||
if (!(*palette = malloc(memsize))) {
|
||||
logsyserr(rp->log, "allocating palette");
|
||||
logsyserr(rp->c.log, "allocating palette");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
237
bmp-read.c
237
bmp-read.c
@@ -53,13 +53,13 @@ API BMPHANDLE bmpread_new(FILE *file)
|
||||
}
|
||||
|
||||
memset(rp, 0, sizeof *rp);
|
||||
rp->magic = HMAGIC_READ;
|
||||
rp->c.magic = HMAGIC_READ;
|
||||
rp->undefined_mode = BMP_UNDEFINED_TO_ALPHA;
|
||||
rp->orientation = BMP_ORIENT_BOTTOMUP;
|
||||
rp->conv64 = BMP_CONV64_SRGB;
|
||||
rp->result_format = BMP_FORMAT_INT;
|
||||
|
||||
if (!(rp->log = logcreate()))
|
||||
if (!(rp->c.log = logcreate()))
|
||||
goto abort;
|
||||
|
||||
if (!file)
|
||||
@@ -119,12 +119,12 @@ API BMPRESULT bmpread_load_info(BMPHANDLE h)
|
||||
case BMPFILE_IC:
|
||||
case BMPFILE_PT:
|
||||
case BMPFILE_BA:
|
||||
logerr(rp->log, "Bitmap array and icon/pointer files not supported");
|
||||
logerr(rp->c.log, "Bitmap array and icon/pointer files not supported");
|
||||
rp->lasterr = BMP_ERR_UNSUPPORTED;
|
||||
goto abort;
|
||||
|
||||
default:
|
||||
logerr(rp->log, "Unkown BMP type 0x%04x\n", (unsigned int) rp->fh->type);
|
||||
logerr(rp->c.log, "Unkown BMP type 0x%04x\n", (unsigned int) rp->fh->type);
|
||||
rp->lasterr = BMP_ERR_UNSUPPORTED;
|
||||
goto abort;
|
||||
}
|
||||
@@ -136,10 +136,15 @@ API BMPRESULT bmpread_load_info(BMPHANDLE h)
|
||||
|
||||
/* negative height flips the image vertically */
|
||||
if (rp->ih->height < 0) {
|
||||
if (rp->ih->height == INT_MIN) {
|
||||
logerr(rp->c.log, "Unsupported image height %ld\n", (long) rp->ih->height);
|
||||
rp->lasterr = BMP_ERR_UNSUPPORTED;
|
||||
goto abort;
|
||||
}
|
||||
rp->orientation = BMP_ORIENT_TOPDOWN;
|
||||
rp->height = (unsigned) (-(int64_t)rp->ih->height);
|
||||
rp->height = -rp->ih->height;
|
||||
} else {
|
||||
rp->height = (unsigned) rp->ih->height;
|
||||
rp->height = rp->ih->height;
|
||||
}
|
||||
|
||||
|
||||
@@ -150,19 +155,19 @@ API BMPRESULT bmpread_load_info(BMPHANDLE h)
|
||||
|
||||
if (rp->ih->compression == BI_JPEG || rp->ih->compression == BI_PNG) {
|
||||
if (!cm_gobble_up(rp, rp->fh->offbits - rp->bytes_read)) {
|
||||
logerr(rp->log, "while seeking to start of jpeg/png data");
|
||||
logerr(rp->c.log, "while seeking to start of jpeg/png data");
|
||||
goto abort;
|
||||
}
|
||||
if (rp->ih->compression == BI_JPEG) {
|
||||
rp->jpeg = true;
|
||||
rp->getinfo_return = BMP_RESULT_JPEG;
|
||||
logerr(rp->log, "embedded JPEG data");
|
||||
logerr(rp->c.log, "embedded JPEG data");
|
||||
rp->lasterr = BMP_ERR_JPEG;
|
||||
return BMP_RESULT_JPEG;
|
||||
} else {
|
||||
rp->png = true;
|
||||
rp->getinfo_return = BMP_RESULT_PNG;
|
||||
logerr(rp->log, "embedded PNG data");
|
||||
logerr(rp->c.log, "embedded PNG data");
|
||||
rp->lasterr = BMP_ERR_PNG;
|
||||
return BMP_RESULT_PNG;
|
||||
}
|
||||
@@ -193,7 +198,7 @@ API BMPRESULT bmpread_load_info(BMPHANDLE h)
|
||||
|
||||
if (rp->insanity_limit &&
|
||||
rp->result_size > rp->insanity_limit) {
|
||||
logerr(rp->log, "file is insanely large");
|
||||
logerr(rp->c.log, "file is insanely large");
|
||||
rp->lasterr = BMP_ERR_INSANE;
|
||||
rp->getinfo_return = BMP_RESULT_INSANE;
|
||||
} else {
|
||||
@@ -230,7 +235,7 @@ API BMPRESULT bmpread_set_64bit_conv(BMPHANDLE h, enum Bmpconv64 conv)
|
||||
|
||||
case BMP_CONV64_NONE:
|
||||
if (rp->result_format_explicit && rp->result_format != BMP_FORMAT_S2_13) {
|
||||
logerr(rp->log, "64-bit conversion %s imcompatible with chosen number format %s.\n",
|
||||
logerr(rp->c.log, "64-bit conversion %s imcompatible with chosen number format %s.\n",
|
||||
cm_conv64_name(conv), cm_format_name(rp->result_format));
|
||||
rp->lasterr = BMP_ERR_CONV64;
|
||||
return BMP_RESULT_ERROR;
|
||||
@@ -242,7 +247,7 @@ API BMPRESULT bmpread_set_64bit_conv(BMPHANDLE h, enum Bmpconv64 conv)
|
||||
rp->conv64_explicit = true;
|
||||
break;
|
||||
default:
|
||||
logerr(rp->log, "Unknown 64-bit conversion %s (%d)", cm_conv64_name(conv), (int) conv);
|
||||
logerr(rp->c.log, "Unknown 64-bit conversion %s (%d)", cm_conv64_name(conv), (int) conv);
|
||||
rp->lasterr = BMP_ERR_CONV64;
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
@@ -262,6 +267,13 @@ API int bmpread_is_64bit(BMPHANDLE h)
|
||||
if (!(rp = cm_read_handle(h)))
|
||||
return 0;
|
||||
|
||||
if (!rp->getinfo_called)
|
||||
bmpread_load_info((BMPHANDLE)(void*)rp);
|
||||
|
||||
if (rp->getinfo_return != BMP_RESULT_OK && rp->getinfo_return != BMP_RESULT_INSANE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (rp->ih->bitcount == 64)
|
||||
return 1;
|
||||
return 0;
|
||||
@@ -269,6 +281,115 @@ API int bmpread_is_64bit(BMPHANDLE h)
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* bmpread_iccprofile_size
|
||||
*****************************************************************************/
|
||||
|
||||
API size_t bmpread_iccprofile_size(BMPHANDLE h)
|
||||
{
|
||||
BMPREAD rp;
|
||||
|
||||
if (!(rp = cm_read_handle(h)))
|
||||
return 0;
|
||||
|
||||
if (!rp->getinfo_called)
|
||||
return 0;
|
||||
|
||||
if (rp->getinfo_return != BMP_RESULT_OK && rp->getinfo_return != BMP_RESULT_INSANE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (rp->ih->cstype == PROFILE_EMBEDDED && rp->ih->profilesize <= MAX_ICCPROFILE_SIZE)
|
||||
return (size_t)rp->ih->profilesize;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/********************************************************
|
||||
* bmpread_load_iccprofile
|
||||
*******************************************************/
|
||||
|
||||
API BMPRESULT bmpread_load_iccprofile(BMPHANDLE h, unsigned char **profile)
|
||||
{
|
||||
BMPREAD rp;
|
||||
size_t memsize;
|
||||
long pos;
|
||||
bool we_allocated = false;
|
||||
bool file_messed_up = false;
|
||||
|
||||
if (!(rp = cm_read_handle(h)))
|
||||
goto abort;
|
||||
|
||||
if (!rp->getinfo_called) {
|
||||
logerr(rp->c.log, "Must call bmpread_load_info() before loading ICC profile");
|
||||
goto abort;
|
||||
}
|
||||
if (rp->ih->cstype != PROFILE_EMBEDDED) {
|
||||
logerr(rp->c.log, "Image has no ICC profile");
|
||||
goto abort;
|
||||
}
|
||||
|
||||
if (rp->ih->profilesize > MAX_ICCPROFILE_SIZE) {
|
||||
logerr(rp->c.log, "ICC profile is too large (%lu). Max is %lu",
|
||||
(unsigned long) rp->ih->profilesize,
|
||||
(unsigned long) MAX_ICCPROFILE_SIZE);
|
||||
goto abort;
|
||||
}
|
||||
|
||||
if (!profile) {
|
||||
logerr(rp->c.log, "profile is NULL");
|
||||
goto abort;
|
||||
}
|
||||
|
||||
memsize = rp->ih->profilesize;
|
||||
if (!*profile) {
|
||||
if (!(*profile = malloc(memsize))) {
|
||||
logsyserr(rp->c.log, "allocating ICC profile");
|
||||
goto abort;
|
||||
}
|
||||
we_allocated = true;
|
||||
}
|
||||
memset(*profile, 0, memsize);
|
||||
|
||||
if (-1 == (pos = ftell(rp->file))) {
|
||||
logsyserr(rp->c.log, "reading current file position");
|
||||
goto abort;
|
||||
}
|
||||
|
||||
if (fseek(rp->file, rp->ih->profiledata, SEEK_SET)) {
|
||||
logsyserr(rp->c.log, "seeking ICC profile in file");
|
||||
goto abort;
|
||||
}
|
||||
|
||||
/* Any failure from here on out cannot be reasonably recovered from, as
|
||||
* the file position will be messed up! */
|
||||
|
||||
file_messed_up = true;
|
||||
|
||||
if (memsize != fread(*profile, 1, memsize, rp->file)) {
|
||||
logsyserr(rp->c.log, "reading ICC profile");
|
||||
goto abort;
|
||||
}
|
||||
|
||||
if (fseek(rp->file, pos, SEEK_SET)) {
|
||||
logsyserr(rp->c.log, "failed to reset file position after reading ICC profile");
|
||||
goto abort;
|
||||
}
|
||||
|
||||
return BMP_RESULT_OK;
|
||||
|
||||
abort:
|
||||
if (profile && *profile && we_allocated) {
|
||||
free(*profile);
|
||||
*profile = NULL;
|
||||
}
|
||||
if (file_messed_up)
|
||||
rp->getinfo_return = BMP_RESULT_ERROR;
|
||||
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* bmpread_dimensions
|
||||
*****************************************************************************/
|
||||
@@ -296,7 +417,7 @@ API BMPRESULT bmpread_dimensions(BMPHANDLE h, int* restrict width,
|
||||
rp->dim_queried_width = true;
|
||||
}
|
||||
if (height) {
|
||||
*height = (int) rp->height;
|
||||
*height = rp->height;
|
||||
rp->dim_queried_height = true;
|
||||
}
|
||||
if (channels) {
|
||||
@@ -335,7 +456,7 @@ BMPRESULT br_set_number_format(BMPREAD_R rp, enum BmpFormat format)
|
||||
if (!(format == BMP_FORMAT_INT ||
|
||||
format == BMP_FORMAT_FLOAT ||
|
||||
format == BMP_FORMAT_S2_13)) {
|
||||
logerr(rp->log, "Invalid number format (%d) specified", (int) format);
|
||||
logerr(rp->c.log, "Invalid number format (%d) specified", (int) format);
|
||||
rp->lasterr = BMP_ERR_FORMAT;
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
@@ -348,14 +469,14 @@ BMPRESULT br_set_number_format(BMPREAD_R rp, enum BmpFormat format)
|
||||
case BMP_FORMAT_FLOAT:
|
||||
case BMP_FORMAT_S2_13:
|
||||
if (rp->getinfo_called && rp->result_indexed) {
|
||||
logerr(rp->log, "Cannot load color index as float or s2.13");
|
||||
logerr(rp->c.log, "Cannot load color index as float or s2.13");
|
||||
rp->lasterr = BMP_ERR_FORMAT;
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
logerr(rp->log, "Invalid number format (%d) specified", (int) format);
|
||||
logerr(rp->c.log, "Invalid number format (%d) specified", (int) format);
|
||||
rp->lasterr = BMP_ERR_FORMAT;
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
@@ -437,7 +558,7 @@ static int s_single_dim_val(BMPHANDLE h, enum Dimint dim)
|
||||
break;
|
||||
case DIM_HEIGHT:
|
||||
rp->dim_queried_height = true;
|
||||
ret = (int) rp->height;
|
||||
ret = rp->height;
|
||||
break;
|
||||
case DIM_CHANNELS:
|
||||
rp->dim_queried_channels = true;
|
||||
@@ -529,7 +650,7 @@ API void bmpread_set_undefined(BMPHANDLE h, enum BmpUndefined mode)
|
||||
return;
|
||||
|
||||
if (mode != BMP_UNDEFINED_TO_ALPHA && mode != BMP_UNDEFINED_LEAVE) {
|
||||
logerr(rp->log, "Invalid undefined-mode selected");
|
||||
logerr(rp->c.log, "Invalid undefined-mode selected");
|
||||
rp->lasterr = BMP_ERR_UNDEFMODE;
|
||||
return;
|
||||
}
|
||||
@@ -560,7 +681,7 @@ API void bmpread_set_undefined(BMPHANDLE h, enum BmpUndefined mode)
|
||||
|
||||
void br_free(BMPREAD rp)
|
||||
{
|
||||
rp->magic = 0;
|
||||
rp->c.magic = 0;
|
||||
|
||||
if (rp->palette)
|
||||
free(rp->palette);
|
||||
@@ -568,8 +689,8 @@ void br_free(BMPREAD rp)
|
||||
free(rp->ih);
|
||||
if (rp->fh)
|
||||
free(rp->fh);
|
||||
if (rp->log)
|
||||
logfree(rp->log);
|
||||
if (rp->c.log)
|
||||
logfree(rp->c.log);
|
||||
free(rp);
|
||||
}
|
||||
|
||||
@@ -584,7 +705,7 @@ static bool s_is_bmptype_supported_indexed(BMPREAD_R rp);
|
||||
static bool s_is_bmptype_supported(BMPREAD_R rp)
|
||||
{
|
||||
if (rp->ih->planes != 1) {
|
||||
logerr(rp->log, "Unsupported number of planes (%d). "
|
||||
logerr(rp->c.log, "Unsupported number of planes (%d). "
|
||||
"Must be 1.", (int) rp->ih->planes);
|
||||
rp->lasterr = BMP_ERR_HEADER;
|
||||
return false;
|
||||
@@ -614,7 +735,7 @@ static bool s_is_bmptype_supported_rgb(BMPREAD_R rp)
|
||||
/* ok */
|
||||
break;
|
||||
default:
|
||||
logerr(rp->log, "Invalid bitcount %d for RGB image", (int) rp->ih->bitcount);
|
||||
logerr(rp->c.log, "Invalid bitcount %d for RGB image", (int) rp->ih->bitcount);
|
||||
rp->lasterr = BMP_ERR_HEADER;
|
||||
return false;
|
||||
}
|
||||
@@ -626,20 +747,20 @@ static bool s_is_bmptype_supported_rgb(BMPREAD_R rp)
|
||||
case BI_BITFIELDS:
|
||||
case BI_ALPHABITFIELDS:
|
||||
if (rp->ih->bitcount == 64) {
|
||||
logerr(rp->log, "Invalid bitcount %d for BITFIELDS", (int) rp->ih->bitcount);
|
||||
logerr(rp->c.log, "Invalid bitcount %d for BITFIELDS", (int) rp->ih->bitcount);
|
||||
rp->lasterr = BMP_ERR_HEADER;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case BI_OS2_RLE24:
|
||||
if (rp->ih->bitcount != 24) {
|
||||
logerr(rp->log, "Invalid bitcount %d for RLE24 compression", (int) rp->ih->bitcount);
|
||||
logerr(rp->c.log, "Invalid bitcount %d for RLE24 compression", (int) rp->ih->bitcount);
|
||||
rp->lasterr = BMP_ERR_HEADER;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logerr(rp->log, "Unsupported compression %s for RGB image",
|
||||
logerr(rp->c.log, "Unsupported compression %s for RGB image",
|
||||
s_compression_name(rp->ih->compression));
|
||||
rp->lasterr = BMP_ERR_UNSUPPORTED;
|
||||
return false;
|
||||
@@ -665,7 +786,7 @@ static bool s_is_bmptype_supported_indexed(BMPREAD_R rp)
|
||||
break;
|
||||
|
||||
default:
|
||||
logerr(rp->log, "Invalid bitcount %d for indexed image",
|
||||
logerr(rp->c.log, "Invalid bitcount %d for indexed image",
|
||||
(int) rp->ih->bitcount);
|
||||
rp->lasterr = BMP_ERR_HEADER;
|
||||
return false;
|
||||
@@ -679,7 +800,7 @@ static bool s_is_bmptype_supported_indexed(BMPREAD_R rp)
|
||||
if ( (rp->ih->compression == BI_RLE4 && rp->ih->bitcount != 4) ||
|
||||
(rp->ih->compression == BI_RLE8 && rp->ih->bitcount != 8) ||
|
||||
(rp->ih->compression == BI_OS2_HUFFMAN && rp->ih->bitcount != 1)) {
|
||||
logerr(rp->log, "Unsupported compression %s for %d-bit data",
|
||||
logerr(rp->c.log, "Unsupported compression %s for %d-bit data",
|
||||
s_compression_name(rp->ih->compression),
|
||||
(int) rp->ih->bitcount);
|
||||
rp->lasterr = BMP_ERR_UNSUPPORTED;
|
||||
@@ -689,7 +810,7 @@ static bool s_is_bmptype_supported_indexed(BMPREAD_R rp)
|
||||
break;
|
||||
|
||||
default:
|
||||
logerr(rp->log, "Unsupported compression %s for indexed image",
|
||||
logerr(rp->c.log, "Unsupported compression %s for indexed image",
|
||||
s_compression_name(rp->ih->compression));
|
||||
rp->lasterr = BMP_ERR_UNSUPPORTED;
|
||||
return false;
|
||||
@@ -717,20 +838,20 @@ static struct Palette* s_read_palette(BMPREAD_R rp)
|
||||
|
||||
|
||||
if (rp->ih->clrused > INT_MAX || rp->ih->clrimportant > rp->ih->clrused) {
|
||||
logerr(rp->log, "Unreasonable color numbers for palette (%lu/%lu)",
|
||||
logerr(rp->c.log, "Unreasonable color numbers for palette (%lu/%lu)",
|
||||
(unsigned long) rp->ih->clrused,
|
||||
(unsigned long) rp->ih->clrimportant);
|
||||
rp->lasterr = BMP_ERR_INVALID;
|
||||
return NULL;
|
||||
}
|
||||
if (rp->fh->offbits - rp->bytes_read > INT_MAX) {
|
||||
logerr(rp->log, "gap to pixeldata too big (%lu)",
|
||||
logerr(rp->c.log, "gap to pixeldata too big (%lu)",
|
||||
(unsigned long) rp->fh->offbits - rp->bytes_read);
|
||||
rp->lasterr = BMP_ERR_INVALID;
|
||||
return NULL;
|
||||
}
|
||||
if (rp->fh->offbits < rp->bytes_read) {
|
||||
logerr(rp->log, "Invalid offset to pixel data");
|
||||
logerr(rp->c.log, "Invalid offset to pixel data");
|
||||
rp->lasterr = BMP_ERR_INVALID;
|
||||
return NULL;
|
||||
}
|
||||
@@ -744,7 +865,7 @@ static struct Palette* s_read_palette(BMPREAD_R rp)
|
||||
if (0 == (colors_in_file = rp->ih->clrused)) {
|
||||
colors_in_file = MIN(colors_full_palette, max_colors_in_file);
|
||||
} else if (colors_in_file > max_colors_in_file) {
|
||||
logerr(rp->log, "given palette size (%d) too large for available data (%d)",
|
||||
logerr(rp->c.log, "given palette size (%d) too large for available data (%d)",
|
||||
colors_in_file, max_colors_in_file);
|
||||
rp->lasterr = BMP_ERR_INVALID;
|
||||
return NULL;
|
||||
@@ -756,7 +877,7 @@ static struct Palette* s_read_palette(BMPREAD_R rp)
|
||||
memsize = sizeof *palette +
|
||||
(colors_in_file - colors_ignore) * sizeof palette->color[0];
|
||||
if (!(palette = malloc(memsize))) {
|
||||
logsyserr(rp->log, "Allocating mem for palette");
|
||||
logsyserr(rp->c.log, "Allocating mem for palette");
|
||||
rp->lasterr = BMP_ERR_MEMORY;
|
||||
return NULL;
|
||||
}
|
||||
@@ -769,10 +890,10 @@ static struct Palette* s_read_palette(BMPREAD_R rp)
|
||||
EOF == (r = getc(rp->file)) ||
|
||||
((bytes_per_entry == 4) && (EOF == getc(rp->file))) ) {
|
||||
if (feof(rp->file)) {
|
||||
logerr(rp->log, "file ended reading palette entries");
|
||||
logerr(rp->c.log, "file ended reading palette entries");
|
||||
rp->lasterr = BMP_ERR_TRUNCATED;
|
||||
} else {
|
||||
logsyserr(rp->log, "reading palette entries");
|
||||
logsyserr(rp->c.log, "reading palette entries");
|
||||
rp->lasterr = BMP_ERR_FILEIO;
|
||||
}
|
||||
free (palette);
|
||||
@@ -786,7 +907,7 @@ static struct Palette* s_read_palette(BMPREAD_R rp)
|
||||
|
||||
for (i = 0; i < colors_ignore; i++) {
|
||||
if (!cm_gobble_up(rp, bytes_per_entry)) {
|
||||
logerr(rp->log, "reading superfluous palette entries");
|
||||
logerr(rp->c.log, "reading superfluous palette entries");
|
||||
free(palette);
|
||||
return NULL;
|
||||
}
|
||||
@@ -813,7 +934,7 @@ bool br_set_resultbits(BMPREAD_R rp)
|
||||
switch (rp->result_format) {
|
||||
case BMP_FORMAT_FLOAT:
|
||||
if (rp->result_indexed) {
|
||||
logerr(rp->log, "Float is invalid number format for indexed image\n");
|
||||
logerr(rp->c.log, "Float is invalid number format for indexed image\n");
|
||||
rp->lasterr = BMP_ERR_FORMAT;
|
||||
return false;
|
||||
}
|
||||
@@ -822,7 +943,7 @@ bool br_set_resultbits(BMPREAD_R rp)
|
||||
|
||||
case BMP_FORMAT_S2_13:
|
||||
if (rp->result_indexed) {
|
||||
logerr(rp->log, "s2.13 is invalid number format for indexed image\n");
|
||||
logerr(rp->c.log, "s2.13 is invalid number format for indexed image\n");
|
||||
rp->lasterr = BMP_ERR_FORMAT;
|
||||
return false;
|
||||
}
|
||||
@@ -843,7 +964,7 @@ bool br_set_resultbits(BMPREAD_R rp)
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logerr(rp->log, "Invalid number format %d\n", rp->result_format);
|
||||
logerr(rp->c.log, "Invalid number format %d\n", rp->result_format);
|
||||
rp->lasterr = BMP_ERR_FORMAT;
|
||||
return false;
|
||||
|
||||
@@ -865,7 +986,7 @@ bool br_set_resultbits(BMPREAD_R rp)
|
||||
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");
|
||||
logerr(rp->c.log, "file is insanely large");
|
||||
rp->lasterr = BMP_ERR_INSANE;
|
||||
rp->getinfo_return = BMP_RESULT_INSANE;
|
||||
}
|
||||
@@ -889,8 +1010,8 @@ static bool s_check_dimensions(BMPREAD_R rp)
|
||||
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);
|
||||
if (npixels > maxpixels || rp->width < 1 || rp->height < 1) {
|
||||
logerr(rp->c.log, "Invalid BMP dimensions (%dx%d)", rp->width, rp->height);
|
||||
rp->lasterr = BMP_ERR_DIMENSIONS;
|
||||
return false;
|
||||
}
|
||||
@@ -924,7 +1045,7 @@ static bool s_read_colormasks(BMPREAD_R rp)
|
||||
break;
|
||||
|
||||
default:
|
||||
logerr(rp->log, "Invalid compression (%s)",
|
||||
logerr(rp->c.log, "Invalid compression (%s)",
|
||||
s_compression_name(rp->ih->compression));
|
||||
rp->lasterr = BMP_ERR_INVALID;
|
||||
return false;
|
||||
@@ -943,19 +1064,19 @@ static bool s_read_colormasks(BMPREAD_R rp)
|
||||
sum_bits += rp->cmask.bits.value[i];
|
||||
}
|
||||
if (max_bits > MIN(rp->ih->bitcount, 32) || sum_bits > rp->ih->bitcount) {
|
||||
logerr(rp->log, "Invalid mask bitcount (max=%d, sum=%d)",
|
||||
logerr(rp->c.log, "Invalid mask bitcount (max=%d, sum=%d)",
|
||||
max_bits, sum_bits);
|
||||
rp->lasterr = BMP_ERR_INVALID;
|
||||
return false;
|
||||
}
|
||||
if (!(rp->cmask.mask.red | rp->cmask.mask.green | rp->cmask.mask.blue)) {
|
||||
logerr(rp->log, "Empty color masks. Corrupt BMP?");
|
||||
logerr(rp->c.log, "Empty color masks. Corrupt BMP?");
|
||||
rp->lasterr = BMP_ERR_INVALID;
|
||||
return false;
|
||||
}
|
||||
if (rp->cmask.mask.red & rp->cmask.mask.green &
|
||||
rp->cmask.mask.blue & rp->cmask.mask.alpha) {
|
||||
logerr(rp->log, "Overlapping color masks. Corrupt BMP?");
|
||||
logerr(rp->c.log, "Overlapping color masks. Corrupt BMP?");
|
||||
rp->lasterr = BMP_ERR_INVALID;
|
||||
return false;
|
||||
}
|
||||
@@ -975,7 +1096,7 @@ static bool s_read_masks_from_bitfields(BMPREAD_R rp)
|
||||
int i;
|
||||
|
||||
if (!(rp->ih->bitcount == 16 || rp->ih->bitcount == 32)) {
|
||||
logerr(rp->log, "Invalid bitcount (%d) for BI_BITFIELDS."
|
||||
logerr(rp->c.log, "Invalid bitcount (%d) for BI_BITFIELDS."
|
||||
"Must be 16 or 32", (int) rp->ih->bitcount);
|
||||
rp->lasterr = BMP_ERR_INVALID;
|
||||
return false;
|
||||
@@ -986,10 +1107,10 @@ static bool s_read_masks_from_bitfields(BMPREAD_R rp)
|
||||
read_u32_le(rp->file, &g) &&
|
||||
read_u32_le(rp->file, &b))) {
|
||||
if (feof(rp->file)) {
|
||||
logerr(rp->log, "File ended reading color masks");
|
||||
logerr(rp->c.log, "File ended reading color masks");
|
||||
rp->lasterr = BMP_ERR_TRUNCATED;
|
||||
} else {
|
||||
logsyserr(rp->log, "Reading BMP color masks");
|
||||
logsyserr(rp->c.log, "Reading BMP color masks");
|
||||
rp->lasterr = BMP_ERR_FILEIO;
|
||||
}
|
||||
return false;
|
||||
@@ -1001,10 +1122,10 @@ static bool s_read_masks_from_bitfields(BMPREAD_R rp)
|
||||
if (rp->ih->compression == BI_ALPHABITFIELDS) {
|
||||
if (!read_u32_le(rp->file, &a)) {
|
||||
if (feof(rp->file)) {
|
||||
logerr(rp->log, "File ended reading color masks");
|
||||
logerr(rp->c.log, "File ended reading color masks");
|
||||
rp->lasterr = BMP_ERR_TRUNCATED;
|
||||
} else {
|
||||
logsyserr(rp->log, "Reading BMP color masks");
|
||||
logsyserr(rp->c.log, "Reading BMP color masks");
|
||||
rp->lasterr = BMP_ERR_FILEIO;
|
||||
}
|
||||
return false;
|
||||
@@ -1053,7 +1174,7 @@ static bool s_create_implicit_colormasks(BMPREAD_R rp)
|
||||
bitsperchannel = 16;
|
||||
break;
|
||||
default:
|
||||
logerr(rp->log, "Invalid bitcount for BMP (%d)", (int) rp->ih->bitcount);
|
||||
logerr(rp->c.log, "Invalid bitcount for BMP (%d)", (int) rp->ih->bitcount);
|
||||
rp->lasterr = BMP_ERR_INVALID;
|
||||
return false;
|
||||
}
|
||||
@@ -1140,11 +1261,11 @@ static bool s_read_file_header(BMPREAD_R rp)
|
||||
}
|
||||
|
||||
if (feof(rp->file)) {
|
||||
logerr(rp->log, "unexpected end-of-file while reading "
|
||||
logerr(rp->c.log, "unexpected end-of-file while reading "
|
||||
"file header");
|
||||
rp->lasterr = BMP_ERR_TRUNCATED;
|
||||
} else {
|
||||
logsyserr(rp->log, "error reading file header");
|
||||
logsyserr(rp->c.log, "error reading file header");
|
||||
rp->lasterr = BMP_ERR_FILEIO;
|
||||
}
|
||||
|
||||
@@ -1193,7 +1314,7 @@ static bool s_read_info_header(BMPREAD_R rp)
|
||||
if (rp->ih->size > 124)
|
||||
rp->ih->version = BMPINFO_FUTURE;
|
||||
else {
|
||||
logerr(rp->log, "Invalid info header size (%lu)",
|
||||
logerr(rp->c.log, "Invalid info header size (%lu)",
|
||||
(unsigned long) rp->ih->size);
|
||||
rp->lasterr = BMP_ERR_HEADER;
|
||||
return false;
|
||||
@@ -1285,10 +1406,10 @@ header_done:
|
||||
|
||||
abort_file_err:
|
||||
if (feof(rp->file)) {
|
||||
logerr(rp->log, "Unexpected end of file while reading BMP info header");
|
||||
logerr(rp->c.log, "Unexpected end of file while reading BMP info header");
|
||||
rp->lasterr = BMP_ERR_TRUNCATED;
|
||||
} else {
|
||||
logsyserr(rp->log, "While reading BMP info header");
|
||||
logsyserr(rp->c.log, "While reading BMP info header");
|
||||
rp->lasterr = BMP_ERR_FILEIO;
|
||||
}
|
||||
return false;
|
||||
|
||||
296
bmp-write.c
296
bmp-write.c
@@ -41,6 +41,7 @@ static void s_decide_outformat(BMPWRITE_R wp);
|
||||
static bool s_write_palette(BMPWRITE_R wp);
|
||||
static bool s_write_bmp_file_header(BMPWRITE_R wp);
|
||||
static bool s_write_bmp_info_header(BMPWRITE_R wp);
|
||||
static bool s_write_iccprofile(BMPWRITE_R wp);
|
||||
static inline int s_write_one_byte(int byte, BMPWRITE_R wp);
|
||||
static bool s_save_header(BMPWRITE_R wp);
|
||||
static bool s_try_saving_image_size(BMPWRITE_R wp);
|
||||
@@ -62,31 +63,31 @@ API BMPHANDLE bmpwrite_new(FILE *file)
|
||||
goto abort;
|
||||
}
|
||||
memset(wp, 0, sizeof *wp);
|
||||
wp->magic = HMAGIC_WRITE;
|
||||
wp->c.magic = HMAGIC_WRITE;
|
||||
|
||||
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()))
|
||||
if (!(wp->c.log = logcreate()))
|
||||
goto abort;
|
||||
|
||||
if (!file) {
|
||||
logerr(wp->log, "Must supply file handle");
|
||||
logerr(wp->c.log, "Must supply file handle");
|
||||
goto abort;
|
||||
}
|
||||
|
||||
wp->file = file;
|
||||
|
||||
if (!(wp->fh = malloc(sizeof *wp->fh))) {
|
||||
logsyserr(wp->log, "allocating bmp file header");
|
||||
logsyserr(wp->c.log, "allocating bmp file header");
|
||||
goto abort;
|
||||
}
|
||||
memset(wp->fh, 0, sizeof *wp->fh);
|
||||
|
||||
if (!(wp->ih = malloc(sizeof *wp->ih))) {
|
||||
logsyserr(wp->log, "allocating bmp info header");
|
||||
logsyserr(wp->c.log, "allocating bmp info header");
|
||||
goto abort;
|
||||
}
|
||||
memset(wp->ih, 0, sizeof *wp->ih);
|
||||
@@ -126,13 +127,13 @@ API BMPRESULT bmpwrite_set_dimensions(BMPHANDLE h,
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
if (!cm_is_one_of(3, source_bitsperchannel, 8, 16, 32)) {
|
||||
logerr(wp->log, "Invalid number of bits per channel: %d",
|
||||
logerr(wp->c.log, "Invalid number of bits per channel: %d",
|
||||
(int) source_bitsperchannel);
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
if (!cm_is_one_of(4, source_channels, 3, 4, 1, 2)) {
|
||||
logerr(wp->log, "Invalid number of channels: %d", (int) source_channels);
|
||||
logerr(wp->c.log, "Invalid number of channels: %d", (int) source_channels);
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
@@ -141,7 +142,7 @@ API BMPRESULT bmpwrite_set_dimensions(BMPHANDLE h,
|
||||
if (width > INT32_MAX || height > INT32_MAX ||
|
||||
width < 1 || height < 1 ||
|
||||
(uint64_t) width * height > SIZE_MAX / wp->source_bytes_per_pixel) {
|
||||
logerr(wp->log, "Invalid dimensions %ux%ux%u @ %ubits",
|
||||
logerr(wp->c.log, "Invalid dimensions %ux%ux%u @ %ubits",
|
||||
width, height, source_channels,
|
||||
source_bitsperchannel);
|
||||
return BMP_RESULT_ERROR;
|
||||
@@ -200,7 +201,7 @@ API BMPRESULT bmpwrite_set_output_bits(BMPHANDLE h, int red, int green, int blue
|
||||
cm_all_lessoreq_int(32, 4, red, green, blue, alpha) &&
|
||||
red + green + blue > 0 &&
|
||||
red + green + blue + alpha <= 32 )) {
|
||||
logerr(wp->log, "Invalid output bit depths specified: %d-%d-%d - %d",
|
||||
logerr(wp->c.log, "Invalid output bit depths specified: %d-%d-%d - %d",
|
||||
red, green, blue, alpha);
|
||||
wp->outbits_set = false;
|
||||
return BMP_RESULT_ERROR;
|
||||
@@ -236,7 +237,7 @@ API BMPRESULT bmpwrite_set_palette(BMPHANDLE h, int numcolors,
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
if (wp->palette) {
|
||||
logerr(wp->log, "Palette already set. Cannot set twice");
|
||||
logerr(wp->c.log, "Palette already set. Cannot set twice");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
@@ -244,14 +245,14 @@ API BMPRESULT bmpwrite_set_palette(BMPHANDLE h, int numcolors,
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
if (numcolors < 2 || numcolors > 256) {
|
||||
logerr(wp->log, "Invalid number of colors for palette (%d)",
|
||||
logerr(wp->c.log, "Invalid number of colors for palette (%d)",
|
||||
numcolors);
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
memsize = sizeof *wp->palette + numcolors * sizeof wp->palette->color[0];
|
||||
if (!(wp->palette = malloc(memsize))) {
|
||||
logsyserr(wp->log, "Allocating palette");
|
||||
logsyserr(wp->c.log, "Allocating palette");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
memset(wp->palette, 0, memsize);
|
||||
@@ -268,6 +269,55 @@ API BMPRESULT bmpwrite_set_palette(BMPHANDLE h, int numcolors,
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* bmpwrite_set_iccprofile
|
||||
*****************************************************************************/
|
||||
|
||||
API BMPRESULT bmpwrite_set_iccprofile(BMPHANDLE h, size_t size,
|
||||
const unsigned char *iccprofile)
|
||||
{
|
||||
BMPWRITE wp;
|
||||
|
||||
assert(MAX_ICCPROFILE_SIZE < INT_MAX);
|
||||
|
||||
if (!(wp = cm_write_handle(h)))
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
if (s_check_already_saved(wp))
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
if (!s_is_setting_compatible(wp, "iccprofile"))
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
if (wp->iccprofile) {
|
||||
free(wp->iccprofile);
|
||||
wp->iccprofile = NULL;
|
||||
wp->iccprofile_size = 0;
|
||||
wp->ih->profilesize = 0;
|
||||
wp->ih->cstype = LCS_WINDOWS_COLOR_SPACE;
|
||||
}
|
||||
|
||||
if (size > MAX_ICCPROFILE_SIZE) {
|
||||
logerr(wp->c.log, "ICC profile is too large (%zuMB). Max is %luMB.",
|
||||
size >> 20, (unsigned long)(MAX_ICCPROFILE_SIZE >> 20));
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
if (!(wp->iccprofile = malloc(size))) {
|
||||
logsyserr(wp->c.log, "Allocating ICC profile");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
memcpy(wp->iccprofile, iccprofile, size);
|
||||
wp->iccprofile_size = (int) size;
|
||||
wp->ih->profilesize = size;
|
||||
wp->ih->cstype = PROFILE_EMBEDDED;
|
||||
wp->ih->intent = LCS_GM_GRAPHICS;
|
||||
|
||||
printf("ICC profile set\n");
|
||||
return BMP_RESULT_OK;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* bmpwrite_set_orientation
|
||||
*****************************************************************************/
|
||||
@@ -285,7 +335,7 @@ API BMPRESULT bmpwrite_set_orientation(BMPHANDLE h, enum BmpOrient orientation)
|
||||
switch (orientation) {
|
||||
case BMP_ORIENT_TOPDOWN:
|
||||
if (wp->rle_requested != BMP_RLE_NONE) {
|
||||
logerr(wp->log, "Topdown is invalid with RLE BMPs");
|
||||
logerr(wp->c.log, "Topdown is invalid with RLE BMPs");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
break;
|
||||
@@ -295,7 +345,7 @@ API BMPRESULT bmpwrite_set_orientation(BMPHANDLE h, enum BmpOrient orientation)
|
||||
break;
|
||||
|
||||
default:
|
||||
logerr(wp->log, "Invalid orientation (%d)", (int) orientation);
|
||||
logerr(wp->c.log, "Invalid orientation (%d)", (int) orientation);
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
@@ -323,7 +373,7 @@ API BMPRESULT bmpwrite_set_rle(BMPHANDLE h, enum BmpRLEtype type)
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
if (!cm_is_one_of(3, (int) type, (int) BMP_RLE_NONE, (int) BMP_RLE_AUTO, (int) BMP_RLE_RLE8)) {
|
||||
logerr(wp->log, "Invalid RLE type specified (%d)", (int) type);
|
||||
logerr(wp->c.log, "Invalid RLE type specified (%d)", (int) type);
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
@@ -467,7 +517,7 @@ API BMPRESULT bmpwrite_set_huffman_img_fg_idx(BMPHANDLE h, int idx)
|
||||
static bool s_check_already_saved(BMPWRITE_R wp)
|
||||
{
|
||||
if (wp->saveimage_done) {
|
||||
logerr(wp->log, "Image already saved.");
|
||||
logerr(wp->c.log, "Image already saved.");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -478,10 +528,42 @@ static bool s_check_already_saved(BMPWRITE_R wp)
|
||||
/*****************************************************************************
|
||||
* s_is_setting_compatible
|
||||
*
|
||||
* setting: "outbits", "srcbits", "srcchannels",
|
||||
* "format", "indexed", "64bit", "rle"
|
||||
* setting: "outbits", "srcbits", "srcchannels", "palette", "allowhuffman"
|
||||
* "format", "indexed", "64bit", "rle", "iccprofile"
|
||||
*****************************************************************************/
|
||||
|
||||
static bool s_is_huffman_activated(BMPWRITE_R wp)
|
||||
{
|
||||
return wp->palette != NULL &&
|
||||
wp->palette_size <= 8 &&
|
||||
wp->rle_requested == BMP_RLE_AUTO &&
|
||||
wp->allow_huffman;
|
||||
}
|
||||
|
||||
static bool s_setting_activates_huffman(BMPWRITE_R wp, const char *setting, int value)
|
||||
{
|
||||
if (!strcmp(setting, "palette")) {
|
||||
if (value <= 2) {
|
||||
if (wp->rle_requested == BMP_RLE_AUTO && wp->allow_huffman)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!strcmp(setting, "rle")) {
|
||||
if (value == BMP_RLE_AUTO) {
|
||||
if (wp->palette && wp->palette_size <= 8 && wp->allow_huffman)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!strcmp(setting, "allowhuff")) {
|
||||
if (wp->palette && wp->palette_size <= 8 && wp->rle_requested == BMP_RLE_AUTO)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool s_is_setting_compatible(BMPWRITE_R wp, const char *setting, ...)
|
||||
{
|
||||
int channels, bits;
|
||||
@@ -495,49 +577,49 @@ static bool s_is_setting_compatible(BMPWRITE_R wp, const char *setting, ...)
|
||||
|
||||
if (!strcmp(setting, "outbits")) {
|
||||
if (wp->palette || wp->out64bit || (wp->rle_requested != BMP_RLE_NONE)) {
|
||||
logerr(wp->log, "output bits cannot be set with indexed, RLE, "
|
||||
logerr(wp->c.log, "output bits cannot be set with indexed, RLE, "
|
||||
"or 64bit BMPs");
|
||||
ret = false;
|
||||
}
|
||||
} else if (!strcmp(setting, "srcbits")) {
|
||||
bits = va_arg(args, int);
|
||||
if (wp->palette && bits != 8) {
|
||||
logerr(wp->log, "indexed images must be 8 bits (not %d)", bits);
|
||||
logerr(wp->c.log, "indexed images must be 8 bits (not %d)", bits);
|
||||
ret = false;
|
||||
} else if (wp->source_format == BMP_FORMAT_FLOAT && bits != 32) {
|
||||
logerr(wp->log, "float images must be 32 bits per channel (not %d)", bits);
|
||||
logerr(wp->c.log, "float images must be 32 bits per channel (not %d)", bits);
|
||||
ret = false;
|
||||
} else if (wp->source_format == BMP_FORMAT_S2_13 && bits != 16) {
|
||||
logerr(wp->log, "s2.13 images must be 16 bits per channel (not %d)", bits);
|
||||
logerr(wp->c.log, "s2.13 images must be 16 bits per channel (not %d)", bits);
|
||||
ret = false;
|
||||
}
|
||||
} else if (!strcmp(setting, "srcchannels")) {
|
||||
channels = va_arg(args, int);
|
||||
if (wp->palette && (channels != 1)) {
|
||||
logerr(wp->log, "Indexed images must have 1 channel (not %d)", channels);
|
||||
logerr(wp->c.log, "Indexed images must have 1 channel (not %d)", channels);
|
||||
ret = false;
|
||||
}
|
||||
if (wp->out64bit && (channels != 3 && channels != 4)) {
|
||||
logerr(wp->log, "64bit images must have 3 or 4 channels (not %d)", channels);
|
||||
logerr(wp->c.log, "64bit images must have 3 or 4 channels (not %d)", channels);
|
||||
ret = false;
|
||||
}
|
||||
} else if (!strcmp(setting, "indexed")) {
|
||||
if (wp->out64bit) {
|
||||
logerr(wp->log, "64bit BMPs cannot be indexed");
|
||||
logerr(wp->c.log, "64bit BMPs cannot be indexed");
|
||||
ret = false;
|
||||
}
|
||||
if (wp->outbits_set) {
|
||||
logerr(wp->log, "BMPs with specified channel bits cannot be indexed");
|
||||
logerr(wp->c.log, "BMPs with specified channel bits cannot be indexed");
|
||||
ret = false;
|
||||
}
|
||||
if (wp->source_format != BMP_FORMAT_INT) {
|
||||
logerr(wp->log, "Indexed image must have INT format (not %s)",
|
||||
logerr(wp->c.log, "Indexed image must have INT format (not %s)",
|
||||
cm_format_name(wp->source_format));
|
||||
ret = false;
|
||||
}
|
||||
if (wp->dimensions_set) {
|
||||
if (!(wp->source_channels == 1 && wp->source_bitsperchannel == 8)) {
|
||||
logerr (wp->log, "Indexed images must be 1 channel, 8 bits");
|
||||
logerr (wp->c.log, "Indexed images must be 1 channel, 8 bits");
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
@@ -546,23 +628,23 @@ static bool s_is_setting_compatible(BMPWRITE_R wp, const char *setting, ...)
|
||||
switch (format) {
|
||||
case BMP_FORMAT_FLOAT:
|
||||
if (wp->dimensions_set && wp->source_bitsperchannel != 32) {
|
||||
logerr(wp->log, "float cannot be %d bits per pixel",
|
||||
logerr(wp->c.log, "float cannot be %d bits per pixel",
|
||||
wp->source_bitsperchannel);
|
||||
ret = false;
|
||||
}
|
||||
if (wp->palette) {
|
||||
logerr(wp->log, "float cannot be used for indexed images");
|
||||
logerr(wp->c.log, "float cannot be used for indexed images");
|
||||
ret = false;
|
||||
}
|
||||
break;
|
||||
case BMP_FORMAT_S2_13:
|
||||
if (wp->dimensions_set && wp->source_bitsperchannel != 16) {
|
||||
logerr(wp->log, "s2.13 cannot be %d bits per pixel",
|
||||
logerr(wp->c.log, "s2.13 cannot be %d bits per pixel",
|
||||
wp->source_bitsperchannel);
|
||||
ret = false;
|
||||
}
|
||||
if (wp->palette) {
|
||||
logerr(wp->log, "s2.13 cannot be used for indexed images");
|
||||
logerr(wp->c.log, "s2.13 cannot be used for indexed images");
|
||||
ret = false;
|
||||
}
|
||||
break;
|
||||
@@ -574,21 +656,42 @@ static bool s_is_setting_compatible(BMPWRITE_R wp, const char *setting, ...)
|
||||
rle = va_arg(args, enum BmpRLEtype);
|
||||
if (rle == BMP_RLE_AUTO || rle == BMP_RLE_RLE8) {
|
||||
if (wp->outorientation != BMP_ORIENT_BOTTOMUP) {
|
||||
logerr(wp->log, "RLE is invalid with top-down BMPs");
|
||||
logerr(wp->c.log, "RLE is invalid with top-down BMPs");
|
||||
ret = false;
|
||||
}
|
||||
if (wp->iccprofile && s_setting_activates_huffman(wp, "rle", rle)) {
|
||||
logerr(wp->c.log, "Huffmann compression cannot be used when storing ICC profile");
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (!strcmp(setting, "palette")) {
|
||||
int numcolors = va_arg(args, int);
|
||||
if (s_setting_activates_huffman(wp, "palette", numcolors)) {
|
||||
logerr(wp->c.log, "Huffmann compression cannot be used when storing ICC profile");
|
||||
ret = false;
|
||||
}
|
||||
} else if (!strcmp(setting, "allowhuffman")) {
|
||||
if (wp->iccprofile) {
|
||||
logerr(wp->c.log, "Huffmann compression cannot be used when storing ICC profile");
|
||||
ret = false;
|
||||
}
|
||||
} else if (!strcmp(setting, "iccprofile")) {
|
||||
if (s_is_huffman_activated(wp)) {
|
||||
logerr(wp->c.log, "Cannot store ICC profile with Huffmann compressed image");
|
||||
ret = false;
|
||||
}
|
||||
} else if (!strcmp(setting, "orientation")) {
|
||||
orientation = va_arg(args, enum BmpOrient);
|
||||
if (orientation == BMP_ORIENT_TOPDOWN) {
|
||||
if (wp->rle_requested != BMP_RLE_NONE) {
|
||||
logerr(wp->log, "RLE is invalid with top-down BMPs");
|
||||
logerr(wp->c.log, "RLE is invalid with top-down BMPs");
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
} else if (!strcmp(setting, "64bit")) {
|
||||
if (wp->palette) {
|
||||
logerr(wp->log, "Indexed images cannot be 64bit");
|
||||
logerr(wp->c.log, "Indexed images cannot be 64bit");
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
@@ -641,7 +744,7 @@ static void s_decide_outformat(BMPWRITE_R wp)
|
||||
wp->ih->compression = BI_RLE8;
|
||||
wp->ih->bitcount = 8;
|
||||
|
||||
} else if (wp->palette->numcolors > 2 || !wp->allow_huffman) {
|
||||
} else if (wp->palette->numcolors > 2 || !wp->allow_huffman || wp->iccprofile) {
|
||||
wp->rle = 4;
|
||||
wp->ih->compression = BI_RLE4;
|
||||
wp->ih->bitcount = 4;
|
||||
@@ -664,7 +767,7 @@ static void s_decide_outformat(BMPWRITE_R wp)
|
||||
}
|
||||
|
||||
} else if (wp->allow_rle24 && wp->source_channels == 3 &&
|
||||
wp->source_bitsperchannel && wp->rle_requested == BMP_RLE_AUTO) {
|
||||
wp->source_bitsperchannel && wp->rle_requested == BMP_RLE_AUTO && !wp->iccprofile) {
|
||||
wp->rle = 24;
|
||||
wp->ih->compression = BI_OS2_RLE24;
|
||||
wp->ih->bitcount = 24;
|
||||
@@ -704,6 +807,12 @@ static void s_decide_outformat(BMPWRITE_R wp)
|
||||
wp->ih->bitcount = (bitsum + 7) / 8 * 8;
|
||||
}
|
||||
|
||||
if (wp->iccprofile) {
|
||||
assert(wp->ih->version >= BMPINFO_V3);
|
||||
wp->ih->version = BMPINFO_V5;
|
||||
wp->ih->size = BMPIHSIZE_V5;
|
||||
}
|
||||
|
||||
if (wp->palette) {
|
||||
wp->ih->clrused = wp->palette->numcolors;
|
||||
} else {
|
||||
@@ -757,7 +866,7 @@ API BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image)
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
if (wp->line_by_line) {
|
||||
logerr(wp->log, "Cannot switch from line-by-line to saving full image");
|
||||
logerr(wp->c.log, "Cannot switch from line-by-line to saving full image");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
|
||||
@@ -785,7 +894,7 @@ API BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image)
|
||||
break;
|
||||
}
|
||||
if (!res) {
|
||||
logerr(wp->log, "failed saving line %d", y);
|
||||
logerr(wp->c.log, "failed saving line %d", y);
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
}
|
||||
@@ -793,18 +902,21 @@ API BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image)
|
||||
if (wp->rle > 1) {
|
||||
if (EOF == s_write_one_byte(0, wp) ||
|
||||
EOF == s_write_one_byte(1, wp)) {
|
||||
logsyserr(wp->log, "Writing RLE end-of-file marker");
|
||||
logsyserr(wp->c.log, "Writing RLE end-of-file marker");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!(huff_encode_rtc(wp) && huff_flush(wp))) {
|
||||
logsyserr(wp->log, "Writing RTC end-of-file marker");
|
||||
logsyserr(wp->c.log, "Writing RTC end-of-file marker");
|
||||
return BMP_RESULT_ERROR;
|
||||
}
|
||||
}
|
||||
s_try_saving_image_size(wp);
|
||||
}
|
||||
if (wp->iccprofile)
|
||||
if (!s_write_iccprofile(wp))
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
return BMP_RESULT_OK;
|
||||
}
|
||||
@@ -855,17 +967,21 @@ API BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line)
|
||||
if (wp->rle > 1) {
|
||||
if (EOF == s_write_one_byte(0, wp) ||
|
||||
EOF == s_write_one_byte(1, wp)) {
|
||||
logsyserr(wp->log, "Writing RLE end-of-file marker");
|
||||
logsyserr(wp->c.log, "Writing RLE end-of-file marker");
|
||||
goto abort;
|
||||
}
|
||||
} else {
|
||||
if (!(huff_encode_rtc(wp) && huff_flush(wp))) {
|
||||
logsyserr(wp->log, "Writing RTC end-of-file marker");
|
||||
logsyserr(wp->c.log, "Writing RTC end-of-file marker");
|
||||
goto abort;
|
||||
}
|
||||
}
|
||||
s_try_saving_image_size(wp);
|
||||
}
|
||||
if (wp->iccprofile) {
|
||||
if (!s_write_iccprofile(wp))
|
||||
goto abort;
|
||||
}
|
||||
wp->saveimage_done = true;
|
||||
}
|
||||
|
||||
@@ -884,30 +1000,30 @@ abort:
|
||||
static bool s_save_header(BMPWRITE_R wp)
|
||||
{
|
||||
if (wp->saveimage_done || wp->line_by_line) {
|
||||
logerr(wp->log, "Image already saved.");
|
||||
logerr(wp->c.log, "Image already saved.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!wp->dimensions_set) {
|
||||
logerr(wp->log, "Must set dimensions before saving");
|
||||
logerr(wp->c.log, "Must set dimensions before saving");
|
||||
return false;
|
||||
}
|
||||
|
||||
s_decide_outformat(wp);
|
||||
|
||||
if (!s_write_bmp_file_header(wp)) {
|
||||
logsyserr(wp->log, "Writing BMP file header");
|
||||
logsyserr(wp->c.log, "Writing BMP file header");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!s_write_bmp_info_header(wp)) {
|
||||
logsyserr(wp->log, "Writing BMP info header");
|
||||
logsyserr(wp->c.log, "Writing BMP info header");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (wp->palette) {
|
||||
if (!s_write_palette(wp)) {
|
||||
logsyserr(wp->log, "Couldn't write palette");
|
||||
logsyserr(wp->c.log, "Couldn't write palette");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -971,7 +1087,7 @@ static bool s_save_line_rgb(BMPWRITE_R wp, const unsigned char *line)
|
||||
bits_used += wp->ih->bitcount;
|
||||
if (bits_used == 8) {
|
||||
if (EOF == s_write_one_byte((int)bytes, wp)) {
|
||||
logsyserr(wp->log, "Writing image to BMP file");
|
||||
logsyserr(wp->c.log, "Writing image to BMP file");
|
||||
return false;
|
||||
}
|
||||
bytes = 0;
|
||||
@@ -984,7 +1100,7 @@ static bool s_save_line_rgb(BMPWRITE_R wp, const unsigned char *line)
|
||||
|
||||
for (i = 0; i < wp->outbytes_per_pixel; i++) {
|
||||
if (EOF == s_write_one_byte((bytes >> (8*i)) & 0xff, wp)) {
|
||||
logsyserr(wp->log, "Writing image to BMP file");
|
||||
logsyserr(wp->c.log, "Writing image to BMP file");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -994,7 +1110,7 @@ static bool s_save_line_rgb(BMPWRITE_R wp, const unsigned char *line)
|
||||
if (wp->palette && bits_used != 0) {
|
||||
bytes <<= 8 - bits_used;
|
||||
if (EOF == s_write_one_byte((int)bytes, wp)) {
|
||||
logsyserr(wp->log, "Writing image to BMP file");
|
||||
logsyserr(wp->c.log, "Writing image to BMP file");
|
||||
return false;
|
||||
}
|
||||
bits_used = 0;
|
||||
@@ -1002,7 +1118,7 @@ static bool s_save_line_rgb(BMPWRITE_R wp, const unsigned char *line)
|
||||
|
||||
for (i = 0; i < wp->padding; i++) {
|
||||
if (EOF == s_write_one_byte(0, wp)) {
|
||||
logsyserr(wp->log, "Writing padding bytes to BMP file");
|
||||
logsyserr(wp->c.log, "Writing padding bytes to BMP file");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1021,7 +1137,7 @@ static bool s_save_line_rgb(BMPWRITE_R wp, const unsigned char *line)
|
||||
* repeat-run.
|
||||
*****************************************************************************/
|
||||
|
||||
static inline int s_length_of_runs(BMPWRITE_R wp, int x, int group, int minlen)
|
||||
static inline int s_length_of_runs(BMPWRITE_R wp, int group, int minlen)
|
||||
{
|
||||
int i, len = 0;
|
||||
|
||||
@@ -1065,7 +1181,7 @@ static bool s_save_line_rle(BMPWRITE_R wp, const unsigned char *line)
|
||||
|
||||
if (!wp->group) {
|
||||
if (!(wp->group = malloc(wp->width * sizeof *wp->group))) {
|
||||
logsyserr(wp->log, "allocating RLE buffer");
|
||||
logsyserr(wp->c.log, "allocating RLE buffer");
|
||||
goto abort;
|
||||
}
|
||||
}
|
||||
@@ -1122,7 +1238,7 @@ static bool s_save_line_rle(BMPWRITE_R wp, const unsigned char *line)
|
||||
* run for e.g. two repeated pixels and then restarting the literal
|
||||
* run at a cost of 2-4 bytes (depending on padding)
|
||||
*/
|
||||
if (i+l < wp->group_count && s_length_of_runs(wp, x+dx, i+l, minlen) <= small_number) {
|
||||
if (i+l < wp->group_count && s_length_of_runs(wp, i+l, minlen) <= small_number) {
|
||||
while (i+l < wp->group_count && wp->group[i+l] > (minlen-1) && dx + wp->group[i+l] < 255) {
|
||||
dx += wp->group[i+l];
|
||||
l++;
|
||||
@@ -1220,7 +1336,7 @@ abort:
|
||||
wp->group = NULL;
|
||||
wp->group_count = 0;
|
||||
}
|
||||
logsyserr(wp->log, "Writing RLE data to BMP file");
|
||||
logsyserr(wp->c.log, "Writing RLE data to BMP file");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1233,14 +1349,16 @@ abort:
|
||||
static bool s_save_line_huff(BMPWRITE_R wp, const unsigned char *line)
|
||||
{
|
||||
int x = 0, len;
|
||||
bool black = false;
|
||||
bool black = false, flipbits;
|
||||
|
||||
flipbits = !wp->huffman_fg_idx ^ wp->c.huffman_black_is_zero;
|
||||
|
||||
if (!huff_encode_eol(wp)) /* each line starts with eol */
|
||||
goto abort;
|
||||
|
||||
while (x < wp->width) {
|
||||
len = 0;
|
||||
while ((len < wp->width - x) && ((!!line[x + len]) == (black ^ !wp->huffman_fg_idx)))
|
||||
while ((len < wp->width - x) && ((!!line[x + len]) == (black ^ flipbits)))
|
||||
len++;
|
||||
if (!huff_encode(wp, len, black))
|
||||
goto abort;
|
||||
@@ -1249,7 +1367,7 @@ static bool s_save_line_huff(BMPWRITE_R wp, const unsigned char *line)
|
||||
}
|
||||
return true;
|
||||
abort:
|
||||
logsyserr(wp->log, "Writing 1-D Huffman data to BMP file");
|
||||
logsyserr(wp->c.log, "Writing 1-D Huffman data to BMP file");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1333,7 +1451,7 @@ static inline unsigned long long s_imgrgb_to_outbytes(BMPWRITE_R wp,
|
||||
break;
|
||||
|
||||
default:
|
||||
logerr(wp->log, "Panic! Bitdepth (%d) other than 8/16/32",
|
||||
logerr(wp->c.log, "Panic! Bitdepth (%d) other than 8/16/32",
|
||||
(int) wp->source_bitsperchannel);
|
||||
return (unsigned long long)-1;
|
||||
}
|
||||
@@ -1390,7 +1508,7 @@ static inline unsigned long long s_imgrgb_to_outbytes(BMPWRITE_R wp,
|
||||
break;
|
||||
|
||||
default:
|
||||
logerr(wp->log, "Panic, invalid source number format %d", wp->source_format);
|
||||
logerr(wp->c.log, "Panic, invalid source number format %d", (int) wp->source_format);
|
||||
return (unsigned long long) -1;
|
||||
}
|
||||
|
||||
@@ -1514,7 +1632,7 @@ static bool s_write_bmp_info_header(BMPWRITE_R wp)
|
||||
if (wp->ih->version == BMPINFO_OS22) {
|
||||
#ifdef DEBUG
|
||||
if (wp->ih->size < 40) {
|
||||
logerr(wp->log, "Panic! Invalid header size %d", (int) wp->ih->size);
|
||||
logerr(wp->c.log, "Panic! Invalid header size %d", (int) wp->ih->size);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
@@ -1547,11 +1665,59 @@ static bool s_write_bmp_info_header(BMPWRITE_R wp)
|
||||
}
|
||||
wp->bytes_written += 68;
|
||||
|
||||
if (wp->ih->version == BMPINFO_V4)
|
||||
return true;
|
||||
|
||||
if (!(write_u32_le(wp->file, wp->ih->intent) &&
|
||||
write_u32_le(wp->file, 0) &&
|
||||
write_u32_le(wp->file, wp->iccprofile_size) &&
|
||||
write_u32_le(wp->file, wp->ih->reserved)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* s_write_iccprofile
|
||||
*****************************************************************************/
|
||||
|
||||
static bool s_write_iccprofile(BMPWRITE_R wp)
|
||||
{
|
||||
uint32_t pos;
|
||||
|
||||
printf("Writing ICC profile\n");
|
||||
if (wp->ih->version < BMPINFO_V5 || !wp->iccprofile)
|
||||
return false;
|
||||
|
||||
pos = wp->bytes_written;
|
||||
|
||||
if (wp->iccprofile_size != fwrite(wp->iccprofile, 1, wp->iccprofile_size, wp->file)) {
|
||||
logsyserr(wp->c.log, "Error writing ICC profile to file");
|
||||
return false;
|
||||
}
|
||||
|
||||
wp->bytes_written += wp->iccprofile_size;
|
||||
|
||||
if (fseek(wp->file, IH_PROFILEDATA_OFFSET, SEEK_SET)) {
|
||||
logsyserr(wp->c.log, "Error writing ICC profile to file");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!write_u32_le(wp->file, pos))
|
||||
return false;
|
||||
|
||||
if (wp->bytes_written < (size_t) LONG_MAX)
|
||||
fseek(wp->file, wp->bytes_written, SEEK_SET);
|
||||
|
||||
printf("Wrote ICC profile\n");
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* s_write_one_byte
|
||||
*****************************************************************************/
|
||||
@@ -1574,7 +1740,7 @@ static inline int s_write_one_byte(int byte, BMPWRITE_R wp)
|
||||
|
||||
void bw_free(BMPWRITE wp)
|
||||
{
|
||||
wp->magic = 0;
|
||||
wp->c.magic = 0;
|
||||
|
||||
if (wp->group)
|
||||
free(wp->group);
|
||||
@@ -1584,8 +1750,8 @@ void bw_free(BMPWRITE wp)
|
||||
free(wp->ih);
|
||||
if (wp->fh)
|
||||
free(wp->fh);
|
||||
if (wp->log)
|
||||
logfree(wp->log);
|
||||
if (wp->c.log)
|
||||
logfree(wp->c.log);
|
||||
|
||||
free(wp);
|
||||
}
|
||||
|
||||
9
bmplib.h
9
bmplib.h
@@ -42,7 +42,7 @@
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct Bmphandle *BMPHANDLE;
|
||||
typedef union Bmphandle *BMPHANDLE;
|
||||
|
||||
|
||||
/*
|
||||
@@ -239,6 +239,9 @@ APIDECL void bmpread_set_insanity_limit(BMPHANDLE h, size_t limit);
|
||||
APIDECL int bmpread_is_64bit(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpread_set_64bit_conv(BMPHANDLE h, BMPCONV64 conv);
|
||||
|
||||
APIDECL size_t bmpread_iccprofile_size(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpread_load_iccprofile(BMPHANDLE h, unsigned char **profile);
|
||||
|
||||
APIDECL BMPINFOVER bmpread_info_header_version(BMPHANDLE h);
|
||||
APIDECL const char* bmpread_info_header_name(BMPHANDLE h);
|
||||
APIDECL int bmpread_info_header_size(BMPHANDLE h);
|
||||
@@ -268,11 +271,15 @@ APIDECL BMPRESULT bmpwrite_set_orientation(BMPHANDLE h, BMPORIENT orientation);
|
||||
APIDECL BMPRESULT bmpwrite_set_64bit(BMPHANDLE h);
|
||||
APIDECL BMPRESULT bmpwrite_set_huffman_img_fg_idx(BMPHANDLE h, int idx);
|
||||
|
||||
APIDECL BMPRESULT bmpwrite_set_iccprofile(BMPHANDLE h, size_t size,
|
||||
const unsigned char *iccprofile);
|
||||
|
||||
APIDECL BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image);
|
||||
APIDECL BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line);
|
||||
|
||||
|
||||
APIDECL BMPRESULT bmp_set_number_format(BMPHANDLE h, BMPFORMAT format);
|
||||
APIDECL BMPRESULT bmp_set_huffman_t4black_value(BMPHANDLE h, int blackidx);
|
||||
|
||||
APIDECL void bmp_free(BMPHANDLE h);
|
||||
|
||||
|
||||
@@ -116,14 +116,14 @@ int main(int argc, char *argv[])
|
||||
fprintf(file, "static const int blackroot = %d;\n", black_tree);
|
||||
fprintf(file, "static const int whiteroot = %d;\n\n\n", white_tree);
|
||||
fprintf(file, "static const struct Node nodebuffer[] = {\n");
|
||||
for (i = 0; i < ARR_SIZE(nodebuffer); i++) {
|
||||
for (i = 0; i < (int) ARR_SIZE(nodebuffer); i++) {
|
||||
fprintf(file, "\t{ %3d, %3d, %4d, %d, %d },\n",
|
||||
n[i].l, n[i].r, n[i].value, n[i].terminal, n[i].makeup);
|
||||
}
|
||||
fputs("};\n\n", file);
|
||||
|
||||
fputs("static const struct Huffcode huff_term_black[] = {\n\t", file);
|
||||
for (i = 0; i < ARR_SIZE(huff_term_black); i++) {
|
||||
for (i = 0; i < (int) ARR_SIZE(huff_term_black); i++) {
|
||||
fprintf(file, "{ 0x%02hx, %2d },",
|
||||
str2bits(huff_term_black[i].bits),
|
||||
(int) strlen(huff_term_black[i].bits));
|
||||
@@ -135,7 +135,7 @@ int main(int argc, char *argv[])
|
||||
fputs("\n};\n\n", file);
|
||||
|
||||
fputs("static const struct Huffcode huff_term_white[] = {\n\t", file);
|
||||
for (i = 0; i < ARR_SIZE(huff_term_white); i++) {
|
||||
for (i = 0; i < (int) ARR_SIZE(huff_term_white); i++) {
|
||||
fprintf(file, "{ 0x%02hx, %2d },",
|
||||
str2bits(huff_term_white[i].bits),
|
||||
(int) strlen(huff_term_white[i].bits));
|
||||
@@ -147,7 +147,7 @@ int main(int argc, char *argv[])
|
||||
fputs("\n};\n\n", file);
|
||||
|
||||
fputs("static const struct Huffcode huff_makeup_black[] = {\n\t", file);
|
||||
for (i = 0; i < ARR_SIZE(huff_makeup_black); i++) {
|
||||
for (i = 0; i < (int) ARR_SIZE(huff_makeup_black); i++) {
|
||||
fprintf(file, "{ 0x%02hx, %2d },",
|
||||
str2bits(huff_makeup_black[i].bits),
|
||||
(int) strlen(huff_makeup_black[i].bits));
|
||||
@@ -159,7 +159,7 @@ int main(int argc, char *argv[])
|
||||
fputs("\n};\n\n", file);
|
||||
|
||||
fputs("static const struct Huffcode huff_makeup_white[] = {\n\t", file);
|
||||
for (i = 0; i < ARR_SIZE(huff_makeup_white); i++) {
|
||||
for (i = 0; i < (int) ARR_SIZE(huff_makeup_white); i++) {
|
||||
fprintf(file, "{ 0x%02hx, %2d },",
|
||||
str2bits(huff_makeup_white[i].bits),
|
||||
(int) strlen(huff_makeup_white[i].bits));
|
||||
@@ -197,20 +197,20 @@ static void s_buildtree(void)
|
||||
|
||||
memset(nodebuffer, 0, sizeof nodebuffer);
|
||||
|
||||
for (i = 0; i < ARR_SIZE(huff_term_black); i++) {
|
||||
for (i = 0; i < (int) ARR_SIZE(huff_term_black); i++) {
|
||||
add_node(&black_tree, huff_term_black[i].bits,
|
||||
huff_term_black[i].number, false);
|
||||
}
|
||||
for (i = 0; i < ARR_SIZE(huff_makeup_black); i++) {
|
||||
for (i = 0; i < (int) ARR_SIZE(huff_makeup_black); i++) {
|
||||
add_node(&black_tree, huff_makeup_black[i].bits,
|
||||
huff_makeup_black[i].number, true);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARR_SIZE(huff_term_white); i++) {
|
||||
for (i = 0; i < (int) ARR_SIZE(huff_term_white); i++) {
|
||||
add_node(&white_tree, huff_term_white[i].bits,
|
||||
huff_term_white[i].number, false);
|
||||
}
|
||||
for (i = 0; i < ARR_SIZE(huff_makeup_white); i++) {
|
||||
for (i = 0; i < (int) ARR_SIZE(huff_makeup_white); i++) {
|
||||
add_node(&white_tree, huff_makeup_white[i].bits,
|
||||
huff_makeup_white[i].number, true);
|
||||
}
|
||||
@@ -229,7 +229,7 @@ static void add_node(int *nodeidx, const char *bits, int value, bool makeup)
|
||||
nodebuffer[*nodeidx].l = -1;
|
||||
nodebuffer[*nodeidx].r = -1;
|
||||
}
|
||||
if (nnodes > ARR_SIZE(nodebuffer)) {
|
||||
if (nnodes > (int) ARR_SIZE(nodebuffer)) {
|
||||
printf("too many nodes (have %d, max is %d)\n",
|
||||
nnodes, (int) ARR_SIZE(nodebuffer));
|
||||
exit(1);
|
||||
|
||||
@@ -227,7 +227,7 @@ bool huff_flush(BMPWRITE_R wp)
|
||||
while (wp->hufbuf_len >= 8) {
|
||||
byte = 0x00ff & (wp->hufbuf >> (wp->hufbuf_len - 8));
|
||||
if (EOF == putc(byte, wp->file)) {
|
||||
logsyserr(wp->log, "writing Huffman bitmap");
|
||||
logsyserr(wp->c.log, "writing Huffman bitmap");
|
||||
return false;
|
||||
}
|
||||
wp->bytes_written++;
|
||||
|
||||
11
logging.c
11
logging.c
@@ -29,6 +29,12 @@
|
||||
#include "config.h"
|
||||
#include "logging.h"
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define MAY_BE_UNUSED __attribute__((unused))
|
||||
#else
|
||||
#define MAY_BE_UNUSED
|
||||
#endif
|
||||
|
||||
|
||||
struct Log {
|
||||
int size;
|
||||
@@ -169,8 +175,9 @@ void logsyserr(LOG log, const char *fmt, ...)
|
||||
* s_log()
|
||||
*********************************************************/
|
||||
|
||||
static void s_log(LOG log, const char *file, int line, const char *function,
|
||||
const char *etxt, const char *fmt, va_list args)
|
||||
static void s_log(LOG log, const char *file MAY_BE_UNUSED, int line MAY_BE_UNUSED,
|
||||
const char *function MAY_BE_UNUSED,
|
||||
const char *etxt, const char *fmt, va_list args)
|
||||
{
|
||||
va_list argsdup;
|
||||
int len = 0,addl_len, required_len;
|
||||
|
||||
15
meson.build
15
meson.build
@@ -1,16 +1,25 @@
|
||||
project('bmplib', 'c', default_options: ['c_std=c11'], version: '1.7.4')
|
||||
project('bmplib', 'c', default_options: ['c_std=c11', 'warning_level=3'], version: '1.7.5')
|
||||
|
||||
cc = meson.get_compiler('c')
|
||||
|
||||
add_project_arguments(['-pedantic','-fvisibility=hidden'], language : 'c')
|
||||
|
||||
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
|
||||
|
||||
if get_option('sanitize')
|
||||
sanitize = [
|
||||
'-fsanitize=signed-integer-overflow',
|
||||
'-fsanitize=undefined',
|
||||
'-fsanitize=float-divide-by-zero',
|
||||
]
|
||||
add_project_arguments(sanitize, language : 'c')
|
||||
add_project_link_arguments(sanitize, language: 'c')
|
||||
endif
|
||||
|
||||
m_dep = cc.find_library('m', required : false)
|
||||
|
||||
conf_data = configuration_data()
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
option('insanity_limit_mb', type: 'integer', min: 0, value: 500)
|
||||
option('sanitize', type: 'boolean', value: false)
|
||||
|
||||
Reference in New Issue
Block a user