blob: cf8b77f2878b94237e7ba569f054750dc2911bab [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2005 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
#include "NgCommon.h"
#include "NgWinBMPFileFormat.h"
#define BMPHeaderFixedSize 40
BYTE4 decompressRLE4Data(BYTE1 *src, BYTE4 numBytes, BYTE4 stride, BYTE1 *dest, BYTE4 destSize)
{
BYTE4 sp = 0;
BYTE4 se = numBytes;
BYTE4 dp = 0;
BYTE4 de = destSize;
BYTE4 x = 0, y = 0;
BYTE4 i;
while (sp < se)
{
int len = src[sp] & 0xFF;
sp++;
if (len == 0)
{
len = src[sp] & 0xFF;
sp++;
switch (len)
{
case 0: /* end of line */
y++;
x = 0;
dp = y * stride;
if (dp >= de)
return -1;
break;
case 1: /* end of bitmap */
return 1;
case 2: /* delta */
x += src[sp] & 0xFF;
sp++;
y += src[sp] & 0xFF;
sp++;
dp = y * stride + x / 2;
if (dp >= de)
return -1;
break;
default: /* absolute mode run */
if ((len & 1) != 0) /* odd run lengths not currently supported */
return -1;
x += len;
len = len / 2;
if (len > (se - sp))
return -1;
if (len > (de - dp))
return -1;
for (i = 0; i < len; i++)
{
dest[dp] = src[sp];
dp++;
sp++;
}
if ((sp & 1) != 0)
sp++; /* word align sp? */
break;
}
} else
{
BYTE1 theByte;
if ((len & 1) != 0)
return -1;
x += len;
len = len / 2;
theByte = src[sp];
sp++;
if (len > (de - dp))
return -1;
for (i = 0; i < len; i++)
{
dest[dp] = theByte;
dp++;
}
}
}
return 1;
}
BYTE4 decompressRLE8Data(BYTE1 *src, BYTE4 numBytes, BYTE4 stride, BYTE1 *dest, BYTE4 destSize)
{
BYTE4 sp = 0;
BYTE4 se = numBytes;
BYTE4 dp = 0;
BYTE4 de = destSize;
BYTE4 x = 0, y = 0;
BYTE4 i;
while (sp < se) {
int len = src[sp] & 0xFF;
sp++;
if (len == 0) {
len = src[sp] & 0xFF;
sp++;
switch (len)
{
case 0: /* end of line */
y++;
x = 0;
dp = y * stride;
if (dp >= de)
return -1;
break;
case 1: /* end of bitmap */
return 1;
case 2: /* delta */
x += src[sp] & 0xFF;
sp++;
y += src[sp] & 0xFF;
sp++;
dp = y * stride + x;
if (dp >= de)
return -1;
break;
default: /* absolute mode run */
if (len > (se - sp))
return -1;
if (len > (de - dp))
return -1;
for (i = 0; i < len; i++)
{
dest[dp] = src[sp];
dp++;
sp++;
}
if ((sp & 1) != 0)
sp++; /* word align sp? */
x += len;
break;
}
} else
{
BYTE1 theByte = src[sp];
sp++;
if (len > (de - dp))
return -1;
for (i = 0; i < len; i++)
{
dest[dp] = theByte;
dp++;
}
x += len;
}
}
return 1;
}
ng_err_t decompressData (BYTE1 *src, BYTE4 numBytes, BYTE1 *dest, BYTE4 destSize, BYTE4 stride, BYTE4 cmp)
{
if (cmp == 1)
{
/* BMP_RLE8_COMPRESSION */
if (decompressRLE8Data (src, numBytes, stride, dest, destSize) <= 0)
return NgError (ERR_NG, "Error decompressRLE8Data failed");
} else if (cmp == 2)
{
/* BMP_RLE4_COMPRESSION */
if (decompressRLE4Data (src, numBytes, stride, dest, destSize) <= 0)
return NgError (ERR_NG, "Error decompressRLE4Data failed");
} else
{
return NgError (ERR_NG, "Error decompressData failed - unsupported compression");
}
return ERR_OK;
}
void flipScanLines(BYTE1 *data, BYTE4 numBytes, int stride, int height)
{
BYTE4 i1 = 0;
BYTE4 i2 = (height - 1) * stride;
BYTE4 i, index;
for (i = 0; i < height / 2; i++)
{
for (index = 0; index < stride; index++)
{
BYTE1 b = data[index + i1];
data[index + i1] = data[index + i2];
data[index + i2] = b;
}
i1 += stride;
i2 -= stride;
}
}
/**
* BmpDecoderReadImage
*
* Decode the content of a bmp file.
*
* in : the input stream
* image : a pointer to a ng_bitmap_image_t
*
* return: ERR_OK if the image was correctly built from the input stream
* ERR_NG otherwise.
*/
ng_err_t NgBmpDecoderReadImage (ng_stream_t *in, ng_bitmap_image_t *image)
{
BYTE4 *fileHeader = (BYTE4*) NgMalloc (5 * sizeof(BYTE4));
BYTE1 *infoHeader, *data;
BYTE4 width, height, stride, dataSize, cmp, pos;
BYTE2 depth;
BYTE2 d0;
NgStreamRead (in, (char *) &d0, sizeof(BYTE2));
fileHeader[0] = (BYTE4)LittleEndianToSystemUBYTE2(d0);
NgStreamRead (in, (char *) &fileHeader[1], sizeof(BYTE4));
fileHeader[1] = LittleEndianToSystemUBYTE4(fileHeader[1]);
NgStreamRead (in, (char *) &d0, sizeof(BYTE2));
fileHeader[2] = (BYTE4)LittleEndianToSystemUBYTE2(d0);
NgStreamRead (in, (char *) &d0, sizeof(BYTE2));
fileHeader[3] = (BYTE4)LittleEndianToSystemUBYTE2(d0);
NgStreamRead (in, (char *) &fileHeader[4], sizeof(BYTE4));
fileHeader[4] = LittleEndianToSystemUBYTE4(fileHeader[4]);
if (NgStreamEof (in))
{
NgFree (fileHeader);
return NgError (ERR_NG, "Error invalid header file");
}
if (fileHeader[0] != 0x4D42)
{
NgFree (fileHeader);
return NgError (ERR_NG, "Error not a BMP file");
}
infoHeader = (BYTE1*) NgMalloc (BMPHeaderFixedSize * sizeof (BYTE1));
NgStreamRead (in, infoHeader, BMPHeaderFixedSize * sizeof (BYTE1));
if (NgStreamEof (in))
{
NgFree (fileHeader);
NgFree (infoHeader);
return NgError (ERR_NG, "Error invalid info header");
}
NgMemCpy (&width, &infoHeader[4], sizeof (BYTE4));
width = LittleEndianToSystemUBYTE4(width);
NgMemCpy (&height, &infoHeader[8], sizeof (BYTE4));
height = LittleEndianToSystemUBYTE4(height);
NgMemCpy (&depth, &infoHeader[14], sizeof (BYTE2));
depth = LittleEndianToSystemUBYTE2(depth);
stride = (width * depth + 7) / 8;
stride = (stride + 3) / 4 * 4; /* Round up to 4 byte multiple */
if (depth <= 8)
{
BYTE4 i, index;
BYTE1 *colors;
BYTE4 numColors;
NgMemCpy (&numColors, &infoHeader[32], sizeof (BYTE4));
numColors = LittleEndianToSystemUBYTE4(numColors);
if (numColors == 0)
{
BYTE2 value;
NgMemCpy (&value, &infoHeader[14], sizeof (BYTE2));
value = LittleEndianToSystemUBYTE2(value);
numColors = 1 << value;
} else
{
if (numColors > 256)
numColors = 256;
}
colors = (BYTE1*) NgMalloc (numColors * 4);
NgStreamRead (in, colors, numColors * 4);
if (NgStreamEof (in))
{
NgFree (fileHeader);
NgFree (infoHeader);
NgFree (colors);
return NgError (ERR_NG, "Error invalid palette info");
}
index = 0;
NgBitmapImageSetSize(image, (UBYTE4)numColors, (UBYTE4)depth,
(UBYTE4)width, (UBYTE4)height);
for (i = 0; i < numColors; i++)
{
ng_color_map_entry_t *color_map = NgBitmapImageColorMap (image, i);
color_map->blue = colors[index++];
color_map->green = colors[index++];
color_map->red = colors[index++];
index++;
}
NgFree (colors);
} else
{
/* direct - 16 and 24 bits */
NgBitmapImageSetSize(image, 0, (UBYTE4)depth,
(UBYTE4)width, (UBYTE4)height);
}
pos = NgStreamGetPosition (in);
if (pos < fileHeader[4])
{
NgStreamSkip (in, fileHeader[4] - pos);
}
dataSize = height * stride;
data = (BYTE1*)NgBitmapImageImageData(image);
NgMemCpy (&cmp, &infoHeader[16], sizeof (BYTE4));
cmp = LittleEndianToSystemUBYTE4(cmp);
if (cmp == 0)
{
/* BMP_NO_COMPRESSION */
BYTE4 cnt;
cnt = NgStreamRead (in, data, dataSize);
if (cnt != dataSize)
{
NgFree (fileHeader);
NgFree (infoHeader);
return NgError (ERR_NG, "Error failed reading uncompressed data");
}
} else
{
BYTE4 compressedSize;
BYTE1 *compressed;
BYTE4 cnt;
ng_err_t res;
NgMemCpy (&compressedSize, &infoHeader[20], sizeof (BYTE4));
compressedSize = LittleEndianToSystemUBYTE4(compressedSize);
compressed = (BYTE1*) NgMalloc (compressedSize * sizeof (BYTE1));
cnt = NgStreamRead (in, compressed, compressedSize);
if (cnt != compressedSize)
{
NgFree (fileHeader);
NgFree (infoHeader);
NgFree (compressed);
return NgError (ERR_NG, "Error failed reading compressed data");
}
res = decompressData (compressed, compressedSize, data, dataSize, stride, cmp);
if (res != ERR_OK)
{
NgFree (fileHeader);
NgFree (infoHeader);
NgFree (compressed);
return NgError (res, "Error failed data decompression");
}
NgFree (compressed);
}
flipScanLines(data, dataSize, stride, height);
NgFree (fileHeader);
NgFree (infoHeader);
return ERR_OK;
}