534 lines
11 KiB
C++
534 lines
11 KiB
C++
//
|
|
// mxToolKit (c) 1999 by Mete Ciragan
|
|
//
|
|
// file: mxBmp.cpp
|
|
// implementation: all
|
|
// last modified: Apr 15 1999, Mete Ciragan
|
|
// copyright: The programs and associated files contained in this
|
|
// distribution were developed by Mete Ciragan. The programs
|
|
// are not in the public domain, but they are freely
|
|
// distributable without licensing fees. These programs are
|
|
// provided without guarantee or warrantee expressed or
|
|
// implied.
|
|
//
|
|
/***
|
|
*
|
|
* Copyright (c) 1998, Valve LLC. All rights reserved.
|
|
*
|
|
* This product contains software technology licensed from Id
|
|
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
|
* All Rights Reserved.
|
|
*
|
|
****/
|
|
|
|
// lbmlib.c
|
|
|
|
#include <mxBmp.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "mxMessageBox.h"
|
|
|
|
mxImage *mxBmpRead( const char *filename )
|
|
{
|
|
int i;
|
|
FILE *pfile = 0;
|
|
mxBitmapFileHeader bmfh;
|
|
mxBitmapInfoHeader bmih;
|
|
mxBitmapRGBQuad rgrgbPalette[256];
|
|
int columns, column, rows, row, bpp = 1;
|
|
int cbBmpBits, padSize = 0, bps = 0;
|
|
byte *pbBmpBits;
|
|
byte *pb, *pbHold;
|
|
int cbPalBytes = 0;
|
|
int biTrueWidth;
|
|
mxImage *image = 0;
|
|
|
|
// File exists?
|
|
if(( pfile = fopen( filename, "rb" )) == 0 )
|
|
return 0;
|
|
|
|
// Read file header
|
|
if( fread( &bmfh, sizeof bmfh, 1, pfile ) != 1 )
|
|
goto GetOut;
|
|
|
|
// Bogus file header check
|
|
if( !( bmfh.bfReserved1 == 0 && bmfh.bfReserved2 == 0 ))
|
|
goto GetOut;
|
|
|
|
// Read info header
|
|
if( fread( &bmih, sizeof bmih, 1, pfile ) != 1 )
|
|
goto GetOut;
|
|
|
|
// Bogus info header check
|
|
if( !( bmih.biSize == sizeof bmih && bmih.biPlanes == 1 ))
|
|
goto GetOut;
|
|
|
|
if( memcmp( (void *)&bmfh.bfType, "BM", 2 ))
|
|
goto GetOut;
|
|
|
|
// Bogus bit depth?
|
|
if( bmih.biBitCount < 8 )
|
|
goto GetOut;
|
|
|
|
// Bogus compression? Only non-compressed supported.
|
|
if( bmih.biCompression != 0 ) //BI_RGB)
|
|
goto GetOut;
|
|
|
|
if( bmih.biBitCount == 8 )
|
|
{
|
|
// Figure out how many entires are actually in the table
|
|
if( bmih.biClrUsed == 0 )
|
|
{
|
|
cbPalBytes = (1 << bmih.biBitCount) * sizeof( mxBitmapRGBQuad );
|
|
bmih.biClrUsed = 256;
|
|
}
|
|
else
|
|
{
|
|
cbPalBytes = bmih.biClrUsed * sizeof( mxBitmapRGBQuad );
|
|
}
|
|
|
|
// Read palette (bmih.biClrUsed entries)
|
|
if( fread( rgrgbPalette, cbPalBytes, 1, pfile ) != 1 )
|
|
goto GetOut;
|
|
}
|
|
|
|
image = new mxImage();
|
|
if( !image ) goto GetOut;
|
|
|
|
if( !image->create( bmih.biWidth, abs( bmih.biHeight ), bmih.biBitCount ))
|
|
{
|
|
delete image;
|
|
goto GetOut;
|
|
}
|
|
|
|
if( bmih.biBitCount <= 8 )
|
|
{
|
|
pb = (byte *)image->palette;
|
|
|
|
// Copy over used entries
|
|
for( i = 0; i < (int)bmih.biClrUsed; i++ )
|
|
{
|
|
*pb++ = rgrgbPalette[i].rgbRed;
|
|
*pb++ = rgrgbPalette[i].rgbGreen;
|
|
*pb++ = rgrgbPalette[i].rgbBlue;
|
|
}
|
|
|
|
// Fill in unused entires will 0,0,0
|
|
for( i = bmih.biClrUsed; i < 256; i++ )
|
|
{
|
|
*pb++ = 0;
|
|
*pb++ = 0;
|
|
*pb++ = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( bmih.biBitCount == 24 )
|
|
bpp = 3;
|
|
else if( bmih.biBitCount == 32 )
|
|
bpp = 4;
|
|
}
|
|
|
|
// Read bitmap bits (remainder of file)
|
|
cbBmpBits = bmfh.bfSize - ftell( pfile );
|
|
|
|
pbHold = pb = (byte *)malloc( cbBmpBits * sizeof( byte ));
|
|
if( pb == 0 )
|
|
{
|
|
delete image;
|
|
goto GetOut;
|
|
}
|
|
|
|
if( fread( pb, cbBmpBits, 1, pfile ) != 1 )
|
|
{
|
|
free( pbHold );
|
|
delete image;
|
|
goto GetOut;
|
|
}
|
|
|
|
// data is actually stored with the width being rounded up to a multiple of 4
|
|
biTrueWidth = (bmih.biWidth + 3) & ~3;
|
|
bps = bmih.biWidth * (bmih.biBitCount >> 3);
|
|
|
|
columns = bmih.biWidth;
|
|
rows = abs( bmih.biHeight );
|
|
|
|
switch( bmih.biBitCount )
|
|
{
|
|
case 8:
|
|
case 24:
|
|
padSize = ( 4 - ( bps % 4 )) % 4;
|
|
break;
|
|
}
|
|
|
|
for( row = rows - 1; row >= 0; row-- )
|
|
{
|
|
pbBmpBits = (byte *)image->data + (row * columns * bpp);
|
|
|
|
for( column = 0; column < columns; column++ )
|
|
{
|
|
byte red, green, blue, alpha;
|
|
int palIndex;
|
|
|
|
switch( bmih.biBitCount )
|
|
{
|
|
case 8:
|
|
palIndex = *pb++;
|
|
*pbBmpBits++ = palIndex;
|
|
break;
|
|
case 24:
|
|
blue = *pb++;
|
|
green = *pb++;
|
|
red = *pb++;
|
|
*pbBmpBits++ = red;
|
|
*pbBmpBits++ = green;
|
|
*pbBmpBits++ = blue;
|
|
break;
|
|
case 32:
|
|
blue = *pb++;
|
|
green = *pb++;
|
|
red = *pb++;
|
|
alpha = *pb++;
|
|
*pbBmpBits++ = red;
|
|
*pbBmpBits++ = green;
|
|
*pbBmpBits++ = blue;
|
|
*pbBmpBits++ = alpha;
|
|
break;
|
|
default:
|
|
free( pbHold );
|
|
delete image;
|
|
goto GetOut;
|
|
}
|
|
}
|
|
|
|
pb += padSize; // actual only for 4-bit bmps
|
|
}
|
|
|
|
free( pbHold );
|
|
GetOut:
|
|
if( pfile )
|
|
fclose( pfile );
|
|
|
|
return image;
|
|
}
|
|
|
|
mxImage *mxBmpReadBuffer( const byte *buffer, size_t size )
|
|
{
|
|
int i;
|
|
mxBitmapFileHeader bmfh;
|
|
mxBitmapInfoHeader bmih;
|
|
mxBitmapRGBQuad rgrgbPalette[256];
|
|
int columns, column, rows, row, bpp = 1;
|
|
int cbBmpBits, padSize = 0, bps = 0;
|
|
const byte *bufstart, *bufend;
|
|
byte *pbBmpBits;
|
|
byte *pb, *pbHold;
|
|
int cbPalBytes = 0;
|
|
int biTrueWidth;
|
|
mxImage *image = 0;
|
|
|
|
// Buffer exists?
|
|
if( !buffer || size <= 0 )
|
|
return 0;
|
|
|
|
bufstart = buffer;
|
|
bufend = bufstart + size;
|
|
|
|
// Read file header
|
|
memcpy( &bmfh, buffer, sizeof bmfh );
|
|
buffer += sizeof( bmfh );
|
|
|
|
// Bogus file header check
|
|
if( !( bmfh.bfReserved1 == 0 && bmfh.bfReserved2 == 0 ))
|
|
goto GetOut;
|
|
|
|
// Read info header
|
|
memcpy( &bmih, buffer, sizeof bmih );
|
|
buffer += sizeof( bmih );
|
|
|
|
// Bogus info header check
|
|
if( !( bmih.biSize == sizeof bmih && bmih.biPlanes == 1 ))
|
|
goto GetOut;
|
|
|
|
if( memcmp( (void *)&bmfh.bfType, "BM", 2 ))
|
|
goto GetOut;
|
|
|
|
// Bogus bit depth?
|
|
if( bmih.biBitCount < 8 )
|
|
goto GetOut;
|
|
|
|
// Bogus compression? Only non-compressed supported.
|
|
if( bmih.biCompression != 0 ) //BI_RGB)
|
|
goto GetOut;
|
|
|
|
if( bmih.biBitCount == 8 )
|
|
{
|
|
// Figure out how many entires are actually in the table
|
|
if( bmih.biClrUsed == 0 )
|
|
{
|
|
cbPalBytes = (1 << bmih.biBitCount) * sizeof( mxBitmapRGBQuad );
|
|
bmih.biClrUsed = 256;
|
|
}
|
|
else
|
|
{
|
|
cbPalBytes = bmih.biClrUsed * sizeof( mxBitmapRGBQuad );
|
|
}
|
|
|
|
// Read palette (bmih.biClrUsed entries)
|
|
memcpy( rgrgbPalette, buffer, cbPalBytes );
|
|
buffer += cbPalBytes;
|
|
}
|
|
|
|
image = new mxImage();
|
|
if( !image ) goto GetOut;
|
|
|
|
if( !image->create( bmih.biWidth, abs( bmih.biHeight ), bmih.biBitCount ))
|
|
{
|
|
delete image;
|
|
goto GetOut;
|
|
}
|
|
|
|
if( bmih.biBitCount <= 8 )
|
|
{
|
|
pb = (byte *)image->palette;
|
|
|
|
// Copy over used entries
|
|
for( i = 0; i < (int)bmih.biClrUsed; i++ )
|
|
{
|
|
*pb++ = rgrgbPalette[i].rgbRed;
|
|
*pb++ = rgrgbPalette[i].rgbGreen;
|
|
*pb++ = rgrgbPalette[i].rgbBlue;
|
|
}
|
|
|
|
// Fill in unused entires will 0,0,0
|
|
for( i = bmih.biClrUsed; i < 256; i++ )
|
|
{
|
|
*pb++ = 0;
|
|
*pb++ = 0;
|
|
*pb++ = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( bmih.biBitCount == 24 )
|
|
bpp = 3;
|
|
else if( bmih.biBitCount == 32 )
|
|
bpp = 4;
|
|
}
|
|
|
|
// Read bitmap bits (remainder of file)
|
|
cbBmpBits = bmfh.bfSize - (buffer - bufstart);
|
|
|
|
pbHold = pb = (byte *)malloc( cbBmpBits * sizeof( byte ));
|
|
if( pb == 0 )
|
|
{
|
|
delete image;
|
|
goto GetOut;
|
|
}
|
|
|
|
memcpy( pb, buffer, cbBmpBits );
|
|
buffer += cbBmpBits;
|
|
|
|
// data is actually stored with the width being rounded up to a multiple of 4
|
|
biTrueWidth = (bmih.biWidth + 3) & ~3;
|
|
bps = bmih.biWidth * (bmih.biBitCount >> 3);
|
|
|
|
columns = bmih.biWidth;
|
|
rows = abs( bmih.biHeight );
|
|
|
|
switch( bmih.biBitCount )
|
|
{
|
|
case 8:
|
|
case 24:
|
|
padSize = ( 4 - ( bps % 4 )) % 4;
|
|
break;
|
|
}
|
|
|
|
for( row = rows - 1; row >= 0; row-- )
|
|
{
|
|
pbBmpBits = (byte *)image->data + (row * columns * bpp);
|
|
|
|
for( column = 0; column < columns; column++ )
|
|
{
|
|
byte red, green, blue, alpha;
|
|
int palIndex;
|
|
|
|
switch( bmih.biBitCount )
|
|
{
|
|
case 8:
|
|
palIndex = *pb++;
|
|
*pbBmpBits++ = palIndex;
|
|
break;
|
|
case 24:
|
|
blue = *pb++;
|
|
green = *pb++;
|
|
red = *pb++;
|
|
*pbBmpBits++ = red;
|
|
*pbBmpBits++ = green;
|
|
*pbBmpBits++ = blue;
|
|
break;
|
|
case 32:
|
|
blue = *pb++;
|
|
green = *pb++;
|
|
red = *pb++;
|
|
alpha = *pb++;
|
|
*pbBmpBits++ = red;
|
|
*pbBmpBits++ = green;
|
|
*pbBmpBits++ = blue;
|
|
*pbBmpBits++ = alpha;
|
|
break;
|
|
default:
|
|
free( pbHold );
|
|
delete image;
|
|
goto GetOut;
|
|
}
|
|
}
|
|
|
|
pb += padSize; // actual only for 4-bit bmps
|
|
}
|
|
|
|
free( pbHold );
|
|
GetOut:
|
|
return image;
|
|
}
|
|
|
|
bool mxBmpWrite( const char *filename, mxImage *image )
|
|
{
|
|
int i, x, y;
|
|
FILE *pfile = 0;
|
|
mxBitmapFileHeader bmfh;
|
|
mxBitmapInfoHeader bmih;
|
|
mxBitmapRGBQuad rgrgbPalette[256];
|
|
int cbBmpBits;
|
|
byte *pbBmpBits;
|
|
byte *pb;
|
|
int cbPalBytes = 0;
|
|
int biTrueWidth;
|
|
int pixel_size;
|
|
|
|
if( !image || !image->data )
|
|
return false;
|
|
|
|
// File exists?
|
|
if(( pfile = fopen( filename, "wb" )) == 0 )
|
|
return false;
|
|
|
|
if( image->bpp == 8 )
|
|
pixel_size = 1;
|
|
else if( image->bpp == 24 )
|
|
pixel_size = 3;
|
|
else if( image->bpp == 32 )
|
|
pixel_size = 4;
|
|
else return false;
|
|
|
|
biTrueWidth = ((image->width + 3) & ~3);
|
|
cbBmpBits = biTrueWidth * image->height * pixel_size;
|
|
if( pixel_size == 1 ) cbPalBytes = 256 * sizeof (mxBitmapRGBQuad);
|
|
|
|
// Bogus file header check
|
|
bmfh.bfType = (word) (('M' << 8) | 'B');
|
|
bmfh.bfSize = sizeof bmfh + sizeof bmih + cbBmpBits + cbPalBytes;
|
|
bmfh.bfOffBits = sizeof bmfh + sizeof bmih + cbPalBytes;
|
|
bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
|
|
|
|
// write file header
|
|
if( fwrite( &bmfh, sizeof bmfh, 1, pfile ) != 1 )
|
|
{
|
|
fclose( pfile );
|
|
return false;
|
|
}
|
|
|
|
bmih.biSize = sizeof bmih;
|
|
bmih.biWidth = biTrueWidth;
|
|
bmih.biHeight = image->height;
|
|
bmih.biPlanes = 1;
|
|
bmih.biBitCount = pixel_size * 8;
|
|
bmih.biCompression = 0; //BI_RGB;
|
|
bmih.biSizeImage = cbBmpBits;
|
|
bmih.biXPelsPerMeter = 0;
|
|
bmih.biYPelsPerMeter = 0;
|
|
bmih.biClrUsed = ( pixel_size == 1 ) ? 256 : 0;
|
|
bmih.biClrImportant = 0;
|
|
|
|
// Write info header
|
|
if( fwrite( &bmih, sizeof bmih, 1, pfile ) != 1 )
|
|
{
|
|
fclose (pfile);
|
|
return false;
|
|
}
|
|
|
|
if( pixel_size == 1 )
|
|
{
|
|
// convert to expanded palette
|
|
pb = (byte *)image->palette;
|
|
|
|
// Copy over used entries
|
|
for (i = 0; i < (int) bmih.biClrUsed; i++)
|
|
{
|
|
rgrgbPalette[i].rgbRed = *pb++;
|
|
rgrgbPalette[i].rgbGreen = *pb++;
|
|
rgrgbPalette[i].rgbBlue = *pb++;
|
|
rgrgbPalette[i].rgbReserved = 0;
|
|
}
|
|
|
|
// Write palette (bmih.biClrUsed entries)
|
|
cbPalBytes = bmih.biClrUsed * sizeof (mxBitmapRGBQuad);
|
|
|
|
if( fwrite( rgrgbPalette, cbPalBytes, 1, pfile ) != 1 )
|
|
{
|
|
fclose (pfile);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
pbBmpBits = (byte *)malloc( cbBmpBits * sizeof( byte ));
|
|
|
|
if( !pbBmpBits )
|
|
{
|
|
fclose( pfile );
|
|
return false;
|
|
}
|
|
|
|
pb = (byte *)image->data;
|
|
|
|
for( y = 0; y < bmih.biHeight; y++ )
|
|
{
|
|
i = (bmih.biHeight - 1 - y ) * (bmih.biWidth);
|
|
|
|
for( x = 0; x < image->width; x++ )
|
|
{
|
|
if( pixel_size == 1 )
|
|
{
|
|
// 8-bit
|
|
pbBmpBits[i] = pb[x];
|
|
}
|
|
else
|
|
{
|
|
// 24 bit
|
|
pbBmpBits[i*pixel_size+0] = pb[x*pixel_size+2];
|
|
pbBmpBits[i*pixel_size+1] = pb[x*pixel_size+1];
|
|
pbBmpBits[i*pixel_size+2] = pb[x*pixel_size+0];
|
|
}
|
|
|
|
if( pixel_size == 4 ) // write alpha channel
|
|
pbBmpBits[i*pixel_size+3] = pb[x*pixel_size+3];
|
|
i++;
|
|
}
|
|
|
|
pb += image->width * pixel_size;
|
|
}
|
|
|
|
// Write bitmap bits (remainder of file)
|
|
if( fwrite( pbBmpBits, cbBmpBits, 1, pfile ) != 1 )
|
|
{
|
|
free( pbBmpBits );
|
|
fclose( pfile );
|
|
return false;
|
|
}
|
|
|
|
free( pbBmpBits );
|
|
fclose( pfile );
|
|
|
|
return true;
|
|
} |