8 Commits
1.7.4 ... icc

Author SHA1 Message Date
Rupert
91066fc509 rough draft writing of ICC profiles 2025-04-13 17:10:42 +02:00
Rupert
de7004a158 add basic ICC profile support
- new functions:
 - bmpread_iccprofile_size()
 - bmpread_load_iccprofile()
- only pass on profile as is, profile is not interpreted or applied
2025-04-12 19:47:14 +02:00
Rupert
7523138fbb bump version to 1.7.5 2025-04-10 18:29:17 +02:00
Rupert
592605e06f add sanitize meson option 2025-04-10 18:29:17 +02:00
Rupert
eeae2205c5 eliminate all -Wextra warnings
revert height back to int and make INT_MIN an invalid height. Having
a possible height value that's only valid for top-down but not bottom-up
BMPs just creates headaches down the way.
2025-04-10 18:29:17 +02:00
Rupert
2d7763de7f refactor huffman bit-flipping for readability 2025-04-10 11:57:12 +02:00
Rupert
7513352a7b add bmp_set_huffman_t4black_value()
ITU-T T.4 defines 'black' and 'white' (referring to fore- and back-
ground, respectively) pixel sequences, but it doesn't prescribe which
of those is represented by 0 or 1. That would have to be defined by the
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.

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.
2025-04-09 23:11:58 +02:00
Rupert
ea7b93ce64 read/write handles: move shared portion into struct Bmpcommon
make the generic handle a union of common/read/write
2025-04-09 18:40:06 +02:00
14 changed files with 591 additions and 215 deletions

View File

@@ -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`

View File

@@ -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).

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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.");
}

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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++;

View File

@@ -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;

View File

@@ -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()

View File

@@ -1 +1,2 @@
option('insanity_limit_mb', type: 'integer', min: 0, value: 500)
option('sanitize', type: 'boolean', value: false)