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 "throw_event.h"
|
||||||
#include "lightReMutexHolder.h"
|
#include "lightReMutexHolder.h"
|
||||||
#include "nativeWindowHandle.h"
|
#include "nativeWindowHandle.h"
|
||||||
|
#include "virtualFileSystem.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
@ -53,6 +54,35 @@
|
|||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
#endif
|
#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;
|
TypeHandle x11GraphicsWindow::_type_handle;
|
||||||
|
|
||||||
#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)&7)))
|
#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)&7)))
|
||||||
@ -1873,23 +1903,252 @@ get_cursor(const Filename &filename) {
|
|||||||
return fi->second;
|
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
|
#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) {
|
if (h == None) {
|
||||||
x11display_cat.warning()
|
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[filename] = h;
|
||||||
_cursor_filenames[resolved] = h;
|
_cursor_filenames[resolved] = h;
|
||||||
return 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:
|
private:
|
||||||
Cursor get_cursor(const Filename &filename);
|
Cursor get_cursor(const Filename &filename);
|
||||||
|
Cursor read_ico(istream &ico);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Display *_display;
|
Display *_display;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user