mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-16 14:16:09 -06:00
287 lines
6.5 KiB
C++
287 lines
6.5 KiB
C++
#include "bmp.h"
|
|
|
|
NAMESPACE_UPP
|
|
|
|
#include "bmphdr.h"
|
|
|
|
bool LoadBMPHeader(Stream& stream, BMPHeader& header, bool file)
|
|
{
|
|
if(!stream.IsOpen())
|
|
return false;
|
|
ASSERT(stream.IsLoading());
|
|
if(file)
|
|
{
|
|
BMP_FILEHEADER bmfh;
|
|
if(!stream.GetAll(&bmfh, sizeof(bmfh)))
|
|
return false;
|
|
bmfh.SwapEndian();
|
|
if(bmfh.bfType != 'B' + 256 * 'M')
|
|
return false;
|
|
}
|
|
Zero(header);
|
|
if(!stream.GetAll(&header, sizeof(BMP_INFOHEADER)) || header.biSize < sizeof(BMP_INFOHEADER))
|
|
return false;
|
|
header.SwapEndian();
|
|
if(header.biBitCount != 1 && header.biBitCount != 4 && header.biBitCount != 8
|
|
&& header.biBitCount != 16 && header.biBitCount != 24 && header.biBitCount != 32)
|
|
return false;
|
|
if(header.biSizeImage == 0) {
|
|
if(header.biCompression != 0 /* BI_RGB */)
|
|
return false;
|
|
header.biSizeImage = tabs(header.biHeight) * (((header.biWidth * header.biBitCount + 31) >> 3) & -4);
|
|
}
|
|
stream.SeekCur(header.biSize - sizeof(BMP_INFOHEADER));
|
|
if(header.biBitCount <= 8) {
|
|
if(!stream.GetAll(header.palette, (header.biClrUsed ? header.biClrUsed : 1 << header.biBitCount) * sizeof(BMP_RGB)))
|
|
return false;
|
|
}
|
|
else if(header.biBitCount == 16 || header.biBitCount == 32)
|
|
{ // prepare 16-bit rgb masks & shifts
|
|
if(header.biCompression == 0 /* BI_RGB */)
|
|
;
|
|
else if(header.biCompression == 3 /* BI_BITFIELDS */)
|
|
{ // read bitfield masks
|
|
if(!stream.GetAll(header.palette, 12))
|
|
return false;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
if(header.biBitCount >= 16 && header.biClrUsed != 0)
|
|
stream.SeekCur(header.biClrUsed * sizeof(BMP_RGB));
|
|
return true;
|
|
}
|
|
|
|
static Size GetDotSize(Size pixel_size, int xpm, int ypm)
|
|
{
|
|
if(!xpm || !ypm)
|
|
return Size(0, 0);
|
|
static const double DOTS_PER_METER = 60000 / 2.54;
|
|
return Size(fround(pixel_size.cx * DOTS_PER_METER / xpm), fround(pixel_size.cy * DOTS_PER_METER / ypm));
|
|
}
|
|
|
|
bool BMPRaster::Create()
|
|
{
|
|
Stream& stream = GetStream();
|
|
size = Size(0, 0);
|
|
if(!stream.IsOpen()) {
|
|
SetError();
|
|
return false;
|
|
}
|
|
ASSERT(stream.IsLoading());
|
|
BMPHeader header;
|
|
if(!LoadBMPHeader(stream, header, file)) {
|
|
SetError();
|
|
return false;
|
|
}
|
|
switch(header.biBitCount) {
|
|
case 1: fmt.Set1mf(); break;
|
|
case 4: fmt.Set4mf(); break;
|
|
case 8: fmt.Set8(); break;
|
|
case 16:
|
|
if(header.biCompression == 3 /* BI_BITFIELD */)
|
|
fmt.Set16le(*(dword *)(header.palette + 0), *(dword *)(header.palette + 1), *(dword *)(header.palette + 2));
|
|
else
|
|
fmt.Set16le(31 << 10, 31 << 5, 31);
|
|
break;
|
|
case 24:
|
|
fmt.Set24le(0xff0000, 0x00ff00, 0x0000ff);
|
|
break;
|
|
case 32:
|
|
if(header.biCompression == 3 /* BI_BITFIELD */)
|
|
fmt.Set32le(*(dword *)(header.palette + 0), *(dword *)(header.palette + 1), *(dword *)(header.palette + 2));
|
|
else
|
|
fmt.Set32le(0xff0000, 0x00ff00, 0x0000ff);
|
|
break;
|
|
}
|
|
if(header.biBitCount <= 8) {
|
|
int ncolors = 1 << header.biBitCount;
|
|
palette.Alloc(ncolors);
|
|
const BMP_RGB *q = header.palette;
|
|
for(int i = 0; i < ncolors; i++) {
|
|
palette[i].r = q->rgbRed;
|
|
palette[i].g = q->rgbGreen;
|
|
palette[i].b = q->rgbBlue;
|
|
palette[i].a = 255;
|
|
q++;
|
|
}
|
|
}
|
|
|
|
size = Size(header.biWidth, tabs(header.biHeight));
|
|
info.kind = IMAGE_OPAQUE;
|
|
info.bpp = header.biBitCount;
|
|
info.dots = GetDotSize(size, header.biXPelsPerMeter, header.biYPelsPerMeter);
|
|
info.hotspot = Point(0, 0);
|
|
info.colors = fmt.GetColorCount();
|
|
topdown = header.biHeight < 0;
|
|
row_bytes = (fmt.GetByteCount(size.cx) + 3) & ~3;
|
|
scanline.Alloc(row_bytes);
|
|
soff = stream.GetPos();
|
|
|
|
if(header.biBitCount == 8 && header.biCompression == 1 /* BI_RLE8 */) {
|
|
ImageBuffer ib(size);
|
|
int x = 0;
|
|
int y = 0;
|
|
RGBA *t = ib[header.biHeight < 0 ? 0 : size.cy - 1];
|
|
while(!stream.IsEof()) {
|
|
int c = stream.Get();
|
|
if(stream.IsEof())
|
|
return false;
|
|
if(c) {
|
|
c = min(c, size.cx - c);
|
|
int q = stream.Get();
|
|
if(q < 0)
|
|
return false;
|
|
Fill(t, palette[q], c);
|
|
t += c;
|
|
x += c;
|
|
}
|
|
else {
|
|
c = stream.Get();
|
|
if(c < 0 || c == 1)
|
|
break;
|
|
if(c == 0) {
|
|
x = 0;
|
|
++y;
|
|
if(y == size.cy)
|
|
break;
|
|
t = ib[header.biHeight < 0 ? y : size.cy - 1 - y];
|
|
}
|
|
else
|
|
if(c == 2) {
|
|
x += stream.Get();
|
|
y += stream.Get();
|
|
if(x < 0 || x >= size.cx || y < 0 || y >= size.cy)
|
|
return false;
|
|
t = ib[header.biHeight < 0 ? y : size.cy - 1 - y];
|
|
}
|
|
else {
|
|
int al = c & 1;
|
|
while(c--) {
|
|
int q = stream.Get();
|
|
if(q < 0 || x >= size.cx)
|
|
return false;
|
|
*t++ = palette[q];
|
|
x++;
|
|
}
|
|
if(al)
|
|
stream.Get();
|
|
}
|
|
}
|
|
}
|
|
rle = new ImageRaster(ib);
|
|
return true;
|
|
}
|
|
if(header.biBitCount == 4 && header.biCompression == 2 /* BI_RLE4 */) {
|
|
ImageBuffer ib(size);
|
|
int x = 0;
|
|
int y = 0;
|
|
RGBA *t = ib[header.biHeight < 0 ? 0 : size.cy - 1];
|
|
while(!stream.IsEof()) {
|
|
int c = stream.Get();
|
|
if(stream.IsEof())
|
|
return false;
|
|
if(c) {
|
|
c = min(c, size.cx - x);
|
|
int q = stream.Get();
|
|
if(q < 0)
|
|
return false;
|
|
int q1 = q & 15;
|
|
q = (q >> 4) & 15;
|
|
x += c;
|
|
while(c--) {
|
|
*t++ = palette[q];
|
|
Swap(q, q1);
|
|
}
|
|
}
|
|
else {
|
|
c = stream.Get();
|
|
if(c < 0 || c == 1)
|
|
break;
|
|
if(c == 0) {
|
|
x = 0;
|
|
++y;
|
|
if(y == size.cy)
|
|
break;
|
|
t = ib[header.biHeight < 0 ? y : size.cy - 1 - y];
|
|
}
|
|
else
|
|
if(c == 2) {
|
|
x += stream.Get();
|
|
y += stream.Get();
|
|
if(x < 0 || x >= size.cx || y < 0 || y >= size.cy)
|
|
return false;
|
|
t = ib[header.biHeight < 0 ? y : size.cy - 1 - y];
|
|
}
|
|
else {
|
|
if(x + c >= size.cx)
|
|
return false;
|
|
x += c;
|
|
int i = 0;
|
|
while(i < c) {
|
|
int q = stream.Get();
|
|
if(q < 0)
|
|
return false;
|
|
*t++ = palette[(q >> 4) & 15];
|
|
if(++i >= c) break;
|
|
*t++ = palette[q & 15];
|
|
++i;
|
|
}
|
|
c = 3 - (c & 3);
|
|
while(c--)
|
|
stream.Get();
|
|
}
|
|
}
|
|
}
|
|
rle = new ImageRaster(ib);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
Size BMPRaster::GetSize()
|
|
{
|
|
return size;
|
|
}
|
|
|
|
Raster::Info BMPRaster::GetInfo()
|
|
{
|
|
return info;
|
|
}
|
|
|
|
Raster::Line BMPRaster::GetLine(int line)
|
|
{
|
|
if(rle)
|
|
return rle->GetLine(line);
|
|
byte *ptr = new byte[row_bytes];
|
|
if(!IsError()) {
|
|
Stream& stream = GetStream();
|
|
topdown = false;
|
|
stream.Seek(soff + (topdown ? line * row_bytes : (size.cy - line - 1) * row_bytes));
|
|
if(stream.GetAll(ptr, row_bytes))
|
|
return Line(ptr, this, true);
|
|
SetError();
|
|
}
|
|
memset(ptr, 0, row_bytes);
|
|
return Line(ptr, this, true);
|
|
}
|
|
|
|
int BMPRaster::GetPaletteCount()
|
|
{
|
|
return fmt.GetPaletteCount();
|
|
}
|
|
|
|
RGBA *BMPRaster::GetPalette()
|
|
{
|
|
return fmt.GetPaletteCount() ? ~palette : NULL;
|
|
}
|
|
|
|
const RasterFormat *BMPRaster::GetFormat()
|
|
{
|
|
return &fmt;
|
|
}
|
|
|
|
BMPRaster::~BMPRaster()
|
|
{
|
|
}
|
|
|
|
END_UPP_NAMESPACE
|