mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 10:54:24 -04:00
mjkoo's patch for loading ico files, with my modifications
This commit is contained in:
parent
b18fd7d192
commit
396c66d191
@ -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;
|
||||
}
|
||||
|
||||
|
@ -76,6 +76,7 @@ protected:
|
||||
|
||||
private:
|
||||
Cursor get_cursor(const Filename &filename);
|
||||
Cursor read_ico(istream &ico);
|
||||
|
||||
protected:
|
||||
Display *_display;
|
||||
|
Loading…
x
Reference in New Issue
Block a user