leocad/common/im_bmp.cpp
2011-09-07 21:06:51 +00:00

492 lines
11 KiB
C++
Executable file

#include <stdio.h>
#include <stdlib.h>
#include "config.h"
#include "quant.h"
#include "image.h"
#include "file.h"
// ========================================================
bool Image::LoadBMP (File& file)
{
lcint32 bmWidth, bmHeight;
lcuint8 bmPlanes, bmBitsPixel, m1, m2;
typedef struct {
unsigned char rgbBlue;
unsigned char rgbGreen;
unsigned char rgbRed;
unsigned char rgbReserved;
} RGBQUAD;
lcint16 res1,res2;
lcint32 filesize, pixoff;
lcint32 bmisize, compression;
lcint32 xscale, yscale;
lcint32 colors, impcol, rc;
lcuint32 sizeimage, m_bytesRead = 0;
FreeData ();
if (file.Read (&m1, 1) != 1)
return false;
m_bytesRead++;
if (file.Read (&m2, 1) != 1)
return false;
m_bytesRead++;
if ((m1 != 'B') || (m2 != 'M'))
return false;
rc = file.ReadLong ((long*)&(filesize), 1); m_bytesRead+=4;
if (rc != 1) { return false; }
rc = file.ReadShort ((int*)&(res1), 1); m_bytesRead+=2;
if (rc != 1) { return false; }
rc = file.ReadShort ((int*)&(res2), 1); m_bytesRead+=2;
if (rc != 1) { return false; }
rc = file.ReadLong ((long*)&(pixoff), 1); m_bytesRead+=4;
if (rc != 1) { return false; }
rc = file.ReadLong ((long*)&(bmisize), 1); m_bytesRead+=4;
if (rc != 1) { return false; }
rc = file.ReadLong ((long*)&(bmWidth), 1); m_bytesRead+=4;
if (rc != 1) { return false; }
rc = file.ReadLong ((long*)&(bmHeight), 1); m_bytesRead+=4;
if (rc != 1) { return false; }
rc = file.ReadShort ((int*)&(bmPlanes), 1); m_bytesRead+=2;
if (rc != 1) { return false; }
rc = file.ReadShort ((int*)&(bmBitsPixel), 1); m_bytesRead+=2;
if (rc != 1) { return false; }
rc = file.ReadLong ((long*)&(compression), 1); m_bytesRead+=4;
if (rc != 1) { return false; }
rc = file.ReadLong ((long*)&(sizeimage), 1); m_bytesRead+=4;
if (rc != 1) {return false; }
rc = file.ReadLong ((long*)&(xscale), 1); m_bytesRead+=4;
if (rc != 1) { return false; }
rc = file.ReadLong ((long*)&(yscale), 1); m_bytesRead+=4;
if (rc != 1) { return false; }
rc = file.ReadLong ((long*)&(colors), 1); m_bytesRead+=4;
if (rc != 1) { return false; }
rc = file.ReadLong ((long*)&(impcol), 1); m_bytesRead+=4;
if (rc != 1) { return false; }
if (colors == 0)
colors = 1 << bmBitsPixel;
RGBQUAD *colormap = NULL;
if (bmBitsPixel != 24)
{
colormap = new RGBQUAD[colors];
if (colormap == NULL)
return false;
int i;
for (i = 0; i < colors; i++)
{
unsigned char r ,g, b, dummy;
rc = file.Read (&b, 1);
m_bytesRead++;
if (rc!=1)
{
delete [] colormap;
return false;
}
rc = file.Read (&g, 1);
m_bytesRead++;
if (rc!=1)
{
delete [] colormap;
return false;
}
rc = file.Read (&r, 1);
m_bytesRead++;
if (rc != 1)
{
delete [] colormap;
return false;
}
rc = file.Read (&dummy, 1);
m_bytesRead++;
if (rc != 1)
{
delete [] colormap;
return false;
}
colormap[i].rgbRed = r;
colormap[i].rgbGreen = g;
colormap[i].rgbBlue = b;
}
}
if ((long)m_bytesRead > pixoff)
{
delete [] colormap;
return false;
}
while ((long)m_bytesRead < pixoff)
{
char dummy;
file.Read (&dummy, 1);
m_bytesRead++;
}
int w = bmWidth;
int h = bmHeight;
// set the output params
m_pData = (unsigned char*)malloc (w*h*3);
long row_size = w * 3;
if (m_pData != NULL)
{
m_nWidth = w;
m_nHeight = h;
m_bAlpha = false;
unsigned char* outbuf = m_pData;
long row = 0;
long rowOffset = 0;
if (compression == 0) // BI_RGB
{
// read rows in reverse order
for (row=bmHeight-1;row>=0;row--)
{
// which row are we working on?
rowOffset = (long unsigned)row*row_size;
if (bmBitsPixel == 24)
{
for (int col=0;col<w;col++)
{
long offset = col * 3;
char pixel[3];
if (file.Read (pixel, 3) ==3)
{
// we swap red and blue here
*(outbuf + rowOffset + offset + 0) = pixel[2]; // r
*(outbuf + rowOffset + offset + 1) = pixel[1]; // g
*(outbuf + rowOffset + offset + 2) = pixel[0]; // b
}
}
m_bytesRead += row_size;
// read DWORD padding
while ((m_bytesRead-pixoff)&3)
{
char dummy;
if (file.Read (&dummy, 1) != 1)
{
FreeData ();
return false;
}
m_bytesRead++;
}
}
else
{
// pixels are packed as 1 , 4 or 8 bit vals. need to unpack them
int bit_count = 0;
unsigned long mask = (1 << bmBitsPixel) - 1;
unsigned char inbyte = 0;
for (int col=0;col<w;col++)
{
int pix = 0;
// if we need another byte
if (bit_count <= 0)
{
bit_count = 8;
if (file.Read (&inbyte, 1) != 1)
{
FreeData ();
delete [] colormap;
return false;
}
m_bytesRead++;
}
// keep track of where we are in the bytes
bit_count -= bmBitsPixel;
pix = ( inbyte >> bit_count) & mask;
// lookup the color from the colormap - stuff it in our buffer
// swap red and blue
*(outbuf + rowOffset + col * 3 + 2) = colormap[pix].rgbBlue;
*(outbuf + rowOffset + col * 3 + 1) = colormap[pix].rgbGreen;
*(outbuf + rowOffset + col * 3 + 0) = colormap[pix].rgbRed;
}
// read DWORD padding
while ((m_bytesRead-pixoff)&3)
{
char dummy;
if (file.Read (&dummy, 1) != 1)
{
FreeData ();
if (colormap)
delete [] colormap;
return false;
}
m_bytesRead++;
}
}
}
}
else
{
int i, x = 0;
unsigned char c, c1 = 0, *pp;
row = 0;
pp = outbuf + (bmHeight-1)*bmWidth*3;
if (bmBitsPixel == 8)
{
while (row < bmHeight)
{
c = file.GetChar ();
if (c)
{
// encoded mode
c1 = file.GetChar ();
for (i = 0; i < c; x++, i++)
{
*pp = colormap[c1].rgbRed; pp++;
*pp = colormap[c1].rgbGreen; pp++;
*pp = colormap[c1].rgbBlue; pp++;
}
}
else
{
// c==0x00, escape codes
c = file.GetChar ();
if (c == 0x00) // end of line
{
row++;
x = 0;
pp = outbuf + (bmHeight-row-1)*bmWidth*3;
}
else if (c == 0x01)
break; // end of pic
else if (c == 0x02) // delta
{
c = file.GetChar ();
x += c;
c = file.GetChar ();
row += c;
pp = outbuf + x*3 + (bmHeight-row-1)*bmWidth*3;
}
else // absolute mode
{
for (i = 0; i < c; x++, i++)
{
c1 = file.GetChar ();
*pp = colormap[c1].rgbRed; pp++;
*pp = colormap[c1].rgbGreen; pp++;
*pp = colormap[c1].rgbBlue; pp++;
}
if (c & 1)
file.GetChar (); // odd length run: read an extra pad byte
}
}
}
}
else if (bmBitsPixel == 4)
{
while (row < bmHeight)
{
c = file.GetChar ();
if (c)
{
// encoded mode
c1 = file.GetChar ();
for (i = 0; i < c; x++, i++)
{
*pp = colormap[(i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f)].rgbRed; pp++;
*pp = colormap[(i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f)].rgbGreen; pp++;
*pp = colormap[(i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f)].rgbBlue; pp++;
}
}
else
{
// c==0x00, escape codes
c = file.GetChar ();
if (c == 0x00) // end of line
{
row++;
x = 0;
pp = outbuf + (bmHeight-row-1)*bmWidth*3;
}
else if (c == 0x01)
break; // end of pic
else if (c == 0x02) // delta
{
c = file.GetChar ();
x += c;
c = file.GetChar ();
row += c;
pp = outbuf + x*3 + (bmHeight-row-1)*bmWidth*3;
}
else // absolute mode
{
for (i = 0; i < c; x++, i++)
{
if ((i&1) == 0)
c1 = file.GetChar ();
*pp = colormap[(i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f)].rgbRed; pp++;
*pp = colormap[(i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f)].rgbGreen; pp++;
*pp = colormap[(i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f)].rgbBlue; pp++;
}
if (((c&3) == 1) || ((c&3) == 2))
file.GetChar (); // odd length run: read an extra pad byte
}
}
}
}
}
if (colormap)
delete [] colormap;
}
return true;
}
// ========================================================
bool Image::SaveBMP (File& file, bool quantize) const
{
unsigned short bits;
unsigned long cmap, bfSize;
unsigned char pal[3][256], *colormappedbuffer = NULL;
if (quantize)
{
colormappedbuffer = (unsigned char*)malloc(m_nWidth*m_nHeight);
dl1quant (m_pData, colormappedbuffer, m_nWidth, m_nHeight, 256, true, pal);
bits = 8;
cmap = 256;
bfSize = 1078 + m_nWidth*m_nHeight;
}
else
{
bits = 24;
cmap = 0;
bfSize = 54 + m_nWidth*m_nHeight*3;
}
long byteswritten = 0;
long pixoff = 54 + cmap*4;
short res = 0;
char m1 ='B', m2 ='M';
file.WriteByte (&m1, 1); byteswritten++; // B
file.WriteByte (&m2, 1); byteswritten++; // M
file.WriteLong (&bfSize, 1); byteswritten+=4;// bfSize
file.WriteShort (&res, 1); byteswritten+=2;// bfReserved1
file.WriteShort (&res, 1); byteswritten+=2;// bfReserved2
file.WriteLong (&pixoff, 1); byteswritten+=4;// bfOffBits
lcuint32 biSize = 40, compress = 0, size = 0;
lcint32 width = m_nWidth, height = m_nHeight, pixels = 0;
lcuint16 planes = 1;
file.WriteLong (&biSize, 1); byteswritten+=4;// biSize
file.WriteLong (&width, 1); byteswritten+=4;// biWidth
file.WriteLong (&height, 1); byteswritten+=4;// biHeight
file.WriteShort (&planes, 1); byteswritten+=2;// biPlanes
file.WriteShort (&bits, 1); byteswritten+=2;// biBitCount
file.WriteLong (&compress, 1); byteswritten+=4;// biCompression
file.WriteLong (&size, 1); byteswritten+=4;// biSizeImage
file.WriteLong (&pixels, 1); byteswritten+=4;// biXPelsPerMeter
file.WriteLong (&pixels, 1); byteswritten+=4;// biYPelsPerMeter
file.WriteLong (&cmap, 1); byteswritten+=4;// biClrUsed
file.WriteLong (&cmap, 1); byteswritten+=4;// biClrImportant
if (quantize)
{
for (int i = 0; i < 256; i++)
{
file.PutChar (pal[2][i]);
file.PutChar (pal[1][i]);
file.PutChar (pal[0][i]);
file.PutChar (0); // dummy
}
for (int row = 0; row < m_nHeight; row++)
{
int pixbuf = 0;
for (int col = 0; col < m_nWidth; col++)
{
int offset = (m_nHeight-row-1) * width + col; // offset into our color-mapped RGB buffer
unsigned char pval = *(colormappedbuffer + offset);
pixbuf = (pixbuf << 8) | pval;
file.PutChar (pixbuf);
pixbuf = 0;
byteswritten++;
}
// DWORD align
while ((byteswritten - pixoff) & 3)
{
file.PutChar (0);
byteswritten++;
}
}
free(colormappedbuffer);
}
else
{
unsigned long widthDW = (((m_nWidth*24) + 31) / 32 * 4);
long row, row_size = m_nWidth*3;
for (row = 0; row < m_nHeight; row++)
{
unsigned char* buf = m_pData+(m_nHeight-row-1)*row_size;
// write a row
for (int col = 0; col < row_size; col += 3)
{
file.PutChar (buf[col+2]);
file.PutChar (buf[col+1]);
file.PutChar (buf[col]);
}
byteswritten += row_size;
for (unsigned long count = row_size; count < widthDW; count++)
{
file.PutChar (0); // dummy
byteswritten++;
}
}
}
return true;
}