Updated image readers.

Removed duplicate code from Fl_JPEG_Image, updated and unified Doxygen comments. Avoiding name conflict in new protected functions.
This commit is contained in:
Matthias Melcher 2020-01-10 18:46:45 +01:00
parent 82d279c234
commit 7e0c82637d
7 changed files with 234 additions and 193 deletions

View File

@ -36,7 +36,7 @@ class FL_EXPORT Fl_BMP_Image : public Fl_RGB_Image {
protected:
void read(class BMPReader &rdr);
void load_bmp_(class BMPReader &rdr);
};

View File

@ -37,7 +37,7 @@ public:
protected:
void read(class GIFReader &rdr);
void load_gif_(class GIFReader &rdr);
};

View File

@ -35,6 +35,11 @@ public:
Fl_JPEG_Image(const char *filename);
Fl_JPEG_Image(const char *name, const unsigned char *data);
protected:
void load_jpg_(const char *filename, const char *sharename, const unsigned char *data);
};
#endif

View File

@ -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.
\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
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.
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)

View File

@ -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.
\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
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.
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

View File

@ -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$".
//

View File

@ -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;