FLTK logo

[master] e0d630e - Add error checking and improve constructor of Fl_BMP_Image

FLTK matrix user chat room
(using Element browser app)   FLTK gitter user chat room   GitHub FLTK Project   FLTK News RSS Feed  
  FLTK Library      Forums      Links      Apps     Login 
 All Forums  |  Back to fltk.commit  ]
 
Previous Message ]Next Message ]

[master] e0d630e - Add error checking and improve constructor of Fl_BMP_Image "Albrecht Schlosser" Sep 27, 2021  
 
commit e0d630ed72f354916efec1e620adf6f276b40564
Author:     Albrecht Schlosser <albrechts.fltk@online.de>
AuthorDate: Mon Sep 27 19:21:21 2021 +0200
Commit:     Albrecht Schlosser <albrechts.fltk@online.de>
CommitDate: Mon Sep 27 19:26:04 2021 +0200

    Add error checking and improve constructor of Fl_BMP_Image
    
    - use new features of Fl_Image_Reader (read error and EOF checks)
    - add length argument to constructor reading from memory

 FL/Fl_BMP_Image.H    |  12 +--
 src/Fl_BMP_Image.cxx | 249 +++++++++++++++++++++++++++++++--------------------
 2 files changed, 156 insertions(+), 105 deletions(-)

diff --git FL/Fl_BMP_Image.H FL/Fl_BMP_Image.H
index c779d65..63dd301 100644
--- FL/Fl_BMP_Image.H
+++ FL/Fl_BMP_Image.H
@@ -1,7 +1,7 @@
 //
 // BMP image header file for the Fast Light Tool Kit (FLTK).
 //
-// Copyright 1998-2020 by Bill Spitzak and others.
+// Copyright 1998-2021 by Bill Spitzak and others.
 //
 // This library is free software. Distribution and use rights are outlined in
 // the file "COPYING" which should have been included with this file.  If this
@@ -27,14 +27,14 @@
  */
 class FL_EXPORT Fl_BMP_Image : public Fl_RGB_Image {
 
-  public:
+public:
 
-    Fl_BMP_Image(const char* filename);
-    Fl_BMP_Image(const char* imagename, const unsigned char *data);
+  Fl_BMP_Image(const char* filename);
+  Fl_BMP_Image(const char* imagename, const unsigned char *data, const long length = -1);
 
-  protected:
+protected:
 
-    void load_bmp_(class Fl_Image_Reader &rdr);
+  void load_bmp_(class Fl_Image_Reader &rdr);
 
 };
 
diff --git src/Fl_BMP_Image.cxx src/Fl_BMP_Image.cxx
index 8fb4cdd..8b326ed 100644
--- src/Fl_BMP_Image.cxx
+++ src/Fl_BMP_Image.cxx
@@ -1,7 +1,7 @@
 //
 // Fl_BMP_Image class for the Fast Light Tool Kit (FLTK).
 //
-// Copyright 2011-2020 by Bill Spitzak and others.
+// Copyright 2011-2021 by Bill Spitzak and others.
 // Copyright 1997-2010 by Easy Software Products.
 // Image support by Matthias Melcher, Copyright 2000-2009.
 //
