forked from old/legacy-imlib2
842 lines
25 KiB
C
842 lines
25 KiB
C
/*
|
|
* Based off of Peter Alm's BMP loader from xmms, with additions from
|
|
* imlib's old BMP loader
|
|
*/
|
|
/*
|
|
* 21.3.2006 - Changes made by Petr Kobalicek
|
|
* - Simplify and make secure RLE encoding
|
|
* - Fix 16 and 32 bit depth (old code was incorrect and it's commented)
|
|
*/
|
|
#include "loader_common.h"
|
|
#include <sys/stat.h>
|
|
|
|
#define DBG_PFX "LDR-bmp"
|
|
#define Dx(fmt...)
|
|
|
|
/* The BITMAPFILEHEADER (size 14) */
|
|
typedef struct {
|
|
DATA8 header[2];
|
|
DATA8 size[4];
|
|
DATA8 rsvd1[2];
|
|
DATA8 rsvd2[2];
|
|
DATA8 offs[4];
|
|
} bfh_t;
|
|
|
|
/* The BITMAPINFOHEADER */
|
|
typedef union {
|
|
DATA32 header_size;
|
|
struct {
|
|
/* BITMAPCOREHEADER (size 12) */
|
|
DATA32 header_size;
|
|
DATA16 width;
|
|
DATA16 height;
|
|
DATA16 planes;
|
|
DATA16 bpp;
|
|
} bch;
|
|
struct {
|
|
/* BITMAPINFOHEADER (size 40) */
|
|
DATA32 header_size;
|
|
DATA32 width;
|
|
DATA32 height;
|
|
DATA16 planes;
|
|
DATA16 bpp;
|
|
DATA32 compression;
|
|
DATA32 size;
|
|
DATA32 res_hor;
|
|
DATA32 res_ver;
|
|
DATA32 colors;
|
|
DATA32 colors_important;
|
|
/* BITMAPV3INFOHEADER (size 56) */
|
|
DATA32 mask_r;
|
|
DATA32 mask_g;
|
|
DATA32 mask_b;
|
|
DATA32 mask_a;
|
|
} bih;
|
|
char bytes[124];
|
|
} bih_t;
|
|
|
|
typedef struct {
|
|
unsigned char rgbBlue;
|
|
unsigned char rgbGreen;
|
|
unsigned char rgbRed;
|
|
unsigned char rgbReserved;
|
|
} RGBQUAD;
|
|
|
|
/* Compression methods */
|
|
#define BI_RGB 0
|
|
#define BI_RLE8 1
|
|
#define BI_RLE4 2
|
|
#define BI_BITFIELDS 3
|
|
#define BI_JPEG 4 /* Unsupported */
|
|
#define BI_PNG 5 /* Unsupported */
|
|
#define BI_ALPHABITFIELDS 6
|
|
#define BI_CMYK 11 /* Unsupported */
|
|
#define BI_CMYKRLE8 12 /* Unsupported */
|
|
#define BI_CMYKRLE4 13 /* Unsupported */
|
|
|
|
enum {
|
|
RLE_NEXT = 0, /* Next line */
|
|
RLE_END = 1, /* End of RLE encoding */
|
|
RLE_MOVE = 2 /* Move by X and Y (Offset is stored in two next bytes) */
|
|
};
|
|
|
|
static int
|
|
WriteleByte(FILE * file, unsigned char val)
|
|
{
|
|
int rc;
|
|
|
|
rc = fputc((int)val & 0xff, file);
|
|
if (rc == EOF)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
WriteleShort(FILE * file, unsigned short val)
|
|
{
|
|
int rc;
|
|
|
|
rc = fputc((int)(val & 0xff), file);
|
|
if (rc == EOF)
|
|
return 0;
|
|
rc = fputc((int)((val >> 8) & 0xff), file);
|
|
if (rc == EOF)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
WriteleLong(FILE * file, unsigned long val)
|
|
{
|
|
int rc;
|
|
|
|
rc = fputc((int)(val & 0xff), file);
|
|
if (rc == EOF)
|
|
return 0;
|
|
rc = fputc((int)((val >> 8) & 0xff), file);
|
|
if (rc == EOF)
|
|
return 0;
|
|
rc = fputc((int)((val >> 16) & 0xff), file);
|
|
if (rc == EOF)
|
|
return 0;
|
|
rc = fputc((int)((val >> 24) & 0xff), file);
|
|
if (rc == EOF)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
load2(ImlibImage * im, int load_data)
|
|
{
|
|
int rc;
|
|
unsigned int offset;
|
|
unsigned int size, comp, imgsize;
|
|
unsigned int bitcount, ncols, skip;
|
|
unsigned char a, r, g, b;
|
|
unsigned char byte = 0, byte1, byte2;
|
|
unsigned int i, k;
|
|
int w, h, x, y, j, l;
|
|
DATA32 *ptr, pixel;
|
|
unsigned char *buffer_ptr, *buffer, *buffer_end, *buffer_end_safe;
|
|
RGBQUAD rgbQuads[256];
|
|
DATA32 argbCmap[256];
|
|
unsigned int amask, rmask, gmask, bmask;
|
|
int ashift1, rshift1, gshift1, bshift1;
|
|
int ashift2, rshift2, gshift2, bshift2;
|
|
bih_t bih;
|
|
|
|
rc = LOAD_FAIL;
|
|
buffer = NULL;
|
|
|
|
/* Load header */
|
|
{
|
|
struct stat statbuf;
|
|
bfh_t bfh;
|
|
|
|
if (fstat(fileno(im->fp), &statbuf) < 0)
|
|
goto quit;
|
|
|
|
size = statbuf.st_size;
|
|
if ((long)size != statbuf.st_size)
|
|
goto quit;
|
|
|
|
if (fread(&bfh, sizeof(bfh), 1, im->fp) != 1)
|
|
goto quit;
|
|
|
|
if (bfh.header[0] != 'B' || bfh.header[1] != 'M')
|
|
goto quit;
|
|
|
|
#define WORD_LE_32(p8) ((p8[3] << 24) | (p8[2] << 16) | (p8[1] << 8) | p8[0])
|
|
offset = WORD_LE_32(bfh.offs);
|
|
|
|
if (offset >= size)
|
|
goto quit;
|
|
|
|
memset(&bih, 0, sizeof(bih));
|
|
if (fread(&bih, 4, 1, im->fp) != 1)
|
|
goto quit;
|
|
|
|
SWAP_LE_32_INPLACE(bih.header_size);
|
|
|
|
D("fsize=%u, hsize=%u, header: fsize=%u offs=%u\n",
|
|
size, bih.header_size, WORD_LE_32(bfh.size), offset);
|
|
|
|
if (bih.header_size < 12 || bih.header_size > sizeof(bih))
|
|
goto quit;
|
|
|
|
if (fread(&bih.header_size + 1, bih.header_size - 4, 1, im->fp) != 1)
|
|
goto quit;
|
|
|
|
comp = BI_RGB;
|
|
amask = rmask = gmask = bmask = 0;
|
|
ashift1 = rshift1 = gshift1 = bshift1 = 0;
|
|
ashift2 = rshift2 = gshift2 = bshift2 = 1;
|
|
|
|
UNSET_FLAG(im->flags, F_HAS_ALPHA);
|
|
|
|
if (bih.header_size == 12)
|
|
{
|
|
w = SWAP_LE_16(bih.bch.width);
|
|
h = SWAP_LE_16(bih.bch.height);
|
|
// planes = SWAP_LE_16(bih.bch.planes);
|
|
bitcount = SWAP_LE_16(bih.bch.bpp);
|
|
}
|
|
else if (bih.header_size >= 16)
|
|
{
|
|
w = SWAP_LE_32(bih.bih.width);
|
|
h = SWAP_LE_32(bih.bih.height);
|
|
// planes = SWAP_LE_16(bih.bih.planes);
|
|
bitcount = SWAP_LE_16(bih.bih.bpp);
|
|
comp = SWAP_LE_32(bih.bih.compression);
|
|
// imgsize = SWAP_LE_32(bih.bih.size); /* We don't use this */
|
|
|
|
if (bih.header_size >= 40 &&
|
|
(comp == BI_BITFIELDS || comp == BI_ALPHABITFIELDS))
|
|
{
|
|
if (bih.header_size == 40)
|
|
{
|
|
ncols = (comp == BI_ALPHABITFIELDS) ? 4 : 3;
|
|
if (fread(&bih.bih.mask_r, 4, ncols, im->fp) != ncols)
|
|
goto quit;
|
|
}
|
|
rmask = SWAP_LE_32(bih.bih.mask_r);
|
|
gmask = SWAP_LE_32(bih.bih.mask_g);
|
|
bmask = SWAP_LE_32(bih.bih.mask_b);
|
|
amask = SWAP_LE_32(bih.bih.mask_a);
|
|
if (amask)
|
|
SET_FLAG(im->flags, F_HAS_ALPHA);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
goto quit;
|
|
}
|
|
|
|
imgsize = size - offset;
|
|
D("w=%3d h=%3d bitcount=%d comp=%d imgsize=%d\n",
|
|
w, h, bitcount, comp, imgsize);
|
|
|
|
/* "Bottom-up" images are loaded but not properly flipped */
|
|
h = abs(h);
|
|
|
|
if (!IMAGE_DIMENSIONS_OK(w, h))
|
|
goto quit;
|
|
|
|
switch (bitcount)
|
|
{
|
|
default:
|
|
goto quit;
|
|
|
|
case 1:
|
|
case 4:
|
|
case 8:
|
|
ncols = (offset - bih.header_size - 14);
|
|
if (bih.header_size == 12)
|
|
{
|
|
ncols /= 3;
|
|
if (ncols > 256)
|
|
ncols = 256;
|
|
for (i = 0; i < ncols; i++)
|
|
if (fread(&rgbQuads[i], 3, 1, im->fp) != 1)
|
|
goto quit;
|
|
}
|
|
else
|
|
{
|
|
ncols /= 4;
|
|
if (ncols > 256)
|
|
ncols = 256;
|
|
if (fread(rgbQuads, 4, ncols, im->fp) != ncols)
|
|
goto quit;
|
|
}
|
|
for (i = 0; i < ncols; i++)
|
|
argbCmap[i] =
|
|
PIXEL_ARGB(0xff, rgbQuads[i].rgbRed, rgbQuads[i].rgbGreen,
|
|
rgbQuads[i].rgbBlue);
|
|
D("ncols=%d\n", ncols);
|
|
break;
|
|
|
|
case 24:
|
|
break;
|
|
|
|
case 16:
|
|
case 32:
|
|
if (comp == BI_BITFIELDS || comp == BI_ALPHABITFIELDS)
|
|
{
|
|
unsigned int bit, bithi;
|
|
unsigned int mask;
|
|
|
|
D("mask ARGB: %08x %08x %08x %08x\n",
|
|
amask, rmask, gmask, bmask);
|
|
if (bitcount == 16)
|
|
{
|
|
amask &= 0xffffU;
|
|
rmask &= 0xffffU;
|
|
gmask &= 0xffffU;
|
|
bmask &= 0xffffU;
|
|
}
|
|
if (rmask == 0 && gmask == 0 && bmask == 0)
|
|
goto quit;
|
|
for (bit = 0; bit < bitcount; bit++)
|
|
{
|
|
/* Find LSB bit positions */
|
|
bithi = bitcount - bit - 1;
|
|
mask = 1 << bithi;
|
|
if (amask & mask)
|
|
ashift1 = bithi;
|
|
if (bmask & mask)
|
|
bshift1 = bithi;
|
|
if (gmask & mask)
|
|
gshift1 = bithi;
|
|
if (rmask & mask)
|
|
rshift1 = bithi;
|
|
|
|
/* Find MSB bit positions */
|
|
mask = 1 << bit;
|
|
if (amask & mask)
|
|
ashift2 = bit;
|
|
if (rmask & mask)
|
|
rshift2 = bit;
|
|
if (gmask & mask)
|
|
gshift2 = bit;
|
|
if (bmask & mask)
|
|
bshift2 = bit;
|
|
}
|
|
|
|
/* Calculate shift2s as bits in mask */
|
|
ashift2 -= ashift1 - 1;
|
|
rshift2 -= rshift1 - 1;
|
|
gshift2 -= gshift1 - 1;
|
|
bshift2 -= bshift1 - 1;
|
|
}
|
|
else if (bitcount == 16)
|
|
{
|
|
rmask = 0x7C00;
|
|
gmask = 0x03E0;
|
|
bmask = 0x001F;
|
|
rshift1 = 10;
|
|
gshift1 = 5;
|
|
bshift1 = 0;
|
|
rshift2 = gshift2 = bshift2 = 5;
|
|
}
|
|
else if (bitcount == 32)
|
|
{
|
|
amask = 0xFF000000;
|
|
rmask = 0x00FF0000;
|
|
gmask = 0x0000FF00;
|
|
bmask = 0x000000FF;
|
|
ashift1 = 24;
|
|
rshift1 = 16;
|
|
gshift1 = 8;
|
|
bshift1 = 0;
|
|
ashift2 = rshift2 = gshift2 = bshift2 = 8;
|
|
}
|
|
|
|
/* Calculate shift2s as scale factor */
|
|
ashift2 = ashift2 > 0 ? (1 << ashift2) - 1 : 1;
|
|
rshift2 = rshift2 > 0 ? (1 << rshift2) - 1 : 1;
|
|
gshift2 = gshift2 > 0 ? (1 << gshift2) - 1 : 1;
|
|
bshift2 = bshift2 > 0 ? (1 << bshift2) - 1 : 1;
|
|
|
|
#define SCALE(c, x) ((((x & c##mask)>> (c##shift1 - 0)) * 255) / c##shift2)
|
|
|
|
D("mask ARGB: %08x %08x %08x %08x\n", amask, rmask, gmask, bmask);
|
|
D("shift1 ARGB: %8d %8d %8d %8d\n",
|
|
ashift1, rshift1, gshift1, bshift1);
|
|
D("shift2 ARGB: %8d %8d %8d %8d\n",
|
|
ashift2, rshift2, gshift2, bshift2);
|
|
D("check ARGB: %08x %08x %08x %08x\n",
|
|
SCALE(a, amask), SCALE(r, rmask),
|
|
SCALE(g, gmask), SCALE(b, bmask));
|
|
break;
|
|
}
|
|
|
|
im->w = w;
|
|
im->h = h;
|
|
}
|
|
|
|
if (!load_data)
|
|
{
|
|
rc = LOAD_SUCCESS;
|
|
goto quit;
|
|
}
|
|
|
|
/* Load data */
|
|
|
|
fseek(im->fp, offset, SEEK_SET);
|
|
|
|
if (!__imlib_AllocateData(im))
|
|
goto quit;
|
|
|
|
buffer = malloc(imgsize);
|
|
if (!buffer)
|
|
goto quit;
|
|
|
|
if (fread(buffer, imgsize, 1, im->fp) != 1)
|
|
goto quit;
|
|
|
|
buffer_ptr = buffer;
|
|
buffer_end = buffer + imgsize;
|
|
|
|
ptr = im->data + ((h - 1) * w);
|
|
|
|
switch (bitcount)
|
|
{
|
|
default: /* It should not be possible to go here */
|
|
goto quit;
|
|
|
|
case 1:
|
|
switch (comp)
|
|
{
|
|
default:
|
|
goto quit;
|
|
|
|
case BI_RGB:
|
|
skip = ((((w + 31) / 32) * 32) - w) / 8;
|
|
for (y = 0; y < h; y++)
|
|
{
|
|
for (x = 0; x < w && buffer_ptr < buffer_end; x++)
|
|
{
|
|
if ((x & 7) == 0)
|
|
byte = *buffer_ptr++;
|
|
k = (byte >> 7) & 1;
|
|
*ptr++ = argbCmap[k];
|
|
byte <<= 1;
|
|
}
|
|
buffer_ptr += skip;
|
|
ptr -= w * 2;
|
|
|
|
if (im->lc && __imlib_LoadProgressRows(im, h - y - 1, -1))
|
|
{
|
|
rc = LOAD_BREAK;
|
|
goto quit;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
switch (comp)
|
|
{
|
|
default:
|
|
goto quit;
|
|
|
|
case BI_RLE4:
|
|
buffer_end_safe = buffer_end - 1;
|
|
|
|
x = 0;
|
|
y = 0;
|
|
|
|
for (; buffer_ptr < buffer_end_safe;)
|
|
{
|
|
byte1 = buffer_ptr[0];
|
|
byte2 = buffer_ptr[1];
|
|
buffer_ptr += 2;
|
|
Dx("%3d %3d: %02x %02x (%d %d)\n",
|
|
x, y, byte1, byte2, byte2 >> 4, byte2 & 0xf);
|
|
if (byte1)
|
|
{
|
|
DATA32 t1, t2;
|
|
|
|
l = byte1;
|
|
/* Check for invalid length */
|
|
if (l + x > w)
|
|
goto bail_bc4;
|
|
|
|
t1 = argbCmap[byte2 >> 4];
|
|
t2 = argbCmap[byte2 & 0xF];
|
|
for (j = 0; j < l; j++)
|
|
{
|
|
*ptr++ = t1;
|
|
if (++j < l)
|
|
*ptr++ = t2;
|
|
}
|
|
x += l;
|
|
}
|
|
else
|
|
{
|
|
switch (byte2)
|
|
{
|
|
case RLE_NEXT:
|
|
x = 0;
|
|
if (++y >= h)
|
|
goto bail_bc4;
|
|
ptr = im->data + (h - y - 1) * w;
|
|
break;
|
|
case RLE_END:
|
|
x = 0;
|
|
y = h;
|
|
buffer_ptr = buffer_end_safe;
|
|
break;
|
|
case RLE_MOVE:
|
|
/* Need to read two bytes */
|
|
if (buffer_ptr >= buffer_end_safe)
|
|
goto bail_bc4;
|
|
x += buffer_ptr[0];
|
|
y += buffer_ptr[1];
|
|
buffer_ptr += 2;
|
|
/* Check for correct coordinates */
|
|
if (x >= w)
|
|
goto bail_bc4;
|
|
if (y >= h)
|
|
goto bail_bc4;
|
|
ptr = im->data + (h - y - 1) * w + x;
|
|
break;
|
|
default:
|
|
l = byte2;
|
|
/* Check for invalid length and valid buffer size */
|
|
if (l + x > w)
|
|
goto bail_bc4;
|
|
if (buffer_ptr + (l >> 1) + (l & 1) > buffer_end)
|
|
goto bail_bc4;
|
|
|
|
for (j = 0; j < l; j++)
|
|
{
|
|
byte = *buffer_ptr++;
|
|
Dx("%3d %3d: %d/%d: %2d %2d\n",
|
|
x, y, j, l, byte >> 4, byte & 0xf);
|
|
*ptr++ = argbCmap[byte >> 4];
|
|
if (++j < l)
|
|
*ptr++ = argbCmap[byte & 0xF];
|
|
}
|
|
x += l;
|
|
|
|
/* Pad to even number of palette bytes */
|
|
buffer_ptr += ((l + 1) / 2) & 1;
|
|
break;
|
|
}
|
|
}
|
|
goto progress_bc4;
|
|
|
|
bail_bc4:
|
|
buffer_ptr = buffer_end_safe;
|
|
|
|
progress_bc4:
|
|
if (im->lc && (x == w) &&
|
|
__imlib_LoadProgressRows(im, h - y - 1, -1))
|
|
{
|
|
rc = LOAD_BREAK;
|
|
goto quit;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case BI_RGB:
|
|
skip = ((((w + 7) / 8) * 8) - w) / 2;
|
|
for (y = 0; y < h; y++)
|
|
{
|
|
for (x = 0; x < w && buffer_ptr < buffer_end; x++)
|
|
{
|
|
if ((x & 1) == 0)
|
|
byte = *buffer_ptr++;
|
|
k = (byte & 0xF0) >> 4;
|
|
*ptr++ = argbCmap[k];
|
|
byte <<= 4;
|
|
}
|
|
buffer_ptr += skip;
|
|
ptr -= w * 2;
|
|
|
|
if (im->lc && __imlib_LoadProgressRows(im, h - y - 1, -1))
|
|
{
|
|
rc = LOAD_BREAK;
|
|
goto quit;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 8:
|
|
switch (comp)
|
|
{
|
|
default:
|
|
goto quit;
|
|
|
|
case BI_RLE8:
|
|
buffer_end_safe = buffer_end - 1;
|
|
|
|
x = 0;
|
|
y = 0;
|
|
for (; buffer_ptr < buffer_end_safe;)
|
|
{
|
|
byte1 = buffer_ptr[0];
|
|
byte2 = buffer_ptr[1];
|
|
buffer_ptr += 2;
|
|
Dx("%3d %3d: %02x %02x\n", x, y, byte1, byte2);
|
|
if (byte1)
|
|
{
|
|
pixel = argbCmap[byte2];
|
|
l = byte1;
|
|
if (x + l > w)
|
|
goto bail_bc8;
|
|
for (j = l; j; j--)
|
|
*ptr++ = pixel;
|
|
x += l;
|
|
}
|
|
else
|
|
{
|
|
switch (byte2)
|
|
{
|
|
case RLE_NEXT:
|
|
x = 0;
|
|
if (++y >= h)
|
|
goto bail_bc8;
|
|
ptr = im->data + ((h - y - 1) * w) + x;
|
|
break;
|
|
case RLE_END:
|
|
x = 0;
|
|
y = h;
|
|
buffer_ptr = buffer_end_safe;
|
|
break;
|
|
case RLE_MOVE:
|
|
/* Need to read two bytes */
|
|
if (buffer_ptr >= buffer_end_safe)
|
|
goto bail_bc8;
|
|
x += buffer_ptr[0];
|
|
y += buffer_ptr[1];
|
|
buffer_ptr += 2;
|
|
/* Check for correct coordinates */
|
|
if (x >= w)
|
|
goto bail_bc8;
|
|
if (y >= h)
|
|
goto bail_bc8;
|
|
ptr = im->data + ((h - y - 1) * w) + x;
|
|
break;
|
|
default:
|
|
l = byte2;
|
|
if (x + l > w)
|
|
goto bail_bc8;
|
|
if (buffer_ptr + l > buffer_end)
|
|
goto bail_bc8;
|
|
for (j = 0; j < l; j++)
|
|
{
|
|
byte = *buffer_ptr++;
|
|
Dx("%3d %3d: %d/%d: %2d\n",
|
|
x, y, j, l, byte);
|
|
*ptr++ = argbCmap[byte];
|
|
}
|
|
x += l;
|
|
if (l & 1)
|
|
buffer_ptr++;
|
|
break;
|
|
}
|
|
}
|
|
goto progress_bc8;
|
|
|
|
bail_bc8:
|
|
buffer_ptr = buffer_end_safe;
|
|
|
|
progress_bc8:
|
|
if (im->lc && (x == w) &&
|
|
__imlib_LoadProgressRows(im, h - y - 1, -1))
|
|
{
|
|
rc = LOAD_BREAK;
|
|
goto quit;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case BI_RGB:
|
|
skip = (((w + 3) / 4) * 4) - w;
|
|
for (y = 0; y < h; y++)
|
|
{
|
|
for (x = 0; x < w && buffer_ptr < buffer_end; x++)
|
|
{
|
|
byte = *buffer_ptr++;
|
|
*ptr++ = argbCmap[byte];
|
|
}
|
|
ptr -= w * 2;
|
|
buffer_ptr += skip;
|
|
|
|
if (im->lc && __imlib_LoadProgressRows(im, h - y - 1, -1))
|
|
{
|
|
rc = LOAD_BREAK;
|
|
goto quit;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 16:
|
|
buffer_end_safe = buffer_end - 1;
|
|
|
|
skip = (((w * 16 + 31) / 32) * 4) - (w * 2);
|
|
for (y = 0; y < h; y++)
|
|
{
|
|
for (x = 0; x < w && buffer_ptr < buffer_end_safe; x++)
|
|
{
|
|
pixel = *(unsigned short *)buffer_ptr;
|
|
|
|
if (im->flags & F_HAS_ALPHA)
|
|
a = SCALE(a, pixel);
|
|
else
|
|
a = 0xff;
|
|
r = SCALE(r, pixel);
|
|
g = SCALE(g, pixel);
|
|
b = SCALE(b, pixel);
|
|
*ptr++ = PIXEL_ARGB(a, r, g, b);
|
|
buffer_ptr += 2;
|
|
}
|
|
ptr -= w * 2;
|
|
buffer_ptr += skip;
|
|
|
|
if (im->lc && __imlib_LoadProgressRows(im, h - y - 1, -1))
|
|
{
|
|
rc = LOAD_BREAK;
|
|
goto quit;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 24:
|
|
buffer_end_safe = buffer_end - 2;
|
|
|
|
skip = (4 - ((w * 3) % 4)) & 3;
|
|
for (y = 0; y < h; y++)
|
|
{
|
|
for (x = 0; x < w && buffer_ptr < buffer_end_safe; x++)
|
|
{
|
|
b = *buffer_ptr++;
|
|
g = *buffer_ptr++;
|
|
r = *buffer_ptr++;
|
|
*ptr++ = PIXEL_ARGB(0xff, r, g, b);
|
|
}
|
|
ptr -= w * 2;
|
|
buffer_ptr += skip;
|
|
|
|
if (im->lc && __imlib_LoadProgressRows(im, h - y - 1, -1))
|
|
{
|
|
rc = LOAD_BREAK;
|
|
goto quit;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 32:
|
|
buffer_end_safe = buffer_end - 3;
|
|
|
|
skip = (((w * 32 + 31) / 32) * 4) - (w * 4);
|
|
for (y = 0; y < h; y++)
|
|
{
|
|
for (x = 0; x < w && buffer_ptr < buffer_end_safe; x++)
|
|
{
|
|
pixel = *(unsigned int *)buffer_ptr;
|
|
|
|
if (im->flags & F_HAS_ALPHA)
|
|
a = SCALE(a, pixel);
|
|
else
|
|
a = 0xff;
|
|
r = SCALE(r, pixel);
|
|
g = SCALE(g, pixel);
|
|
b = SCALE(b, pixel);
|
|
*ptr++ = PIXEL_ARGB(a, r, g, b);
|
|
buffer_ptr += 4;
|
|
}
|
|
ptr -= w * 2;
|
|
buffer_ptr += skip;
|
|
|
|
if (im->lc && __imlib_LoadProgressRows(im, h - y - 1, -1))
|
|
{
|
|
rc = LOAD_BREAK;
|
|
goto quit;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
rc = LOAD_SUCCESS;
|
|
|
|
quit:
|
|
if (rc <= 0)
|
|
__imlib_FreeData(im);
|
|
free(buffer);
|
|
|
|
return rc;
|
|
}
|
|
|
|
char
|
|
save(ImlibImage * im, ImlibProgressFunction progress, char progress_granularity)
|
|
{
|
|
int rc;
|
|
FILE *f;
|
|
int i, j, pad;
|
|
DATA32 pixel;
|
|
|
|
f = fopen(im->real_file, "wb");
|
|
if (!f)
|
|
return LOAD_FAIL;
|
|
|
|
rc = LOAD_SUCCESS;
|
|
|
|
/* calculate number of bytes to pad on end of each row */
|
|
pad = (4 - ((im->w * 3) % 4)) & 0x03;
|
|
|
|
/* write BMP file header */
|
|
WriteleShort(f, 0x4d42); /* prefix */
|
|
WriteleLong(f, 54 + ((3 * im->w) + pad) * im->h); /* filesize (padding should be considered) */
|
|
WriteleShort(f, 0x0000); /* reserved #1 */
|
|
WriteleShort(f, 0x0000); /* reserved #2 */
|
|
WriteleLong(f, 54); /* offset to image data */
|
|
|
|
/* write BMP bitmap header */
|
|
WriteleLong(f, 40); /* 40-byte header */
|
|
WriteleLong(f, im->w);
|
|
WriteleLong(f, im->h);
|
|
WriteleShort(f, 1); /* one plane */
|
|
WriteleShort(f, 24); /* bits per pixel */
|
|
WriteleLong(f, 0); /* no compression */
|
|
WriteleLong(f, ((3 * im->w) + pad) * im->h); /* padding should be counted */
|
|
for (i = 0; i < 4; i++)
|
|
WriteleLong(f, 0x0000); /* pad to end of header */
|
|
|
|
/* write actual BMP data */
|
|
for (i = 0; i < im->h; i++)
|
|
{
|
|
for (j = 0; j < im->w; j++)
|
|
{
|
|
pixel = im->data[im->w * (im->h - i - 1) + j];
|
|
WriteleByte(f, PIXEL_B(pixel));
|
|
WriteleByte(f, PIXEL_G(pixel));
|
|
WriteleByte(f, PIXEL_R(pixel));
|
|
}
|
|
for (j = 0; j < pad; j++)
|
|
WriteleByte(f, 0);
|
|
}
|
|
|
|
fclose(f);
|
|
|
|
return rc;
|
|
}
|
|
|
|
void
|
|
formats(ImlibLoader * l)
|
|
{
|
|
static const char *const list_formats[] = { "bmp" };
|
|
__imlib_LoaderSetFormats(l, list_formats,
|
|
sizeof(list_formats) / sizeof(char *));
|
|
}
|