home *** CD-ROM | disk | FTP | other *** search
/ Compressed Image File Formats / CompressedImageFileFormatsJohnMiano.iso / pc / Library / source / bmpdecod.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-12-20  |  16.2 KB  |  604 lines

  1. //
  2. // Copyright (c) 1997,1998 Colosseum Builders, Inc.
  3. // All rights reserved.
  4. //
  5. // Colosseum Builders, Inc. makes no warranty, expressed or implied
  6. // with regards to this software. It is provided as is.
  7. //
  8. // See the README.TXT file that came with this software for restrictions
  9. // on the use and redistribution of this file or send E-mail to
  10. // info@colosseumbuilders.com
  11. //
  12.  
  13. //
  14. // BMP Decoder Library.
  15. //
  16. // Title:   BmpDecoder Class Implementation
  17. //
  18. // Author: John M. Miano  miano@colosseumbuilders.com
  19. //
  20. //
  21.  
  22. #include <windows.h>
  23.  
  24. #include "bmpdecod.h"
  25.  
  26. //
  27. //  Description:
  28. //
  29. //    Class default constructor
  30. //
  31.  
  32. BmpDecoder::BmpDecoder ()
  33. {
  34.   return ;
  35. }
  36.  
  37. //
  38. //  Description:
  39. //
  40. //    Class Copy Constructor
  41. //
  42. BmpDecoder::BmpDecoder (const BmpDecoder &source)
  43. {
  44.   Initialize () ;
  45.   DoCopy (source) ;
  46.   return ;
  47. }
  48.  
  49. //
  50. //  Description:
  51. //
  52. //    Class Destructor
  53. //
  54. BmpDecoder::~BmpDecoder ()
  55. {
  56.   return ;
  57. }
  58.  
  59. //
  60. //  Description:
  61. //
  62. //    Assignment operator.
  63. //
  64. //  Parameters:
  65. //   source: The object to copy
  66. //
  67. BmpDecoder &BmpDecoder::operator=(const BmpDecoder &source)
  68. {
  69.   DoCopy (source) ;
  70.   return *this ;
  71. }
  72.  
  73. //
  74. //  Description:
  75. //
  76. //    Common class initialization function for use by constructors.
  77. //
  78. void BmpDecoder::Initialize ()
  79. {
  80.   return ;
  81. }
  82.  
  83. //
  84. //  Description:
  85. //
  86. //    Common class copy function.
  87. //
  88. //  Parameters:
  89. //    source:  The object to copy.
  90. //
  91. void BmpDecoder::DoCopy (const BmpDecoder &source)
  92. {
  93.   BitmapImageDecoder::DoCopy (source) ;
  94.   return ;
  95. }
  96.  
  97. //
  98. //  Description:
  99. //
  100. //    This function reads an image from a Windows BMP stream.
  101. //
  102. //  Parameters:
  103. //    strm: The input stream
  104. //    image: The image to be read
  105. //    
  106. void BmpDecoder::ReadImage (std::istream &strm, BitmapImage &image)
  107. {
  108.   bool os2format ;
  109.  
  110.   // We need this because MSVC++ does not follow standard scoping
  111.   // rules in for statements.
  112.   unsigned int ii ;
  113.  
  114.   unsigned int bytesread = 0 ;
  115.  
  116.   BITMAPFILEHEADER fileheader ;
  117.   strm.read ((char *) &fileheader, sizeof (fileheader)) ;
  118.   if (strm.gcount () != sizeof(fileheader))
  119.     throw EBmpFileReadError () ;
  120.   bytesread += sizeof (fileheader) ;
  121.  
  122.   const UBYTE2 signature = 'B' | ('M' << 8) ;
  123.   if (fileheader.bfType != signature)
  124.     throw EBmpNotABmpFile () ;
  125.  
  126.   // The header can come in one of two flavors.  They both
  127.   // begin with a 4-byte headersize.
  128.  
  129.   UBYTE4 headersize ;
  130.   strm.read ((char *) &headersize, sizeof (headersize)) ;
  131.  
  132.   unsigned long width ;
  133.   long height ;
  134.   unsigned int bitcount ;
  135.   unsigned int compression ;
  136.   if (headersize == sizeof (BITMAPCOREHEADER))
  137.   {
  138.     // OS/2 Format Header
  139.  
  140.     BITMAPCOREHEADER header ;
  141.     header.bcSize = headersize ;
  142.     strm.read ((char *) &header.bcWidth,
  143.           sizeof (header) - sizeof (headersize)) ;
  144.     bytesread += sizeof (header) ;
  145.  
  146.     width = header.bcWidth ;
  147.     height = header.bcHeight ;
  148.     bitcount = header.bcBitCount ;
  149.  
  150.     compression = BI_RGB ;
  151.  
  152.     os2format = true ;
  153.   }
  154.   else if (headersize >= sizeof (BITMAPINFOHEADER))
  155.   {
  156.     BITMAPINFOHEADER header ;
  157.     header.biSize = headersize ;
  158.     strm.read ((char *) &header.biWidth,
  159.            sizeof (header) - sizeof (headersize)) ;
  160.     bytesread += sizeof (header) ;
  161.     compression = header.biCompression ;
  162.  
  163.     width = header.biWidth ;
  164.     height = header.biHeight ;
  165.     bitcount = header.biBitCount ;
  166.  
  167.     for (unsigned int ii = 0 ;
  168.          ii < headersize - sizeof (BITMAPINFOHEADER) ;
  169.          ++ ii)
  170.     {
  171.       ++ bytesread ;
  172.       UBYTE1 data ;
  173.       strm.read ((char *) &data, 1) ;
  174.     }
  175.     os2format = false ;
  176.   }
  177.   else
  178.   {
  179.     throw EBmpCorruptFile () ;
  180.   }
  181.  
  182.   // Calculate the number of colors and make sure that the
  183.   // compression method is compatible.
  184.   unsigned int colorcount ;
  185.   switch (bitcount)
  186.   {
  187.   case 1:
  188.     if (compression != BI_RGB)
  189.       throw EBmpNotSupported () ;
  190.     colorcount = 1 << bitcount ;
  191.     break ;
  192.   case 4:
  193.     if (compression != BI_RGB && compression != BI_RLE4)
  194.       throw EBmpNotSupported () ;
  195.     colorcount = 1 << bitcount ;
  196.     break ;
  197.   case 8:
  198.     if (compression != BI_RGB &&  compression != BI_RLE8)
  199.       throw EBmpNotSupported () ;
  200.     colorcount = 1 << bitcount ;
  201.     break ;
  202.   case 24:
  203.     if (compression != BI_RGB)
  204.       throw EBmpNotSupported () ;
  205.     colorcount = 0 ;
  206.     break ;
  207.   default:
  208.     throw EBmpNotSupported () ;
  209.   }
  210.  
  211.   // Allocate storage for the image.
  212.   image.SetSize (colorcount, bitcount, width, height) ;
  213.  
  214.   // Read the color map.
  215.   if (os2format)
  216.   {
  217.     for (ii = 0 ; ii < colorcount ; ++ ii)
  218.     {
  219.       RGBTRIPLE color ;
  220.       strm.read ((char *) &color, sizeof (color)) ;
  221.       image.ColorMap (ii).red = color.rgbtRed ;
  222.       image.ColorMap (ii).blue = color.rgbtBlue ;
  223.       image.ColorMap (ii).green = color.rgbtGreen ;
  224.  
  225.       bytesread += sizeof (color) ;
  226.     }
  227.   }
  228.   else
  229.   {
  230.     for (ii = 0 ; ii < colorcount ; ++ ii)
  231.     {
  232.       RGBQUAD color ;
  233.       strm.read ((char *) &color, sizeof (color)) ;
  234.       image.ColorMap (ii).red = color.rgbRed ;
  235.       image.ColorMap (ii).blue = color.rgbBlue ;
  236.       image.ColorMap (ii).green = color.rgbGreen ;
  237.  
  238.       bytesread += sizeof (color) ;
  239.     }
  240.   }
  241.  
  242.   // It is poss ible to have a file where the image data does not
  243.   // immediately follow the color map (or headers). If there is
  244.   // padding we skip over it.
  245.   if (bytesread > fileheader.bfOffBits)
  246.     throw EBmpCorruptFile () ;
  247.   for (ii = bytesread ; ii < fileheader.bfOffBits ; ++ ii)
  248.   {
  249.     UBYTE1 data ;
  250.     strm.read ((char *) &data, 1) ;
  251.   }
  252.  
  253.   // Read the image data.
  254.   CallProgressFunction (0) ;
  255.   if (bitcount != 24)
  256.   {
  257.     // In this block we handle images that use a color map.
  258.  
  259.     if (compression == BI_RGB)
  260.     {
  261.       // Simplest case -- No compression. We can just read the
  262.       // raw data from the file.
  263.  
  264.       // Number of bits required for each pixel row.
  265.       unsigned int bitwidth = bitcount * width ;
  266.       // Number of bytes need to store each pixel row.
  267.       unsigned int rowwidth = (bitwidth + 7)/8 ;
  268.       // Number of bytes used to store each row in the BMP file.
  269.       // This is is rowwidth rounded up to the nearest 4 bytes.
  270.       unsigned int physicalrowsize = (rowwidth + 0x3) & ~0x3 ;
  271.       // The number of pad bytes for each row in the BMP file.
  272.       unsigned int padsize = physicalrowsize -  rowwidth ;
  273.  
  274.       // Images with positive heights are stored bottom up. Images with
  275.       // negative height are stored top down.
  276.       if (height > 0)
  277.       {
  278.         for (unsigned int ii = 0 ; ii < height ; ++ ii)
  279.         {
  280.           CallProgressFunction (ii * 100 / height) ;
  281.         
  282.           // The pixel rows are stored in reverse order.
  283.           unsigned int index = (height - ii - 1) ;
  284.           strm.read ((char *)&image [index][0], rowwidth) ;
  285.           if (strm.gcount () != rowwidth)
  286.             throw EBmpFileReadError () ;
  287.  
  288.           // Skip over the pad bytes.
  289.           static char pad [4] ;
  290.           strm.read (pad, padsize) ;
  291.         }
  292.       }
  293.       else
  294.       {
  295.         for (unsigned int ii = 0 ; ii < - height ; ++ ii)
  296.         {
  297.           CallProgressFunction (ii * 100 / - height) ;
  298.         
  299.           strm.read ((char *)&image [ii][0], rowwidth) ;
  300.           if (strm.gcount () != rowwidth)
  301.             throw EBmpFileReadError () ;
  302.  
  303.           // Skip over the pad bytes.
  304.           static char pad [4] ;
  305.           strm.read (pad, padsize) ;
  306.         }
  307.       }
  308.  
  309.     } //
  310.     else if (compression == BI_RLE8)
  311.     {
  312.       // Handle the case of 8-bit Run-Length Encoding.
  313.  
  314.       unsigned int row = height - 1 ;  // Current row
  315.       unsigned int col = 0 ;           // Current column
  316.       bool done = false ;
  317.       while (! strm.eof () && ! done)
  318.       {
  319.         CallProgressFunction ((height - row - 1) * 100 / height) ;
  320.  
  321.         // Structure for reading RLE commands.
  322.         struct
  323.         {
  324.           UBYTE1 count ;
  325.           UBYTE1 command ;
  326.         } opcode ;
  327.  
  328.         strm.read ((char *) &opcode, sizeof (opcode)) ;
  329.         if (opcode.count == 0)
  330.         {
  331.           // A byte count of zero means that this is a special
  332.           // instruction.
  333.           switch (opcode.command)
  334.           {
  335.           case 0: // 0 => Move to next row
  336.             -- row ;
  337.             col = 0 ;
  338.             break ;
  339.           case 1: // 1 => Image is finished
  340.             done = true ;
  341.             break ;
  342.           case 2: // 2 => Move to a new relative position.
  343.             {
  344.               // Read the relative position.
  345.               UBYTE1 dx ;
  346.               UBYTE1 dy ;
  347.               strm.read ((char *) &dx, 1) ;
  348.               strm.read ((char *) &dy, 1) ;
  349.               col += dx ;
  350.               row -= dy ;
  351.             }
  352.             break ;
  353.           default:
  354.             {
  355.               // Store absolute data. The command code is the
  356.               // number of absolute bytes to store.
  357.               if (row >= height || col + opcode.command > width)
  358.                   throw EBmpCorruptFile () ;
  359.               UBYTE1 data ;
  360.               for (unsigned int ii = 0 ; ii < opcode.command ; ++ ii)
  361.               {
  362.                 strm.read ((char *) &data, 1) ;
  363.                 image [row][col] = data ;
  364.                 ++ col ;
  365.               }
  366.               // An odd number of bytes is followed by a pad byte.
  367.               if ((opcode.command & 1) != 0)
  368.                 strm.read ((char *) &data, 1) ;
  369.             }
  370.             break ;
  371.           }
  372.         }
  373.         else
  374.         {
  375.           // Store a run of the same color value.
  376.           if (row >= height || col + opcode.count > width)
  377.             throw EBmpCorruptFile () ;
  378.           for (unsigned int ii = 0 ; ii < opcode.count ; ++ ii)
  379.           {
  380.             image [row][col] = opcode.command ;
  381.             ++ col ;
  382.           }
  383.         }
  384.       }
  385.       if (! done)
  386.         throw EBmpCorruptFile () ;
  387.     }
  388.     else if (compression == BI_RLE4)
  389.     {
  390.       // In this block we handle 4-bit Run-Length Encoded images.
  391.  
  392.       // The mechanism here is the same as for BI_RLE8 with two
  393.       // exceptions. Here we are dealing with 4-bit nibbles rather
  394.       // than whole bytes. This results in some extra work. In
  395.       // addition, the coding of runs includes two color values.
  396.  
  397.       unsigned int row = height - 1 ;
  398.       unsigned int col = 0 ;
  399.       bool done = false ;
  400.       while (! strm.eof () && ! done)
  401.       {
  402.         CallProgressFunction ((height - row - 1) * 100 / height) ;
  403.  
  404.         struct
  405.         {
  406.           UBYTE1 count ;
  407.           UBYTE1 command ;
  408.         } opcode ;
  409.  
  410.         strm.read ((char *) &opcode, sizeof (opcode)) ;
  411.         if (opcode.count == 0)
  412.         {
  413.           switch (opcode.command)
  414.           {
  415.           case 0:    // Advance to next pixel row
  416.             -- row ;
  417.             col = 0 ;
  418.             break ;
  419.           case 1:    // Image complete
  420.             done = true ;
  421.             break ;
  422.           case 2:   // Move to relative location the image.
  423.             {
  424.               UBYTE1 dx ;
  425.               UBYTE1 dy ;
  426.               strm.read ((char *) &dx, 1) ;
  427.               strm.read ((char *) &dy, 1) ;
  428.               col += dx ;
  429.               row -= dy ;
  430.             }
  431.             break ;
  432.           default:
  433.             {
  434.               UBYTE1 data ;
  435.                  UBYTE1 hi ;
  436.               UBYTE1 lo ;
  437.               if (row >= height || col + opcode.command > width)
  438.                   throw EBmpCorruptFile () ;
  439.               for (unsigned int ii = 0 ; ii < opcode.command ; ++ ii)
  440.               {
  441.                 if ((ii & 1) == 0)
  442.                 {
  443.                   strm.read ((char *) &data, 1) ;
  444.                   lo = data & 0xF ;
  445.                   hi = (data & 0xF0) >> 4 ;
  446.                 }
  447.                 if ((col & 1) == 0)
  448.                 {
  449.                   if ((ii & 1) == 0)
  450.                   {
  451.                     image [row][col/2] = hi << 4 ;
  452.                   }
  453.                   else
  454.                   {
  455.                     image [row][col/2] = lo << 4 ;
  456.                   }
  457.                 }
  458.                 else
  459.                 {
  460.                   if ((ii & 1) == 0)
  461.                   {
  462.                    image [row][col/2] |= hi  ;
  463.                   }
  464.                   else
  465.                   {
  466.                     image [row][col/2] |= lo ;
  467.                   }
  468.                 }
  469.                 ++ col ;
  470.               }
  471.               // If the number of bytes used in this instruction
  472.               // is odd then there is a padding byte.
  473.               switch (opcode.command & 0x3)
  474.               {
  475.               case 1: case 2:
  476.                 strm.read ((char *) &data, 1) ;
  477.                 break ;
  478.               }
  479.             }
  480.             break ;
  481.           }
  482.         }
  483.         else
  484.         {
  485.           // Process a run of the same color value pairs.
  486.           UBYTE1 hi = opcode.command >> 4 ;
  487.           UBYTE1 lo = opcode.command & 0xF ;
  488.           if (row >= height || col + opcode.count > width)
  489.             throw EBmpCorruptFile () ;
  490.  
  491.           for (unsigned int ii = 0 ; ii < opcode.count ; ++ ii)
  492.           {
  493.             if ((col & 1) == 0)
  494.             {
  495.               if ((ii & 1) == 0)
  496.               {
  497.                 image [row][col/2] = hi << 4 ;
  498.               }
  499.               else
  500.               {
  501.                 image [row][col/2] = lo << 4 ;
  502.               }
  503.             }
  504.             else
  505.             {
  506.               if ((ii & 1) == 0)
  507.               {
  508.                image [row][col/2] |= hi  ;
  509.               }
  510.               else
  511.               {
  512.                 image [row][col/2] |= lo ;
  513.               }
  514.             }
  515.             ++ col ;
  516.           }
  517.         }
  518.       }
  519.       if (! done)
  520.         throw EBmpCorruptFile () ;
  521.     }
  522.     else
  523.     {
  524.       // Invalid compression type
  525.       throw EBmpCorruptFile () ;
  526.     }
  527.   }
  528.   else
  529.   {
  530.     // Read the data for a 24-bit image.
  531.  
  532.     // Number of bytes used to store each pixel row in the
  533.     // the file. This value is rounded up to the nearest
  534.     // multiple of four.
  535.     unsigned int physicalrowsize = (3 * width + 0x3) & ~0x3 ;
  536.     // Size of the padding for each row.
  537.     unsigned int padsize = physicalrowsize -  3 * width ;
  538.  
  539.     // Images with positive heights are stored bottom up. Images with
  540.     // negative height are stored top down.
  541.     if (height > 0)
  542.     {
  543.       for (unsigned int yy = 0 ; yy < height ; ++ yy)
  544.       {
  545.         CallProgressFunction (yy * 100 / height) ;
  546.  
  547.         unsigned int index = height - yy - 1 ;
  548.         // Have to read the sample values separately because the colors
  549.         // are in reverse order: BGR. If you are on Windows you could
  550.         // red the whole thing a row at a time.
  551.         for (unsigned int xx = 0 ; xx < 3 * width ; xx += 3)
  552.         {
  553.           strm.read ((char *)&image[index][xx + BitmapImage::BlueOffset], 1) ;
  554.           strm.read ((char *)&image[index][xx + BitmapImage::GreenOffset], 1) ;
  555.           strm.read ((char *)&image[index][xx + BitmapImage::RedOffset], 1) ;
  556.         }
  557.         static char pad [4] ;
  558.         strm.read (pad, padsize) ;
  559.       }
  560.     }
  561.     else
  562.     {
  563.       for (unsigned int yy = 0 ; yy < -height ; ++ yy)
  564.       {
  565.         CallProgressFunction (yy * 100 / -height) ;
  566.  
  567.         // Have to read the sample values separately because the colors
  568.         // are in reverse order: BGR. If you are on Windows you could
  569.         // red the whole thing a row at a time.
  570.         for (unsigned int xx = 0 ; xx < 3 * width ; xx += 3)
  571.         {
  572.           strm.read ((char *)&image[ii][xx + BitmapImage::BlueOffset], 1) ;
  573.           strm.read ((char *)&image[ii][xx + BitmapImage::GreenOffset], 1) ;
  574.           strm.read ((char *)&image[ii][xx + BitmapImage::RedOffset], 1) ;
  575.         }
  576.         static char pad [4] ;
  577.         strm.read (pad, padsize) ;
  578.       }
  579.     }
  580.   }
  581.   CallProgressFunction (100) ;
  582.   return ;
  583. }
  584.  
  585. //
  586. //  Description:
  587. //
  588. //    This function is used to call the progres function.
  589. //
  590. //  Parameters:
  591. //    percent:  The percent complete (0..100)
  592. //
  593. void BmpDecoder::CallProgressFunction (unsigned int percent)
  594. {
  595.   if (progress_function == NULL)
  596.     return ;
  597.   bool cancel = false ;
  598.   progress_function (*this, progress_data, 1, 1, percent, cancel) ;
  599.   if (cancel)
  600.     throw EGraphicsAbort () ;
  601.   return ;
  602. }
  603.  
  604.