2017-05-09 00:13:29 +12:00
|
|
|
#ifdef _MSC_VER
|
2016-03-19 06:57:51 +13:00
|
|
|
#pragma warning(disable : 4996)
|
|
|
|
#endif
|
|
|
|
|
2016-04-15 20:34:36 +12:00
|
|
|
#include <memory>
|
|
|
|
|
2016-03-19 06:57:51 +13:00
|
|
|
#include "tiio_bmp.h"
|
|
|
|
// #include "bmp/filebmp.h"
|
|
|
|
#include "tpixel.h"
|
|
|
|
#include "tpixelgr.h"
|
|
|
|
#include "tproperty.h"
|
|
|
|
//#include "tconvert.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
namespace {
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
typedef struct {
|
|
|
|
UINT bfSize;
|
|
|
|
UINT bfOffBits;
|
|
|
|
UINT biSize;
|
|
|
|
UINT biWidth;
|
|
|
|
UINT biHeight;
|
|
|
|
UINT biPlanes;
|
|
|
|
UINT biBitCount;
|
|
|
|
UINT biCompression;
|
|
|
|
UINT biSizeImage;
|
|
|
|
UINT biXPelsPerMeter;
|
|
|
|
UINT biYPelsPerMeter;
|
|
|
|
UINT biClrUsed;
|
|
|
|
UINT biClrImportant;
|
|
|
|
UINT biFilesize;
|
|
|
|
UINT biPad;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
} BMP_HEADER;
|
|
|
|
|
|
|
|
const int BMP_WIN_SIZE = 40;
|
|
|
|
const int BMP_OS2_SIZE = 64;
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
const int BMP_BI_RGB = 0;
|
2016-03-19 06:57:51 +13:00
|
|
|
const int BMP_BI_RLE8 = 1;
|
|
|
|
const int BMP_BI_RLE4 = 2;
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
UINT getshort(FILE *fp) {
|
|
|
|
int c = getc(fp), c1 = getc(fp);
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
return ((UINT)c) + (((UINT)c1) << 8);
|
2016-03-19 06:57:51 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
UINT getint(FILE *fp) {
|
|
|
|
int c = getc(fp), c1 = getc(fp), c2 = getc(fp), c3 = getc(fp);
|
|
|
|
|
|
|
|
return (((UINT)c) << 0) + (((UINT)c1) << 8) + (((UINT)c2) << 16) +
|
|
|
|
(((UINT)c3) << 24);
|
2016-03-19 06:57:51 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
void putshort(FILE *fp, int i) {
|
|
|
|
int c = (((UINT)i)) & 0xff, c1 = (((UINT)i) >> 8) & 0xff;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
putc(c, fp);
|
|
|
|
putc(c1, fp);
|
2016-03-19 06:57:51 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
void putint(FILE *fp, int i) {
|
|
|
|
int c = ((UINT)i) & 0xff, c1 = (((UINT)i) >> 8) & 0xff,
|
|
|
|
c2 = (((UINT)i) >> 16) & 0xff, c3 = (((UINT)i) >> 24) & 0xff;
|
|
|
|
|
|
|
|
putc(c, fp);
|
|
|
|
putc(c1, fp);
|
|
|
|
putc(c2, fp);
|
|
|
|
putc(c3, fp);
|
2016-03-19 06:57:51 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
} // namespace
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
|
2016-06-29 18:17:12 +12:00
|
|
|
class BmpReader final : public Tiio::Reader {
|
2016-06-15 18:43:10 +12:00
|
|
|
FILE *m_chan;
|
|
|
|
BMP_HEADER m_header;
|
|
|
|
char *m_line;
|
|
|
|
int m_lineSize;
|
|
|
|
std::unique_ptr<TPixel[]> m_cmap;
|
|
|
|
bool m_corrupted;
|
|
|
|
typedef int (BmpReader::*ReadLineMethod)(char *buffer, int x0, int x1,
|
|
|
|
int shrink);
|
|
|
|
ReadLineMethod m_readLineMethod;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
public:
|
2016-06-15 18:43:10 +12:00
|
|
|
BmpReader();
|
|
|
|
~BmpReader();
|
|
|
|
|
2016-06-19 20:06:29 +12:00
|
|
|
void open(FILE *file) override;
|
2016-06-15 18:43:10 +12:00
|
|
|
|
|
|
|
int readNoLine(char *buffer, int x0, int x1, int shrink);
|
|
|
|
|
|
|
|
void skipBytes(int count) {
|
|
|
|
// fseek(m_chan, count, SEEK_CUR); //inefficiente se count è piccolo
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
getc(m_chan);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int read1Line(char *buffer, int x0, int x1, int shrink);
|
|
|
|
int read4Line(char *buffer, int x0, int x1, int shrink);
|
|
|
|
int read8Line(char *buffer, int x0, int x1, int shrink);
|
|
|
|
int read8LineRle(char *buffer, int x0, int x1, int shrink);
|
|
|
|
int read16m555Line(char *buffer, int x0, int x1, int shrink);
|
|
|
|
int read16m565Line(char *buffer, int x0, int x1, int shrink);
|
|
|
|
int read24Line(char *buffer, int x0, int x1, int shrink);
|
|
|
|
int read32Line(char *buffer, int x0, int x1, int shrink);
|
|
|
|
|
2016-06-19 20:06:29 +12:00
|
|
|
void readLine(char *buffer, int x0, int x1, int shrink) override;
|
|
|
|
int skipLines(int lineCount) override {
|
2016-06-15 18:43:10 +12:00
|
|
|
fseek(m_chan, lineCount * m_lineSize, SEEK_CUR);
|
|
|
|
/* for(int i=0;i<lineCount;i++)
|
|
|
|
skipBytes(m_lineSize);*/
|
|
|
|
return lineCount;
|
|
|
|
}
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
private:
|
2016-06-15 18:43:10 +12:00
|
|
|
void readHeader();
|
2016-03-19 06:57:51 +13:00
|
|
|
};
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
BmpReader::BmpReader()
|
2016-06-15 18:43:10 +12:00
|
|
|
: m_chan(0), m_corrupted(false), m_readLineMethod(&BmpReader::readNoLine) {
|
|
|
|
memset(&m_header, 0, sizeof m_header);
|
2016-03-19 06:57:51 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
BmpReader::~BmpReader() {}
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
void BmpReader::open(FILE *file) {
|
|
|
|
m_chan = file;
|
|
|
|
readHeader();
|
2016-03-19 06:57:51 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
void BmpReader::readHeader() {
|
|
|
|
if (!m_chan) return;
|
|
|
|
|
|
|
|
fseek(m_chan, 0L, SEEK_END);
|
|
|
|
m_header.biFilesize = ftell(m_chan);
|
|
|
|
fseek(m_chan, 0L, 0);
|
|
|
|
|
|
|
|
/* read the file type (first two bytes) */
|
|
|
|
char signature[2];
|
|
|
|
signature[0] = fgetc(m_chan);
|
|
|
|
signature[1] = fgetc(m_chan);
|
|
|
|
if (signature[0] != 'B' || signature[1] != 'M') {
|
|
|
|
m_corrupted = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_header.bfSize = getint(m_chan);
|
|
|
|
|
|
|
|
/* reserved and ignored */
|
|
|
|
getshort(m_chan);
|
|
|
|
getshort(m_chan);
|
|
|
|
|
|
|
|
m_header.bfOffBits = getint(m_chan);
|
|
|
|
m_header.biSize = getint(m_chan);
|
|
|
|
|
|
|
|
if ((int)m_header.biSize == BMP_WIN_SIZE ||
|
|
|
|
(int)m_header.biSize == BMP_OS2_SIZE) {
|
|
|
|
m_header.biWidth = getint(m_chan);
|
|
|
|
m_header.biHeight = getint(m_chan);
|
|
|
|
m_header.biPlanes = getshort(m_chan);
|
|
|
|
m_header.biBitCount = getshort(m_chan);
|
|
|
|
m_header.biCompression = getint(m_chan);
|
|
|
|
m_header.biSizeImage = getint(m_chan);
|
|
|
|
m_header.biXPelsPerMeter = getint(m_chan);
|
|
|
|
m_header.biYPelsPerMeter = getint(m_chan);
|
|
|
|
m_header.biClrUsed = getint(m_chan);
|
|
|
|
m_header.biClrImportant = getint(m_chan);
|
|
|
|
} else // old bitmap format
|
|
|
|
{
|
|
|
|
m_header.biWidth = getshort(m_chan);
|
|
|
|
m_header.biHeight = getshort(m_chan);
|
|
|
|
m_header.biPlanes = getshort(m_chan);
|
|
|
|
m_header.biBitCount = getshort(m_chan);
|
|
|
|
|
|
|
|
/* Not in old versions so have to compute them */
|
|
|
|
m_header.biSizeImage =
|
|
|
|
(((m_header.biPlanes * m_header.biBitCount * m_header.biWidth) + 31) /
|
|
|
|
32) *
|
|
|
|
4 * m_header.biHeight;
|
|
|
|
|
|
|
|
m_header.biCompression = BMP_BI_RGB;
|
|
|
|
m_header.biXPelsPerMeter = 0;
|
|
|
|
m_header.biYPelsPerMeter = 0;
|
|
|
|
m_header.biClrUsed = 0;
|
|
|
|
m_header.biClrImportant = 0;
|
|
|
|
}
|
|
|
|
m_header.biPad = 0;
|
|
|
|
|
|
|
|
m_info.m_lx = m_header.biWidth;
|
|
|
|
m_info.m_ly = m_header.biHeight;
|
|
|
|
|
|
|
|
/*
|
|
|
|
BMP_HEADER *hd = m_header;
|
|
|
|
if ((int)hd->biSize != BMP_WIN_OS2_OLD)
|
2016-03-19 06:57:51 +13:00
|
|
|
{
|
2016-06-15 18:43:10 +12:00
|
|
|
// skip ahead to colormap, using biSize
|
|
|
|
int c = hd->biSize - 40; // 40 bytes read from biSize to biClrImportant
|
|
|
|
for (int i=0; i<c; i++) getc(m_chan);
|
|
|
|
hd->biPad = hd->bfOffBits - (hd->biSize + 14);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
hd->biPad = 0;
|
2016-03-19 06:57:51 +13:00
|
|
|
*/
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
// load up colormap, if any
|
|
|
|
if (m_header.biBitCount < 16) {
|
|
|
|
int cmaplen =
|
|
|
|
(m_header.biClrUsed) ? m_header.biClrUsed : 1 << m_header.biBitCount;
|
|
|
|
assert(cmaplen <= 256);
|
|
|
|
m_cmap.reset(new TPixel[256]);
|
|
|
|
TPixel32 *pix = m_cmap.get();
|
|
|
|
for (int i = 0; i < cmaplen; i++) {
|
|
|
|
pix->b = getc(m_chan);
|
|
|
|
pix->g = getc(m_chan);
|
|
|
|
pix->r = getc(m_chan);
|
|
|
|
pix->m = 255;
|
|
|
|
getc(m_chan);
|
|
|
|
++pix;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
if (hd->biSize != BMP_WIN_OS2_OLD)
|
|
|
|
{
|
|
|
|
// Waste any unused bytes between the colour map (if present)
|
|
|
|
// and the start of the actual bitmap data.
|
|
|
|
while (hd->biPad > 0)
|
|
|
|
{
|
|
|
|
(void) getc(m_chan);
|
|
|
|
hd->biPad--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
// get information about the portion of the image to load
|
|
|
|
// get_info_region(&info, x1, y1, x2, y2, scale,
|
|
|
|
// (int)hd->biWidth, (int)hd->biHeight, TNZ_BOTLEFT);
|
|
|
|
|
|
|
|
// skip_bmp_lines(fp, hd->biWidth, (unsigned int)(info.startScanRow-1),
|
|
|
|
// (unsigned int)SEEK_CUR, subtype);
|
|
|
|
|
|
|
|
int lx = m_info.m_lx;
|
|
|
|
|
|
|
|
switch (m_header.biBitCount) {
|
|
|
|
case 1:
|
|
|
|
m_info.m_samplePerPixel = 1;
|
|
|
|
m_readLineMethod = &BmpReader::read1Line;
|
|
|
|
m_lineSize = (lx + 7) / 8;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
m_info.m_samplePerPixel = 1;
|
|
|
|
if (m_header.biCompression == 0) m_readLineMethod = &BmpReader::read4Line;
|
|
|
|
m_lineSize = (lx + 1) / 2;
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
m_info.m_samplePerPixel = 1;
|
|
|
|
if (m_header.biCompression == 0)
|
|
|
|
m_readLineMethod = &BmpReader::read8Line;
|
|
|
|
else if (m_header.biCompression == 1)
|
|
|
|
m_readLineMethod = &BmpReader::read8LineRle;
|
|
|
|
m_lineSize = lx;
|
|
|
|
break;
|
|
|
|
case 16:
|
|
|
|
m_info.m_samplePerPixel = 3;
|
|
|
|
if (m_header.biCompression == 0) // BI_RGB
|
|
|
|
m_readLineMethod = &BmpReader::read16m555Line;
|
|
|
|
else if (m_header.biCompression == 3) // BI_BITFIELDS
|
|
|
|
{
|
|
|
|
unsigned int rmask = 0, gmask = 0, bmask = 0;
|
|
|
|
rmask = getint(m_chan);
|
|
|
|
gmask = getint(m_chan);
|
|
|
|
bmask = getint(m_chan);
|
|
|
|
if (gmask == 0x7E0)
|
|
|
|
m_readLineMethod = &BmpReader::read16m565Line;
|
|
|
|
else
|
|
|
|
m_readLineMethod = &BmpReader::read16m555Line;
|
|
|
|
}
|
|
|
|
m_lineSize = lx * 2;
|
|
|
|
break;
|
|
|
|
case 24:
|
|
|
|
m_info.m_samplePerPixel = 3;
|
|
|
|
m_readLineMethod = &BmpReader::read24Line;
|
|
|
|
m_lineSize = lx * 3;
|
|
|
|
break;
|
|
|
|
case 32:
|
|
|
|
m_info.m_samplePerPixel = 3;
|
|
|
|
m_readLineMethod = &BmpReader::read32Line;
|
|
|
|
m_lineSize = lx * 4;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
m_lineSize += 3 - ((m_lineSize + 3) & 3);
|
|
|
|
fseek(m_chan, m_header.bfOffBits, SEEK_SET);
|
2016-03-19 06:57:51 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
void BmpReader::readLine(char *buffer, int x0, int x1, int shrink) {
|
|
|
|
(this->*m_readLineMethod)(buffer, x0, x1, shrink);
|
2016-03-19 06:57:51 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
int BmpReader::readNoLine(char *buffer, int x0, int x1, int shrink) {
|
|
|
|
skipBytes(m_lineSize);
|
|
|
|
return 0;
|
2016-03-19 06:57:51 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
int BmpReader::read1Line(char *buffer, int x0, int x1, int shrink) {
|
|
|
|
TPixel32 *pix = (TPixel32 *)buffer;
|
|
|
|
|
|
|
|
// pix += x0;
|
|
|
|
if (x0 > 0) skipBytes(x0 / 8);
|
|
|
|
|
|
|
|
TPixel32 *endPix = pix + x1 + 1;
|
|
|
|
|
|
|
|
int value = 0;
|
|
|
|
int index = x0;
|
|
|
|
|
|
|
|
if (x0 % 8 != 0) {
|
|
|
|
value = getc(m_chan);
|
|
|
|
for (index = x0; index < x0 + 8 - (x0 % 8); index += shrink) {
|
|
|
|
pix[index] = m_cmap[(value >> (7 - ((index) % 8))) & 1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
value = getc(m_chan);
|
|
|
|
int prevBlock = index / 8;
|
|
|
|
for (int j = index; pix + j < endPix; j += shrink) {
|
|
|
|
if (j / 8 > prevBlock) value = getc(m_chan);
|
|
|
|
prevBlock = j / 8;
|
|
|
|
pix[j] = m_cmap[(value >> (7 - (j % 8))) & 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
while(pix+8<=endPix)
|
2016-03-19 06:57:51 +13:00
|
|
|
{
|
2016-06-15 18:43:10 +12:00
|
|
|
value = getc(m_chan);
|
|
|
|
pix[0] = m_cmap[(value>>7)&1];
|
|
|
|
pix[1] = m_cmap[(value>>6)&1];
|
|
|
|
pix[2] = m_cmap[(value>>5)&1];
|
|
|
|
pix[3] = m_cmap[(value>>4)&1];
|
|
|
|
pix[4] = m_cmap[(value>>3)&1];
|
|
|
|
pix[5] = m_cmap[(value>>2)&1];
|
|
|
|
pix[6] = m_cmap[(value>>1)&1];
|
|
|
|
pix[7] = m_cmap[(value>>0)&1];
|
|
|
|
pix +=8*shrink;
|
|
|
|
if(shrink>1) value = getc(m_chan);
|
|
|
|
//pix+=8*shrink;
|
|
|
|
//if(pix+8<endPix && shrink>1) skipBytes(shrink-1);
|
|
|
|
}
|
|
|
|
if(pix<endPix)
|
|
|
|
{
|
|
|
|
value = getc(m_chan);
|
|
|
|
assert(pix+8>=endPix);
|
|
|
|
for(int j=0; pix+j<endPix; j++)
|
|
|
|
pix[j] = m_cmap[(value>>(7-j))&1];
|
|
|
|
} */
|
|
|
|
if ((m_info.m_lx - x1) / 8 > 0) {
|
|
|
|
skipBytes((m_info.m_lx - x1) / 8);
|
|
|
|
}
|
|
|
|
|
|
|
|
int bytes = (m_info.m_lx + 7) / 8;
|
|
|
|
if (m_lineSize - bytes > 0) skipBytes(m_lineSize - bytes);
|
|
|
|
return 0;
|
2016-03-19 06:57:51 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
int BmpReader::read4Line(char *buffer, int x0, int x1, int shrink) {
|
|
|
|
TPixel32 *pix = (TPixel32 *)buffer;
|
|
|
|
pix += 2 * x0;
|
|
|
|
if (x0 > 0) skipBytes(x0 / 2);
|
|
|
|
TPixel32 *endPix = pix + x1 - x0 + 1;
|
|
|
|
|
|
|
|
int value;
|
|
|
|
while (pix + 2 <= endPix) {
|
|
|
|
value = getc(m_chan);
|
|
|
|
pix[0] = m_cmap[value & 0xF];
|
|
|
|
pix[1] = m_cmap[(value >> 4) & 0xF];
|
|
|
|
// pix+=2*shrink;
|
|
|
|
pix++;
|
|
|
|
// if(pix+2<=endPix && shrink>1) skipBytes(shrink-1);
|
|
|
|
}
|
|
|
|
if (pix < endPix) {
|
|
|
|
value = getc(m_chan);
|
|
|
|
pix[0] = m_cmap[value & 0xF];
|
|
|
|
}
|
|
|
|
if ((m_info.m_lx - x1) / 2 > 0) {
|
|
|
|
skipBytes((m_info.m_lx - x1) / 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
int bytes = (m_info.m_lx + 1) / 2;
|
|
|
|
if (m_lineSize - bytes) skipBytes(m_lineSize - bytes);
|
|
|
|
return 0;
|
2016-03-19 06:57:51 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
int BmpReader::read8Line(char *buffer, int x0, int x1, int shrink) {
|
|
|
|
TPixel32 *pix = (TPixel32 *)buffer;
|
|
|
|
|
|
|
|
if (x0 > 0) skipBytes(x0);
|
|
|
|
pix += x0;
|
|
|
|
TPixel32 *endPix = pix + x1 - x0 + 1;
|
|
|
|
|
|
|
|
while (pix < endPix) {
|
|
|
|
int value = getc(m_chan);
|
|
|
|
*pix++ = m_cmap[value];
|
|
|
|
if (pix < endPix && shrink > 1) {
|
|
|
|
skipBytes(shrink - 1);
|
|
|
|
pix += (shrink - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_info.m_lx - x1 - 1 > 0) {
|
|
|
|
skipBytes(m_info.m_lx - x1 - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_lineSize - m_info.m_lx > 0) skipBytes(m_lineSize - m_info.m_lx);
|
|
|
|
return 0;
|
2016-03-19 06:57:51 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
int BmpReader::read8LineRle(char *buffer, int x0, int x1, int shrink) {
|
|
|
|
TPixel32 *pix = (TPixel32 *)buffer;
|
|
|
|
if (x0 > 0) skipBytes(x0);
|
|
|
|
pix += x0;
|
|
|
|
TPixel32 *endPix = pix + (x1 - x0 + 1);
|
|
|
|
|
|
|
|
while (pix < endPix) {
|
|
|
|
int i, count = getc(m_chan);
|
|
|
|
assert(count >= 0);
|
|
|
|
if (count == 0) {
|
|
|
|
int pixels = getc(m_chan);
|
|
|
|
|
|
|
|
assert(pixels >= 0 && pixels != 2);
|
|
|
|
if (pixels < 3) return 0;
|
|
|
|
for (i = 0; i < pixels; i++) *pix++ = m_cmap[getc(m_chan)];
|
|
|
|
if ((1 + 1 + pixels) & 0x1) getc(m_chan);
|
|
|
|
} else {
|
|
|
|
int value = getc(m_chan);
|
|
|
|
assert(value >= 0);
|
|
|
|
for (i = 0; i < count; i++) *pix++ = m_cmap[value];
|
|
|
|
}
|
|
|
|
if (pix < endPix && shrink > 1) {
|
|
|
|
skipBytes(shrink - 1);
|
|
|
|
pix += (shrink - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_info.m_lx - x1 - 1 > 0) {
|
|
|
|
skipBytes(m_info.m_lx - x1 - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_lineSize - m_info.m_lx > 0) skipBytes(m_lineSize - m_info.m_lx);
|
|
|
|
int val = getc(m_chan);
|
|
|
|
assert(val == 0); // end scanline or end bmp
|
|
|
|
val = getc(m_chan);
|
|
|
|
assert(val == 0 || val == 1);
|
|
|
|
return 0;
|
2016-03-19 06:57:51 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
int BmpReader::read16m555Line(char *buffer, int x0, int x1, int shrink) {
|
|
|
|
TPixel32 *pix = (TPixel32 *)buffer;
|
|
|
|
if (x0 > 0) skipBytes(2 * x0);
|
|
|
|
pix += x0;
|
|
|
|
TPixel32 *endPix = pix + x1 - x0 + 1;
|
|
|
|
|
|
|
|
while (pix < endPix) {
|
|
|
|
int v = getshort(m_chan);
|
|
|
|
int r = (v >> 10) & 0x1F;
|
|
|
|
int g = (v >> 5) & 0x1F;
|
|
|
|
int b = v & 0x1F;
|
|
|
|
pix->r = r << 3 | r >> 2;
|
|
|
|
pix->g = g << 3 | g >> 2;
|
|
|
|
pix->b = b << 3 | b >> 2;
|
|
|
|
pix->m = 255;
|
|
|
|
pix += shrink;
|
|
|
|
if (pix < endPix && shrink > 1) skipBytes(2 * (shrink - 1));
|
|
|
|
}
|
|
|
|
if (m_info.m_lx - x1 - 1 > 0) skipBytes(2 * (m_info.m_lx - x1 - 1));
|
|
|
|
|
|
|
|
if (m_lineSize - 2 * m_info.m_lx > 0) skipBytes(m_lineSize - 2 * m_info.m_lx);
|
|
|
|
return 0;
|
2016-03-19 06:57:51 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
int BmpReader::read16m565Line(char *buffer, int x0, int x1, int shrink) {
|
|
|
|
TPixel32 *pix = (TPixel32 *)buffer;
|
|
|
|
if (x0 > 0) skipBytes(2 * x0);
|
|
|
|
pix += x0;
|
|
|
|
TPixel32 *endPix = pix + x1 - x0 + 1;
|
|
|
|
|
|
|
|
while (pix < endPix) {
|
|
|
|
int v = getshort(m_chan);
|
|
|
|
int r = (v >> 11) & 0x1F;
|
|
|
|
int g = (v >> 5) & 0x3F;
|
|
|
|
int b = v & 0x1F;
|
|
|
|
pix->r = r << 3 | r >> 2;
|
|
|
|
pix->g = g << 2 | g >> 4;
|
|
|
|
pix->b = b << 3 | b >> 2;
|
|
|
|
pix->m = 255;
|
|
|
|
pix += shrink;
|
|
|
|
if (pix < endPix && shrink > 1) skipBytes(2 * (shrink - 1));
|
|
|
|
}
|
|
|
|
if (m_info.m_lx - x1 - 1 > 0) skipBytes(2 * (m_info.m_lx - x1 - 1));
|
|
|
|
|
|
|
|
if (m_lineSize - 2 * m_info.m_lx > 0) skipBytes(m_lineSize - 2 * m_info.m_lx);
|
|
|
|
return 0;
|
2016-03-19 06:57:51 +13:00
|
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
int BmpReader::read24Line(char *buffer, int x0, int x1, int shrink) {
|
|
|
|
TPixel32 *pix = (TPixel32 *)buffer;
|
|
|
|
if (x0 > 0) skipBytes(3 * x0);
|
|
|
|
pix += x0;
|
|
|
|
TPixel32 *endPix = pix + x1 - x0 + 1;
|
|
|
|
|
|
|
|
while (pix < endPix) {
|
|
|
|
pix->b = getc(m_chan);
|
|
|
|
pix->g = getc(m_chan);
|
|
|
|
pix->r = getc(m_chan);
|
|
|
|
pix->m = 255;
|
|
|
|
pix += shrink;
|
|
|
|
if (pix < endPix && shrink > 1) skipBytes(3 * (shrink - 1));
|
|
|
|
}
|
|
|
|
if (m_info.m_lx - x1 - 1 > 0) skipBytes(3 * (m_info.m_lx - x1 - 1));
|
|
|
|
|
|
|
|
if (m_lineSize - 3 * m_info.m_lx > 0) skipBytes(m_lineSize - 3 * m_info.m_lx);
|
|
|
|
return 0;
|
2016-03-19 06:57:51 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
int BmpReader::read32Line(char *buffer, int x0, int x1, int shrink) {
|
|
|
|
TPixel32 *pix = (TPixel32 *)buffer;
|
|
|
|
if (x0 > 0) skipBytes(4 * x0);
|
|
|
|
pix += x0;
|
|
|
|
TPixel32 *endPix = pix + x1 - x0 + 1;
|
|
|
|
|
|
|
|
while (pix < endPix) {
|
|
|
|
pix->b = getc(m_chan);
|
|
|
|
pix->g = getc(m_chan);
|
|
|
|
pix->r = getc(m_chan);
|
|
|
|
pix->m = getc(m_chan);
|
|
|
|
pix += shrink;
|
|
|
|
if (pix < endPix && shrink > 1) skipBytes(4 * (shrink - 1));
|
|
|
|
}
|
|
|
|
if (m_info.m_lx - x1 - 1 > 0) skipBytes(4 * (m_info.m_lx - x1 - 1));
|
|
|
|
if (m_lineSize - 4 * m_info.m_lx > 0) skipBytes(m_lineSize - 4 * m_info.m_lx);
|
|
|
|
return 0;
|
2016-03-19 06:57:51 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
|
2016-06-29 18:17:12 +12:00
|
|
|
class BmpWriter final : public Tiio::Writer {
|
2016-06-15 18:43:10 +12:00
|
|
|
FILE *m_chan;
|
|
|
|
int m_bitPerPixel;
|
|
|
|
int m_compression;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
public:
|
2016-06-15 18:43:10 +12:00
|
|
|
BmpWriter();
|
|
|
|
~BmpWriter();
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-19 20:06:29 +12:00
|
|
|
void open(FILE *file, const TImageInfo &info) override;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-19 20:06:29 +12:00
|
|
|
void flush() override { fflush(m_chan); }
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-19 20:06:29 +12:00
|
|
|
void writeLine(char *buffer) override;
|
2018-12-19 18:29:00 +13:00
|
|
|
|
2020-11-06 07:51:52 +13:00
|
|
|
// for now Tahoma2D does not support bmp format with alpha channel
|
2018-12-19 18:29:00 +13:00
|
|
|
bool writeAlphaSupported() const override { return false; }
|
2016-03-19 06:57:51 +13:00
|
|
|
};
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
BmpWriter::BmpWriter() : m_chan(0) {}
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
BmpWriter::~BmpWriter() { delete m_properties; }
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
void BmpWriter::open(FILE *file, const TImageInfo &info) {
|
|
|
|
m_chan = file;
|
|
|
|
|
|
|
|
m_info = info;
|
|
|
|
int lx = m_info.m_lx;
|
|
|
|
int ly = m_info.m_ly;
|
|
|
|
|
|
|
|
if (!m_properties) m_properties = new Tiio::BmpWriterProperties();
|
|
|
|
|
|
|
|
TEnumProperty *p =
|
|
|
|
(TEnumProperty *)(m_properties->getProperty("Bits Per Pixel"));
|
|
|
|
assert(p);
|
|
|
|
std::string str = ::to_string(p->getValue());
|
|
|
|
m_bitPerPixel = atoi(str.c_str());
|
|
|
|
|
|
|
|
int cmapSize = 0;
|
|
|
|
std::vector<TPixel> *colormap = 0;
|
|
|
|
|
|
|
|
if (m_bitPerPixel == 8) {
|
|
|
|
TPointerProperty *colormapProp =
|
|
|
|
(TPointerProperty *)(m_properties->getProperty("Colormap"));
|
|
|
|
if (colormapProp) {
|
|
|
|
colormap = (std::vector<TPixel> *)colormapProp->getValue();
|
|
|
|
cmapSize = colormap->size();
|
|
|
|
} else
|
|
|
|
cmapSize = 256;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(m_bitPerPixel == 8 || m_bitPerPixel == 24);
|
|
|
|
|
|
|
|
int bytePerLine =
|
|
|
|
((lx * m_bitPerPixel + 31) / 32) * (m_bitPerPixel == 8 ? 1 : 4);
|
|
|
|
|
|
|
|
int fileSize = 14 // file header
|
|
|
|
+ 40 // info header
|
|
|
|
+ cmapSize * 4 // colormap size
|
|
|
|
+ bytePerLine * ly // image size
|
|
|
|
;
|
|
|
|
int offset = 14 + 40 + cmapSize * 4;
|
|
|
|
int compression = 0;
|
|
|
|
int imageSize = bytePerLine * ly;
|
|
|
|
|
|
|
|
putc('B', m_chan);
|
|
|
|
putc('M', m_chan); // BMP file magic number
|
|
|
|
|
|
|
|
putint(m_chan, fileSize);
|
|
|
|
putshort(m_chan, 0); // reserved1
|
|
|
|
putshort(m_chan, 0); // reserved2
|
|
|
|
|
|
|
|
putint(m_chan, offset); // offset from BOfile to BObitmap
|
|
|
|
|
|
|
|
putint(m_chan, 40); // size of bitmap info header
|
|
|
|
putint(m_chan, m_info.m_lx); // width
|
|
|
|
putint(m_chan, m_info.m_ly); // height
|
|
|
|
putshort(m_chan, 1); // must be '1'
|
|
|
|
putshort(m_chan, m_bitPerPixel); // 1,4,8, or 24
|
|
|
|
putint(m_chan, compression); // BMP_BI_RGB, BMP_BI_RLE8 or BMP_BI_RLE4
|
|
|
|
putint(m_chan, imageSize); // size of raw image data
|
|
|
|
putint(m_chan, 0); // dpi * 39" per meter
|
|
|
|
putint(m_chan, 0); // dpi * 39" per meter
|
|
|
|
putint(m_chan, cmapSize); // colors used in cmap
|
|
|
|
putint(m_chan, 0); // same as above
|
|
|
|
|
|
|
|
if (colormap)
|
|
|
|
for (int i = 0; i < (int)colormap->size(); i++) {
|
|
|
|
putc((*colormap)[i].b, m_chan);
|
|
|
|
putc((*colormap)[i].g, m_chan);
|
|
|
|
putc((*colormap)[i].r, m_chan);
|
|
|
|
putc(0, m_chan);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
for (int i = 0; i < cmapSize; i++) // palette!!
|
|
|
|
{
|
|
|
|
putc(i, m_chan);
|
|
|
|
putc(i, m_chan);
|
|
|
|
putc(i, m_chan);
|
|
|
|
putc(0, m_chan);
|
|
|
|
}
|
2016-03-19 06:57:51 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
void BmpWriter::writeLine(char *buffer) {
|
|
|
|
int lx = m_info.m_lx;
|
|
|
|
|
|
|
|
int j;
|
|
|
|
|
|
|
|
switch (m_bitPerPixel) {
|
|
|
|
case 24: {
|
|
|
|
TPixel32 *pix = (TPixel32 *)buffer;
|
|
|
|
for (j = 0; j < lx; j++) {
|
|
|
|
putc(pix->b, m_chan);
|
|
|
|
putc(pix->g, m_chan);
|
|
|
|
putc(pix->r, m_chan);
|
|
|
|
++pix;
|
|
|
|
}
|
|
|
|
int bytes = lx * 3;
|
|
|
|
while (bytes & 3) {
|
|
|
|
putc(0, m_chan);
|
|
|
|
bytes++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 8: {
|
|
|
|
TPixelGR8 *pix = (TPixelGR8 *)buffer;
|
|
|
|
for (j = 0; j < lx; j++) {
|
|
|
|
putc(pix->value, m_chan);
|
|
|
|
++pix;
|
|
|
|
}
|
|
|
|
while (lx & 3) {
|
|
|
|
putc(0, m_chan);
|
|
|
|
lx++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
ftell(m_chan);
|
2016-03-19 06:57:51 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
Tiio::Reader *Tiio::makeBmpReader() { return new BmpReader(); }
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
Tiio::Writer *Tiio::makeBmpWriter() { return new BmpWriter(); }
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
|
|
|
|
Tiio::BmpWriterProperties::BmpWriterProperties()
|
2016-06-15 18:43:10 +12:00
|
|
|
: m_pixelSize("Bits Per Pixel") {
|
|
|
|
m_pixelSize.addValue(L"24 bits");
|
|
|
|
m_pixelSize.addValue(L"8 bits (Greyscale)");
|
|
|
|
bind(m_pixelSize);
|
2016-03-19 06:57:51 +13:00
|
|
|
}
|
2018-06-04 15:18:43 +12:00
|
|
|
|
|
|
|
void Tiio::BmpWriterProperties::updateTranslation() {
|
|
|
|
m_pixelSize.setQStringName(tr("Bits Per Pixel"));
|
|
|
|
m_pixelSize.setItemUIName(L"24 bits", tr("24 bits"));
|
|
|
|
m_pixelSize.setItemUIName(L"8 bits (Greyscale)", tr("8 bits (Greyscale)"));
|
|
|
|
}
|