#ifndef NANA_PAINT_DETAIL_IMAGE_ICO_HPP #define NANA_PAINT_DETAIL_IMAGE_ICO_HPP #include "image_pixbuf.hpp" #include #if defined(NANA_WINDOWS) # include #endif namespace nana { namespace paint { namespace detail { // These next two structs represent how the icon information is stored // in an ICO file. typedef struct { std::uint8_t bWidth; // Width of the image std::uint8_t bHeight; // Height of the image (times 2) std::uint8_t bColorCount; // Number of colors in image (0 if >=8bpp) std::uint8_t bReserved; // Reserved std::uint16_t wPlanes; // Color Planes std::uint16_t wBitCount; // Bits per pixel std::uint32_t dwBytesInRes; // how many bytes in this resource? std::uint32_t dwImageOffset; // where in the file is this image } ICONDIRENTRY, *LPICONDIRENTRY; typedef struct { std::uint16_t idReserved; // Reserved std::uint16_t idType; // resource type (1 for icons) std::uint16_t idCount; // how many images? //ICONDIRENTRY idEntries[1]; // the entries for each image } ICONDIR, *LPICONDIR; // size - 40 bytes typedef struct { std::uint32_t biSize; std::uint32_t biWidth; std::uint32_t biHeight; // Icon Height (added height of XOR-Bitmap and AND-Bitmap) std::uint16_t biPlanes; std::uint16_t biBitCount; std::uint32_t biCompression; std::int32_t biSizeImage; std::uint32_t biXPelsPerMeter; std::uint32_t biYPelsPerMeter; std::uint32_t biClrUsed; std::uint32_t biClrImportant; } s_BITMAPINFOHEADER, *s_PBITMAPINFOHEADER; // 46 bytes typedef struct { s_BITMAPINFOHEADER icHeader; // DIB header std::uint32_t icColors[1]; // Color table (short 4 bytes) //RGBQUAD std::uint8_t icXOR[1]; // DIB bits for XOR mask std::uint8_t icAND[1]; // DIB bits for AND mask } ICONIMAGE, *LPICONIMAGE; class image_ico : public basic_image_pixbuf { bool _m_read_ico(const void* data, std::size_t /*size*/) { auto width = 0; auto height = 0; auto buffer = (std::uint8_t *)data; auto icoDir = reinterpret_cast(buffer); int iconsCount = icoDir->idCount; if (icoDir->idReserved != 0 || icoDir->idType != 1 || iconsCount == 0 || iconsCount > 20) return false; auto cursor = buffer; cursor += 6; auto dirEntry = reinterpret_cast(cursor); auto maxSize = 0; auto offset = 0; auto maxBitCount = 0; for (auto i = 0; i < iconsCount; i++, ++dirEntry) { int w = dirEntry->bWidth; int h = dirEntry->bHeight; int bitCount = dirEntry->wBitCount; if (w * h > maxSize || bitCount > maxBitCount) // we choose icon with max resolution { width = w; height = h; offset = dirEntry->dwImageOffset; maxSize = w * h; maxBitCount = bitCount; } } if (offset == 0) return false; cursor = buffer; cursor += offset; auto icon = reinterpret_cast(cursor); auto realBitsCount = static_cast(icon->icHeader.biBitCount); auto hasAndMask = (realBitsCount < 32) && (height != static_cast(icon->icHeader.biHeight)); cursor += 40; pixbuf_.open(width, height); // rgba + vertical swap if (realBitsCount >= 32) { for (auto x = 0; x < width; ++x) for (auto y = 0; y < height; ++y) { pixbuf_.alpha_channel(true); auto shift2 = 4 * (x + (height - y - 1) * width); pixel_color_t image; image.element.red = cursor[shift2 + 2]; image.element.green = cursor[shift2 + 1]; image.element.blue = cursor[shift2]; image.element.alpha_channel = cursor[shift2 + 3]; pixbuf_.pixel(x, y, image); } } else if (realBitsCount == 24) { for (auto x = 0; x < width; x++) for (auto y = 0; y < height; y++) { pixbuf_.alpha_channel(true); auto shift2 = 3 * (x + (height - y - 1) * width); pixel_color_t image; image.element.red = cursor[shift2 + 2]; image.element.green = cursor[shift2 + 1]; image.element.blue = cursor[shift2]; image.element.alpha_channel = 255; pixbuf_.pixel(x, y, image); } } else if (realBitsCount == 8) /// 256 colors { // 256 color table auto colors = reinterpret_cast(cursor); cursor += 256 * 4; for (auto x = 0; x < width; x++) for (auto y = 0; y < height; y++) { pixbuf_.alpha_channel(true); auto shift2 = (x + (height - y - 1) * width); auto index = 4 * cursor[shift2]; pixel_color_t image; image.element.red = colors[index + 2]; image.element.green = colors[index + 1]; image.element.blue = colors[index]; image.element.alpha_channel = 255; pixbuf_.pixel(x, y, image); } } else if (realBitsCount == 4) /// 16 colors { // 16 color table auto colors = reinterpret_cast(cursor); cursor += 16 * 4; for (auto x = 0; x < width; x++) for (auto y = 0; y < height; y++) { auto shift2 = (x + (height - y - 1) * width); auto index = cursor[shift2 / 2]; if (shift2 % 2 == 0) index = (index >> 4) & 0xF; else index = index & 0xF; index *= 4; pixbuf_.alpha_channel(true); pixel_color_t image; image.element.red = colors[index + 2]; image.element.green = colors[index + 1]; image.element.blue = colors[index]; image.element.alpha_channel = 255; pixbuf_.pixel(x, y, image); } } else if (realBitsCount == 1) /// 2 colors { // 2 color table auto colors = reinterpret_cast(cursor); cursor += 2 * 4; auto boundary = width; //!!! 32 bit boundary (http://www.daubnet.com/en/file-format-ico) while (boundary % 32 != 0) boundary++; for (auto x = 0; x < width; x++) for (auto y = 0; y < height; y++) { auto shift2 = (x + (height - y - 1) * boundary); auto index = cursor[shift2 / 8]; // select 1 bit only unsigned char bit = 7 - (x % 8); index = (index >> bit) & 0x01; index *= 4; pixbuf_.alpha_channel(true); pixel_color_t image; image.element.red = colors[index + 2]; image.element.green = colors[index + 1]; image.element.blue = colors[index]; image.element.alpha_channel = 255; pixbuf_.pixel(x, y, image); } } // Read AND mask after base color data - 1 BIT MASK if (hasAndMask) { auto boundary = width * realBitsCount; //!!! 32 bit boundary (http://www.daubnet.com/en/file-format-ico) while (boundary % 32 != 0) boundary++; cursor += boundary * height / 8; boundary = width; while (boundary % 32 != 0) boundary++; for (auto y = 0; y < height; y++) for (auto x = 0; x < width; x++) { unsigned char bit = 7 - (x % 8); auto shift2 = (x + (height - y - 1) * boundary) / 8; auto mask = (0x01 & (static_cast(cursor[shift2]) >> bit)); auto pc = pixbuf_.pixel(x, y); auto alpha = pc.element.alpha_channel; alpha *= 1 - mask; pc.element.alpha_channel = alpha; pixbuf_.pixel(x, y, pc); } } return true; } public: ~image_ico() { #if defined(NANA_WINDOWS) if (native_handle_) ::DestroyIcon(reinterpret_cast(native_handle_)); #endif } bool open(const std::experimental::filesystem::path& ico_file) override { std::ifstream file(ico_file.string(), std::ios::binary); if (!file.is_open()) return false; // allocates a buffer for the image file.seekg(0, std::ios::end); const auto bytes = static_cast(file.tellg()); file.seekg(0, std::ios::beg); auto buffer = new char[bytes]; // read data from the file and set them in the buffer file.read(buffer, bytes); auto okret = _m_read_ico(buffer, bytes); // delete buffer and return delete[] buffer; if (okret) path_ = ico_file; return okret; } bool open(const void* data, std::size_t bytes) override { if (_m_read_ico(data, bytes)) { #if defined(NANA_WINDOWS) native_handle_ = ::CreateIconFromResourceEx(reinterpret_cast(const_cast(data)), static_cast(bytes), TRUE, 0x00030000, 0, 0, LR_DEFAULTCOLOR); #endif return true; } return false; } void* native_handle() { #if defined(NANA_WINDOWS) if (native_handle_) return native_handle_; native_handle_ = ::LoadImage(nullptr, path_.c_str(), IMAGE_ICON, 0, 0, LR_LOADFROMFILE); return native_handle_; #else return nullptr; #endif } private: std::experimental::filesystem::path path_; #if defined(NANA_WINDOWS) void* native_handle_{nullptr}; #endif }; }//end namespace detail }//end namespace paint }//end namespace nana #endif