From 36cd0a397c6d8fee7ffc5b59d607ead523226e53 Mon Sep 17 00:00:00 2001 From: ManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com> Date: Mon, 28 Nov 2022 17:26:37 +0100 Subject: New Fl_ICO_Image class to read Windows .ico icon files Many thanks to @darealshinji for contributing all the code for this new FLTK image class (see branch Fl_ICO_Image of https://github.com/darealshinji/fltk). --- CHANGES.txt | 1 + FL/Fl_BMP_Image.H | 4 +- FL/Fl_ICO_Image.H | 55 +++++++++++++ FL/Fl_PNG_Image.H | 7 +- src/CMakeLists.txt | 1 + src/Fl_BMP_Image.cxx | 48 +++++++----- src/Fl_ICO_Image.cxx | 202 ++++++++++++++++++++++++++++++++++++++++++++++++ src/Fl_PNG_Image.cxx | 20 ++++- src/Makefile | 1 + src/fl_images_core.cxx | 6 +- test/pixmap_browser.cxx | 4 +- 11 files changed, 318 insertions(+), 31 deletions(-) create mode 100644 FL/Fl_ICO_Image.H create mode 100644 src/Fl_ICO_Image.cxx diff --git a/CHANGES.txt b/CHANGES.txt index 1a18436d3..a10439bc1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -45,6 +45,7 @@ Changes in FLTK 1.4.0 Released: ??? ?? 2022 and Fl_Image::data_h() give the width and height of the image data. - New Fl_SVG_Image class: gives support of scalable vector graphics images to FLTK using the nanosvg software. + - New Fl_ICO_Image class to read Windows .ico icon files. - New classes Fl_SVG_File_Surface and Fl_EPS_File_Surface to save any FLTK graphics to SVG or EPS files, respectively. - Windows platform: added support for using a manifest to set the diff --git a/FL/Fl_BMP_Image.H b/FL/Fl_BMP_Image.H index 63dd301df..462d61957 100644 --- a/FL/Fl_BMP_Image.H +++ b/FL/Fl_BMP_Image.H @@ -1,7 +1,7 @@ // // BMP image header file for the Fast Light Tool Kit (FLTK). // -// Copyright 1998-2021 by Bill Spitzak and others. +// Copyright 1998-2022 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 @@ -34,7 +34,7 @@ public: protected: - void load_bmp_(class Fl_Image_Reader &rdr); + void load_bmp_(class Fl_Image_Reader &rdr, int ico_height = 0, int ico_width = 0); }; diff --git a/FL/Fl_ICO_Image.H b/FL/Fl_ICO_Image.H new file mode 100644 index 000000000..9693cc545 --- /dev/null +++ b/FL/Fl_ICO_Image.H @@ -0,0 +1,55 @@ +// +// ICO image header file for the Fast Light Tool Kit (FLTK). +// +// Copyright 2022 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 +// file is missing or damaged, see the license at: +// +// http://www.fltk.org/COPYING.php +// +// Please report all bugs and problems on the following page: +// +// http://www.fltk.org/str.php +// + +// https://en.wikipedia.org/wiki/ICO_(file_format) +// http://www.daubnet.com/en/file-format-ico + +#ifndef Fl_ICO_Image_H +# define Fl_ICO_Image_H +# include "Fl_BMP_Image.H" + +/** + The Fl_ICO_Image class supports loading, caching, and drawing of Windows icon (.ico) files. + */ +class FL_EXPORT Fl_ICO_Image : public Fl_BMP_Image { + +public: + /** Windows ICONDIRENTRY structure */ + struct IconDirEntry { + int bWidth; ///< Image width + int bHeight; ///< Image height + int bColorCount; ///< Number of colors (0 if ≥ 8bpp) + int bReserved; ///< Reserved + int wPlanes; ///< Color Planes + int wBitCount; ///< Bits per pixel + int dwBytesInRes; ///< Resource size in bytes + int dwImageOffset; ///< Offset to the image + }; + + Fl_ICO_Image(const char *filename, int id = -1, const unsigned char *data = NULL, const size_t datasize = 0); + ~Fl_ICO_Image(); +/** Gives the number of icons of various resolutions present in the ICO object */ + int idcount() { return idcount_; } + /** Returns the array of idcount() loaded IconDirEntry structures */ + IconDirEntry * const icondirentry() const { return icondirentry_; } + +private: + int idcount_; + struct IconDirEntry *icondirentry_; + void load_ico_(class Fl_Image_Reader &rdr, int id); +}; + +#endif // Fl_ICO_Image_H diff --git a/FL/Fl_PNG_Image.H b/FL/Fl_PNG_Image.H index f0910a515..1b5e32179 100644 --- a/FL/Fl_PNG_Image.H +++ b/FL/Fl_PNG_Image.H @@ -1,7 +1,7 @@ // // PNG image header file for the Fast Light Tool Kit (FLTK). // -// Copyright 1998-2021 by Bill Spitzak and others. +// Copyright 1998-2022 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 @@ -28,13 +28,14 @@ and alpha-based transparency. */ class FL_EXPORT Fl_PNG_Image : public Fl_RGB_Image { - + friend class Fl_ICO_Image; public: Fl_PNG_Image(const char* filename); Fl_PNG_Image (const char *name_png, const unsigned char *buffer, int datasize); private: - void load_png_(const char *name_png, const unsigned char *buffer_png, int datasize); + Fl_PNG_Image(const char *filename, int offset); // used by Fl_ICO_Image + void load_png_(const char *name_png, int offset, const unsigned char *buffer_png, int datasize); }; // Support functions to write PNG image files (since 1.4.0) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fb71a49b3..49825d6be 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -465,6 +465,7 @@ set (IMGCPPFILES Fl_File_Icon2.cxx Fl_GIF_Image.cxx Fl_Help_Dialog.cxx + Fl_ICO_Image.cxx Fl_JPEG_Image.cxx Fl_PNG_Image.cxx Fl_PNM_Image.cxx diff --git a/src/Fl_BMP_Image.cxx b/src/Fl_BMP_Image.cxx index de444f99b..7610652cb 100644 --- a/src/Fl_BMP_Image.cxx +++ b/src/Fl_BMP_Image.cxx @@ -1,7 +1,7 @@ // // Fl_BMP_Image class for the Fast Light Tool Kit (FLTK). // -// Copyright 2011-2021 by Bill Spitzak and others. +// Copyright 2011-2022 by Bill Spitzak and others. // Copyright 1997-2010 by Easy Software Products. // Image support by Matthias Melcher, Copyright 2000-2009. // @@ -129,7 +129,7 @@ Fl_BMP_Image::Fl_BMP_Image(const char *imagename, const unsigned char *data, con 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) +void Fl_BMP_Image::load_bmp_(Fl_Image_Reader &rdr, int ico_height, int ico_width) { int info_size, // Size of info header width, // Width of image (pixels) @@ -147,7 +147,7 @@ void Fl_BMP_Image::load_bmp_(Fl_Image_Reader &rdr) row_order, // 1 = normal; -1 = flipped row order start_y, // Beginning Y end_y; // Ending Y - long offbits; // Offset to image data + long offbits = 0; // Offset to image data uchar bit, // Bit in image byte; // Byte in image uchar *ptr; // Pointer into pixels @@ -163,17 +163,19 @@ void Fl_BMP_Image::load_bmp_(Fl_Image_Reader &rdr) 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 - bit = rdr.read_byte(); - if (byte != 'B' || bit != 'M') { - ld(ERR_FORMAT); - return; - } + if (ico_height < 1) { + byte = rdr.read_byte(); // Check "BM" sync chars + bit = rdr.read_byte(); + if (byte != 'B' || bit != 'M') { + ld(ERR_FORMAT); + return; + } - rdr.read_dword(); // Skip size - rdr.read_word(); // Skip reserved stuff - rdr.read_word(); - offbits = (long)rdr.read_dword();// Read offset to image data + rdr.read_dword(); // Skip size + rdr.read_word(); // Skip reserved stuff + rdr.read_word(); + offbits = (long)rdr.read_dword();// Read offset to image data + } // Then the bitmap information... info_size = rdr.read_dword(); @@ -196,12 +198,20 @@ void Fl_BMP_Image::load_bmp_(Fl_Image_Reader &rdr) repcount = info_size - 12; } else { - // New BMP header... - width = rdr.read_long(); - // If the height is negative, the row order is flipped - temp = rdr.read_long(); - if (temp < 0) row_order = 1; - height = abs(temp); + if (ico_height > 0 && ico_width > 0) { + rdr.read_long(); + rdr.read_long(); + width = ico_width; + height = ico_height; + } else { + // New BMP header... + w(rdr.read_long()); + // If the height is negative, the row order is flipped + temp = rdr.read_long(); + if (temp < 0) row_order = 1; + height = abs(temp); + } + rdr.read_word(); depth = rdr.read_word(); compression = rdr.read_dword(); diff --git a/src/Fl_ICO_Image.cxx b/src/Fl_ICO_Image.cxx new file mode 100644 index 000000000..9de71737b --- /dev/null +++ b/src/Fl_ICO_Image.cxx @@ -0,0 +1,202 @@ +// +// Fl_ICO_Image class for the Fast Light Tool Kit (FLTK). +// +// Copyright 2022 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 +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please report all bugs and problems on the following page: +// +// https://www.fltk.org/str.php +// + + +// +// Include necessary header files... +// + +#include +#include "config.h" +#include "Fl_Image_Reader.h" +#include +#if defined(HAVE_LIBPNG) && defined(HAVE_LIBZ) +# include +#endif + +/** + Loads the named icon image from the given .ico filename or from memory + \param filename Name of a .ico file, or of the in-memory image + \param id When id is -1 (default), the highest-resolution icon is loaded; + when id ≥ 0, load the icon with this ID; + when id = -2, load all IconDirEntry structures but no image. + \param data NULL, or in-memory icon data + \param datasize Size in bytes of the \p data byte array (used when \p data is not NULL) +*/ +Fl_ICO_Image::Fl_ICO_Image(const char *filename, int id, const unsigned char *data, const size_t datasize) +: Fl_BMP_Image(0,0), + idcount_(0), + icondirentry_(0) +{ + Fl_Image_Reader rdr; + int r; + + w(0); h(0); d(0); ld(0); + alloc_array = 0; + array = 0; + + if (data) { + r = rdr.open(filename, data, datasize); + } else { + r = rdr.open(filename); + } + + if (r == -1) { + ld(ERR_FILE_ACCESS); + } else { + load_ico_(rdr, id); + } +} + +/** Destructor */ +Fl_ICO_Image::~Fl_ICO_Image() { + delete[] icondirentry_; +} + + +/* + This method attempts to load the biggest image resource available + inside a .ICO file (Windows Icon format). + */ +void Fl_ICO_Image::load_ico_(Fl_Image_Reader &rdr, int id) +{ + int pickedID = -1; + + // Check file header (ICONDIR, 6 bytes) + + if (rdr.read_word() != 0 || rdr.read_word() != 1) { + Fl::error("Fl_ICO_Image: %s is not an ICO file.\n", rdr.name()); + ld(ERR_FORMAT); + return; + } + + idcount_ = rdr.read_word(); + + if (idcount() == 0) { + Fl::error("Fl_ICO_Image: %s - no image resources found\n", rdr.name()); + ld(ERR_FORMAT); + return; + } + + + // read entries (IconDirEntry, 16 bytes each) + + icondirentry_ = new IconDirEntry[idcount()]; + + for (int i = 0; i < idcount(); ++i) { + icondirentry_[i].bWidth = (int)rdr.read_byte(); + icondirentry_[i].bHeight = (int)rdr.read_byte(); + icondirentry_[i].bColorCount = (int)rdr.read_byte(); + icondirentry_[i].bReserved = (int)rdr.read_byte(); + icondirentry_[i].wPlanes = (int)rdr.read_word(); + icondirentry_[i].wBitCount = (int)rdr.read_word(); + icondirentry_[i].dwBytesInRes = (int)rdr.read_dword(); + icondirentry_[i].dwImageOffset = (int)rdr.read_dword(); + + if (icondirentry_[i].bWidth == 0) icondirentry_[i].bWidth = 256; + if (icondirentry_[i].bHeight == 0) icondirentry_[i].bHeight = 256; + } + + if (id <= -2) return; + + if (!icondirentry_ || idcount() < 1 || id >= idcount()) { + ld(ERR_FORMAT); + return; + } + + if (id == -1) { + // pick icon with highest resolution + highest bitcount + int highestRes = 0, bitcount = 0; + for (int i = 0; i < idcount(); ++i) { + int res = icondirentry_[i].bWidth * icondirentry_[i].bHeight; + if (res > highestRes || (res == highestRes && icondirentry_[i].wBitCount > bitcount)) { + highestRes = res; + bitcount = icondirentry_[i].wBitCount; + pickedID = i; + } + } + } else { + pickedID = id; + } + + if (pickedID < 0 || + icondirentry_[pickedID].bWidth <= 0 || + icondirentry_[pickedID].bHeight <= 0 || + icondirentry_[pickedID].dwImageOffset <= 0|| + icondirentry_[pickedID].dwBytesInRes <= 0) + { + ld(ERR_FORMAT); + return; + } + + rdr.seek(icondirentry_[pickedID].dwImageOffset); + + + // Check for a PNG image resource + uchar b[8]; + for (int i=0; i<8; ++i) b[i] = rdr.read_byte(); + + if (b[0]==0x89 && b[1]=='P' && b[2]=='N' && b[3]=='G' && + b[4]=='\r' && b[5]=='\n' && b[6]==0x1A && b[7]=='\n') + { +#if defined(HAVE_LIBPNG) && defined(HAVE_LIBZ) + Fl_PNG_Image *png = new Fl_PNG_Image(rdr.name(), icondirentry_[pickedID].dwImageOffset); + + int loaded = png ? png->fail() : ERR_FILE_ACCESS; + if (loaded < 0) { + w(0); h(0); d(0); + ld(loaded); + if (png) delete png; + return; + } + + w(png->w()); + h(png->h()); + d(png->d()); + + // take over pointer of Fl_PNG_Image's array + array = png->array; + alloc_array = 1; + png->array = NULL; + png->alloc_array = 0; + + delete png; + return; +#else + Fl::error("Fl_ICO_Image: %s - cannot decode PNG resource (no libpng support)!\n", rdr.name()); + w(0); h(0); d(0); + ld(ERR_FORMAT); + return; +#endif + } + + + // Bitmap resource + + w(icondirentry_[pickedID].bWidth); + h(icondirentry_[pickedID].bHeight); + d(4); + + if (((size_t)w()) * h() * d() > max_size()) { + Fl::warning("ICO file \"%s\" is too large!\n", rdr.name()); + w(0); h(0); d(0); + ld(ERR_FORMAT); + return; + } + + rdr.seek(icondirentry_[pickedID].dwImageOffset); + load_bmp_(rdr, h(), w()); +} diff --git a/src/Fl_PNG_Image.cxx b/src/Fl_PNG_Image.cxx index e4584b188..6dbc75b15 100644 --- a/src/Fl_PNG_Image.cxx +++ b/src/Fl_PNG_Image.cxx @@ -4,7 +4,7 @@ // Copyright 1997-2012 by Easy Software Products. // Image support by Matthias Melcher, Copyright 2000-2009. // -// Copyright 2013-2021 by Bill Spitzak and others. +// Copyright 2013-2022 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 @@ -87,9 +87,15 @@ extern "C" { */ Fl_PNG_Image::Fl_PNG_Image (const char *filename): Fl_RGB_Image(0,0,0) { - load_png_(filename, NULL, 0); + load_png_(filename, 0, NULL, 0); } +// private c'tor used by Fl_ICO_Image +// \param offset Offset to seek for the begin of PNG data inside a .ICO file +Fl_PNG_Image::Fl_PNG_Image (const char *filename, int offset): Fl_RGB_Image(0,0,0) +{ + load_png_(filename, offset, NULL, 0); +} /** \brief Constructor that reads a PNG image from memory. @@ -106,11 +112,11 @@ Fl_PNG_Image::Fl_PNG_Image (const char *filename): Fl_RGB_Image(0,0,0) Fl_PNG_Image::Fl_PNG_Image ( const char *name_png, const unsigned char *buffer, int maxsize): Fl_RGB_Image(0,0,0) { - load_png_(name_png, buffer, maxsize); + load_png_(name_png, 0, buffer, maxsize); } -void Fl_PNG_Image::load_png_(const char *name_png, const unsigned char *buffer_png, int maxsize) +void Fl_PNG_Image::load_png_(const char *name_png, int offset, const unsigned char *buffer_png, int maxsize) { #if defined(HAVE_LIBPNG) && defined(HAVE_LIBZ) int i; // Looping var @@ -134,6 +140,12 @@ void Fl_PNG_Image::load_png_(const char *name_png, const unsigned char *buffer_p delete fp; return; } + if (offset > 0 && fseek(*fp, (long)offset, SEEK_SET) == -1) { + fclose(*fp); + ld(ERR_FORMAT); + delete fp; + return; + } } const char *display_name = (name_png ? name_png : "In-memory PNG data"); diff --git a/src/Makefile b/src/Makefile index a55173867..d8640f2b5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -228,6 +228,7 @@ IMGCPPFILES = \ Fl_File_Icon2.cxx \ Fl_GIF_Image.cxx \ Fl_Help_Dialog.cxx \ + Fl_ICO_Image.cxx \ Fl_JPEG_Image.cxx \ Fl_PNG_Image.cxx \ Fl_PNM_Image.cxx \ diff --git a/src/fl_images_core.cxx b/src/fl_images_core.cxx index 07224fa31..5c2a7e98b 100644 --- a/src/fl_images_core.cxx +++ b/src/fl_images_core.cxx @@ -2,7 +2,7 @@ // FLTK images library core. // // Copyright 1997-2010 by Easy Software Products. -// Copyright 2011-2021 by Bill Spitzak and others. +// Copyright 2011-2022 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 @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -95,6 +96,9 @@ fl_check_images(const char *name, // I - Filename if (memcmp(header, "BM", 2) == 0) // BMP file return new Fl_BMP_Image(name); + if (memcmp(header, "\0\0\1\0", 4) == 0 && header[5] == 0) // ICO file + return new Fl_ICO_Image(name); + // PNM if (header[0] == 'P' && header[1] >= '1' && header[1] <= '7') diff --git a/test/pixmap_browser.cxx b/test/pixmap_browser.cxx index 7f30c8abe..e9fc1ccf1 100644 --- a/test/pixmap_browser.cxx +++ b/test/pixmap_browser.cxx @@ -1,7 +1,7 @@ // // A shared image test program for the Fast Light Tool Kit (FLTK). // -// Copyright 1998-2015 by Bill Spitzak and others. +// Copyright 1998-2022 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 @@ -77,7 +77,7 @@ void file_cb(const char *n) { void button_cb(Fl_Widget *,void *) { fl_file_chooser_callback(file_cb); - const char *fname = fl_file_chooser("Image file?","*.{bm,bmp,gif,jpg,pbm,pgm,png,ppm,xbm,xpm" + const char *fname = fl_file_chooser("Image file?","*.{bm,bmp,gif,ico,jpg,pbm,pgm,png,ppm,xbm,xpm" #ifdef FLTK_USE_SVG ",svg" #ifdef HAVE_LIBZ -- cgit v1.2.3