Ver código fonte

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

Maxwell J. Koo 15 anos atrás
pai
commit
396c66d191

+ 267 - 8
panda/src/x11display/x11GraphicsWindow.cxx

@@ -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;
+}
+

+ 1 - 0
panda/src/x11display/x11GraphicsWindow.h

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