#ifdef WIN32 #pragma warning(disable : 4996) #endif #include "tiio_bmp.h" // #include "bmp/filebmp.h" #include "tpixel.h" #include "tpixelgr.h" #include "tproperty.h" //#include "tconvert.h" #include //========================================================= namespace { //========================================================= 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; } BMP_HEADER; const int BMP_WIN_SIZE = 40; const int BMP_OS2_SIZE = 64; const int BMP_BI_RGB = 0; const int BMP_BI_RLE8 = 1; const int BMP_BI_RLE4 = 2; //========================================================= UINT getshort(FILE *fp) { int c = getc(fp), c1 = getc(fp); return ((UINT)c) + (((UINT)c1) << 8); } //--------------------------------------------------------- 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); } //--------------------------------------------------------- void putshort(FILE *fp, int i) { int c = (((UINT)i)) & 0xff, c1 = (((UINT)i) >> 8) & 0xff; putc(c, fp); putc(c1, fp); } //--------------------------------------------------------- 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); } //========================================================= } // namespace //========================================================= class BmpReader : public Tiio::Reader { FILE *m_chan; BMP_HEADER m_header; char *m_line; int m_lineSize; TPixel *m_cmap; bool m_corrupted; typedef int (BmpReader::*ReadLineMethod)(char *buffer, int x0, int x1, int shrink); ReadLineMethod m_readLineMethod; public: BmpReader(); ~BmpReader(); void open(FILE *file); 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); void readLine(char *buffer, int x0, int x1, int shrink); int skipLines(int lineCount) { fseek(m_chan, lineCount * m_lineSize, SEEK_CUR); /* for(int i=0;ibiSize != BMP_WIN_OS2_OLD) { // skip ahead to colormap, using biSize int c = hd->biSize - 40; // 40 bytes read from biSize to biClrImportant for (int i=0; ibiPad = hd->bfOffBits - (hd->biSize + 14); } else hd->biPad = 0; */ // 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 = new TPixel[256]; TPixel32 *pix = m_cmap; 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); } //--------------------------------------------------------- void BmpReader::readLine(char *buffer, int x0, int x1, int shrink) { (this->*m_readLineMethod)(buffer, x0, x1, shrink); } //--------------------------------------------------------- int BmpReader::readNoLine(char *buffer, int x0, int x1, int shrink) { skipBytes(m_lineSize); return 0; } //--------------------------------------------------------- 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) { 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+81) skipBytes(shrink-1); } if(pix=endPix); for(int j=0; pix+j>(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; } //--------------------------------------------------------- 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; } //--------------------------------------------------------- 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; } //--------------------------------------------------------- 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; } //--------------------------------------------------------- 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; } //--------------------------------------------------------- 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; } //--------------------------------------------------------- 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; } //--------------------------------------------------------- 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; } //========================================================= class BmpWriter : public Tiio::Writer { FILE *m_chan; int m_bitPerPixel; int m_compression; public: BmpWriter(); ~BmpWriter(); void open(FILE *file, const TImageInfo &info); void flush() { fflush(m_chan); } void writeLine(char *buffer); }; //--------------------------------------------------------- BmpWriter::BmpWriter() : m_chan(0) { } //--------------------------------------------------------- BmpWriter::~BmpWriter() { delete m_properties; } //--------------------------------------------------------- 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); string str = toString(p->getValue()); m_bitPerPixel = atoi(str.c_str()); int cmapSize = 0; vector *colormap = 0; if (m_bitPerPixel == 8) { TPointerProperty *colormapProp = (TPointerProperty *)(m_properties->getProperty("Colormap")); if (colormapProp) { colormap = (vector *)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); } } //--------------------------------------------------------- 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++; } } 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++; } } DEFAULT: assert(false); } ftell(m_chan); } //========================================================= Tiio::Reader *Tiio::makeBmpReader() { return new BmpReader(); } //========================================================= Tiio::Writer *Tiio::makeBmpWriter() { return new BmpWriter(); } //========================================================= Tiio::BmpWriterProperties::BmpWriterProperties() : m_pixelSize("Bits Per Pixel") { m_pixelSize.addValue(L"24 bits"); m_pixelSize.addValue(L"8 bits (Greyscale)"); bind(m_pixelSize); }