@@ -40,20 +40,20 @@
 
 
 /**
- \brief This constructor loads the named BMP image from the given BMP filename.
+  This constructor loads the named BMP image from the given BMP filename.
 
- The destructor frees all memory and server resources that are used by
- the image.
+  The destructor frees all memory and server resources that are used by
+  the image.
 
- Use Fl_Image::fail() to check if Fl_BMP_Image failed to load. fail() returns
- ERR_FILE_ACCESS if the file could not be opened or read, ERR_FORMAT if the
- BMP format could not be decoded, and ERR_NO_IMAGE if the image could not
- be loaded for another reason.
+  Use Fl_Image::fail() to check if Fl_BMP_Image failed to load. fail() returns
+  ERR_FILE_ACCESS if the file could not be opened or read, ERR_FORMAT if the
+  BMP format could not be decoded, and ERR_NO_IMAGE if the image could not
+  be loaded for another reason.
 
- \param[in] filename a full path and name pointing to a valid BMP file.
+  \param[in] filename a full path and name pointing to a BMP file.
 
- \see Fl_BMP_Image::Fl_BMP_Image(const char *imagename, const unsigned char *data)
- */
+  \see Fl_BMP_Image::Fl_BMP_Image(const char* imagename, const unsigned char *data, const long length = -1);
+*/
 Fl_BMP_Image::Fl_BMP_Image(const char *filename) // I - File to read
 : Fl_RGB_Image(0,0,0)
 {
@@ -66,29 +66,44 @@ Fl_BMP_Image::Fl_BMP_Image(const char *filename) // I - File to read
 }
 
 /**
- \brief Read a BMP image from memory.
+  This constructor loads a BMP image from memory.
+
+  Construct an image from a block of memory inside the application. Fluid offers
+  "binary data" chunks as a great way to add image data into the C++ source code.
+  \p imagename can be NULL. If a name is given, the image is added to the list of
+  shared images and will be available by that name.
+
+  The destructor frees all memory and server resources that are used by
+  the image.
+
+  The (new and optional) third parameter \p length \b should be used so buffer
+  overruns (i.e. truncated images) can be checked. See note below.
+
+  If \p length is not used
+  - it defaults to -1 (unlimited size)
+  - buffer overruns will not be checked.
 
- Construct an image from a block of memory inside the application. Fluid offers
- "binary data" chunks as a great way to add image data into the C++ source code.
- imagename can be NULL. If a name is given, the image is added to the list of
- shared images and will be available by that name.
+  \note The optional parameter \p length is available since FLTK 1.4.0.
+    Not using it is deprecated and old code should be modified to use it.
+    This parameter will likely become mandatory in a future FLTK version.
 
- Use Fl_Image::fail() to check if Fl_BMP_Image failed to load. fail() returns
- ERR_FILE_ACCESS if the image could not be read from memory, ERR_FORMAT if the
- BMP format could not be decoded, and ERR_NO_IMAGE if the image could not
- be loaded for another reason.
+  Use Fl_Image::fail() to check if Fl_BMP_Image failed to load. fail() returns
+  ERR_FILE_ACCESS if the image could not be read from memory, ERR_FORMAT if the
+  BMP format could not be decoded, and ERR_NO_IMAGE if the image could not
+  be loaded for another reason.
 
- \param[in] imagename  A name given to this image or NULL
- \param[in] data       Pointer to the start of the BMP image in memory. This code will not check for buffer overruns.
+  \param[in] imagename  A name given to this image or NULL
+  \param[in] data       Pointer to the start of the BMP image in memory.
+  \param[in] length     Length of the BMP image in memory.
 
- \see Fl_BMP_Image::Fl_BMP_Image(const char *filename)
- \see Fl_Shared_Image
+  \see Fl_BMP_Image::Fl_BMP_Image(const char *filename)
+  \see Fl_Shared_Image
 */
