/* * 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); }