home *** CD-ROM | disk | FTP | other *** search
/ Mastering MFC Development / MMD.ISO / labs / c12 / lab01 / ex01 / dib.cpp next >
Encoding:
C/C++ Source or Header  |  1997-02-20  |  9.7 KB  |  408 lines

  1. // DIB.cpp : implementation file
  2. //
  3.  
  4. #include "stdafx.h"
  5. #include "DIB.h"
  6.  
  7. #ifdef _DEBUG
  8. #define new DEBUG_NEW
  9. #undef THIS_FILE
  10. static char THIS_FILE[] = __FILE__;
  11. #endif
  12.  
  13. /////////////////////////////////////////////////////////////////////////////
  14. // DIB Helper Functions
  15.  
  16. BOOL IsWinDIB(BITMAPINFOHEADER* pBIH)
  17. {
  18.     ASSERT(pBIH);
  19.     if (((BITMAPCOREHEADER*)pBIH)->bcSize == sizeof(BITMAPCOREHEADER)) 
  20.     {
  21.          return FALSE;
  22.     }
  23.     return TRUE;
  24. }
  25.  
  26. int NumDIBColorBits(LPBITMAPINFO pBmpInfo) 
  27. {
  28.     BITMAPINFOHEADER* pBIH;
  29.     BITMAPCOREHEADER* pBCH;
  30.     int iBitCount;
  31.  
  32.     ASSERT(pBmpInfo);
  33.  
  34.     pBIH = &(pBmpInfo->bmiHeader);
  35.     pBCH = (BITMAPCOREHEADER*) pBIH;
  36.  
  37.     // Start off by assuming the color table size from
  38.     // the bit-per-pixel field.
  39.     if (IsWinDIB(pBIH)) 
  40.     {
  41.         iBitCount = pBIH->biBitCount;
  42.     } 
  43.     else 
  44.     {
  45.         iBitCount = pBCH->bcBitCount;
  46.     }
  47.     return iBitCount;
  48. }
  49.  
  50. int NumDIBColorEntries(LPBITMAPINFO pBmpInfo) 
  51. {
  52.     BITMAPINFOHEADER* pBIH;
  53.     BITMAPCOREHEADER* pBCH;
  54.     int iColors, iBitCount;
  55.  
  56.     ASSERT(pBmpInfo);
  57.  
  58.     pBIH = &(pBmpInfo->bmiHeader);
  59.     pBCH = (BITMAPCOREHEADER*) pBIH;
  60.  
  61.     // Start off by assuming the color table size from
  62.     // the bit-per-pixel field.
  63.     if (IsWinDIB(pBIH)) 
  64.     {
  65.         iBitCount = pBIH->biBitCount;
  66.     } 
  67.     else 
  68.     {
  69.         iBitCount = pBCH->bcBitCount;
  70.     }
  71.  
  72.     switch (iBitCount) 
  73.     {
  74.         case 1:
  75.             iColors = 2;
  76.             break;
  77.         case 4:
  78.             iColors = 16;
  79.             break;
  80.         case 8:
  81.             iColors = 256;
  82.             break;
  83.             
  84.         default:
  85.             iColors = 0;
  86.             break;
  87.     }
  88.  
  89.     // If this is a Windows DIB, then the color table length
  90.     // is determined by the biClrUsed field if the value in
  91.     // the field is nonzero.
  92.     if (IsWinDIB(pBIH) && (0 != pBIH->biClrUsed)) 
  93.     {
  94.         iColors = pBIH->biClrUsed;
  95.     }
  96.  
  97.     return iColors;
  98. }
  99.  
  100. /////////////////////////////////////////////////////////////////////////////
  101. // CDIB Class Implementation
  102.  
  103. IMPLEMENT_SERIAL(CDIB, CObject, 0)
  104.  
  105. CDIB::CDIB()
  106. {
  107.     m_pBMI = 0;
  108.     m_pBits = 0;
  109.     Create(16, 16);
  110. }
  111.  
  112. CDIB::~CDIB()
  113. {
  114.     // Free the memory.
  115.     if (0 != m_pBMI)
  116.         free(m_pBMI);
  117.     if (0 != m_pBits)
  118.         free(m_pBits);
  119. }
  120.  
  121.  
  122. BOOL CDIB::Create(int iWidth, int iHeight)
  123. {
  124.     // Delete any existing stuff.
  125.     if (0 != m_pBMI)
  126.         free(m_pBMI);
  127.     if (0 != m_pBits)
  128.         free(m_pBits);
  129.  
  130.     // Allocate memory for the header.
  131.     m_pBMI = (BITMAPINFO*) malloc(sizeof(BITMAPINFOHEADER) +
  132.                                   256 * sizeof(RGBQUAD));
  133.  
  134.     // Allocate memory for the bits (DWORD aligned).
  135.     int iBitsSize = ((iWidth + 3) & ~3) * iHeight;
  136.     m_pBits = (BYTE*)malloc(iBitsSize);
  137.  
  138.     // Fill in the header info.
  139.     BITMAPINFOHEADER* pBI = (BITMAPINFOHEADER*) m_pBMI;
  140.     pBI->biSize = sizeof(BITMAPINFOHEADER);
  141.     pBI->biWidth = iWidth;
  142.     pBI->biHeight = iHeight;
  143.     pBI->biPlanes = 1;
  144.     pBI->biBitCount = 8;
  145.     pBI->biCompression = BI_RGB;
  146.     pBI->biSizeImage = 0;
  147.     pBI->biXPelsPerMeter = 0;
  148.     pBI->biYPelsPerMeter = 0;
  149.     pBI->biClrUsed = 0;
  150.     pBI->biClrImportant = 0;
  151.  
  152.     // Create a gray scale color table.
  153.     RGBQUAD* prgb = GetClrTabAddress();
  154.     for (int i = 0; i < 256; i++) 
  155.     {
  156.         prgb->rgbBlue = prgb->rgbGreen = prgb->rgbRed = (BYTE) i;
  157.         prgb->rgbReserved = 0;
  158.         prgb++;
  159.     }
  160.  
  161.     // Set all the bits to a known state (black).
  162.     memset(m_pBits, iBitsSize, 0);
  163.  
  164.     return TRUE;
  165. }
  166.  
  167. // Get the number of color table entries.
  168. int CDIB::GetNumClrEntries()
  169. {
  170.     return NumDIBColorEntries(m_pBMI);
  171. }
  172.  
  173. LONG CDIB::ScanLineWidth() const
  174. {
  175.     //    For easier reading
  176.     #define    DWORD_PADDED(x) (((x)+3)&~3)
  177.     
  178.     return DWORD_PADDED((m_pBMI->bmiHeader.biBitCount * DibWidth() + 7)/8);
  179. }
  180.  
  181. BOOL CDIB::Load(CFile* fp)
  182. {
  183.     BOOL bIsPM = FALSE;
  184.     BITMAPINFO* pBmpInfo = 0;
  185.     BYTE* pBits = 0;
  186.  
  187.     // Get the current file position.
  188.     DWORD dwFileStart = fp->GetPosition();
  189.  
  190.     // Read the file header to get the file size and to
  191.     // find out where the bits start in the file.
  192.     BITMAPFILEHEADER BmpFileHdr;
  193.     int iBytes;
  194.     iBytes = fp->Read(&BmpFileHdr, sizeof(BmpFileHdr));
  195.  
  196.     // Check that we have the magic 'BM' at the start.
  197.     if (BmpFileHdr.bfType != 0x4D42) 
  198.     {
  199.         TRACE("Not a bitmap file");
  200.         goto $abort;
  201.     }
  202.  
  203.     // Make a wild guess that the file is in Windows DIB
  204.     // format and read the BITMAPINFOHEADER. If the file turns
  205.     // out to be a PM DIB file, we'll convert it later.
  206.     BITMAPINFOHEADER BmpInfoHdr;
  207.     iBytes = fp->Read(&BmpInfoHdr, sizeof(BmpInfoHdr)); 
  208.  
  209.     // Check that we got a real Windows DIB file.
  210.     if (BmpInfoHdr.biSize != sizeof(BITMAPINFOHEADER)) 
  211.     {
  212.         // Set a flag to convert PM file to Win format later.
  213.         bIsPM = TRUE;
  214.  
  215.         // Back up the file pointer and read the BITMAPCOREHEADER
  216.         // and create the BITMAPINFOHEADER from it.
  217.         fp->Seek(dwFileStart + sizeof(BITMAPFILEHEADER), CFile::begin);
  218.         BITMAPCOREHEADER BmpCoreHdr;
  219.         iBytes = fp->Read(&BmpCoreHdr, sizeof(BmpCoreHdr)); 
  220.  
  221.         BmpInfoHdr.biSize = sizeof(BITMAPINFOHEADER);
  222.         BmpInfoHdr.biWidth = (int) BmpCoreHdr.bcWidth;
  223.         BmpInfoHdr.biHeight = (int) BmpCoreHdr.bcHeight;
  224.         BmpInfoHdr.biPlanes = BmpCoreHdr.bcPlanes;
  225.         BmpInfoHdr.biBitCount = BmpCoreHdr.bcBitCount;
  226.         BmpInfoHdr.biCompression = BI_RGB;
  227.         BmpInfoHdr.biSizeImage = 0;
  228.         BmpInfoHdr.biXPelsPerMeter = 0;
  229.         BmpInfoHdr.biYPelsPerMeter = 0;
  230.         BmpInfoHdr.biClrUsed = 0;
  231.         BmpInfoHdr.biClrImportant = 0;
  232.     }
  233.  
  234.     // Allocate the memory blocks.
  235.     // Copy the BmpInfoHdr we have so far,
  236.     // and then read in the color table from the file.
  237.     int iColors;
  238.     int iColorTableSize;
  239.     iColors = NumDIBColorEntries((LPBITMAPINFO) &BmpInfoHdr);
  240.     iColorTableSize = 256 * sizeof(RGBQUAD);
  241.     int iBitsSize;
  242.     int iBISize;
  243.     iBISize = sizeof(BITMAPINFOHEADER) + 
  244.               iColorTableSize;
  245.     iBitsSize = BmpFileHdr.bfSize - 
  246.                 BmpFileHdr.bfOffBits;
  247.  
  248.     // Allocate the memory for the header.
  249.     pBmpInfo = (LPBITMAPINFO) malloc(iBISize);
  250.  
  251.     // Copy the header we already have.
  252.     memcpy(pBmpInfo, &BmpInfoHdr, sizeof(BITMAPINFOHEADER));
  253.  
  254.     // Now read the color table in from the file.
  255.     if (bIsPM == FALSE) 
  256.     {
  257.         // Read the color table from the file.
  258.         iBytes = fp->Read(((LPBYTE) pBmpInfo) + 
  259.                           sizeof(BITMAPINFOHEADER), iColorTableSize);
  260.     } 
  261.     else 
  262.     {
  263.         // Read each PM color table entry in turn and convert it
  264.         // to Win DIB format as we go.
  265.         LPRGBQUAD lpRGB;
  266.         lpRGB = (LPRGBQUAD) ((LPBYTE) pBmpInfo + 
  267.                                       sizeof(BITMAPINFOHEADER));
  268.         int i;
  269.         RGBTRIPLE rgbt;
  270.         for (i=0; i<iColors; i++) 
  271.         {
  272.             iBytes = fp->Read(&rgbt, sizeof(RGBTRIPLE));
  273.             lpRGB->rgbBlue = rgbt.rgbtBlue;
  274.             lpRGB->rgbGreen = rgbt.rgbtGreen;
  275.             lpRGB->rgbRed = rgbt.rgbtRed;
  276.             lpRGB->rgbReserved = 0;
  277.             lpRGB++;
  278.         }
  279.     }
  280.  
  281.     // Allocate the memory for the bits
  282.     // and read the bits from the file.
  283.     pBits = (BYTE*) malloc(iBitsSize);
  284.     if (!pBits) 
  285.     {
  286.         TRACE("Out of memory for DIB bits");
  287.         goto $abort;
  288.     }
  289.  
  290.     // Seek to the bits in the file.
  291.     fp->Seek(dwFileStart + BmpFileHdr.bfOffBits, CFile::begin);
  292.  
  293.     // Read the bits.
  294.     iBytes = fp->Read(pBits, iBitsSize);
  295.  
  296.     // Everything went OK.
  297.     if (0 != m_pBMI)
  298.         free(m_pBMI);
  299.     m_pBMI = pBmpInfo; 
  300.     if (0 != m_pBits)
  301.         free(m_pBits);
  302.     m_pBits = pBits;
  303.     return TRUE;
  304.                 
  305. $abort: // Something went wrong.
  306.     if (pBmpInfo) free(pBmpInfo);
  307.     if (pBits) free(pBits);
  308.     return FALSE;    
  309. }
  310.  
  311. BOOL CDIB::Save(CFile* fp)
  312. {
  313.     BITMAPFILEHEADER bfh;
  314.  
  315.     // Construct the file header.
  316.     bfh.bfType = 0x4D42; // 'BM'
  317.     bfh.bfSize = 
  318.         sizeof(BITMAPFILEHEADER) +
  319.         sizeof(BITMAPINFOHEADER) +
  320.         256 * sizeof(RGBQUAD) +
  321.         ScanLineWidth() * DibHeight();
  322.     
  323.     bfh.bfReserved1 = 0;
  324.     bfh.bfReserved2 = 0;
  325.     bfh.bfOffBits =
  326.         sizeof(BITMAPFILEHEADER) +
  327.         sizeof(BITMAPINFOHEADER) +
  328.         256 * sizeof(RGBQUAD);
  329.  
  330.     // Write the file header.
  331.     int iSize = sizeof(bfh);
  332.     TRY 
  333.     {
  334.         fp->Write(&bfh, iSize);
  335.     } 
  336.     CATCH(CFileException, e) 
  337.     {
  338.         TRACE("Failed to write file header");
  339.         return FALSE;
  340.     } 
  341.     END_CATCH
  342.  
  343.     // Write the BITMAPINFO structure.
  344.     // Note: we assume that there are always 256 colors in the
  345.     // color table.
  346.     ASSERT(m_pBMI);
  347.     iSize =    sizeof(BITMAPINFOHEADER) +
  348.             256 * sizeof(RGBQUAD);
  349.     TRY 
  350.     {
  351.         fp->Write(m_pBMI, iSize);
  352.     } 
  353.     CATCH(CFileException, e) 
  354.     {
  355.         TRACE("Failed to write BITMAPINFO");
  356.         return FALSE;
  357.     } 
  358.     END_CATCH
  359.  
  360.     // Write the bits.
  361.     iSize = ScanLineWidth() * DibHeight();
  362.     TRY 
  363.     {
  364.         fp->Write(m_pBits, iSize);
  365.     } 
  366.     CATCH(CFileException, e) 
  367.     {
  368.         TRACE("Failed to write bits");
  369.         return FALSE;
  370.     } 
  371.     END_CATCH
  372.  
  373.     return TRUE;
  374. }
  375.  
  376. //////////////////////////////////////////////////////////////////////////
  377. // CDIB serialization
  378.  
  379. void CDIB::Serialize(CArchive& ar)
  380. {
  381.     ar.Flush();
  382.     CFile* fp = ar.GetFile();
  383.  
  384.     if (ar.IsStoring()) 
  385.     {
  386.         Save(fp);
  387.     } 
  388.     else 
  389.     {
  390.         Load(fp);
  391.     }
  392. }
  393.  
  394. //////////////////////////////////////////////////////////////////
  395. // CDDIB diagnostics
  396.  
  397. #ifdef _DEBUG
  398. void CDIB::AssertValid() const
  399. {
  400.     CObject::AssertValid();
  401. }
  402.  
  403. void CDIB::Dump(CDumpContext& dc) const
  404. {
  405.     CObject::Dump(dc);
  406. }
  407. #endif //_DEBUG
  408.