-Fl_BMP_Image::Fl_BMP_Image(const char *imagename, const unsigned char *data)
+Fl_BMP_Image::Fl_BMP_Image(const char *imagename, const unsigned char *data, const long length)
 : Fl_RGB_Image(0,0,0)
 {
   Fl_Image_Reader rdr;
-  if (rdr.open(imagename, data) == -1) {
+  if (rdr.open(imagename, data, length) == -1) {
     ld(ERR_FILE_ACCESS);
   } else {
     load_bmp_(rdr);
@@ -96,35 +111,55 @@ Fl_BMP_Image::Fl_BMP_Image(const char *imagename, const unsigned char *data)
 }
 
 /*
- This method reads BMP image data and creates an RGB or RGBA image. The BMP
- format supports only 1 bit for alpha. To avoid code duplication, we use
- an Fl_Image_Reader that reads data from either a file or from memory.
- */
+  This macro can be used to check for end of file (EOF) or other read errors.
+  In case of an error or EOF an error message is issued and the image loading
+  is terminated with error code ERR_FORMAT.
+*/
+#define CHECK_ERROR \
+  if (rdr.error()) { \
+    Fl::error("[%d] Fl_BMP_Image: %s - unexpected EOF or read error at offset %ld", \
+              __LINE__, rdr.name(), rdr.tell()); \
+    ld(ERR_FORMAT); \
+    return; \
+  }
+
+/*
+  This method reads BMP image data and creates an RGB or RGBA image. The BMP
+  format supports only 1 bit for alpha. To avoid code duplication, we use
+  an Fl_Image_Reader that reads data from either a file or from memory.
+*/
 void Fl_BMP_Image::load_bmp_(Fl_Image_Reader &rdr)
 {
-  int     info_size,    // Size of info header
-          depth,        // Depth of image (bits)
-          bDepth = 3,   // Depth of image (bytes)
-          compression,  // Type of compression
-          colors_used,  // Number of colors used
-          x, y,         // Looping vars
-          color,        // Color of RLE pixel
-          repcount,     // Number of times to repeat
-          temp,         // Temporary color
-          align,        // Alignment bytes
-          dataSize,     // number of bytes in image data set
-          row_order,    // 1 = normal;  -1 = flipped row order
-          start_y,      // Beginning Y
-          end_y;        // Ending Y
-  long    offbits;      // Offset to image data
-  uchar   bit,          // Bit in image
-          byte;         // Byte in image
-  uchar   *ptr;         // Pointer into pixels
-  uchar   colormap[256][3]; // Colormap
-  uchar   havemask;     // Single bit mask follows image data
-  int     use_5_6_5;    // Use 5:6:5 for R:G:B channels in 16 bit images
-
-  // Reader is already open at this point.
+  int   info_size,        // Size of info header
+        width,            // Width of image (pixels)
+        height,           // Height of image (pixels)
+        depth,            // Depth of image (bits)
+        bDepth = 3,       // Depth of image (bytes)
+        compression,      // Type of compression
+        colors_used,      // Number of colors used
+        x, y,             // Looping vars
+        color,            // Color of RLE pixel
+        repcount,         // Number of times to repeat
+        temp,             // Temporary color
+        align,            // Alignment bytes
+        dataSize,         // number of bytes in image data set
+        row_order,        // 1 = normal;  -1 = flipped row order
+        start_y,          // Beginning Y
+        end_y;            // Ending Y
+  long  offbits;          // Offset to image data
+  uchar bit,              // Bit in image
+        byte;             // Byte in image
+  uchar *ptr;             // Pointer into pixels
+  uchar colormap[256][3]; // Colormap
+  uchar havemask;         // Single bit mask follows image data
+  int   use_5_6_5;        // Use 5:6:5 for R:G:B channels in 16 bit images
+
+  // Implementation notes: Reader is already open at this point.
+  // Use local variables (width, height) until image is complete
+  // so we can easily use CHECK_ERROR to return with ld(ERR_FORMAT).
+  // We use CHECK_ERROR only at some essential points.
+
+  w(0); h(0); d(0); ld(0);      // make sure these are all zero
 
   // Get the header...
   byte = rdr.read_byte();       // Check "BM" sync chars
@@ -141,6 +176,7 @@ void Fl_BMP_Image::load_bmp_(Fl_Image_Reader &rdr)
 
   // Then the bitmap information...
   info_size = rdr.read_dword();
+  CHECK_ERROR
 
   //  printf("offbits = %ld, info_size = %d\n", offbits, info_size);
 
@@ -150,8 +186,8 @@ void Fl_BMP_Image::load_bmp_(Fl_Image_Reader &rdr)
 
   if (info_size < 40) {
     // Old Windows/OS2 BMP header...
-    w(rdr.read_word());
-    h(rdr.read_word());
+    width = rdr.read_word();
+    height = rdr.read_word();
     rdr.read_word();
     depth = rdr.read_word();
     compression = BI_RGB;
@@ -160,11 +196,11 @@ void Fl_BMP_Image::load_bmp_(Fl_Image_Reader &rdr)
     repcount = info_size - 12;
   } else {
     // New BMP header...
-    w(rdr.read_long());
+    width = rdr.read_long();
     // If the height is negative, the row order is flipped
     temp = rdr.read_long();
     if (temp < 0) row_order = 1;
-    h(abs(temp));
+    height = abs(temp);
     rdr.read_word();
     depth = rdr.read_word();
     compression = rdr.read_dword();
@@ -176,29 +212,29 @@ void Fl_BMP_Image::load_bmp_(Fl_Image_Reader &rdr)
 
     repcount = info_size - 40;
 
-    if (!compression && depth>=8 && w()>32/depth) {
+    if (!compression && depth >= 8 && width > 32/depth) {
       int Bpp = depth/8;
-      int maskSize = (((w()*Bpp+3)&~3)*h()) + (((((w()+7)/8)+3)&~3)*h());
-      if (maskSize==2*dataSize) {
+      int maskSize = (((width*Bpp+3)&~3)*height) + (((((width+7)/8)+3)&~3)*height);
+      if (maskSize == 2*dataSize) {
         havemask = 1;
-        h(h()/2);
+        height = height/2;
         bDepth = 4;
       }
     }
   }
+  CHECK_ERROR
 
-  //  printf("w() = %d, h() = %d, depth = %d, compression = %d, colors_used = %d, repcount = %d\n",
-  //         w(), h(), depth, compression, colors_used, repcount);
+  //  printf("width =%4d, height =%4d, depth = %2d, compression = %d, colors_used = %3d, repcount = %2d, row_order = %2d\n",
+  //         width, height, depth, compression, colors_used, repcount, row_order);
 
   // Skip remaining header bytes...
-  while (repcount > 0) {
-    rdr.read_byte();
-    repcount --;
-  }
+  if (repcount > 0)
+    rdr.skip(repcount);
+  CHECK_ERROR
 
   // Check header data...
-  if (!w() || !h() || !depth) {
-    w(0); h(0); d(0); ld(ERR_FORMAT);
+  if (!width || !height || !depth) {
+    ld(ERR_FORMAT);
     return;
   }
 
@@ -206,15 +242,16 @@ void Fl_BMP_Image::load_bmp_(Fl_Image_Reader &rdr)
   if (colors_used == 0 && depth <= 8)
     colors_used = 1 << depth;
 
-  for (repcount = 0; repcount < colors_used; repcount ++) {
+  for (int i = 0; i < colors_used; i++) {
     // Read BGR color...
-    colormap[repcount][0] = rdr.read_byte();
-    colormap[repcount][1] = rdr.read_byte();
-    colormap[repcount][2] = rdr.read_byte();
+    colormap[i][0] = rdr.read_byte();
+    colormap[i][1] = rdr.read_byte();
+    colormap[i][2] = rdr.read_byte();
 
     // Skip pad byte for new BMP files...
     if (info_size > 12) rdr.read_byte();
   }
+  CHECK_ERROR
 
   // Read first dword of colormap. It tells us if 5:5:5 or 5:6:5 for 16 bit
   if (depth == 16)
@@ -222,18 +259,18 @@ void Fl_BMP_Image::load_bmp_(Fl_Image_Reader &rdr)
 
   // Set byte depth for RGBA images
   if (depth == 32)
-    bDepth=4;
+    bDepth = 4;
 
   // Setup image and buffers...
-  d(bDepth);
   if (offbits) rdr.seek(offbits);
+  CHECK_ERROR
 
-  if (((size_t)w()) * h() * d() > max_size() ) {
+  if (((size_t)width) * height * bDepth > max_size() ) {
     Fl::warning("BMP file \"%s\" is too large!\n", rdr.name());
-    w(0); h(0); d(0); ld(ERR_FORMAT);
+    ld(ERR_FORMAT);
     return;
   }
-  array = new uchar[w() * h() * d()];
+  array = new uchar[width * height * bDepth];
   alloc_array = 1;
 
   // Read the image data...
@@ -244,20 +281,20 @@ void Fl_BMP_Image::load_bmp_(Fl_Image_Reader &rdr)
   temp  = 0;
 
   if (row_order < 0) {
-    start_y = h() - 1;
+    start_y = height - 1;
     end_y   = -1;
   } else {
     start_y = 0;
-    end_y   = h();
+    end_y   = height;
   }
 
   for (y = start_y; y != end_y; y += row_order) {
-    ptr = (uchar *)array + y * w() * d();
+    ptr = (uchar *)array + y * width * bDepth;
 
     switch (depth)
     {
       case 1 : // Bitmap
-        for (x = w(), bit = 128; x > 0; x --) {
+        for (x = width, bit = 128; x > 0; x --) {
           if (bit == 128) byte = rdr.read_byte();
 
           if (byte & bit) {
@@ -277,13 +314,13 @@ void Fl_BMP_Image::load_bmp_(Fl_Image_Reader &rdr)
         }
 
         // Read remaining bytes to align to 32 bits...
-        for (temp = (w() + 7) / 8; temp & 3; temp ++) {
+        for (temp = (width + 7) / 8; temp & 3; temp ++) {
           rdr.read_byte();
         }
         break;
 
       case 4 : // 16-color
-        for (x = w(), bit = 0xf0; x > 0; x --) {
+        for (x = width, bit = 0xf0; x > 0; x --) {
           // Get a new repcount as needed...
           if (repcount == 0) {
             if (compression != BI_RLE4) {
@@ -305,7 +342,7 @@ void Fl_BMP_Image::load_bmp_(Fl_Image_Reader &rdr)
                   break;
                 } else if (repcount == 2) {
                   // Delta...
-                  repcount = rdr.read_byte() * rdr.read_byte() * w();
+                  repcount = rdr.read_byte() * rdr.read_byte() * width;
                   color = 0;
                 } else {
                   // Absolute...
@@ -343,17 +380,18 @@ void Fl_BMP_Image::load_bmp_(Fl_Image_Reader &rdr)
           }
 
         }
+        CHECK_ERROR
 
         if (!compression) {
           // Read remaining bytes to align to 32 bits...
-          for (temp = (w() + 1) / 2; temp & 3; temp ++) {
+          for (temp = (width + 1) / 2; temp & 3; temp ++) {
             rdr.read_byte();
           }
         }
         break;
 
       case 8 : // 256-color
-        for (x = w(); x > 0; x --) {
+        for (x = width; x > 0; x --) {
           // Get a new repcount as needed...
           if (compression != BI_RLE8) {
             repcount = 1;
@@ -365,6 +403,7 @@ void Fl_BMP_Image::load_bmp_(Fl_Image_Reader &rdr)
               align --;
               rdr.read_byte();
             }
+            CHECK_ERROR
 
             if ((repcount = rdr.read_byte()) == 0) {
               if ((repcount = rdr.read_byte()) == 0) {
@@ -376,7 +415,7 @@ void Fl_BMP_Image::load_bmp_(Fl_Image_Reader &rdr)
                 break;
               } else if (repcount == 2) {
                 // Delta...
-                repcount = rdr.read_byte() * rdr.read_byte() * w();
+                repcount = rdr.read_byte() * rdr.read_byte() * width;
                 color = 0;
               } else {
                 // Absolute...
@@ -387,6 +426,7 @@ void Fl_BMP_Image::load_bmp_(Fl_Image_Reader &rdr)
               color = rdr.read_byte();
             }
           }
+          CHECK_ERROR
 
           // Get a new color as needed...
           if (color < 0) temp = rdr.read_byte();
@@ -403,14 +443,14 @@ void Fl_BMP_Image::load_bmp_(Fl_Image_Reader &rdr)
 
         if (!compression) {
           // Read remaining bytes to align to 32 bits...
-          for (temp = w(); temp & 3; temp ++) {
+          for (temp = width; temp & 3; temp ++) {
             rdr.read_byte();
           }
         }
         break;
 
       case 16 : // 16-bit 5:5:5 or 5:6:5 RGB
-        for (x = w(); x > 0; x --, ptr += bDepth) {
+        for (x = width; x > 0; x --, ptr += bDepth) {
           uchar b = rdr.read_byte(), a = rdr.read_byte() ;
           if (use_5_6_5) {
             ptr[2] = (uchar)(( b << 3 ) & 0xf8);
@@ -424,26 +464,26 @@ void Fl_BMP_Image::load_bmp_(Fl_Image_Reader &rdr)
         }
 
         // Read remaining bytes to align to 32 bits...
-        for (temp = w() * 2; temp & 3; temp ++) {
+        for (temp = width * 2; temp & 3; temp ++) {
           rdr.read_byte();
         }
         break;
 
       case 24 : // 24-bit RGB
-        for (x = w(); x > 0; x --, ptr += bDepth) {
+        for (x = width; x > 0; x --, ptr += bDepth) {
           ptr[2] = rdr.read_byte();
           ptr[1] = rdr.read_byte();
           ptr[0] = rdr.read_byte();
         }
 
         // Read remaining bytes to align to 32 bits...
-        for (temp = w() * 3; temp & 3; temp ++) {
+        for (temp = width * 3; temp & 3; temp ++) {
           rdr.read_byte();
         }
         break;
 
       case 32 : // 32-bit RGBA
-        for (x = w(); x > 0; x --, ptr += bDepth) {
+        for (x = width; x > 0; x --, ptr += bDepth) {
           ptr[2] = rdr.read_byte();
           ptr[1] = rdr.read_byte();
           ptr[0] = rdr.read_byte();
@@ -451,12 +491,13 @@ void Fl_BMP_Image::load_bmp_(Fl_Image_Reader &rdr)
         }
         break;
     }
+    CHECK_ERROR
   }
 
   if (havemask) {
-    for (y = h() - 1; y >= 0; y --) {
-      ptr = (uchar *)array + y * w() * d() + 3;
-      for (x = w(), bit = 128; x > 0; x --, ptr+=bDepth) {
+    for (y = height - 1; y >= 0; y --) {
+      ptr = (uchar *)array + y * width * bDepth + 3;
+      for (x = width, bit = 128; x > 0; x --, ptr += bDepth) {
         if (bit == 128) byte = rdr.read_byte();
         if (byte & bit)
           *ptr = 0;
@@ -468,9 +509,19 @@ void Fl_BMP_Image::load_bmp_(Fl_Image_Reader &rdr)
           bit = 128;
       }
       // Read remaining bytes to align to 32 bits...
-      for (temp = (w() + 7) / 8; temp & 3; temp ++)
+      for (temp = (width + 7) / 8; temp & 3; temp ++)
         rdr.read_byte();
     }
   }
+
+  CHECK_ERROR
+
+  // Success: set image attributes and return
   // File is closed when returning...
-}
+
+  w(width);
+  h(height);
+  d(bDepth);
+  ld(0);
+
+} // load_bmp_()
Direct Link to Message ]
 
     
Previous Message ]Next Message ]
 
 

Comments are owned by the poster. All other content is copyright 1998-2025 by Bill Spitzak and others. This project is hosted by The FLTK Team. Please report site problems to 'erco@seriss.com'.