Skip to content

Commit 7c45e89

Browse files
committed
X11: fix .ico cursor bug. Support PNG-compressed .ico files.
1 parent a973107 commit 7c45e89

File tree

2 files changed

+122
-84
lines changed

2 files changed

+122
-84
lines changed

doc/ReleaseNotes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ This issue fixes several bugs that were still found in 1.9.2.
4141
* Fix tinydisplay texture errors on shutdown
4242
* Fix mipmap filtering issues in tinydisplay renderer
4343
* Fix exception when creating intervals before ShowBase is started
44+
* Fix rare X11 .ico cursor bug; also now supports PNG-compressed icons
4445

4546
------------------------ RELEASE 1.9.2 ------------------------
4647

panda/src/x11display/x11GraphicsWindow.cxx

Lines changed: 121 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
#include "nativeWindowHandle.h"
2929
#include "virtualFileSystem.h"
3030
#include "get_x11.h"
31+
#include "pnmImage.h"
32+
#include "pnmFileTypeRegistry.h"
3133

3234
#include <errno.h>
3335
#include <fcntl.h>
@@ -2303,109 +2305,144 @@ read_ico(istream &ico) {
23032305
// Seek to the image in the ICO.
23042306
ico.seekg(entries[entry].offset);
23052307
if (!ico.good()) goto cleanup;
2306-
ico.read(reinterpret_cast<char *>(&infoHeader), sizeof(IcoInfoHeader));
2307-
if (!ico.good()) goto cleanup;
2308-
bitsPerPixel = infoHeader.bitsPerPixel;
23092308

2310-
// TODO: Support PNG compressed ICOs.
2311-
if (infoHeader.compression != 0) goto cleanup;
2309+
if (ico.peek() == 0x89) {
2310+
// Hang on, this is actually a PNG header.
2311+
PNMImage img;
2312+
PNMFileTypeRegistry *reg = PNMFileTypeRegistry::get_global_ptr();
2313+
if (!img.read(ico, "", reg->get_type_from_extension("png"))) {
2314+
goto cleanup;
2315+
}
2316+
img.set_maxval(255);
2317+
2318+
image = XcursorImageCreate(img.get_x_size(), img.get_y_size());
23122319

2313-
// Load the color palette, if one exists.
2314-
if (bitsPerPixel != 24 && bitsPerPixel != 32) {
2315-
colorCount = 1 << bitsPerPixel;
2316-
palette = new IcoColor[colorCount];
2317-
ico.read(reinterpret_cast<char *>(palette), colorCount * sizeof(IcoColor));
2320+
xel *ptr = img.get_array();
2321+
xelval *alpha = img.get_alpha_array();
2322+
size_t num_pixels = (size_t)img.get_x_size() * (size_t)img.get_y_size();
2323+
unsigned int *dest = image->pixels;
2324+
2325+
if (alpha != NULL) {
2326+
for (size_t p = 0; p < num_pixels; ++p) {
2327+
*dest++ = (*alpha << 24U) | (ptr->r << 16U) | (ptr->g << 8U) | (ptr->b);
2328+
++ptr;
2329+
++alpha;
2330+
}
2331+
} else {
2332+
for (size_t p = 0; p < num_pixels; ++p) {
2333+
*dest++ = 0xff000000U | (ptr->r << 16U) | (ptr->g << 8U) | (ptr->b);
2334+
++ptr;
2335+
}
2336+
}
2337+
2338+
} else {
2339+
ico.read(reinterpret_cast<char *>(&infoHeader), sizeof(IcoInfoHeader));
23182340
if (!ico.good()) goto cleanup;
2319-
}
2341+
bitsPerPixel = infoHeader.bitsPerPixel;
23202342

2321-
// Read in the pixel data.
2322-
xorBmpSize = (infoHeader.width * (infoHeader.height / 2) * bitsPerPixel) / 8;
2323-
andBmpSize = (infoHeader.width * (infoHeader.height / 2)) / 8;
2324-
curXor = xorBmp = new char[xorBmpSize];
2325-
curAnd = andBmp = new char[andBmpSize];
2326-
ico.read(xorBmp, xorBmpSize);
2327-
if (!ico.good()) goto cleanup;
2328-
ico.read(andBmp, andBmpSize);
2329-
if (!ico.good()) goto cleanup;
2343+
if (infoHeader.compression != 0) goto cleanup;
23302344

2331-
// If this is an actual CUR not an ICO set up the hotspot properly.
2332-
image = XcursorImageCreate(infoHeader.width, infoHeader.height / 2);
2333-
if (header.type == 2) { image->xhot = entries[entry].xhot; image->yhot = entries[entry].yhot; }
2334-
2335-
// Support all the formats that GIMP supports, minus PNG compressed ICOs.
2336-
// Would need to use libpng to decode the compressed ones.
2337-
switch (bitsPerPixel) {
2338-
case 1:
2339-
case 4:
2340-
case 8:
2341-
// For colors less that a byte wide, shift and mask the palette indices
2342-
// off each element of the xorBmp and append them to the image.
2343-
mask = ((1 << bitsPerPixel) - 1);
2344-
for (i = image->height - 1; i >= 0; i--) {
2345-
for (j = 0; j < image->width; j += 8 / bitsPerPixel) {
2346-
for (k = 0; k < 8 / bitsPerPixel; k++) {
2347-
shift = 8 - ((k + 1) * bitsPerPixel);
2348-
color = palette[(*curXor & (mask << shift)) >> shift];
2349-
image->pixels[(i * image->width) + j + k] = (color.red << 16) +
2350-
(color.green << 8) +
2351-
(color.blue);
2352-
}
2345+
// Load the color palette, if one exists.
2346+
if (bitsPerPixel != 24 && bitsPerPixel != 32) {
2347+
colorCount = 1 << bitsPerPixel;
2348+
palette = new IcoColor[colorCount];
2349+
ico.read(reinterpret_cast<char *>(palette), colorCount * sizeof(IcoColor));
2350+
if (!ico.good()) goto cleanup;
2351+
}
23532352

2354-
curXor++;
2355-
}
2353+
// Read in the pixel data.
2354+
xorBmpSize = (infoHeader.width * (infoHeader.height / 2) * bitsPerPixel) / 8;
2355+
andBmpSize = (infoHeader.width * (infoHeader.height / 2)) / 8;
2356+
curXor = xorBmp = new char[xorBmpSize];
2357+
curAnd = andBmp = new char[andBmpSize];
2358+
ico.read(xorBmp, xorBmpSize);
2359+
if (!ico.good()) goto cleanup;
2360+
ico.read(andBmp, andBmpSize);
2361+
if (!ico.good()) goto cleanup;
23562362

2357-
// Set the alpha byte properly according to the andBmp.
2358-
for (j = 0; j < image->width; j += 8) {
2359-
for (k = 0; k < 8; k++) {
2360-
shift = 7 - k;
2361-
image->pixels[(i * image->width) + j + k] |=
2362-
((*curAnd & (1 << shift)) >> shift) ? 0x0 : (0xff << 24);
2363+
image = XcursorImageCreate(infoHeader.width, infoHeader.height / 2);
2364+
2365+
// Support all the formats that GIMP supports.
2366+
switch (bitsPerPixel) {
2367+
case 1:
2368+
case 4:
2369+
case 8:
2370+
// For colors less that a byte wide, shift and mask the palette indices
2371+
// off each element of the xorBmp and append them to the image.
2372+
mask = ((1 << bitsPerPixel) - 1);
2373+
for (i = image->height - 1; i >= 0; i--) {
2374+
for (j = 0; j < image->width; j += 8 / bitsPerPixel) {
2375+
for (k = 0; k < 8 / bitsPerPixel; k++) {
2376+
shift = 8 - ((k + 1) * bitsPerPixel);
2377+
color = palette[(*curXor & (mask << shift)) >> shift];
2378+
image->pixels[(i * image->width) + j + k] = (color.red << 16) +
2379+
(color.green << 8) +
2380+
(color.blue);
2381+
}
2382+
2383+
curXor++;
23632384
}
23642385

2365-
curAnd++;
2366-
}
2367-
}
2386+
// Set the alpha byte properly according to the andBmp.
2387+
for (j = 0; j < image->width; j += 8) {
2388+
for (k = 0; k < 8; k++) {
2389+
shift = 7 - k;
2390+
image->pixels[(i * image->width) + j + k] |=
2391+
((*curAnd & (1 << shift)) >> shift) ? 0x0 : (0xff << 24);
2392+
}
23682393

2369-
break;
2370-
case 24:
2371-
// Pack each of the three bytes into a single color, BGR -> 0RGB
2372-
for (i = image->height - 1; i >= 0; i--) {
2373-
for (j = 0; j < image->width; j++) {
2374-
image->pixels[(i * image->width) + j] = (*(curXor + 2) << 16) +
2375-
(*(curXor + 1) << 8) + (*curXor);
2376-
curXor += 3;
2394+
curAnd++;
2395+
}
23772396
}
2397+
break;
23782398

2379-
// Set the alpha byte properly according to the andBmp.
2380-
for (j = 0; j < image->width; j += 8) {
2381-
for (k = 0; k < 8; k++) {
2382-
shift = 7 - k;
2383-
image->pixels[(i * image->width) + j + k] |=
2384-
((*curAnd & (1 << shift)) >> shift) ? 0x0 : (0xff << 24);
2399+
case 24:
2400+
// Pack each of the three bytes into a single color, BGR -> 0RGB
2401+
for (i = image->height - 1; i >= 0; i--) {
2402+
for (j = 0; j < image->width; j++) {
2403+
image->pixels[(i * image->width) + j] = (*(curXor + 2) << 16) +
2404+
(*(curXor + 1) << 8) + (*curXor);
2405+
curXor += 3;
23852406
}
23862407

2387-
curAnd++;
2388-
}
2408+
// Set the alpha byte properly according to the andBmp.
2409+
for (j = 0; j < image->width; j += 8) {
2410+
for (k = 0; k < 8; k++) {
2411+
shift = 7 - k;
2412+
image->pixels[(i * image->width) + j + k] |=
2413+
((*curAnd & (1 << shift)) >> shift) ? 0x0 : (0xff << 24);
2414+
}
23892415

2390-
}
2416+
curAnd++;
2417+
}
2418+
}
2419+
break;
23912420

2392-
break;
2393-
case 32:
2394-
// Pack each of the four bytes into a single color, BGRA -> ARGB
2395-
for (i = image->height - 1; i >= 0; i--) {
2396-
for (j = 0; j < image->width; j++) {
2397-
image->pixels[(i * image->width) + j] = (*(curXor + 3) << 24) +
2398-
(*(curXor + 2) << 16) +
2399-
(*(curXor + 1) << 8) +
2400-
(*curXor);
2401-
curXor += 4;
2421+
case 32:
2422+
// Pack each of the four bytes into a single color, BGRA -> ARGB
2423+
for (i = image->height - 1; i >= 0; i--) {
2424+
for (j = 0; j < image->width; j++) {
2425+
image->pixels[(i * image->width) + j] = (*(curXor + 3) << 24) +
2426+
(*(curXor + 2) << 16) +
2427+
(*(curXor + 1) << 8) +
2428+
(*curXor);
2429+
curXor += 4;
2430+
}
24022431
}
2432+
break;
2433+
2434+
default:
2435+
goto cleanup;
24032436
}
2437+
}
24042438

2405-
break;
2406-
default:
2407-
goto cleanup;
2408-
break;
2439+
// If this is an actual CUR not an ICO set up the hotspot properly.
2440+
if (header.type == 2) {
2441+
image->xhot = entries[entry].xhot;
2442+
image->yhot = entries[entry].yhot;
2443+
} else {
2444+
image->xhot = 0;
2445+
image->yhot = 0;
24092446
}
24102447

24112448
ret = XcursorImageLoadCursor(_display, image);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy