// license:BSD-3-Clause // copyright-holders:Dankan1890;Victor Laskin /*************************************************************************** ui/icorender.h ICOns file loader. Original code by Victor Laskin (victor.laskin@gmail.com) http://vitiy.info/Code/ico.cpp ***************************************************************************/ #pragma once #ifndef __UI_ICORENDER_H__ #define __UI_ICORENDER_H__ // These next two structs represent how the icon information is stored // in an ICO file. typedef struct { UINT8 bWidth; // Width of the image UINT8 bHeight; // Height of the image (times 2) UINT8 bColorCount; // Number of colors in image (0 if >=8bpp) UINT8 bReserved; // Reserved UINT16 wPlanes; // Color Planes UINT16 wBitCount; // Bits per pixel UINT32 dwBytesInRes; // how many bytes in this resource? UINT32 dwImageOffset; // where in the file is this image } ICONDIRENTRY, *LPICONDIRENTRY; typedef struct { UINT16 idReserved; // Reserved UINT16 idType; // resource type (1 for icons) UINT16 idCount; // how many images? //ICONDIRENTRY idEntries[1]; // the entries for each image } ICONDIR, *LPICONDIR; // size - 40 bytes typedef struct { UINT32 biSize; UINT32 biWidth; UINT32 biHeight; // Icon Height (added height of XOR-Bitmap and AND-Bitmap) UINT16 biPlanes; UINT16 biBitCount; UINT32 biCompression; INT32 biSizeImage; UINT32 biXPelsPerMeter; UINT32 biYPelsPerMeter; UINT32 biClrUsed; UINT32 biClrImportant; } s_BITMAPINFOHEADER, *s_PBITMAPINFOHEADER; // 46 bytes typedef struct{ s_BITMAPINFOHEADER icHeader; // DIB header UINT32 icColors[1]; // Color table (short 4 bytes) //RGBQUAD UINT8 icXOR[1]; // DIB bits for XOR mask UINT8 icAND[1]; // DIB bits for AND mask } ICONIMAGE, *LPICONIMAGE; //------------------------------------------------- // load an ICO file into a bitmap //------------------------------------------------- void render_load_ico(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, const char *filename) { INT32 width = 0; INT32 height = 0; // deallocate previous bitmap bitmap.reset(); // define file's full name std::string fname; if (!dirname) fname = filename; else fname.assign(dirname).append(PATH_SEPARATOR).append(filename); file_error filerr = file.open(fname.c_str()); if (filerr != FILERR_NONE) return; // allocates a buffer for the image UINT64 size = file.size(); UINT8 *buffer = global_alloc_array(UINT8, size + 1); // read data from the file and set them in the buffer file.read(buffer, size); LPICONDIR icoDir = (LPICONDIR)buffer; int iconsCount = icoDir->idCount; if (icoDir->idReserved != 0 || icoDir->idType != 1 || iconsCount == 0 || iconsCount > 20) { file.close(); global_free_array(buffer); return; } UINT8* cursor = buffer; cursor += 6; ICONDIRENTRY* dirEntry = (ICONDIRENTRY*)(cursor); int maxSize = 0; int offset = 0; int maxBitCount = 0; for (int 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; } } if (offset == 0) return; cursor = buffer; cursor += offset; ICONIMAGE* icon = (ICONIMAGE*)(cursor); int realBitsCount = (int)icon->icHeader.biBitCount; bool hasAndMask = (realBitsCount < 32) && (height != icon->icHeader.biHeight); cursor += 40; bitmap.allocate(width, height); // rgba + vertical swap if (realBitsCount >= 32) { for (int x = 0; x < width; ++x) for (int y = 0; y < height; ++y) { int shift2 = 4 * (x + (height - y - 1) * width); bitmap.pix32(y, x) = rgb_t(cursor[shift2 + 3], cursor[shift2 + 2], cursor[shift2 + 1], cursor[shift2]); } } else if (realBitsCount == 24) for (int x = 0; x < width; ++x) for (int y = 0; y < height; ++y) { int shift2 = 3 * (x + (height - y - 1) * width); bitmap.pix32(y, x) = rgb_t(255, cursor[shift2 + 2], cursor[shift2 + 1], cursor[shift2]); } else if (realBitsCount == 8) // 256 colors { // 256 color table UINT8 *colors = cursor; cursor += 256 * 4; for (int x = 0; x < width; ++x) for (int y = 0; y < height; ++y) { int shift2 = (x + (height - y - 1) * width); int index = 4 * cursor[shift2]; bitmap.pix32(y, x) = rgb_t(255, colors[index + 2], colors[index + 1], colors[index]); } } else if (realBitsCount == 4) // 16 colors { // 16 color table UINT8 *colors = cursor; cursor += 16 * 4; for (int x = 0; x < width; ++x) for (int y = 0; y < height; ++y) { int shift2 = (x + (height - y - 1) * width); UINT8 index = cursor[shift2 / 2]; if (shift2 % 2 == 0) index = (index >> 4) & 0xF; else index = index & 0xF; index *= 4; bitmap.pix32(y, x) = rgb_t(255, colors[index + 2], colors[index + 1], colors[index]); } } else if (realBitsCount == 1) // 2 colors { // 2 color table UINT8 *colors = cursor; cursor += 2 * 4; int boundary = width; // !!! 32 bit boundary (http://www.daubnet.com/en/file-format-ico) while (boundary % 32 != 0) boundary++; for (int x = 0; x < width; ++x) for (int y = 0; y < height; ++y) { int shift2 = (x + (height - y - 1) * boundary); UINT8 index = cursor[shift2 / 8]; // select 1 bit only UINT8 bit = 7 - (x % 8); index = (index >> bit) & 0x01; index *= 4; bitmap.pix32(y, x) = rgb_t(255, colors[index + 2], colors[index + 1], colors[index]); } } // Read AND mask after base color data - 1 BIT MASK if (hasAndMask) { int 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 (int y = 0; y < height; ++y) for (int x = 0; x < width; ++x) { UINT8 bit = 7 - (x % 8); int shift2 = (x + (height - y - 1) * boundary) / 8; int mask = (0x01 & ((UINT8)cursor[shift2] >> bit)); rgb_t colors = bitmap.pix32(y, x); UINT8 alpha = colors.a(); alpha *= 1 - mask; colors.set_a(alpha); bitmap.pix32(y, x) = colors; } } file.close(); global_free_array(buffer); } #endif /* __UI_ICORENDER_H__ */