diff --git a/bmp-common.h b/bmp-common.h
index 8866dd9..cb11722 100644
--- a/bmp-common.h
+++ b/bmp-common.h
@@ -102,6 +102,7 @@ enum ReadState {
RS_DIMENSIONS_QUERIED,
RS_LOAD_STARTED,
RS_LOAD_DONE,
+ RS_ARRAY,
RS_FATAL,
};
@@ -111,6 +112,9 @@ struct Bmpread {
size_t bytes_read; /* number of bytes we have read from the file */
struct Bmpfile *fh;
struct Bmpinfo *ih;
+ struct Arraylist *arrayimgs;
+ int narrayimgs;
+ bool is_arrayimg;
unsigned int insanity_limit;
int width;
int height;
@@ -337,6 +341,14 @@ struct Bmpinfo {
enum BmpInfoVer version;
};
+struct Bmparray {
+ uint16_t type;
+ uint32_t size;
+ uint32_t offsetnext;
+ uint16_t screenwidth;
+ uint16_t screenheight;
+};
+
#define IH_PROFILEDATA_OFFSET (14L + 112L)
#define MAX_ICCPROFILE_SIZE (1UL << 20)
diff --git a/bmp-read-icons.c b/bmp-read-icons.c
new file mode 100644
index 0000000..bf8096b
--- /dev/null
+++ b/bmp-read-icons.c
@@ -0,0 +1,353 @@
+/* bmplib - bmp-read-icons.c
+ *
+ * Copyright (c) 2025, Rupert Weber.
+ *
+ * This file is part of bmplib.
+ * bmplib is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.
+ * If not, see
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define BMPLIB_LIB
+
+#include "config.h"
+#include "bmplib.h"
+#include "logging.h"
+#include "bmp-common.h"
+#include "bmp-read-icons.h"
+
+
+
+
+/********************************************************
+ * bmpread_array_num
+ *******************************************************/
+
+API int bmpread_array_num(BMPHANDLE h)
+{
+ BMPREAD rp;
+
+ if (!(rp = cm_read_handle(h)))
+ return BMP_RESULT_ERROR;
+
+ if (rp->read_state != RS_ARRAY) {
+ logerr(rp->c.log, "Not a bitmap array");
+ return -1;
+ }
+
+ return rp->narrayimgs;
+}
+
+
+/********************************************************
+ * bmpread_array_info
+ *******************************************************/
+
+API BMPRESULT bmpread_array_info(BMPHANDLE h, struct BmpArrayInfo *ai, int idx)
+{
+ BMPREAD rp;
+
+ if (!(rp = cm_read_handle(h)))
+ return BMP_RESULT_ERROR;
+
+ if (rp->read_state != RS_ARRAY) {
+ logerr(rp->c.log, "Not a bitmap array");
+ return BMP_RESULT_ERROR;
+ }
+
+ if (idx < 0 || idx >= rp->narrayimgs) {
+ logerr(rp->c.log, "Invalid array index %d. Max is %d", idx, rp->narrayimgs - 1);
+ return BMP_RESULT_ERROR;
+ }
+
+ if (!ai) {
+ logerr(rp->c.log, "Invalid array info pointer (NULL)");
+ return BMP_RESULT_ERROR;
+ }
+
+ struct Arraylist *img = &rp->arrayimgs[idx];
+ BMPREAD imgrp = (BMPREAD)img->handle;
+
+ memset(ai, 0, sizeof *ai);
+
+ ai->type = imgrp->fh->type;
+ ai->handle = img->handle;
+ ai->width = imgrp->width;
+ ai->height = imgrp->height;
+ if (imgrp->ih->bitcount <= 8)
+ ai->ncolors = 1 << imgrp->ih->bitcount;
+ else
+ ai->ncolors = 0;
+ ai->screenwidth = img->ah.screenwidth;
+ ai->screenheight = img->ah.screenheight;
+
+ return BMP_RESULT_OK;
+}
+
+
+/********************************************************
+ * icon_read_array
+ *******************************************************/
+static bool s_read_array_header(BMPREAD_R rp, struct Bmparray *ah);
+static void s_array_header_from_file_header(struct Bmparray *ah, struct Bmpfile *fh);
+
+bool icon_read_array(BMPREAD_R rp)
+{
+ struct Arraylist *imgs = NULL;
+ struct Bmparray ah = { 0 };
+ int n = 0;
+ const int nmax = 16;
+ bool invalid = false;
+
+ if (!(imgs = calloc(nmax, sizeof *imgs))) {
+ logsyserr(rp->c.log, "Allocating bitmap array list");
+ rp->lasterr = BMP_ERR_MEMORY;
+ return false;
+ }
+
+ s_array_header_from_file_header(&ah, rp->fh);
+
+ while (n < nmax) {
+ if (ah.type != BMPFILE_BA)
+ break;
+
+ memcpy(&imgs[n].ah, &ah, sizeof ah);
+
+ imgs[n].handle = bmpread_new(rp->file);
+ if (imgs[n].handle) {
+ if (BMP_RESULT_OK == bmpread_load_info(imgs[n].handle)) {
+ ((BMPREAD)imgs[n].handle)->is_arrayimg = true;
+ n++;
+ } else {
+ bmp_free(imgs[n].handle);
+ invalid = true;
+ rp->lasterr = BMP_ERR_HEADER;
+ }
+ } else {
+ invalid = true;
+ rp->lasterr = BMP_ERR_HEADER;
+ }
+
+ if (!ah.offsetnext)
+ break;
+
+#if ( LONG_MAX <= 0x7fffffffL )
+ if (ah.offsetnext > (unsigned long)LONG_MAX) {
+ logerr(rp->c.log, "Invalid offset to next array image: %lu", (unsigned long)ah.offsetnext);
+ invalid = true;
+ rp->lasterr = BMP_ERR_HEADER;
+ break;
+ }
+#endif
+ if (fseek(rp->file, ah.offsetnext, SEEK_SET)) {
+ logsyserr(rp->c.log, "Seeking next array header");
+ invalid = true;
+ rp->lasterr = BMP_ERR_FILEIO;
+ break;
+ }
+
+ if (!s_read_array_header(rp, &ah)) {
+ invalid = true;
+ break;
+ }
+ }
+
+ rp->arrayimgs = imgs;
+ rp->narrayimgs = n;
+
+ return !invalid;
+}
+
+
+/********************************************************
+ * s_read_array_header
+ *******************************************************/
+
+static bool s_read_array_header(BMPREAD_R rp, struct Bmparray *ah)
+{
+ if (read_u16_le(rp->file, &ah->type) &&
+ read_u32_le(rp->file, &ah->size) &&
+ read_u32_le(rp->file, &ah->offsetnext) &&
+ read_u16_le(rp->file, &ah->screenwidth) &&
+ read_u16_le(rp->file, &ah->screenheight)) {
+
+ return true;
+ }
+
+ if (feof(rp->file)) {
+ logerr(rp->c.log, "unexpected end-of-file while reading "
+ "array header");
+ rp->lasterr = BMP_ERR_TRUNCATED;
+ } else {
+ logsyserr(rp->c.log, "error reading array header");
+ rp->lasterr = BMP_ERR_FILEIO;
+ }
+
+ return false;
+}
+
+
+/********************************************************
+ * s_array_header_from_file_header
+ *******************************************************/
+
+static void s_array_header_from_file_header(struct Bmparray *ah, struct Bmpfile *fh)
+{
+ ah->type = fh->type;
+ ah->size = fh->size;
+ ah->offsetnext = (uint32_t)fh->reserved2 << 16 | fh->reserved1;
+ ah->screenwidth = fh->offbits & 0xffff;
+ ah->screenheight = (fh->offbits >> 16) & 0xffff;
+}
+
+
+/********************************************************
+ * icon_load_masks
+ *******************************************************/
+
+long icon_load_masks(BMPREAD_R rp)
+{
+ /* OS/2 icons and pointers contain 1-bit AND and XOR masks, stacked in a single
+ * image. For monochrome (IC/PT), that's all the image; for color (CI/CP), these are
+ * followed by a complete color image (including headers), the masks are only used
+ * for transparency information.
+ */
+
+ BMPHANDLE hmono = NULL;
+ BMPREAD rpmono;
+ unsigned char *monobuf = NULL;
+ size_t bufsize;
+ unsigned bmptype = rp->fh->type;
+ long posmono = 0, poscolor = 0;
+
+ if (fseek(rp->file, -14, SEEK_CUR)) {
+ logsyserr(rp->c.log, "Seeking to start of icon/pointer");
+ goto abort;
+ }
+
+ if (-1 == (posmono = ftell(rp->file))) {
+ logsyserr(rp->c.log, "Saving file position");
+ goto abort;
+ }
+
+ /* first, load monochrome XOR/AND bitmap. We'll use the
+ * AND bitmap as alpha channel for the color bitmap
+ */
+
+ if (!(hmono = bmpread_new(rp->file))) {
+ logerr(rp->c.log, "Getting handle for monochrome XOR/AND map");
+ goto abort;
+ }
+ rpmono = cm_read_handle(hmono);
+
+ rpmono->read_state = RS_EXPECT_ICON_MASK;
+ if (BMP_RESULT_OK != bmpread_load_info(hmono)) {
+ logerr(rp->c.log, "%s", bmp_errmsg(hmono));
+ goto abort;
+ }
+
+ if (rpmono->fh->type != bmptype) {
+ logerr(rp->c.log, "File type mismatch. Have 0x%04x, expected 0x%04x",
+ (unsigned)rpmono->fh->type, bmptype);
+ }
+
+ if (rp->fh->type == BMPFILE_CI || rp->fh->type == BMPFILE_CP) {
+ if (-1 == (poscolor = ftell(rp->file))) {
+ logsyserr(rp->c.log, "Saving position of color header");
+ goto abort;
+ }
+ }
+
+ if (!(rpmono->width > 0 && rpmono->height > 0 && rpmono->width <=512 && rpmono->height <= 512)) {
+ logerr(rp->c.log, "Invalid icon/pointer dimensions: %dx%d", rpmono->width, rpmono->height);
+ goto abort;
+ }
+
+ if (rpmono->ih->bitcount != 1) {
+ logerr(rp->c.log, "Invalid icon/pointer monochrome bitcount: %d", rpmono->ih->bitcount);
+ goto abort;
+ }
+
+ if (rpmono->height & 1) {
+ logerr(rp->c.log, "Invalid odd icon/pointer height: %d (must be even)", rpmono->height);
+ goto abort;
+ }
+
+ int width, height, bitsperchannel, channels;
+ if (BMP_RESULT_OK != bmpread_dimensions(hmono, &width, &height, &channels, &bitsperchannel, NULL)) {
+ logerr(rp->c.log, "%s", bmp_errmsg(hmono));
+ goto abort;
+ }
+
+ height /= 2; /* mochrome contains two stacked bitmaps (AND and XOR) */
+
+ if (channels != 3 || bitsperchannel != 8) {
+ logerr(rp->c.log, "Unexpected result color depth for monochrome image: "
+ "%d channels, %d bits/channel", channels, bitsperchannel);
+ goto abort;
+ }
+
+ /* store the AND/XOR bitmaps in the main BMPREAD struct */
+
+ bufsize = bmpread_buffersize(hmono);
+ if (BMP_RESULT_OK != bmpread_load_image(hmono, &monobuf)) {
+ logerr(rp->c.log, "%s", bmp_errmsg(hmono));
+ goto abort;
+ }
+
+ if (!(bufsize > 0 && monobuf != NULL)) {
+ logerr(rp->c.log, "Panic! unkown error while loading monochrome bitmap");
+ goto abort;
+ }
+
+ if (!(rp->icon_mono_and = malloc(width * height))) {
+ logsyserr(rp->c.log, "Allocating mono AND bitmap");
+ goto abort;
+ }
+ if (!(rp->icon_mono_xor = malloc(width * height))) {
+ logsyserr(rp->c.log, "Allocating mono XOR bitmap");
+ goto abort;
+ }
+
+ for (int i = 0; i < width * height; i++)
+ rp->icon_mono_and[i] = 255 - monobuf[3 * i];
+
+ for (int i = 0; i < width * height; i++)
+ rp->icon_mono_xor[i] = monobuf[3 * (width * height + i)];
+
+ rp->icon_mono_width = width;
+ rp->icon_mono_height = height;
+ free(monobuf);
+ monobuf = NULL;
+ bmp_free(hmono);
+ hmono = NULL;
+
+ if (rp->fh->type == BMPFILE_CI || rp->fh->type == BMPFILE_CP)
+ return poscolor;
+
+ return posmono;
+
+abort:
+ if (hmono)
+ bmp_free(hmono);
+ if (monobuf)
+ free(monobuf);
+ return -1;
+}
diff --git a/bmp-read-icons.h b/bmp-read-icons.h
new file mode 100644
index 0000000..dcb343a
--- /dev/null
+++ b/bmp-read-icons.h
@@ -0,0 +1,28 @@
+/* bmplib - bmp-read-icons.h
+ *
+ * Copyright (c) 2025, Rupert Weber.
+ *
+ * This file is part of bmplib.
+ * bmplib is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.
+ * If not, see
+ */
+
+
+struct Arraylist {
+ struct Bmparray ah;
+ BMPHANDLE handle;
+};
+
+long icon_load_masks(BMPREAD_R rp);
+bool icon_read_array(BMPREAD_R rp);
diff --git a/bmp-read-loadimage.c b/bmp-read-loadimage.c
index 720579b..6a97a7f 100644
--- a/bmp-read-loadimage.c
+++ b/bmp-read-loadimage.c
@@ -130,6 +130,11 @@ static BMPRESULT s_load_image_or_line(BMPREAD_R rp, unsigned char **restrict buf
return BMP_RESULT_ERROR;
}
+ if (rp->read_state >= RS_ARRAY) {
+ logerr(rp->c.log, "Invalid operation on bitmap array");
+ return BMP_RESULT_ERROR;
+ }
+
if (rp->read_state >= RS_LOAD_DONE) {
logerr(rp->c.log, "Cannot load image more than once!");
return BMP_RESULT_ERROR;
diff --git a/bmp-read.c b/bmp-read.c
index 764e9c9..9e19881 100644
--- a/bmp-read.c
+++ b/bmp-read.c
@@ -32,6 +32,7 @@
#include "bmplib.h"
#include "logging.h"
#include "bmp-common.h"
+#include "bmp-read-icons.h"
#include "bmp-read.h"
@@ -92,7 +93,6 @@ abort:
*****************************************************************************/
static bool s_read_file_header(BMPREAD_R rp);
static bool s_read_info_header(BMPREAD_R rp);
-static long s_load_icon_masks(BMPREAD_R rp);
static bool s_is_bmptype_supported(BMPREAD_R rp);
static struct Palette* s_read_palette(BMPREAD_R rp);
static bool s_read_colormasks(BMPREAD_R rp);
@@ -123,7 +123,7 @@ API BMPRESULT bmpread_load_info(BMPHANDLE h)
case BMPFILE_IC:
case BMPFILE_PT:
if (rp->read_state < RS_EXPECT_ICON_MASK) {
- pos = s_load_icon_masks(rp);
+ pos = icon_load_masks(rp);
if (pos < 0)
goto abort;
@@ -151,9 +151,18 @@ API BMPRESULT bmpread_load_info(BMPHANDLE h)
break;
case BMPFILE_BA:
- logerr(rp->c.log, "OS/2 Bitmap arrays not supported");
- rp->lasterr = BMP_ERR_UNSUPPORTED;
- goto abort;
+ if (rp->is_arrayimg) {
+ logerr(rp->c.log, "Invalid nested bitmap array");
+ goto abort;
+ }
+ if (!icon_read_array(rp)) {
+ logerr(rp->c.log, "Failed to read icon array index");
+ goto abort;
+ }
+
+ rp->read_state = RS_ARRAY;
+ return BMP_RESULT_ARRAY;
+
default:
logerr(rp->c.log, "Unkown BMP type 0x%04x\n", (unsigned int) rp->fh->type);
@@ -262,141 +271,6 @@ abort:
}
-/*****************************************************************************
- * s_load_icon_masks
- *****************************************************************************/
-
-static long s_load_icon_masks(BMPREAD_R rp)
-{
- /* OS/2 icons and pointers contain 1-bit AND and XOR masks, stacked in a single
- * image. For monochrome (IC/PT), that's all the image; for color (CI/CP), these are
- * followed by a complete color image (including headers), the masks are only used
- * for transparency information.
- */
-
- BMPHANDLE hmono = NULL;
- BMPREAD rpmono;
- unsigned char *monobuf = NULL;
- size_t bufsize;
- unsigned bmptype = rp->fh->type;
- long posmono = 0, poscolor = 0;
-
- if (fseek(rp->file, -14, SEEK_CUR)) {
- logsyserr(rp->c.log, "Seeking to start of icon/pointer");
- goto abort;
- }
-
- if (-1 == (posmono = ftell(rp->file))) {
- logsyserr(rp->c.log, "Saving file position");
- goto abort;
- }
-
- /* first, load monochrome XOR/AND bitmap. We'll use the
- * AND bitmap as alpha channel
- */
-
- if (!(hmono = bmpread_new(rp->file))) {
- logerr(rp->c.log, "Getting handle for monochrome XOR/AND map");
- goto abort;
- }
- rpmono = cm_read_handle(hmono);
-
- rpmono->read_state = RS_EXPECT_ICON_MASK;
- if (BMP_RESULT_OK != bmpread_load_info(hmono)) {
- logerr(rp->c.log, "%s", bmp_errmsg(hmono));
- goto abort;
- }
-
- if (rpmono->fh->type != bmptype) {
- logerr(rp->c.log, "File type mismatch. Have 0x%04x, expected 0x%04x",
- (unsigned)rpmono->fh->type, bmptype);
- }
-
- if (rp->fh->type == BMPFILE_CI || rp->fh->type == BMPFILE_CP) {
- if (-1 == (poscolor = ftell(rp->file))) {
- logsyserr(rp->c.log, "Saving position of color header");
- goto abort;
- }
- }
-
- if (!(rpmono->width > 0 && rpmono->height > 0 && rpmono->width <=512 && rpmono->height <= 512)) {
- logerr(rp->c.log, "Invalid icon/pointer dimensions: %dx%d", rpmono->width, rpmono->height);
- goto abort;
- }
-
- if (rpmono->ih->bitcount != 1) {
- logerr(rp->c.log, "Invalid icon/pointer monochrome bitcount: %d", rpmono->ih->bitcount);
- goto abort;
- }
-
- if (rpmono->height & 1) {
- logerr(rp->c.log, "Invalid odd icon/pointer height: %d (must be even)", rpmono->height);
- goto abort;
- }
-
- int width, height, bitsperchannel, channels;
- if (BMP_RESULT_OK != bmpread_dimensions(hmono, &width, &height, &channels, &bitsperchannel, NULL)) {
- logerr(rp->c.log, "%s", bmp_errmsg(hmono));
- goto abort;
- }
-
- height /= 2; /* mochrome contains two stacked bitmaps (AND and XOR) */
-
- if (channels != 3 || bitsperchannel != 8) {
- logerr(rp->c.log, "Unexpected result color depth for monochrome image: %d channels, %d bits/channel",
- channels, bitsperchannel);
- goto abort;
- }
-
- /* store the AND/XOR bitmaps in the main BMPREAD struct */
-
- bufsize = bmpread_buffersize(hmono);
- if (BMP_RESULT_OK != bmpread_load_image(hmono, &monobuf)) {
- logerr(rp->c.log, "%s", bmp_errmsg(hmono));
- goto abort;
- }
-
- if (!(bufsize > 0 && monobuf != NULL)) {
- logerr(rp->c.log, "Panic! unkown error while loading monochrome bitmap");
- goto abort;
- }
-
- if (!(rp->icon_mono_and = malloc(width * height))) {
- logsyserr(rp->c.log, "Allocating mono AND bitmap");
- goto abort;
- }
- if (!(rp->icon_mono_xor = malloc(width * height))) {
- logsyserr(rp->c.log, "Allocating mono XOR bitmap");
- goto abort;
- }
-
- for (int i = 0; i < width * height; i++)
- rp->icon_mono_and[i] = 255 - monobuf[3 * i];
-
- for (int i = 0; i < width * height; i++)
- rp->icon_mono_xor[i] = monobuf[3 * (width * height + i)];
-
- rp->icon_mono_width = width;
- rp->icon_mono_height = height;
- free(monobuf);
- monobuf = NULL;
- bmp_free(hmono);
- hmono = NULL;
-
- if (rp->fh->type == BMPFILE_CI || rp->fh->type == BMPFILE_CP)
- return poscolor;
-
- return posmono;
-
-abort:
- if (hmono)
- bmp_free(hmono);
- if (monobuf)
- free(monobuf);
- return -1;
-}
-
-
/*****************************************************************************
* bmpread_set_64bit_conv
*****************************************************************************/
@@ -454,7 +328,7 @@ API int bmpread_is_64bit(BMPHANDLE h)
if (!(rp = cm_read_handle(h)))
return 0;
- if (rp->read_state < RS_HEADER_OK || rp->read_state >= RS_FATAL)
+ if (rp->read_state < RS_HEADER_OK || rp->read_state >= RS_ARRAY)
return 0;
if (rp->ih->bitcount == 64)
@@ -475,7 +349,7 @@ API size_t bmpread_iccprofile_size(BMPHANDLE h)
if (!(rp = cm_read_handle(h)))
return 0;
- if (rp->read_state < RS_HEADER_OK || rp->read_state >= RS_FATAL)
+ if (rp->read_state < RS_HEADER_OK || rp->read_state >= RS_ARRAY)
return 0;
if (rp->ih->cstype == PROFILE_EMBEDDED && rp->ih->profilesize <= MAX_ICCPROFILE_SIZE) {
@@ -501,7 +375,7 @@ API BMPRESULT bmpread_load_iccprofile(BMPHANDLE h, unsigned char **profile)
if (!(rp = cm_read_handle(h)))
goto abort;
- if (rp->read_state < RS_HEADER_OK || rp->read_state >= RS_FATAL) {
+ if (rp->read_state < RS_HEADER_OK || rp->read_state >= RS_ARRAY) {
logerr(rp->c.log, "Must load info before loading ICC profile");
goto abort;
}
@@ -598,7 +472,7 @@ API BMPRESULT bmpread_dimensions(BMPHANDLE h, int* restrict width,
if (rp->read_state < RS_HEADER_OK)
bmpread_load_info((BMPHANDLE)(void*)rp);
- if (rp->read_state < RS_HEADER_OK || rp->read_state >= RS_FATAL)
+ if (rp->read_state < RS_HEADER_OK || rp->read_state >= RS_ARRAY)
return BMP_RESULT_ERROR;
if (width) {
@@ -643,6 +517,9 @@ BMPRESULT br_set_number_format(BMPREAD_R rp, enum BmpFormat format)
return BMP_RESULT_OK;
}
+ if (rp->read_state >= RS_ARRAY)
+ return BMP_RESULT_ERROR;
+
switch (format) {
case BMP_FORMAT_INT:
/* always ok */
@@ -737,7 +614,7 @@ static int s_single_dim_val(BMPHANDLE h, enum Dimint dim)
if (!(rp = cm_read_handle(h)))
return 0;
- if (rp->read_state < RS_HEADER_OK || rp->read_state >= RS_FATAL)
+ if (rp->read_state < RS_HEADER_OK || rp->read_state >= RS_ARRAY)
return 0;
switch (dim) {
@@ -789,7 +666,7 @@ API size_t bmpread_buffersize(BMPHANDLE h)
if (!(rp = cm_read_handle(h)))
return 0;
- if (rp->read_state < RS_HEADER_OK || rp->read_state >= RS_FATAL)
+ if (rp->read_state < RS_HEADER_OK || rp->read_state >= RS_ARRAY)
return 0;
rp->read_state = MAX(RS_DIMENSIONS_QUERIED, rp->read_state);
@@ -877,6 +754,16 @@ void br_free(BMPREAD rp)
{
rp->c.magic = 0;
+ if (rp->is_arrayimg)
+ return;
+
+ if (rp->arrayimgs) {
+ for (int i = 0; i < rp->narrayimgs; i++) {
+ ((BMPREAD)rp->arrayimgs[i].handle)->is_arrayimg = false;
+ br_free((BMPREAD)rp->arrayimgs[i].handle);
+ }
+ free(rp->arrayimgs);
+ }
if (rp->icon_mono_and)
free(rp->icon_mono_and);
if (rp->icon_mono_xor)
@@ -1696,7 +1583,7 @@ static int s_info_int(BMPHANDLE h, enum Infoint info)
if (!(rp = cm_read_handle(h)))
return 0;
- if (rp->read_state < RS_HEADER_OK || rp->read_state >= RS_FATAL)
+ if (rp->read_state < RS_HEADER_OK || rp->read_state >= RS_ARRAY)
return 0;
switch (info) {
@@ -1767,7 +1654,7 @@ API BMPRESULT bmpread_info_channel_bits(BMPHANDLE h, int *r, int *g, int *b, int
if (!(rp = cm_read_handle(h)))
return BMP_RESULT_ERROR;
- if (rp->read_state < RS_HEADER_OK || rp->read_state >= RS_FATAL)
+ if (rp->read_state < RS_HEADER_OK || rp->read_state >= RS_ARRAY)
return BMP_RESULT_ERROR;
if (rp->ih->compression == BI_OS2_RLE24) {
diff --git a/bmplib.h b/bmplib.h
index 054fb22..920b98e 100644
--- a/bmplib.h
+++ b/bmplib.h
@@ -245,9 +245,10 @@ enum BmpImagetype {
typedef enum BmpImagetype BMPIMAGETYPE;
struct BmpArrayInfo {
+ BMPHANDLE handle;
BMPIMAGETYPE type;
int width, height;
- int ncolors; /* -1 = RGB */
+ int ncolors; /* 0 = RGB */
int screenwidth, screenheight; /* typically 0, or 1024x768 for 'hi-res' */
};
@@ -290,6 +291,11 @@ 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 int bmpread_array_num(BMPHANDLE h);
+APIDECL BMPRESULT bmpread_array_info(BMPHANDLE h, struct BmpArrayInfo *ai, int idx);
+
+
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);
diff --git a/meson.build b/meson.build
index 89d38f7..a85b17c 100644
--- a/meson.build
+++ b/meson.build
@@ -37,6 +37,7 @@ bmplib_sources = ['bmp-read.c',
'bmp-write.c',
'bmp-read-loadimage.c',
'bmp-read-loadindexed.c',
+ 'bmp-read-icons.c',
'bmp-common.c',
'huffman.c',
'logging.c']
@@ -74,6 +75,7 @@ test_read_conversions = executable('test_read_conversions',
'test-read-conversions.c',
'bmp-common.c',
'bmp-read.c',
+ 'bmp-read-icons.c',
'bmp-write.c',
'huffman.c',
'logging.c',
@@ -85,6 +87,7 @@ test_read_io = executable('test_read_io',
'test-read-io.c',
'bmp-common.c',
'bmp-read.c',
+ 'bmp-read-icons.c',
'bmp-write.c',
'huffman.c',
'logging.c',
@@ -97,6 +100,7 @@ test_write_io = executable('test_write_io',
'test-write-io.c',
'bmp-common.c',
'bmp-read.c',
+ 'bmp-read-icons.c',
'bmp-read-loadimage.c',
'huffman.c',
'logging.c',