mirror of
https://github.com/rupertwh/bmplib.git
synced 2026-04-11 00:32:47 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91066fc509 | ||
|
|
de7004a158 |
11
bmp-common.h
11
bmp-common.h
@@ -168,6 +168,8 @@ struct Bmpwrite {
|
||||
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;
|
||||
@@ -258,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" */
|
||||
@@ -317,7 +320,9 @@ struct Bmpinfo {
|
||||
enum BmpInfoVer version;
|
||||
};
|
||||
|
||||
#define IH_PROFILEDATA_OFFSET (14L + 112L)
|
||||
|
||||
#define MAX_ICCPROFILE_SIZE (1UL << 20)
|
||||
|
||||
|
||||
#define BI_RGB 0
|
||||
@@ -344,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
|
||||
|
||||
116
bmp-read.c
116
bmp-read.c
@@ -267,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;
|
||||
@@ -274,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
|
||||
*****************************************************************************/
|
||||
|
||||
172
bmp-write.c
172
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);
|
||||
@@ -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
|
||||
*****************************************************************************/
|
||||
@@ -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;
|
||||
@@ -577,6 +659,27 @@ static bool s_is_setting_compatible(BMPWRITE_R wp, const char *setting, ...)
|
||||
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);
|
||||
@@ -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 {
|
||||
@@ -805,6 +914,9 @@ API BMPRESULT bmpwrite_save_image(BMPHANDLE h, const unsigned char *image)
|
||||
}
|
||||
s_try_saving_image_size(wp);
|
||||
}
|
||||
if (wp->iccprofile)
|
||||
if (!s_write_iccprofile(wp))
|
||||
return BMP_RESULT_ERROR;
|
||||
|
||||
return BMP_RESULT_OK;
|
||||
}
|
||||
@@ -866,6 +978,10 @@ API BMPRESULT bmpwrite_save_line(BMPHANDLE h, const unsigned char *line)
|
||||
}
|
||||
s_try_saving_image_size(wp);
|
||||
}
|
||||
if (wp->iccprofile) {
|
||||
if (!s_write_iccprofile(wp))
|
||||
goto abort;
|
||||
}
|
||||
wp->saveimage_done = true;
|
||||
}
|
||||
|
||||
@@ -1549,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
|
||||
*****************************************************************************/
|
||||
|
||||
6
bmplib.h
6
bmplib.h
@@ -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,6 +271,9 @@ 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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user