//============================================================================== // bxread.cpp // Classes and functions to read and write Microsoft Windows bitmap (BMP) // graphic image files. // // Acknowledgements // Derived from source code written by David R. Tribble, Jan 1992. // // Copyright ©2008 by David R. Tribble, all rights reserved. // Permission is granted to any person or entity except those designated // by the United States Department of State as a terrorist, or terrorist // government or agency, to use and distribute this source code provided // that the original copyright notice remains present and unaltered. //============================================================================== // Identification static char REV[] = "@(#)drt/bmp/bxread.cpp $Revision: 1.4 $$Date: 2008/06/28 16:36:33 $"; // System includes #include #define sys_errno_h 1 #include #define sys_iso646_h 1 #include #define sys_stddef_h 1 #include #define sys_stdint_h 1 #include #define sys_stdio_h 1 #include #define sys_string_h 1 // Local includes #include "bmpdefs.h" #include "bmpfile.hpp" //------------------------------------------------------------------------------ // DrtBmpFile::read_header() // Read the file header of a BMP file. // // @return // True if successful, otherwise false on error. // // @since // 1.1, 2008-04-28 //------------------------------------------------------------------------------ bool DrtBmpFile::read_header() { #if DrtBmpFile_VS/100 != 1 #error class DrtBmpFile has changed #endif #if BMP_HEADER_PFX_VS != 1 #error struct bmp_header_pfx has changed #endif #if BMP_HEADER_VS != 1 #error struct bmp_header has changed #endif #if OS2_BMP_HEADER_VS != 1 #error struct os2_bmp_header has changed #endif long pos; int len; int off; // Check the object if (m_magic != DrtBmpFile_MAGIC or m_vers/100 != DrtBmpFile_VS/100) { sys_errno = EINVAL; return false; } // Read the BMP file header if (m_fp == NULL) { sys_errno = EBADF; return false; } if (m_flags & F_DID_HDR) return true; // Read the BMP header prefix from the file ::memset(&m_hdr, 0, sizeof(m_hdr)); pos = ::ftell(m_fp); len = ::fread(&m_hdr, 1, sizeof(struct bmp_header_pfx), m_fp); if (len != sizeof(struct bmp_header_pfx)) goto fail; // Check for bad magic if (m_hdr.id != BMP_HDR_MAGIC) goto fail; // Header prefix was successfully read, check for an OS/2 header if (m_hdr.infoSize == 12) { struct os2_bmp_header hdr2; unsigned hlen; // File header is an OS/2 BMP header, read the rest of it m_flags |= (F_OS2 | F_RGB3); ::memcpy(&hdr2, &m_hdr, sizeof(bmp_header_pfx)); off = sizeof(struct bmp_header_pfx); hlen = sizeof(struct os2_bmp_header) - off; len = ::fread((char *)&hdr2 + off, 1, hlen, m_fp); if (len != hlen) goto fail; // Fill in the Win32 header with the OS/2 header info m_hdr.width = hdr2.width; m_hdr.depth = hdr2.depth; m_hdr.biPlanes = hdr2.biPlanes; m_hdr.bits = hdr2.bits; } else if (m_hdr.infoSize >= 40) { unsigned hlen; // File header is a Win32 BMP header, read the rest of it off = sizeof(struct bmp_header_pfx); hlen = sizeof(struct bmp_header) - off; len = ::fread((char *)&m_hdr + off, 1, hlen, m_fp); if (len != hlen) goto fail; // Skip any extra bytes in the header hlen += sizeof(m_hdr.infoSize); while (hlen < m_hdr.infoSize) { unsigned char buf[100]; // Read and discard some bytes from the file header sys_errno = E2BIG; len = m_hdr.infoSize - hlen; len = (len <= sizeof(buf) ? len : sizeof(buf)); len = ::fread(buf, 1, len, m_fp); if (len < 1) goto fail; hlen += len; } } else { // Error, file header is the wrong size sys_errno = ENOENT; goto fail; } // Adjust the BMP file info m_flags |= (F_HAS_HDR | F_DID_HDR); m_ncols = get_palette_size(); switch (m_hdr.bits) { case 1: case 2: case 4: case 8: m_linesz = m_hdr.width / (8/m_hdr.bits); // nplanes? break; case 24: m_linesz = m_hdr.width * 24/8; // nplanes? break; } m_linesz = (m_linesz + 4-1)/4 * 4; // Nearest longword m_byteno = 0; m_bitno = 0; m_rep = 0; m_pix = 0; m_byte = 0; // Read was successful return true; fail: // Failure occurred, back up the file pointer ::fseek(m_fp, pos, SEEK_SET); return false; } //------------------------------------------------------------------------------ // DrtBmpFile::read_palette() // Read the color palette of a BMP file. // // @return // True if successful, otherwise false on error. // // @since // 1.1, 2008-04-28 //------------------------------------------------------------------------------ bool DrtBmpFile::read_palette() { #if DrtBmpFile_VS/100 != 1 #error class DrtBmpFile has changed #endif // Check the object if (m_magic != DrtBmpFile_MAGIC or m_vers/100 != DrtBmpFile_VS/100) { sys_errno = EINVAL; return false; } // Read the BMP color palette, if there is one if (m_flags & F_DID_MAP) return true; if (m_hdr.bits > 0 and m_hdr.bits <= 8) { unsigned int len; unsigned int alen; struct bmp_rgb * map4; // Determine actual size of color palette alen = len = (1u << m_hdr.bits); if (m_hdr.biClrUsed > 0) alen = m_hdr.biClrUsed; // Read the BMP palette map4 = new struct bmp_rgb[0x100]; ::memset(map4, 0x00, sizeof(map4)); if (m_flags & F_OS2) { struct os2_bmp_rgb map3[0x100]; // Read OS/2 BMP color map ::memset(map3, 0x00, sizeof(map3)); if (::fread(map3, sizeof(map3[0]), alen, m_fp) != alen) { delete[] map4; return false; } for (int i = 0; i < alen; i++) { map4[i].r = map3[i].r; map4[i].g = map3[i].g; map4[i].b = map3[i].b; map4[i].a = 0x00; } m_map = map4; } else { // Read the BMP color map if (::fread(map4, sizeof(map4[0]), alen, m_fp) != alen) { delete[] map4; return false; } m_map = map4; } } // Done m_flags |= F_DID_MAP; return true; } //------------------------------------------------------------------------------ // DrtBmpFile::read_byte() // Read the next data byte from a BMP file. // // @return // A byte value in the range [0x00,0xFF], or -1 if the end of the file has // reached or an error occurs. // // @since // 1.2, 2008-05-05 //------------------------------------------------------------------------------ int DrtBmpFile::read_byte() { #if DrtBmpFile_VS/100 != 1 #error class DrtBmpFile has changed #endif int b; // Check the object if (m_magic != DrtBmpFile_MAGIC or m_vers/100 != DrtBmpFile_VS/100) { sys_errno = EINVAL; return -1; } // Check the file if (m_fp == NULL) { sys_errno = EBADF; return -1; } #if IS_NEW_CODE___ // Skip padding bytes if (m_byteno > ... m_linesz|m_h.width ?) read_eoln(); #endif // Read the next data (pixel) byte switch (m_hdr.biCompression) { case BMP_CMP_RLE8: while (m_rep == 0x00) { // Read next RLE pair b = getc(m_fp); if (b == EOF) return -1; m_rep = b; // Repeat count m_byteno++; b = getc(m_fp); if (b == EOF) return -1; m_pix = b; // Byte value m_byteno++; } m_rep--; b = m_pix; break; #ifdef IS_INCOMPLETE___ case BMP_CMP_RLE4: ... break; #endif case BMP_CMP_NONE: default: b = getc(m_fp); if (b == EOF) return -1; m_pix = b; m_byteno++; break; } m_byte = b; return b; } //------------------------------------------------------------------------------ // DrtBmpFile::read_rgb() // Read a pixel from a BMP file. // // @param pix // Pointer to an RGB triple to fill with the next pixel value. // // @return // An pixel value as a 24-bit integer (r<<16 + g<<8 + b), // or -1 if the end of the image is reached. // // @since // 1.2, 2008-05-05 //------------------------------------------------------------------------------ int32_t DrtBmpFile::read_rgb(struct bmp_rgbpix *pix) { #if DrtBmpFile_VS/100 != 1 #error class DrtBmpFile has changed #endif int byte; int col; // Check the object if (m_magic != DrtBmpFile_MAGIC or m_vers/100 != DrtBmpFile_VS/100) { sys_errno = EINVAL; return -1; } // Check the file if (m_fp == NULL) { sys_errno = EBADF; return -1; } // Read the BMP file header if (not (m_flags & F_DID_HDR)) if (not read_header()) return -1; // Read the BMP color palette if (not (m_flags & F_DID_MAP)) if (not read_palette()) return -1; // Read the next RGB pixel value switch (m_hdr.bits) { case 1: case 2: case 4: case 8: default: if (m_bitno == 0) { // Get next byte's worth of pixels byte = read_byte(); if (byte < 0) return -1; m_byte = byte; m_bitno = 8; } // Get next pixel from current byte switch (m_hdr.bits) { case 1: col = (m_byte & 0x80) >> 7; m_byte <<= 1; m_bitno -= 1; break; case 2: col = (m_byte & 0xC0) >> 6; m_byte <<= 2; m_bitno -= 2; break; case 4: col = (m_byte & 0xF0) >> 4; m_byte <<= 4; m_bitno -= 4; break; case 8: default: col = m_byte; m_bitno = 0; break; } if (m_map != NULL) { pix->r = m_map[col].r; pix->g = m_map[col].g; pix->b = m_map[col].b; } else { pix->r = 0; pix->g = 0; pix->b = 0; } break; case 24: // Build 24-bit pixel from next 3 bytes byte = read_byte(); // Blue component if (byte < 0) return -1; pix->b = byte; col = byte; byte = read_byte(); // Green component if (byte < 0) return -1; pix->g = byte; col = col + ((uint32_t)byte << 8); byte = read_byte(); // Red component if (byte < 0) return -1; pix->r = byte; col = col + ((uint32_t)byte << 16); break; } return col; } //------------------------------------------------------------------------------ // DrtBmpFile::read_pixel() // Read a pixel from a BMP file. // // @return // An pixel value as a 24-bit integer (r<<16 + g<<8 + b), // or -1 if the end of the image is reached. // // @since // 1.2, 2008-05-05 //------------------------------------------------------------------------------ int32_t DrtBmpFile::read_pixel() { #if DrtBmpFile_VS/100 != 1 #error class DrtBmpFile has changed #endif int byte; long pix; // Check the object if (m_magic != DrtBmpFile_MAGIC or m_vers/100 != DrtBmpFile_VS/100) { sys_errno = EINVAL; return -1; } // Check the file if (m_fp == NULL) { sys_errno = EBADF; return -1; } // Read the BMP file header if (not (m_flags & F_DID_HDR)) if (not read_header()) return -1; // Read the BMP color palette if (not (m_flags & F_DID_MAP)) if (not read_palette()) return -1; // Read an RGB pixel value switch (m_hdr.bits) { case 1: case 2: case 4: case 8: default: if (m_bitno == 0) { // Get the next byte's worth of pixels byte = read_byte(); if (byte < 0) return -1; m_byte = byte; m_bitno = 8; } // Get the next pixel from current byte switch (m_hdr.bits) { case 1: pix = (m_byte & 0x80) >> 7; m_byte <<= 1; m_bitno -= 1; break; case 2: pix = (m_byte & 0xC0) >> 6; m_byte <<= 2; m_bitno -= 2; break; case 4: pix = (m_byte & 0xF0) >> 4; m_byte <<= 4; m_bitno -= 4; break; case 8: default: pix = m_byte; m_bitno = 0; break; } break; case 24: // Build a 24-bit pixel from next 3 bytes byte = read_byte(); // Blue component if (byte < 0) return -1; pix = byte; byte = read_byte(); // Green component if (byte < 0) return -1; pix = pix + ((uint32_t)byte << 8); byte = read_byte(); // Red component if (byte < 0) return -1; pix = pix + ((uint32_t)byte << 16); break; } return pix; } //------------------------------------------------------------------------------ // DrtBmpFile::read_eoln() // Read to the end of the pixel row in a BMP file. // // @return // True if successful, otherwise false on error (i.e., premature end of // file). // // @since // 1.2, 2008-05-05 //------------------------------------------------------------------------------ bool DrtBmpFile::read_eoln() { #if DrtBmpFile_VS/100 != 1 #error class DrtBmpFile has changed #endif // Check the object if (m_magic != DrtBmpFile_MAGIC or m_vers/100 != DrtBmpFile_VS/100) { sys_errno = EINVAL; return false; } // Check the file if (m_fp == NULL) { sys_errno = EBADF; return false; } // Read the BMP file header if (not (m_flags & F_DID_HDR)) if (not read_header()) return false; // Read the BMP color palette if (not (m_flags & F_DID_MAP)) if (not read_palette()) return false; // Skip padding bytes while (m_byteno < m_linesz) { if (getc(m_fp) < 0) return false; m_byteno++; } // Read to the end of the current pixel row switch (m_hdr.biCompression) { case BMP_CMP_RLE8: m_rep = 0x00; m_bitno = 0; break; #ifdef IS_INCOMPLETE___ case BMP_CMP_RLE4: ... break; #endif case BMP_CMP_NONE: default: m_rep = 0x00; m_bitno = 0; break; } m_byteno = 0; return true; } // End bxread.cpp