/* * File: ximabmp.cpp * Purpose: Platform Independent BMP Image Class Loader and Writer * 07/Aug/2001 Davide Pizzolato - www.xdp.it * CxImage version 7.0.0 31/Dec/2010 */ #include "ximabmp.h" #if CXIMAGE_SUPPORT_BMP #include "ximaiter.h" //////////////////////////////////////////////////////////////////////////////// #if CXIMAGE_SUPPORT_ENCODE //////////////////////////////////////////////////////////////////////////////// bool CxImageBMP::Encode(CxFile * hFile) { if (EncodeSafeCheck(hFile)) return false; BITMAPFILEHEADER hdr; hdr.bfType = 0x4d42; // 'BM' WINDOWS_BITMAP_SIGNATURE hdr.bfSize = GetSize() + 14 /*sizeof(BITMAPFILEHEADER)*/; hdr.bfReserved1 = hdr.bfReserved2 = 0; hdr.bfOffBits = 14 /*sizeof(BITMAPFILEHEADER)*/ + head.biSize + GetPaletteSize(); hdr.bfType = m_ntohs(hdr.bfType); hdr.bfSize = m_ntohl(hdr.bfSize); hdr.bfOffBits = m_ntohl(hdr.bfOffBits); #if CXIMAGE_SUPPORT_ALPHA if (GetNumColors()==0 && AlphaIsValid()){ BITMAPINFOHEADER infohdr; memcpy(&infohdr,&head,sizeof(BITMAPINFOHEADER)); infohdr.biCompression = BI_RGB; infohdr.biBitCount = 32; uint32_t dwEffWidth = ((((infohdr.biBitCount * infohdr.biWidth) + 31) / 32) * 4); infohdr.biSizeImage = dwEffWidth * infohdr.biHeight; hdr.bfSize = infohdr.biSize + infohdr.biSizeImage + 14 /*sizeof(BITMAPFILEHEADER)*/; hdr.bfSize = m_ntohl(hdr.bfSize); bihtoh(&infohdr); // Write the file header hFile->Write(&hdr,min(14,sizeof(BITMAPFILEHEADER)),1); hFile->Write(&infohdr,sizeof(BITMAPINFOHEADER),1); //and DIB+ALPHA interlaced uint8_t *srcalpha = AlphaGetPointer(); for(int32_t y = 0; y < infohdr.biHeight; ++y){ uint8_t *srcdib = GetBits(y); for(int32_t x = 0; x < infohdr.biWidth; ++x){ hFile->Write(srcdib,3,1); hFile->Write(srcalpha,1,1); srcdib += 3; ++srcalpha; } } } else #endif //CXIMAGE_SUPPORT_ALPHA { // Write the file header hFile->Write(&hdr,min(14,sizeof(BITMAPFILEHEADER)),1); //copy attributes memcpy(pDib,&head,sizeof(BITMAPINFOHEADER)); bihtoh((BITMAPINFOHEADER*)pDib); // Write the DIB header and the pixels hFile->Write(pDib,GetSize(),1); bihtoh((BITMAPINFOHEADER*)pDib); } return true; } //////////////////////////////////////////////////////////////////////////////// #endif //CXIMAGE_SUPPORT_ENCODE //////////////////////////////////////////////////////////////////////////////// #if CXIMAGE_SUPPORT_DECODE //////////////////////////////////////////////////////////////////////////////// bool CxImageBMP::Decode(CxFile * hFile) { if (hFile == NULL) return false; BITMAPFILEHEADER bf; uint32_t off = hFile->Tell(); // cx_try { if (hFile->Read(&bf,min(14,sizeof(bf)),1)==0) cx_throw("Not a BMP"); bf.bfSize = m_ntohl(bf.bfSize); bf.bfOffBits = m_ntohl(bf.bfOffBits); if (m_ntohs(bf.bfType) != BFT_BITMAP) { //do we have a RC HEADER? bf.bfOffBits = 0L; hFile->Seek(off,SEEK_SET); } BITMAPINFOHEADER bmpHeader; if (!DibReadBitmapInfo(hFile,&bmpHeader)); cx_throw("Error reading BMP info"); uint32_t dwCompression=bmpHeader.biCompression; uint32_t dwBitCount=bmpHeader.biBitCount; //preserve for BI_BITFIELDS compression bool bIsOldBmp = bmpHeader.biSize == sizeof(BITMAPCOREHEADER); bool bTopDownDib = bmpHeader.biHeight<0; // check if it's a top-down bitmap if (bTopDownDib) bmpHeader.biHeight=-bmpHeader.biHeight; if (info.nEscape == -1) { // Return output dimensions only head.biWidth = bmpHeader.biWidth; head.biHeight = bmpHeader.biHeight; info.dwType = CXIMAGE_FORMAT_BMP; cx_throw("output dimensions returned"); } if (!Create(bmpHeader.biWidth,bmpHeader.biHeight,bmpHeader.biBitCount,CXIMAGE_FORMAT_BMP)) cx_throw(""); SetXDPI((int32_t) floor(bmpHeader.biXPelsPerMeter * 254.0 / 10000.0 + 0.5)); SetYDPI((int32_t) floor(bmpHeader.biYPelsPerMeter * 254.0 / 10000.0 + 0.5)); if (info.nEscape) cx_throw("Cancelled"); // - cancel decoding RGBQUAD *pRgb = GetPalette(); if (pRgb){ if (bIsOldBmp){ // convert a old color table (3 byte entries) to a new // color table (4 byte entries) hFile->Read((void*)pRgb,DibNumColors(&bmpHeader) * sizeof(RGBTRIPLE),1); for (int32_t i=DibNumColors(&head)-1; i>=0; i--){ pRgb[i].rgbRed = ((RGBTRIPLE *)pRgb)[i].rgbtRed; pRgb[i].rgbBlue = ((RGBTRIPLE *)pRgb)[i].rgbtBlue; pRgb[i].rgbGreen = ((RGBTRIPLE *)pRgb)[i].rgbtGreen; pRgb[i].rgbReserved = (uint8_t)0; } } else { hFile->Read((void*)pRgb,DibNumColors(&bmpHeader) * sizeof(RGBQUAD),1); //force rgbReserved=0, to avoid problems with some WinXp bitmaps for (uint32_t i=0; i - cancel decoding switch (dwBitCount) { case 32 : uint32_t bfmask[3]; if (dwCompression == BI_BITFIELDS) { hFile->Read(bfmask, 12, 1); } else { bfmask[0]=0x00FF0000; bfmask[1]=0x0000FF00; bfmask[2]=0x000000FF; } if (bf.bfOffBits != 0L) hFile->Seek(off + bf.bfOffBits,SEEK_SET); if (dwCompression == BI_BITFIELDS || dwCompression == BI_RGB){ int32_t imagesize=4*head.biHeight*head.biWidth; uint8_t* buff32=(uint8_t*)malloc(imagesize); if (buff32){ hFile->Read(buff32, imagesize,1); // read in the pixels #if CXIMAGE_SUPPORT_ALPHA if (dwCompression == BI_RGB){ AlphaCreate(); if (AlphaIsValid()){ bool bAlphaOk = false; uint8_t* p; for (int32_t y=0; ySeek(off + bf.bfOffBits,SEEK_SET); if (dwCompression == BI_RGB){ hFile->Read(info.pImage, head.biSizeImage,1); // read in the pixels } else cx_throw("unknown compression"); break; case 16 : { uint32_t bfmask[3]; if (dwCompression == BI_BITFIELDS) { hFile->Read(bfmask, 12, 1); } else { bfmask[0]=0x7C00; bfmask[1]=0x3E0; bfmask[2]=0x1F; //RGB555 } // bf.bfOffBits required after the bitfield mask if (bf.bfOffBits != 0L) hFile->Seek(off + bf.bfOffBits,SEEK_SET); // read in the pixels hFile->Read(info.pImage, head.biHeight*((head.biWidth+1)/2)*4,1); // transform into RGB Bitfield2RGB(info.pImage,bfmask[0],bfmask[1],bfmask[2],16); break; } case 8 : case 4 : case 1 : if (bf.bfOffBits != 0L) hFile->Seek(off + bf.bfOffBits,SEEK_SET); switch (dwCompression) { case BI_RGB : hFile->Read(info.pImage, head.biSizeImage,1); // read in the pixels break; case BI_RLE4 : { uint8_t status_byte = 0; uint8_t second_byte = 0; int32_t scanline = 0; int32_t bits = 0; BOOL low_nibble = FALSE; CImageIterator iter(this); for (BOOL bContinue = TRUE; bContinue && hFile->Read(&status_byte, sizeof(uint8_t), 1);) { switch (status_byte) { case RLE_COMMAND : hFile->Read(&status_byte, sizeof(uint8_t), 1); switch (status_byte) { case RLE_ENDOFLINE : bits = 0; scanline++; low_nibble = FALSE; break; case RLE_ENDOFBITMAP : bContinue=FALSE; break; case RLE_DELTA : { // read the delta values uint8_t delta_x; uint8_t delta_y; hFile->Read(&delta_x, sizeof(uint8_t), 1); hFile->Read(&delta_y, sizeof(uint8_t), 1); // apply them bits += delta_x / 2; scanline += delta_y; break; } default : hFile->Read(&second_byte, sizeof(uint8_t), 1); uint8_t *sline = iter.GetRow(scanline); for (int32_t i = 0; i < status_byte; i++) { if ((uint8_t*)(sline+bits) < (uint8_t*)(info.pImage+head.biSizeImage)){ if (low_nibble) { if (i&1) *(sline + bits) |= (second_byte & 0x0f); else *(sline + bits) |= (second_byte & 0xf0)>>4; bits++; } else { if (i&1) *(sline + bits) = (uint8_t)(second_byte & 0x0f)<<4; else *(sline + bits) = (uint8_t)(second_byte & 0xf0); } } if ((i & 1) && (i != (status_byte - 1))) hFile->Read(&second_byte, sizeof(uint8_t), 1); low_nibble = !low_nibble; } if ((((status_byte+1) >> 1) & 1 ) == 1) hFile->Read(&second_byte, sizeof(uint8_t), 1); break; }; break; default : { uint8_t *sline = iter.GetRow(scanline); hFile->Read(&second_byte, sizeof(uint8_t), 1); for (unsigned i = 0; i < status_byte; i++) { if ((uint8_t*)(sline+bits) < (uint8_t*)(info.pImage+head.biSizeImage)){ if (low_nibble) { if (i&1) *(sline + bits) |= (second_byte & 0x0f); else *(sline + bits) |= (second_byte & 0xf0)>>4; bits++; } else { if (i&1) *(sline + bits) = (uint8_t)(second_byte & 0x0f)<<4; else *(sline + bits) = (uint8_t)(second_byte & 0xf0); } } low_nibble = !low_nibble; } } break; }; } break; } case BI_RLE8 : { uint8_t status_byte = 0; uint8_t second_byte = 0; int32_t scanline = 0; int32_t bits = 0; CImageIterator iter(this); for (BOOL bContinue = TRUE; bContinue && hFile->Read(&status_byte, sizeof(uint8_t), 1);) { switch (status_byte) { case RLE_COMMAND : hFile->Read(&status_byte, sizeof(uint8_t), 1); switch (status_byte) { case RLE_ENDOFLINE : bits = 0; scanline++; break; case RLE_ENDOFBITMAP : bContinue=FALSE; break; case RLE_DELTA : { // read the delta values uint8_t delta_x; uint8_t delta_y; hFile->Read(&delta_x, sizeof(uint8_t), 1); hFile->Read(&delta_y, sizeof(uint8_t), 1); // apply them bits += delta_x; scanline += delta_y; break; } default : hFile->Read((void *)(iter.GetRow(scanline) + bits), sizeof(uint8_t) * status_byte, 1); // align run length to even number of bytes if ((status_byte & 1) == 1) hFile->Read(&second_byte, sizeof(uint8_t), 1); bits += status_byte; break; }; break; default : uint8_t *sline = iter.GetRow(scanline); hFile->Read(&second_byte, sizeof(uint8_t), 1); for (unsigned i = 0; i < status_byte; i++) { if ((uint8_t*)(sline+bits) < (uint8_t*)(info.pImage+head.biSizeImage)){ *(sline + bits) = second_byte; bits++; } else { break; } } break; }; } break; } default : cx_throw("compression type not supported"); } } if (bTopDownDib) Flip(); // } cx_catch { if (strcmp(message,"")) strncpy(info.szLastError,message,255); if (info.nEscape == -1 && info.dwType == CXIMAGE_FORMAT_BMP) return true; return false; } return true; } //////////////////////////////////////////////////////////////////////////////// /* ReadDibBitmapInfo() * * Will read a file in DIB format and return a global HANDLE to its * BITMAPINFO. This function will work with both "old" and "new" * bitmap formats, but will always return a "new" BITMAPINFO. */ bool CxImageBMP::DibReadBitmapInfo(CxFile* fh, BITMAPINFOHEADER *pdib) { if ((fh==NULL)||(pdib==NULL)) return false; if (fh->Read(pdib,sizeof(BITMAPINFOHEADER),1)==0) return false; bihtoh(pdib); switch (pdib->biSize) // what type of bitmap info is this? { case sizeof(BITMAPINFOHEADER): break; case 64: //sizeof(OS2_BMP_HEADER): fh->Seek((int32_t)(64 - sizeof(BITMAPINFOHEADER)),SEEK_CUR); break; case 124: //sizeof(BITMAPV5HEADER): fh->Seek((long)(124-sizeof(BITMAPINFOHEADER)), SEEK_CUR); break; case sizeof(BITMAPCOREHEADER): { BITMAPCOREHEADER bc = *(BITMAPCOREHEADER*)pdib; pdib->biSize = bc.bcSize; pdib->biWidth = (uint32_t)bc.bcWidth; pdib->biHeight = (uint32_t)bc.bcHeight; pdib->biPlanes = bc.bcPlanes; pdib->biBitCount = bc.bcBitCount; pdib->biCompression = BI_RGB; pdib->biSizeImage = 0; pdib->biXPelsPerMeter = 0; pdib->biYPelsPerMeter = 0; pdib->biClrUsed = 0; pdib->biClrImportant = 0; fh->Seek((int32_t)(sizeof(BITMAPCOREHEADER)-sizeof(BITMAPINFOHEADER)), SEEK_CUR); } break; default: //give a last chance if (pdib->biSize>(sizeof(BITMAPINFOHEADER))&& (pdib->biSizeImage>=(uint32_t)(pdib->biHeight*((((pdib->biBitCount*pdib->biWidth)+31)/32)*4)))&& (pdib->biPlanes==1)&&(pdib->biClrUsed==0)) { if (pdib->biCompression==BI_RGB) fh->Seek((int32_t)(pdib->biSize - sizeof(BITMAPINFOHEADER)),SEEK_CUR); break; } return false; } FixBitmapInfo(pdib); return true; } //////////////////////////////////////////////////////////////////////////////// #endif //CXIMAGE_SUPPORT_DECODE //////////////////////////////////////////////////////////////////////////////// #endif // CXIMAGE_SUPPORT_BMP ////////////////////////////////////////////////////////////////////////////////