mjkoo's patch for loading ico files, with my modifications

This commit is contained in:
Maxwell J. Koo 2010-12-26 10:55:30 +00:00 committed by rdb
parent b18fd7d192
commit 396c66d191
2 changed files with 268 additions and 8 deletions

View File

@ -25,6 +25,7 @@
#include "throw_event.h"
#include "lightReMutexHolder.h"
#include "nativeWindowHandle.h"
#include "virtualFileSystem.h"
#include <errno.h>
#include <fcntl.h>
@ -53,6 +54,35 @@
#include <linux/input.h>
#endif
#ifdef HAVE_XCURSOR
static int xcursor_read(XcursorFile *file, unsigned char *buf, int len) {
istream* str = (istream*) file->closure;
str->read((char*) buf, len);
return str->gcount();
}
static int xcursor_write(XcursorFile *file, unsigned char *buf, int len) {
// Not implemented, we don't need it.
nassertr_always(false, 0);
}
static int xcursor_seek(XcursorFile *file, long offset, int whence) {
istream* str = (istream*) file->closure;
switch (whence) {
case SEEK_SET:
str->seekg(offset, istream::beg);
break;
case SEEK_CUR:
str->seekg(offset, istream::cur);
break;
case SEEK_END:
str->seekg(offset, istream::end);
}
return str->tellg();
}
#endif
TypeHandle x11GraphicsWindow::_type_handle;
#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)&7)))
@ -1873,23 +1903,252 @@ get_cursor(const Filename &filename) {
return fi->second;
}
Filename os = resolved.to_os_specific();
// Open the file through the virtual file system.
istream *str = VirtualFileSystem::get_global_ptr()->open_read_file(resolved, true);
if (str == NULL) {
x11display_cat.warning()
<< "Could not open cursor file " << filename << "\n";
return None;
}
// Check the first four bytes to see what kind of file it is.
char magic[4];
str->read(magic, 4);
if (!str->good()) {
x11display_cat.warning()
<< "Could not read from cursor file " << filename << "\n";
return None;
}
str->seekg(0, istream::beg);
Cursor h = None;
if (memcmp(magic, "Xcur", 4) == 0) {
// X11 cursor.
#ifdef HAVE_XCURSOR
Cursor h = XcursorFilenameLoadCursor(_display, os.c_str());
x11display_cat.debug()
<< "Loading X11 cursor " << filename << "\n";
XcursorFile xcfile;
xcfile.closure = str;
xcfile.read = &xcursor_read;
xcfile.write = &xcursor_write;
xcfile.seek = &xcursor_seek;
XcursorImages *images = XcursorXcFileLoadImages(&xcfile, XcursorGetDefaultSize(_display));
if (images != NULL) {
h = XcursorImagesLoadCursor(_display, images);
XcursorImagesDestroy(images);
}
#endif
} else {
// Windows .ico or .cur file.
x11display_cat.debug()
<< "Loading Windows cursor " << filename << "\n";
h = read_ico(*str);
}
delete str;
if (h == None) {
x11display_cat.warning()
<< "x11 cursor filename '" << os << "' could not be loaded!!\n";
<< "X11 cursor filename '" << resolved << "' could not be loaded!\n";
}
#else
// Can't support loading cursor from image, so fall back to default
Cursor h = None;
#endif
_cursor_filenames[filename] = h;
_cursor_filenames[resolved] = h;
return h;
}
////////////////////////////////////////////////////////////////////
// Function: x11GraphicsWindow::load_ico
// Access: Private
// Description: Reads a Windows .ico or .cur file from the
// indicated stream and returns it as an X11 Cursor.
// If the file cannot be loaded, returns None.
////////////////////////////////////////////////////////////////////
Cursor x11GraphicsWindow::
read_ico(istream &ico) {
// Local structs, this is just POD, make input easier
typedef struct {
uint16_t reserved, type, count;
} IcoHeader;
typedef struct {
uint8_t width, height, colorCount, reserved;
uint16_t xhot, yhot;
uint32_t bitmapSize, offset;
} IcoEntry;
typedef struct {
uint32_t headerSize, width, height;
uint16_t planes, bitsPerPixel;
uint32_t compression, imageSize, xPixelsPerM, yPixelsPerM, colorsUsed, colorsImportant;
} IcoInfoHeader;
typedef struct {
uint8_t blue, green, red, reserved;
} IcoColor;
int i, entry = 0;
unsigned int j, k, mask, shift;
size_t colorCount, bitsPerPixel;
IcoHeader header;
IcoInfoHeader infoHeader;
IcoEntry *entries = NULL;
IcoColor color, *palette = NULL;
size_t xorBmpSize, andBmpSize;
char *curXor, *curAnd;
char *xorBmp = NULL, *andBmp = NULL;
XcursorImage *image = NULL;
Cursor ret = None;
#ifdef HAVE_XCURSOR
int def_size = XcursorGetDefaultSize(_display);
#endif
// Get our header, note that ICO = type 1 and CUR = type 2.
ico.read(reinterpret_cast<char *>(&header), sizeof(IcoHeader));
if (!ico.good()) goto cleanup;
cerr << header.type;
if (header.type != 1 && header.type != 2) goto cleanup;
if (header.count < 1) goto cleanup;
// Read the entry table into memory, select the largest entry.
entries = new IcoEntry[header.count];
ico.read(reinterpret_cast<char *>(entries), header.count * sizeof(IcoEntry));
if (!ico.good()) goto cleanup;
for (i = 1; i < header.count; i++) {
#ifdef HAVE_XCURSOR
if (entries[i].width == def_size && entries[i].height == def_size) {
// Wait, this is the default cursor size. This is perfect.
entry = i;
break;
}
#endif
if (entries[i].width > entries[entry].width ||
entries[i].height > entries[entry].height)
entry = i;
}
// Seek to the image in the ICO.
ico.seekg(entries[entry].offset);
if (!ico.good()) goto cleanup;
ico.read(reinterpret_cast<char *>(&infoHeader), sizeof(IcoInfoHeader));
if (!ico.good()) goto cleanup;
bitsPerPixel = infoHeader.bitsPerPixel;
// TODO: Support PNG compressed ICOs.
if (infoHeader.compression != 0) goto cleanup;
// Load the color palette, if one exists.
colorCount = entries[entry].colorCount == 0 ? 256 : entries[entry].colorCount;
palette = new IcoColor[colorCount];
if (bitsPerPixel <= 8) ico.read(reinterpret_cast<char *>(palette), colorCount * sizeof(IcoColor));
if (!ico.good()) goto cleanup;
// Read in the pixel data.
xorBmpSize = (infoHeader.width * (infoHeader.height / 2) * bitsPerPixel) / 8;
andBmpSize = (infoHeader.width * (infoHeader.height / 2)) / 8;
curXor = xorBmp = new char[xorBmpSize];
curAnd = andBmp = new char[andBmpSize];
ico.read(xorBmp, xorBmpSize);
if (!ico.good()) goto cleanup;
ico.read(andBmp, andBmpSize);
if (!ico.good()) goto cleanup;
#ifdef HAVE_XCURSOR
// If this is an actual CUR not an ICO set up the hotspot properly.
image = XcursorImageCreate(infoHeader.width, infoHeader.height / 2);
if (header.type == 2) { image->xhot = entries[entry].xhot; image->yhot = entries[entry].yhot; }
// Support all the formats that GIMP supports, minus PNG compressed ICOs.
// Would need to use libpng to decode the compressed ones.
switch (bitsPerPixel) {
case 1:
case 4:
case 8:
// For colors less that a byte wide, shift and mask the palette indices
// off each element of the xorBmp and append them to the image.
mask = ((1 << bitsPerPixel) - 1);
for (i = image->height - 1; i >= 0; i--) {
for (j = 0; j < image->width; j += 8 / bitsPerPixel) {
for (k = 0; k < 8 / bitsPerPixel; k++) {
shift = 8 - ((k + 1) * bitsPerPixel);
color = palette[(*curXor & (mask << shift)) >> shift];
image->pixels[(i * image->width) + j + k] = (color.red << 16) +
(color.green << 8) +
(color.blue);
}
curXor++;
}
// Set the alpha byte properly according to the andBmp.
for (j = 0; j < image->width; j += 8) {
for (k = 0; k < 8; k++) {
shift = 7 - k;
image->pixels[(i * image->width) + j + k] |=
((*curAnd & (1 << shift)) >> shift) ? 0x0 : (0xff << 24);
}
curAnd++;
}
}
break;
case 24:
// Pack each of the three bytes into a single color, BGR -> 0RGB
for (i = image->height - 1; i >= 0; i--) {
for (j = 0; j < image->width; j++) {
image->pixels[(i * image->width) + j] = (*(curXor + 2) << 16) +
(*(curXor + 1) << 8) + (*curXor);
curXor += 3;
}
// Set the alpha byte properly according to the andBmp.
for (j = 0; j < image->width; j += 8) {
for (k = 0; k < 8; k++) {
shift = 7 - k;
image->pixels[(i * image->width) + j + k] |=
((*curAnd & (1 << shift)) >> shift) ? 0x0 : (0xff << 24);
}
curAnd++;
}
}
break;
case 32:
// Pack each of the four bytes into a single color, BGRA -> ARGB
for (i = image->height - 1; i >= 0; i--) {
for (j = 0; j < image->width; j++) {
image->pixels[(i * image->width) + j] = (*(curXor + 3) << 24) +
(*(curXor + 2) << 16) +
(*(curXor + 1) << 8) +
(*curXor);
curXor += 4;
}
}
break;
default:
goto cleanup;
break;
}
ret = XcursorImageLoadCursor(_display, image);
#endif
cleanup:
#ifdef HAVE_XCURSOR
XcursorImageDestroy(image);
#endif
delete[] entries;
delete[] palette;
delete[] xorBmp;
delete[] andBmp;
return ret;
}

View File

@ -76,6 +76,7 @@ protected:
private:
Cursor get_cursor(const Filename &filename);
Cursor read_ico(istream &ico);
protected:
Display *_display;