diff options
| author | Matthias Melcher <git@matthiasm.com> | 2020-01-10 18:46:45 +0100 |
|---|---|---|
| committer | Matthias Melcher <git@matthiasm.com> | 2020-01-10 18:46:45 +0100 |
| commit | 7e0c82637d5cb2de0d7baa5a3c2ebe8491663b06 (patch) | |
| tree | 13471c7faf41ae4ae49f9f9f0ab69c2b1b4286f5 /src | |
| parent | 82d279c2341c88da802b82e0d8732399d4f6b481 (diff) | |
Updated image readers.
Removed duplicate code from Fl_JPEG_Image, updated and unified Doxygen comments. Avoiding name conflict in new protected functions.
Diffstat (limited to 'src')
| -rw-r--r-- | src/Fl_BMP_Image.cxx | 79 | ||||
| -rw-r--r-- | src/Fl_GIF_Image.cxx | 81 | ||||
| -rw-r--r-- | src/Fl_JPEG_Image.cxx | 246 | ||||
| -rw-r--r-- | src/Fl_PNM_Image.cxx | 12 |
4 files changed, 227 insertions, 191 deletions
diff --git a/src/Fl_BMP_Image.cxx b/src/Fl_BMP_Image.cxx index 11d4890f1..b2faf2de9 100644 --- a/src/Fl_BMP_Image.cxx +++ b/src/Fl_BMP_Image.cxx @@ -48,24 +48,35 @@ // Local reader class... // +/* + This class reads data chunks from a file or from memory in LSB-first + byte order. + + TODO: GIFReader and BMPReader are very similar and should be combined to avoid + code duplication. + */ class BMPReader { public: + // Create the reader. BMPReader() : pIsFile(0), pIsData(0), pFile(0L), pData(0L), pStart(0L), pName(0L) { } + // Initialize the reader to access the file system, filename is copied + // and stored. int open(const char *filename) { - if (filename) - pName = strdup(filename); - if ((pFile = fl_fopen(filename, "rb")) == NULL) { + if (!filename) + return -1; + pName = strdup(filename); + if ( (pFile = fl_fopen(filename, "rb")) == NULL ) { return -1; - } else { - pIsFile = 1; - return 0; } + pIsFile = 1; + return 0; } + // Initialize the reader for memory access, name is copied and stored int open(const char *imagename, const unsigned char *data) { if (imagename) pName = strdup(imagename); @@ -77,6 +88,7 @@ public: return -1; } } + // Close and destroy the reader ~BMPReader() { if (pIsFile && pFile) { fclose(pFile); @@ -84,6 +96,7 @@ public: if (pName) ::free(pName); } + // Read a single byte form memory or a file uchar read_byte() { if (pIsFile) { return getc(pFile); @@ -93,7 +106,7 @@ public: return 0; } } - // 'read_word()' - Read a 16-bit unsigned integer. + // Read a 16-bit unsigned integer, LSB-first unsigned short read_word() { unsigned char b0, b1; // Bytes from file if (pIsFile) { @@ -108,7 +121,7 @@ public: return 0; } } - // 'read_dword()' - Read a 32-bit unsigned integer. + // Read a 32-bit unsigned integer, LSB-first unsigned int read_dword() { unsigned char b0, b1, b2, b3; // Bytes from file if (pIsFile) { @@ -127,10 +140,12 @@ public: return 0; } } - // 'read_long()' - Read a 32-bit signed integer. + // Read a 32-bit signed integer, LSB-first int read_long() { return (int)read_dword(); }; + // Move the current read position to a byte offset fro the beginning of the + // file or the original start address in memory void seek(unsigned int n) { if (pIsFile) { fseek(pFile, n , SEEK_SET); @@ -138,19 +153,26 @@ public: pData = pStart + n; } } + // return the name or filename for this reader const char *name() { return pName; } private: + // open() sets this if we read form a file char pIsFile; + // open() sets this if we read form memory char pIsData; + // a pointer to the opened file FILE *pFile; + // a pointer to the current byte in memory const unsigned char *pData; + // a pointer to the start of the image data const unsigned char *pStart; + // a copy of the name associated with this reader char *pName; }; /** - The constructor loads the named BMP image from the given bmp filename. + \brief The 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. @@ -159,39 +181,58 @@ private: 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. + + \see Fl_BMP_Image::Fl_BMP_Image(const char *imagename, const unsigned char *data) */ Fl_BMP_Image::Fl_BMP_Image(const char *filename) // I - File to read : Fl_RGB_Image(0,0,0) { BMPReader f; if (f.open(filename)==-1) { - ld(ERR_FORMAT); + ld(ERR_FILE_ACCESS); } else { - read(f); + load_bmp_(f); } } /** - The constructor loads the named BMP image from the given memory address. + \brief Read 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. + 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. - \param[in] imagename the name of the bitmap - \param[in] data a pointer to the BMP data in memory. There is no checking for buffer overruns + 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] 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. \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_RGB_Image(0,0,0) { BMPReader d; if (d.open(imagename, data)==-1) { - ld(ERR_FORMAT); + ld(ERR_FILE_ACCESS); } else { - read(d); + load_bmp_(d); } } - -void Fl_BMP_Image::read(class BMPReader &rdr) +/* + 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 + a BMPReader that reads data from either a file or from memory. + */ +void Fl_BMP_Image::load_bmp_(class BMPReader &rdr) { int info_size, // Size of info header depth, // Depth of image (bits) diff --git a/src/Fl_GIF_Image.cxx b/src/Fl_GIF_Image.cxx index 91b8c13a5..4054eac77 100644 --- a/src/Fl_GIF_Image.cxx +++ b/src/Fl_GIF_Image.cxx @@ -81,35 +81,47 @@ typedef unsigned char uchar; // Local reader class... // +/* + This class reads data chunks from a file or from memory in LSB-first + byte order. + + TODO: GIFReader and BMPReader are very similar and should be combined to avoid + code duplication. + */ class GIFReader { public: + // Create the reader. GIFReader() : pIsFile(0), pIsData(0), - pFile(0L), pData(0L), pStart(0L), + pFile(0L), pData(0L), pName(0L) { } + // Initialize the reader to access the file system, filename is copied + // and stored. int open(const char *filename) { - if (filename) - pName = strdup(filename); - if ((pFile = fl_fopen(filename, "rb")) == NULL) { + if (!filename) + return -1; + pName = strdup(filename); + if ( (pFile = fl_fopen(filename, "rb")) == NULL ) { return -1; - } else { - pIsFile = 1; - return 0; } + pIsFile = 1; + return 0; } + // Initialize the reader for memory access, name is copied and stored int open(const char *imagename, const unsigned char *data) { if (imagename) pName = strdup(imagename); if (data) { - pStart = pData = data; + pData = data; pIsData = 1; return 0; } else { return -1; } } + // Close and destroy the reader ~GIFReader() { if (pIsFile && pFile) { fclose(pFile); @@ -117,6 +129,7 @@ public: if (pName) ::free(pName); } + // Read a single byte form memory or a file uchar read_byte() { if (pIsFile) { return getc(pFile); @@ -126,7 +139,7 @@ public: return 0; } } - // 'read_word()' - Read a 16-bit unsigned integer. + // Read a 16-bit unsigned integer, LSB-first unsigned short read_word() { unsigned char b0, b1; // Bytes from file if (pIsFile) { @@ -141,19 +154,27 @@ public: return 0; } } + // return the name or filename for this reader const char *name() { return pName; } private: + // open() sets this if we read form a file char pIsFile; + // open() sets this if we read form memory char pIsData; + // a pointer to the opened file FILE *pFile; + // a pointer to the current byte in memory const unsigned char *pData; - const unsigned char *pStart; + // a copy of the name associated with this reader char *pName; }; /** - The constructor loads the named GIF image. + \brief The constructor loads the named GIF image. + + IF a GIF is animated, Fl_GIF_Image will only read and display the first frame + of the animation. The destructor frees all memory and server resources that are used by the image. @@ -162,6 +183,10 @@ private: ERR_FILE_ACCESS if the file could not be opened or read, ERR_FORMAT if the GIF 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 GIF file. + + \see Fl_GIF_Image::Fl_GIF_Image(const char *imagename, const unsigned char *data) */ Fl_GIF_Image::Fl_GIF_Image(const char *filename) : Fl_Pixmap((char *const*)0) @@ -171,32 +196,50 @@ Fl_GIF_Image::Fl_GIF_Image(const char *filename) : Fl::error("Fl_GIF_Image: Unable to open %s!", filename); ld(ERR_FILE_ACCESS); } else { - read(f); + load_gif_(f); } } /** - The constructor loads the named GIF image. + \brief The constructor loads a GIF 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. + 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. - \param[in] imagename the name of the GIF image - \param[in] data a pointer to the GIF data in memory. There is no checking for buffer overruns + IF a GIF is animated, Fl_GIF_Image will only read and display the first frame + of the animation. + + Use Fl_Image::fail() to check if Fl_GIF_Image failed to load. fail() returns + ERR_FILE_ACCESS if the file could not be opened or read, ERR_FORMAT if the + GIF 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 GIF image in memory. This code will not check for buffer overruns. \see Fl_GIF_Image::Fl_GIF_Image(const char *filename) + \see Fl_Shared_Image */ Fl_GIF_Image::Fl_GIF_Image(const char *imagename, const unsigned char *data) : Fl_Pixmap((char *const*)0) { GIFReader d; if (d.open(imagename, data)==-1) { - ld(ERR_FORMAT); + ld(ERR_FILE_ACCESS); } else { - read(d); + load_gif_(d); } } - -void Fl_GIF_Image::read(GIFReader &rdr) +/* + This method reads GIF image data and creates an RGB or RGBA image. The GIF + format supports only 1 bit for alpha. To avoid code duplication, we use + a GIFReader that reads data from either a file or from memory. + */ +void Fl_GIF_Image::load_gif_(GIFReader &rdr) { char **new_data; // Data array diff --git a/src/Fl_JPEG_Image.cxx b/src/Fl_JPEG_Image.cxx index 6dcf030b5..42e76d694 100644 --- a/src/Fl_JPEG_Image.cxx +++ b/src/Fl_JPEG_Image.cxx @@ -95,107 +95,42 @@ extern "C" { w(), h(), and d() should return values greater than zero. \param[in] filename a full path and name pointing to a valid jpeg file. + + \see Fl_JPEG_Image::Fl_JPEG_Image(const char *imagename, const unsigned char *data) */ -Fl_JPEG_Image::Fl_JPEG_Image(const char *filename) // I - File to load -: Fl_RGB_Image(0,0,0) { -#ifdef HAVE_LIBJPEG - FILE *fp; // File pointer - jpeg_decompress_struct dinfo; // Decompressor info - fl_jpeg_error_mgr jerr; // Error handler info - JSAMPROW row; // Sample row pointer - - // the following variables are pointers allocating some private space that - // is not reset by 'setjmp()' - char* max_finish_decompress_err; // count errors and give up afer a while - char* max_destroy_decompress_err; // to avoid recusion and deadlock - - // Clear data... - alloc_array = 0; - array = (uchar *)0; - - // Open the image file... - if ((fp = fl_fopen(filename, "rb")) == NULL) { - ld(ERR_FILE_ACCESS); - return; - } - - // Setup the decompressor info and read the header... - dinfo.err = jpeg_std_error((jpeg_error_mgr *)&jerr); - jerr.pub_.error_exit = fl_jpeg_error_handler; - jerr.pub_.output_message = fl_jpeg_output_handler; - - // Setup error loop variables - max_finish_decompress_err = (char*)malloc(1); // allocate space on the frame for error counters - max_destroy_decompress_err = (char*)malloc(1); // otherwise, the variables are reset on the longjmp - *max_finish_decompress_err=10; - *max_destroy_decompress_err=10; - - if (setjmp(jerr.errhand_)) - { - // JPEG error handling... - Fl::warning("JPEG file \"%s\" is too large or contains errors!\n", filename); - // if any of the cleanup routines hits another error, we would end up - // in a loop. So instead, we decrement max_err for some upper cleanup limit. - if ( ((*max_finish_decompress_err)-- > 0) && array) - jpeg_finish_decompress(&dinfo); - if ( (*max_destroy_decompress_err)-- > 0) - jpeg_destroy_decompress(&dinfo); - - fclose(fp); - - w(0); - h(0); - d(0); - - if (array) { - delete[] (uchar *)array; - array = 0; - alloc_array = 0; - } - - free(max_destroy_decompress_err); - free(max_finish_decompress_err); - - ld(ERR_FORMAT); - return; - } - - jpeg_create_decompress(&dinfo); - jpeg_stdio_src(&dinfo, fp); - jpeg_read_header(&dinfo, TRUE); - - dinfo.quantize_colors = (boolean)FALSE; - dinfo.out_color_space = JCS_RGB; - dinfo.out_color_components = 3; - dinfo.output_components = 3; - - jpeg_calc_output_dimensions(&dinfo); - - w(dinfo.output_width); - h(dinfo.output_height); - d(dinfo.output_components); - - if (((size_t)w()) * h() * d() > max_size() ) longjmp(jerr.errhand_, 1); - array = new uchar[w() * h() * d()]; - alloc_array = 1; - - jpeg_start_decompress(&dinfo); - - while (dinfo.output_scanline < dinfo.output_height) { - row = (JSAMPROW)(array + - dinfo.output_scanline * dinfo.output_width * - dinfo.output_components); - jpeg_read_scanlines(&dinfo, &row, (JDIMENSION)1); - } - - jpeg_finish_decompress(&dinfo); - jpeg_destroy_decompress(&dinfo); - - free(max_destroy_decompress_err); - free(max_finish_decompress_err); - - fclose(fp); -#endif // HAVE_LIBJPEG +Fl_JPEG_Image::Fl_JPEG_Image(const char *filename) +: Fl_RGB_Image(0,0,0) +{ + load_jpg_(filename, 0L, 0L); +} + +/** + \brief The constructor loads the JPEG 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. + name_png can be NULL. If a name is given, the image is added to the list of + shared images (see: Fl_Shared_Image) and will be available by that name. + + The inherited destructor frees all memory and server resources that are used + by the image. + + Use Fl_Image::fail() to check if Fl_JPEG_Image failed to load. fail() returns + ERR_FILE_ACCESS if the file could not be opened or read, ERR_FORMAT if the + JPEG format could not be decoded, and ERR_NO_IMAGE if the image could not + be loaded for another reason. If the image has loaded correctly, + w(), h(), and d() should return values greater than zero. + + \param name A unique name or NULL + \param data A pointer to the memory location of the JPEG image + + \see Fl_JPEG_Image::Fl_JPEG_Image(const char *filename) + \see Fl_Shared_Image + */ +Fl_JPEG_Image::Fl_JPEG_Image(const char *name, const unsigned char *data) +: Fl_RGB_Image(0,0,0) +{ + load_jpg_(0L, name, data); } @@ -270,121 +205,136 @@ static void jpeg_mem_src(j_decompress_ptr cinfo, const unsigned char *data) #endif // HAVE_LIBJPEG -/** - \brief The constructor loads the JPEG 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. - name_png can be NULL. If a name is given, the image is added to the list of - shared images (see: Fl_Shared_Image) and will be available by that name. - - The inherited destructor frees all memory and server resources that are used - by the image. - - Use Fl_Image::fail() to check if Fl_JPEG_Image failed to load. fail() returns - ERR_FILE_ACCESS if the file could not be opened or read, ERR_FORMAT if the - JPEG format could not be decoded, and ERR_NO_IMAGE if the image could not - be loaded for another reason. If the image has loaded correctly, - w(), h(), and d() should return values greater than zero. - - \param name A unique name or NULL - \param data A pointer to the memory location of the JPEG image +/* + This method reads JPEG image data and creates an RGB or grayscale image. + To avoid code duplication, we set filename if we want to read form a file or + data to read from memory instead. Sharename can be set if the image is + supposed to be added to teh Fl_Shared_Image list. */ -Fl_JPEG_Image::Fl_JPEG_Image(const char *name, const unsigned char *data) -: Fl_RGB_Image(0,0,0) { +void Fl_JPEG_Image::load_jpg_(const char *filename, const char *sharename, const unsigned char *data) +{ #ifdef HAVE_LIBJPEG - jpeg_decompress_struct dinfo; // Decompressor info - fl_jpeg_error_mgr jerr; // Error handler info - JSAMPROW row; // Sample row pointer - + FILE *fp = 0L; // File pointer + jpeg_decompress_struct dinfo; // Decompressor info + fl_jpeg_error_mgr jerr; // Error handler info + JSAMPROW row; // Sample row pointer + // the following variables are pointers allocating some private space that // is not reset by 'setjmp()' char* max_finish_decompress_err; // count errors and give up afer a while char* max_destroy_decompress_err; // to avoid recusion and deadlock - + // Clear data... alloc_array = 0; array = (uchar *)0; - + + // Open the image file if we read from the file system + if (filename) { + if ((fp = fl_fopen(filename, "rb")) == NULL) { + ld(ERR_FILE_ACCESS); + return; + } + } else { + if (data==0L) { + ld(ERR_FILE_ACCESS); + return; + } + } + // Setup the decompressor info and read the header... dinfo.err = jpeg_std_error((jpeg_error_mgr *)&jerr); jerr.pub_.error_exit = fl_jpeg_error_handler; jerr.pub_.output_message = fl_jpeg_output_handler; - + // Setup error loop variables max_finish_decompress_err = (char*)malloc(1); // allocate space on the frame for error counters max_destroy_decompress_err = (char*)malloc(1); // otherwise, the variables are reset on the longjmp *max_finish_decompress_err=10; *max_destroy_decompress_err=10; - + if (setjmp(jerr.errhand_)) { // JPEG error handling... - Fl::warning("JPEG data is too large or contains errors!\n"); - // if any of the cleanup routines hits another error, we would end up + const char *name = "<unnamed>"; + if (filename) name = filename; + else if (sharename) name = sharename; + Fl::warning("JPEG file \"%s\" is too large or contains errors!\n", name); + // if any of the cleanup routines hits another error, we would end up // in a loop. So instead, we decrement max_err for some upper cleanup limit. if ( ((*max_finish_decompress_err)-- > 0) && array) jpeg_finish_decompress(&dinfo); if ( (*max_destroy_decompress_err)-- > 0) jpeg_destroy_decompress(&dinfo); - + + if (fp) + fclose(fp); + w(0); h(0); d(0); - + if (array) { delete[] (uchar *)array; array = 0; alloc_array = 0; } - + free(max_destroy_decompress_err); free(max_finish_decompress_err); - + + ld(ERR_FORMAT); return; } - + jpeg_create_decompress(&dinfo); - jpeg_mem_src(&dinfo, data); + if (fp) { + jpeg_stdio_src(&dinfo, fp); + } else { + jpeg_mem_src(&dinfo, data); + } jpeg_read_header(&dinfo, TRUE); - + dinfo.quantize_colors = (boolean)FALSE; dinfo.out_color_space = JCS_RGB; dinfo.out_color_components = 3; dinfo.output_components = 3; - + jpeg_calc_output_dimensions(&dinfo); - - w(dinfo.output_width); + + w(dinfo.output_width); h(dinfo.output_height); d(dinfo.output_components); - + if (((size_t)w()) * h() * d() > max_size() ) longjmp(jerr.errhand_, 1); array = new uchar[w() * h() * d()]; alloc_array = 1; - + jpeg_start_decompress(&dinfo); - + while (dinfo.output_scanline < dinfo.output_height) { row = (JSAMPROW)(array + dinfo.output_scanline * dinfo.output_width * dinfo.output_components); jpeg_read_scanlines(&dinfo, &row, (JDIMENSION)1); } - + jpeg_finish_decompress(&dinfo); jpeg_destroy_decompress(&dinfo); - + free(max_destroy_decompress_err); free(max_finish_decompress_err); - if (w() && h() && name) { - Fl_Shared_Image *si = new Fl_Shared_Image(name, this); + if (fp) + fclose(fp); + + if (sharename && w() && h()) { + Fl_Shared_Image *si = new Fl_Shared_Image(sharename, this); si->add(); } #endif // HAVE_LIBJPEG } + // // End of "$Id$". // diff --git a/src/Fl_PNM_Image.cxx b/src/Fl_PNM_Image.cxx index 76fcac89c..94a7ea8fd 100644 --- a/src/Fl_PNM_Image.cxx +++ b/src/Fl_PNM_Image.cxx @@ -48,8 +48,10 @@ ERR_FILE_ACCESS if the file could not be opened or read, ERR_FORMAT if the PNM 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 jpeg file. */ -Fl_PNM_Image::Fl_PNM_Image(const char *name) // I - File to read +Fl_PNM_Image::Fl_PNM_Image(const char *filename) // I - File to read : Fl_RGB_Image(0,0,0) { FILE *fp; // File pointer int x, y; // Looping vars @@ -63,7 +65,7 @@ Fl_PNM_Image::Fl_PNM_Image(const char *name) // I - File to read maxval; // Maximum pixel value - if ((fp = fl_fopen(name, "rb")) == NULL) { + if ((fp = fl_fopen(filename, "rb")) == NULL) { ld(ERR_FILE_ACCESS); return; } @@ -84,7 +86,7 @@ Fl_PNM_Image::Fl_PNM_Image(const char *name) // I - File to read lineptr = fgets(line, sizeof(line), fp); if (!lineptr) { fclose(fp); - Fl::error("Early end-of-file in PNM file \"%s\"!", name); + Fl::error("Early end-of-file in PNM file \"%s\"!", filename); ld(ERR_FILE_ACCESS); return; } @@ -128,10 +130,10 @@ Fl_PNM_Image::Fl_PNM_Image(const char *name) // I - File to read if (format == 1 || format == 2 || format == 4 || format == 5) d(1); else d(3); -// printf("%s = %dx%dx%d\n", name, w(), h(), d()); +// printf("%s = %dx%dx%d\n", filename, w(), h(), d()); if (((size_t)w()) * h() * d() > max_size() ) { - Fl::warning("PNM file \"%s\" is too large!\n", name); + Fl::warning("PNM file \"%s\" is too large!\n", filename); fclose(fp); w(0); h(0); d(0); ld(ERR_FORMAT); return; |
