return TRUE;
}
+
+#define BMP_SAFE_FREE(x) { if (x != NULL) { free(x); x = NULL; } }
+#define bmp_retm_if(expr, val) do { \
+ if (expr) { \
+ BMP_SAFE_FREE(val); \
+ return; \
+ } \
+ } while (0)
+
+#define bmp_retvm_if(expr, val, retv) do { \
+ if (expr) { \
+ BMP_SAFE_FREE(val); \
+ return retv; \
+ } \
+ } while (0)
+
+static uint16_t
+bmp_write_to_buffer(void *src, unsigned char **dst, size_t *size, size_t *offset, size_t len)
+{
+ if (src == NULL || len <= 0)
+ return -1;
+
+ *size += len;
+ if ((*offset + len) > *size)
+ return -1;
+
+ if (*dst == NULL)
+ *dst = (unsigned char *)calloc(1, *size);
+ else
+ *dst = (unsigned char *)realloc(*dst, *size);
+
+ if (*dst == NULL)
+ return -1;
+
+ memcpy(*dst + *offset, src, len);
+ *offset += len;
+
+ return len;
+}
+
+static void
+bmp_write_header2(bmpfile_t *bmp, unsigned char **ptr, size_t *size)
+{
+ uint16_t ret;
+ size_t offset = *size;
+ bmp_header_t header = bmp->header;
+
+ if (_is_big_endian()) bmp_header_swap_endianess(&header);
+
+ ret = bmp_write_to_buffer(header.magic, ptr, size, &offset, sizeof(header.magic));
+ bmp_retm_if(ret < 0, *ptr);
+ ret = bmp_write_to_buffer(&(header.filesz), ptr, size, &offset, sizeof(uint32_t));
+ bmp_retm_if(ret < 0, *ptr);
+ ret = bmp_write_to_buffer(&(header.creator1), ptr, size, &offset, sizeof(uint16_t));
+ bmp_retm_if(ret < 0, *ptr);
+ ret = bmp_write_to_buffer(&(header.creator2), ptr, size, &offset, sizeof(uint16_t));
+ bmp_retm_if(ret < 0, *ptr);
+ ret = bmp_write_to_buffer(&(header.offset), ptr, size, &offset, sizeof(uint32_t));
+ bmp_retm_if(ret < 0, *ptr);
+}
+
+static void
+bmp_write_dib2(bmpfile_t *bmp, unsigned char **ptr, size_t *size)
+{
+ uint16_t ret;
+ size_t offset = *size;
+ bmp_dib_v3_header_t dib = bmp->dib;
+
+ if (*ptr == NULL)
+ return;
+
+ if (_is_big_endian()) bmp_dib_v3_header_swap_endianess(&dib);
+
+ ret = bmp_write_to_buffer(&(dib), ptr, size, &offset, sizeof(bmp_dib_v3_header_t));
+ bmp_retm_if(ret < 0, *ptr);
+}
+
+static void
+bmp_write_palette2(bmpfile_t *bmp, unsigned char **ptr, size_t *size)
+{
+ uint16_t ret;
+ size_t offset = *size;
+
+ if (*ptr == NULL)
+ return;
+
+ if (bmp->dib.depth == 1 || bmp->dib.depth == 4 || bmp->dib.depth == 8) {
+ int i;
+ for (i = 0; i < bmp->dib.ncolors; ++i) {
+ ret = bmp_write_to_buffer(&(bmp->colors[i]), ptr, size, &offset, sizeof(rgb_pixel_t));
+ bmp_retm_if(ret < 0, *ptr);
+ }
+ }
+ else if (bmp->dib.depth == 16) { /* the bit masks, not palette */
+ uint16_t red_mask = 63488; /* bits 1-5 */
+ uint16_t green_mask = 2016; /* bits 6-11 */
+ uint16_t blue_mask = 31; /* bits 12-16 */
+ uint16_t zero_word = 0;
+
+ if (_is_big_endian()) {
+ red_mask = UINT16_SWAP_LE_BE_CONSTANT(red_mask);
+ green_mask = UINT16_SWAP_LE_BE_CONSTANT(green_mask);
+ blue_mask = UINT16_SWAP_LE_BE_CONSTANT(blue_mask);
+ }
+
+ ret = bmp_write_to_buffer(&red_mask, ptr, size, &offset, sizeof(uint16_t));
+ bmp_retm_if(ret < 0, *ptr);
+ ret = bmp_write_to_buffer(&zero_word, ptr, size, &offset, sizeof(uint16_t));
+ bmp_retm_if(ret < 0, *ptr);
+
+ ret = bmp_write_to_buffer(&green_mask, ptr, size, &offset, sizeof(uint16_t));
+ bmp_retm_if(ret < 0, *ptr);
+ ret = bmp_write_to_buffer(&zero_word, ptr, size, &offset, sizeof(uint16_t));
+ bmp_retm_if(ret < 0, *ptr);
+
+ ret = bmp_write_to_buffer(&blue_mask, ptr, size, &offset, sizeof(uint16_t));
+ bmp_retm_if(ret < 0, *ptr);
+ ret = bmp_write_to_buffer(&zero_word, ptr, size, &offset, sizeof(uint16_t));
+ bmp_retm_if(ret < 0, *ptr);
+ }
+}
+
+
+bool
+bmp_save2(bmpfile_t *bmp, void **buffer, size_t *size)
+{
+ int row;
+ unsigned char *buf;
+ unsigned char *ptr = NULL;
+ size_t osize = 0, offset = 0;
+ uint16_t ret;
+
+ /* Write bmp to buffer */
+ bmp_write_header2(bmp, &ptr, &osize);
+ bmp_write_dib2(bmp, &ptr, &osize);
+ bmp_write_palette2(bmp, &ptr, &osize);
+
+ if (ptr == NULL) {
+ *size = 0;
+ return FALSE;
+ }
+ offset = osize;
+
+ if (bmp->dib.depth == 16) {
+ uint32_t data_bytes = bmp->dib.width * 2;
+ uint32_t padding_bytes = 4 - data_bytes % 4;
+
+ for (row = bmp->dib.height - 1; row >= 0; --row) {
+ int i;
+ unsigned char zero_byte = 0;
+ uint32_t write_number = 0;
+
+ for (i = 0; write_number < data_bytes; ++i, write_number += 2) {
+ uint16_t red = (uint16_t)(bmp->pixels[i][row].red / 8);
+ uint16_t green = (uint16_t)(bmp->pixels[i][row].green / 4);
+ uint16_t blue = (uint16_t)(bmp->pixels[i][row].blue / 8);
+ uint16_t value = (red << 11) + (green << 5) + blue;
+
+ if (_is_big_endian()) value = UINT16_SWAP_LE_BE_CONSTANT(value);
+
+ ret = bmp_write_to_buffer(&value, &ptr, &osize, &offset, sizeof(uint16_t));
+ bmp_retvm_if(ret < 0, ptr, FALSE);
+ }
+
+ for (write_number = 0; write_number < padding_bytes; ++write_number) {
+ ret = bmp_write_to_buffer(&zero_byte, &ptr, &osize, &offset, 1);
+ bmp_retvm_if(ret < 0, ptr, FALSE);
+ }
+ }
+ }
+ else {
+ double bytes_per_pixel;
+ int bytes_per_line;
+
+ bytes_per_pixel = (bmp->dib.depth * 1.0) / 8.0;
+ bytes_per_line = (int)ceil(bytes_per_pixel * bmp->dib.width);
+ if (bytes_per_line % 4 != 0)
+ bytes_per_line += 4 - bytes_per_line % 4;
+
+ buf = malloc(bytes_per_line);
+
+ for (row = bmp->dib.height - 1; row >= 0; --row) {
+ memset(buf, 0, bytes_per_line);
+
+ switch (bmp->dib.depth) {
+ case 1:
+ bmp_get_row_data_for_1(bmp, buf, bytes_per_line, row);
+ break;
+
+ case 4:
+ bmp_get_row_data_for_4(bmp, buf, bytes_per_line, row);
+ break;
+
+ case 8:
+ bmp_get_row_data_for_8(bmp, buf, bytes_per_line, row);
+ break;
+
+ case 24:
+ bmp_get_row_data_for_24(bmp, buf, bytes_per_line, row);
+ break;
+
+ case 32:
+ bmp_get_row_data_for_32(bmp, buf, bytes_per_line, row);
+ break;
+ }
+
+ ret = bmp_write_to_buffer(buf, &ptr, &osize, &offset, bytes_per_line);
+ if (ret < 0) {
+ free(buf);
+ BMP_SAFE_FREE(ptr);
+ *size = 0;
+ return FALSE;
+ }
+ }
+ free(buf);
+ }
+
+ *buffer = (void *)ptr;
+ *size = osize;
+
+ return TRUE;
+}
+