diff options
| author | ManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com> | 2022-11-28 17:26:37 +0100 |
|---|---|---|
| committer | ManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com> | 2022-12-09 10:43:20 +0100 |
| commit | 36cd0a397c6d8fee7ffc5b59d607ead523226e53 (patch) | |
| tree | 91f0bd3628ae3d7b7553706b24b66b1bb4620c6b /src/Fl_ICO_Image.cxx | |
| parent | 0bb30d8f92839af826fb389fccd625c74e981a13 (diff) | |
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).
Diffstat (limited to 'src/Fl_ICO_Image.cxx')
| -rw-r--r-- | src/Fl_ICO_Image.cxx | 202 |
1 files changed, 202 insertions, 0 deletions
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 <FL/Fl.H> +#include "config.h" +#include "Fl_Image_Reader.h" +#include <FL/Fl_ICO_Image.H> +#if defined(HAVE_LIBPNG) && defined(HAVE_LIBZ) +# include <FL/Fl_PNG_Image.H> +#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()); +} |
