/*
* This file is part of rasdaman community.
*
* Rasdaman community is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Rasdaman community 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with rasdaman community. If not, see .
*
* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann /
rasdaman GmbH.
*
* For more information please see
* or contact Peter Baumann via .
/
/**
* SOURCE: bmp.cc
*
* MODULE: conversion
*
* CLASSES: r_Conv_BMP
*
* COMMENTS:
* Provides functions to convert data to BMP and back.
*
*/
#include
#include
#include
#include
#include
#include "raslib/rminit.hh"
#include "raslib/rmdebug.hh"
#include "raslib/parseparams.hh"
#include "conversion/bmp.hh"
#include "conversion/memfs.hh"
// Some Windows-structs, redefined for platform-independent use.
typedef struct {
unsigned short type;
r_ULong size;
unsigned short res0;
unsigned short res1;
r_ULong offset;
} bitmap_file_header_t;
typedef struct {
r_ULong size;
r_Long width;
r_Long height;
unsigned short planes;
unsigned short bitCount;
r_ULong compression;
r_ULong sizeImage;
r_Long xpels;
r_Long ypels;
r_ULong clrUsed;
r_ULong clrImportant;
} bitmap_info_header_t;
typedef struct {
unsigned char blue;
unsigned char green;
unsigned char red;
unsigned char null;
} rgb_quad_t;
// Identifier of BMP data (first two bytes)
const unsigned short BMP_IDENTIFIER=0x4d42;
// Compression types (correspond to BI_RGB, BI_RLE8, BI_RLE4)
const int COMPRESS_NONE=0;
const int COMPRESS_RLE8=1;
const int COMPRESS_RLE4=2;
// Size of BITMAPFILEHEADER (the Windows struct)
const int BMPFILEHEADERSIZE=sizeof(bitmap_file_header_t) - sizeof(BMP_IDENTIFIER);
// Size of BITMAPINFOHEADER (the Windows struct)
const int BMPINFOHEADERSIZE=sizeof(bitmap_info_header_t);
// Total header size
const int BMPHEADERSIZE=(BMPFILEHEADERSIZE + BMPINFOHEADERSIZE);
// Shortcuts for reading and writing short and long types from/to little endian bytestreams
#define READ_LE_SHORT(p,s) \
s = p[0] | (p[1] << 8); p += 2;
#define READ_LE_LONG(p,l) \
l = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); p += 4;
#define WRITE_LE_SHORT(p,s) \
p[0] = s & 0xff; p[1] = (s >> 8) & 0xff; p += 2;
#define WRITE_LE_LONG(p,l) \
p[0] = l & 0xff; p[1] = (l >> 8) & 0xff; p[2] = (l >> 16) & 0xff; p[3] = (l >> 24) & 0xff; p += 4;
void r_Conv_BMP::initBMP( void )
{
memFS = NULL;
compress = 1;
if (params == NULL)
params = new r_Parse_Params;
params->add("compress", &compress, r_Parse_Params::param_type_int);
}
r_Conv_BMP::r_Conv_BMP(const char *src, const r_Minterval &interv, int tp) throw(r_Error)
: r_Convertor(src, interv, tp)
{
initBMP();
}
r_Conv_BMP::r_Conv_BMP(const char *src, const r_Minterval &interv, const r_Type *tp) throw(r_Error)
: r_Convertor(src, interv, tp)
{
initBMP();
}
r_Conv_BMP::~r_Conv_BMP(void)
{
if (memFS != NULL)
{
memfs_killfs((void*)memFS);
delete memFS;
memFS=NULL;
}
}
unsigned char *r_Conv_BMP::flushLiterals(int numLit, int pixelAdd, unsigned char *dest, const unsigned char *lastLit, const unsigned char *mapColours)
{
unsigned char *destPtr = dest;
while (numLit != 0)
{
// Must code literal runs of less than 3 bytes as runs
if (numLit < 3)
{
while (numLit > 0)
{
*destPtr++ = 1; *destPtr++ = mapColours[*lastLit];
lastLit += pixelAdd; numLit--;
}
}
else
{
int litLength=0;
r_Ptr align=0;
litLength = numLit; if (litLength > 255) litLength = 255;
*destPtr++ = 0x00; *destPtr++ = (unsigned char)litLength;
numLit -= litLength;
while (litLength > 0)
{
*destPtr++ = mapColours[*lastLit]; lastLit += pixelAdd; litLength--;
}
align = (r_Ptr)destPtr; if ((align & 1) != 0) *destPtr++ = 0;
}
}
return destPtr;
}
r_convDesc &r_Conv_BMP::convertTo( const char *options) throw(r_Error)
{
void *handle=NULL;
bitmap_info_header_t ihead;
rgb_quad_t *palette = NULL;
int i=0, j=0;
int paletteSize=0, pixelSize=0;
int destPitch=0, pixelAdd=0, lineAdd=0;
int width=0, height=0;
r_ULong offset=0;
r_ULong fileSize=0;
unsigned char *dest=NULL, *destPtr=NULL;
const unsigned char *srcLine=NULL, *srcPtr=NULL;
unsigned char bmpHeaders[BMPHEADERSIZE];
unsigned char mapColours[256];
memFS = new memFSContext; handle = (void*)memFS;
if ((memFS == NULL) || (memfs_initfs(handle) < 0))
{
RMInit::logOut << "r_Conv_BMP::convertTo(): couldn't initialize memfs!" << endl;
throw r_Error(MEMMORYALLOCATIONERROR);
}
memfs_newfile(handle);
width = (int)(desc.srcInterv[0].high() - desc.srcInterv[0].low() + 1);
height = (int)(desc.srcInterv[1].high() - desc.srcInterv[1].low() + 1);
params->process(options);
ihead.size = BMPINFOHEADERSIZE;
ihead.width = (r_Long)width;
ihead.height = (r_Long)height;
ihead.planes = 1;
ihead.compression = COMPRESS_NONE;
ihead.xpels = 0; ihead.ypels = 0;
switch (desc.baseType)
{
case ctype_bool:
ihead.bitCount = 1; destPitch = ((width + 31) & ~31) >> 3; paletteSize = 2; pixelSize = 1;
ihead.clrUsed = 2; ihead.clrImportant = 2;
palette = new rgb_quad_t[2];
palette[0].red = 0x00; palette[0].green = 0x00; palette[0].blue = 0x00; palette[0].null = 0x00;
palette[1].red = 0xff; palette[1].green = 0xff; palette[1].blue = 0xff; palette[1].null = 0x00;
break;
case ctype_char:
{
ihead.bitCount = 8; destPitch = ((width + 3) & ~3); pixelSize = 1;
if (compress != 0) ihead.compression = COMPRESS_RLE8;
// Determine which colours actually appear in the image
memset(mapColours, 0, 256);
srcLine = (const unsigned char*)desc.src;
for (i=0; i>= 1;
if (mask == 0)
{
*destPtr++ = val; mask = 0x80; val = 0;
}
}
if (mask != 0x80) *destPtr++ = val;
}
break;
case ctype_char:
for (i=0; i0; i--) *destPtr++ = 0;
memfs_write(handle, dest, destPitch);
}
delete [] dest; dest = NULL;
}
else // implies RLE 8
{
if ((dest = new unsigned char[2*destPitch]) == NULL)
{
RMInit::logOut << "r_Conv_BMP::convertTo(): out of memory!" << endl;
throw r_Error(MEMMORYALLOCATIONERROR);
}
for (j=0; j encode literals + run
// If a literal sequence has to be broken for the run we require a longer run.
// For the sequence a run shorter than 5 bytes is best merged into lit.
// For the sequence the first run should be merged with lit if it's
// less than 3 bytes long. Therefore on average 4 bytes minimum runs are best.
if (((numLit == 0) && (k > 1)) || (k > 3))
{
// First output the pending literals, if any
destPtr = flushLiterals(numLit, pixelAdd, destPtr, lastLit, mapColours);
numLit = 0;
i += k;
// Now output the run
while (k > 0)
{
int runLength=0;
runLength = k; if (runLength > 255) runLength = 255;
*destPtr++ = runLength; *destPtr++ = mapColours[*srcPtr];
k -= runLength;
}
srcPtr = tryRun; lastLit = srcPtr;
}
else
{
numLit++; srcPtr += pixelAdd; i++;
}
}
// Flush remaining literals
destPtr = flushLiterals(numLit, pixelAdd, destPtr, lastLit, mapColours);
// EOL
*destPtr++ = 0; *destPtr++ = 0;
memfs_write(handle, dest, (destPtr - dest));
}
// EOB
dest[0] = 0; dest[1] = 1;
memfs_write(handle, dest, 2);
delete [] dest; dest = NULL;
}
fileSize = memfs_size(handle);
RMDBGONCE( 3, RMDebug::module_conversion, "r_Conv_BMP", "convertTo(): size: " << fileSize );
offset = BMPHEADERSIZE + paletteSize * sizeof(rgb_quad_t);
dest = bmpHeaders;
ihead.sizeImage = fileSize - offset;
WRITE_LE_SHORT(dest, BMP_IDENTIFIER);
WRITE_LE_LONG(dest, fileSize);
WRITE_LE_SHORT(dest, 0);
WRITE_LE_SHORT(dest, 0);
WRITE_LE_LONG(dest, offset);
WRITE_LE_LONG(dest, ihead.size);
WRITE_LE_LONG(dest, ihead.width);
WRITE_LE_LONG(dest, ihead.height);
WRITE_LE_SHORT(dest, ihead.planes);
WRITE_LE_SHORT(dest, ihead.bitCount);
WRITE_LE_LONG(dest, ihead.compression);
WRITE_LE_LONG(dest, ihead.sizeImage);
WRITE_LE_LONG(dest, ihead.xpels);
WRITE_LE_LONG(dest, ihead.ypels);
WRITE_LE_LONG(dest, ihead.clrUsed);
WRITE_LE_LONG(dest, ihead.clrImportant);
memfs_seek(handle, 0, SEEK_SET);
memfs_write(handle, bmpHeaders, BMPHEADERSIZE);
if ((desc.dest = (char*)mystore.storage_alloc(fileSize)) == NULL)
{
RMInit::logOut << "r_Conv_BMP::convertTo(): out of memory!" << endl;
throw r_Error(MEMMORYALLOCATIONERROR);
}
memfs_seek(handle, 0, SEEK_SET);
memfs_read(handle, desc.dest, fileSize);
memfs_killfs(handle);
delete memFS; memFS = NULL;
desc.destInterv = r_Minterval(1);
desc.destInterv << r_Sinterval((r_Range)0, (r_Range)fileSize-1);
desc.destType = r_Type::get_any_type("char");
return desc;
}
// Auxiliary makro for RLE coders
#define BMP_RLE_LINEFEED \
destLine -= lineAdd; destPtr = destLine; j++; i = 0;
r_convDesc &r_Conv_BMP::convertFrom(const char *options) throw(r_Error)
{
bitmap_file_header_t fhead;
bitmap_info_header_t ihead;
const rgb_quad_t *palette=NULL;
const unsigned char *bmp=NULL;
int i=0, j=0;
int srcPitch=0;
int pixelSize=0, destType=0;
int paletteIsGrey=0, paletteSize=0;
int width=0, height=0;
unsigned char emit0=0, emit1=0; // in case of bitmap -> bool: values to emit for 0 and 1
int lineAdd=0, pixelAdd=0;
bmp = (const unsigned char*)desc.src;
// Read file header
READ_LE_SHORT(bmp, fhead.type);
READ_LE_LONG(bmp, fhead.size);
READ_LE_SHORT(bmp, fhead.res0);
READ_LE_SHORT(bmp, fhead.res1);
READ_LE_LONG(bmp, fhead.offset);
if (fhead.type != BMP_IDENTIFIER)
{
RMInit::logOut << "r_Conv_BMP::convertFrom(): not a BMP file" << endl;
throw r_Error(r_Error::r_Error_General);
}
// Read info header
READ_LE_LONG(bmp, ihead.size);
READ_LE_LONG(bmp, ihead.width);
READ_LE_LONG(bmp, ihead.height);
READ_LE_SHORT(bmp, ihead.planes);
READ_LE_SHORT(bmp, ihead.bitCount);
READ_LE_LONG(bmp, ihead.compression);
READ_LE_LONG(bmp, ihead.sizeImage);
READ_LE_LONG(bmp, ihead.xpels);
READ_LE_LONG(bmp, ihead.ypels);
READ_LE_LONG(bmp, ihead.clrUsed);
READ_LE_LONG(bmp, ihead.clrImportant);
width = (int)ihead.width; height = (int)ihead.height;
RMDBGIF(4, RMDebug::module_conversion, "r_Conv_BMP", \
RMInit::dbgOut << "File: type " << std::hex << fhead.type << ", size " << std::dec << fhead.size; \
RMInit::dbgOut << ", rsv0 " << fhead.res0 << ", rsv1 " << fhead.res1 << ", offs " << fhead.offset << endl; \
RMInit::dbgOut << "Info: size " << ihead.size << ", width " << ihead.width << ", height " << ihead.height; \
RMInit::dbgOut << ", planes " << ihead.planes << ", bits " << ihead.bitCount << ", comp " << ihead.compression; \
RMInit::dbgOut << ", sizeImg " << ihead.sizeImage << ", xpels " << ihead.xpels << ", ypels " << ihead.ypels; \
RMInit::dbgOut << ", clrUsed " << ihead.clrUsed << ", clrImp " << ihead.clrImportant << endl; )
palette = (const rgb_quad_t*)(desc.src + BMPFILEHEADERSIZE + ihead.size);
paletteIsGrey = 0;
paletteSize = ihead.clrUsed;
if ((paletteSize == 0) && (ihead.bitCount != 24)) paletteSize = (1 << ihead.bitCount);
switch (ihead.bitCount)
{
case 1:
srcPitch = ((width + 31) & ~31) >> 3;
// Grey?
if ((palette[0].red == palette[0].green) && (palette[0].green == palette[0].blue) &&
(palette[1].red == palette[1].green) && (palette[1].green == palette[1].blue))
{
paletteIsGrey = 1; pixelSize = 1;
// Yes; also black + white?
if (((palette[0].red == 0x00) && (palette[1].red == 0xff)) ||
((palette[0].red == 0xff) && (palette[1].red == 0x00)))
{
// Yes
destType = ctype_bool;
if (palette[0].red == 0)
{
emit0 = 0; emit1 = 1;
}
else
{
emit0 = 1; emit1 = 0;
}
}
else
{
// No
destType = ctype_char;
}
}
else
{
// Convert to RGB
destType = ctype_rgb; pixelSize = 3;
}
break;
case 4:
case 8:
{
if (ihead.bitCount == 4)
{
srcPitch = ((width + 7) & ~7) >> 1;
}
else
{
srcPitch = ((width + 3) & ~3); }
// Check whether the palette is greyscale
for (i=0; i 0)
{
destPtr[0] = palette[0].red; destPtr[1] = palette[0].green; destPtr[2] = palette[0].blue;
destPtr += pixelSize; i--;
}
}
else
{
while (i > 0)
{
destPtr[0] = palette[0].red;
destPtr += pixelSize; i--;
}
}
}
destLine = dest + ((height - 1) * pixelSize);
switch (ihead.compression)
{
case COMPRESS_NONE:
for (j=0; j>= 1;
}
break;
case ctype_char:
for (i=0; i>= 1;
}
break;
case ctype_rgb:
for (i=0; i>= 1;
}
break;
}
}
break;
case 4:
{
switch (destType)
{
case ctype_char:
for (i=0; i> 4].red;
destPtr += pixelAdd;
*destPtr = palette[(*imgPtr & 0x0f)].red;
destPtr += pixelAdd; imgPtr++;
}
if (i < width)
{
*destPtr = palette[(*imgPtr) >> 4].red;
}
break;
case ctype_rgb:
for (i=0; i> 4;
destPtr[0] = palette[idx].red;
destPtr[1] = palette[idx].green;
destPtr[2] = palette[idx].blue;
destPtr += pixelAdd;
idx = (*imgPtr) & 0x0f;
destPtr[0] = palette[idx].red;
destPtr[1] = palette[idx].green;
destPtr[2] = palette[idx].blue;
destPtr += pixelAdd; imgPtr++;
}
if (i < width)
{
int idx;
idx = (*imgPtr) >> 4;
destPtr[0] = palette[idx].red;
destPtr[1] = palette[idx].green;
destPtr[2] = palette[idx].blue;
}
break;
}
}
break;
case 8:
switch (destType)
{
case ctype_char:
for (i=0; i= 0)
{
unsigned char val=0, cmd=0;
//cout << "(" << i << "," << j << ")" << endl;
cmd = *imgPtr++;
if (cmd == 0) // escape
{
cmd = *imgPtr++;
switch (cmd)
{
case 0: // end of line
//cout << "EOL" << endl;
BMP_RLE_LINEFEED;
break;
case 1: // end of bitmap
//cout << "EOB" << endl;
j = -1;
break;
case 2: // delta
//cout << "DELTA" << endl;
val = *imgPtr++; i += val; destPtr += val * pixelAdd;
val = *imgPtr++; j += val; destPtr -= val * lineAdd; destLine -= val * lineAdd;
break;
default: // absolute mode
//cout << "ABS" << endl;
while (cmd > 0)
{
val = *imgPtr++;
if (j < height)
{
if (i >= width)
{
BMP_RLE_LINEFEED;
}
destPtr[0] = palette[val].red;
if (paletteIsGrey == 0)
{
destPtr[1] = palette[val].green;
destPtr[2] = palette[val].blue;
}
destPtr += pixelAdd; i++;
}
cmd--;
}
// Align srcPtr to a Windows-Word position (==> aligned to short)
if ((((r_Ptr)imgPtr) & 1) != 0) imgPtr++;
break;
}
}
else // repeat sequence
{
//cout << "RUN" << endl;
val = *imgPtr++;
while (cmd > 0)
{
if (j < height)
{
if (i >= width)
{
BMP_RLE_LINEFEED;
}
destPtr[0] = palette[val].red;
if (paletteIsGrey == 0)
{
destPtr[1] = palette[val].green;
destPtr[2] = palette[val].blue;
}
destPtr += pixelAdd; i++;
}
cmd--;
}
}
}
}
break;
case COMPRESS_RLE4:
{
i = 0; j = 0; destPtr = destLine; imgPtr = imgLine;
while (j >= 0)
{
unsigned char val=0, cmd=0;
cmd = *imgPtr++;
if (cmd == 0) // escape
{
cmd = *imgPtr++;
switch (cmd)
{
case 0: // end of line
BMP_RLE_LINEFEED;
break;
case 1: // end of bitmap
j = -1;
break;
case 2: // delta
val = *imgPtr++; i += val; destPtr += val * pixelAdd;
val = *imgPtr++; j += val; destPtr -= val * lineAdd; destLine -= val * lineAdd;
break;
default: // absolute mode
while (cmd > 1)
{
val = *imgPtr++;
if (j < height)
{
if (i >= width)
{
BMP_RLE_LINEFEED;
}
destPtr[0] = palette[val >> 4].red;
if (paletteIsGrey == 0)
{
destPtr[1] = palette[val >> 4].green;
destPtr[2] = palette[val >> 4].blue;
}
destPtr += pixelAdd; i++;
if (i >= width)
{
BMP_RLE_LINEFEED;
}
destPtr[0] = palette[val & 0x0f].red;
if (paletteIsGrey == 0)
{
destPtr[1] = palette[val & 0x0f].green;
destPtr[2] = palette[val & 0x0f].blue;
}
destPtr += pixelAdd; i++;
}
cmd-=2;
}
if (cmd != 0)
{
val = *imgPtr++;
if (j < height)
{
if (i >= width)
{
BMP_RLE_LINEFEED;
}
destPtr[0] = palette[val >> 4].red;
if (paletteIsGrey == 0)
{
destPtr[1] = palette[val >> 4].green;
destPtr[2] = palette[val >> 4].blue;
}
destPtr += pixelAdd; i++;
}
}
// Align srcPtr to a Windows-Word position (==> aligned to short)
if ((((r_Ptr)imgPtr) & 1) != 0) imgPtr++;
break;
}
}
else // repeat sequence
{
val = *imgPtr++;
while (cmd > 1)
{
if (j < height)
{
if (i >= width)
{
BMP_RLE_LINEFEED;
}
destPtr[0] = palette[val >> 4].red;
if (paletteIsGrey == 0)
{
destPtr[1] = palette[val >> 4].green;
destPtr[2] = palette[val >> 4].blue;
}
destPtr += pixelAdd; i++;
if (i >= width)
{
BMP_RLE_LINEFEED;
}
destPtr[0] = palette[val & 0x0f].red;
if (paletteIsGrey == 0)
{
destPtr[1] = palette[val & 0x0f].green;
destPtr[2] = palette[val & 0x0f].blue;
}
destPtr += pixelAdd; i++;
}
cmd -= 2;
}
if (cmd != 0)
{
if (j < height)
{
if (i >= width)
{
BMP_RLE_LINEFEED;
}
destPtr[0] = palette[val >> 4].red;
if (paletteIsGrey == 0)
{
destPtr[1] = palette[val >> 4].green;
destPtr[2] = palette[val >> 4].blue;
}
destPtr += pixelAdd; i++;
}
}
}
}
}
break;
default:
{
RMInit::logOut << "r_Conv_BMP::convertFrom(): bad compression type" << endl;
mystore.storage_free(dest);
throw r_Error(r_Error::r_Error_General);
}
}
desc.dest = (char*)dest; desc.baseType = destType;
desc.destInterv = r_Minterval(2);
desc.destInterv << r_Sinterval((r_Range)0, (r_Range)width-1)
<< r_Sinterval((r_Range)0, (r_Range)height-1);
desc.destType = get_external_type(desc.baseType);
return desc;
}
const char *r_Conv_BMP::get_name( void ) const
{
return format_name_bmp;
}
r_Data_Format r_Conv_BMP::get_data_format( void ) const
{
return r_BMP;
}
r_Convertor *r_Conv_BMP::clone( void ) const
{
return new r_Conv_BMP(desc.src, desc.srcInterv, desc.baseType);
}