17914d74bSmrg/* 26b7436aeSmrg * Copyright © 2024 Thomas E. Dickey 37914d74bSmrg * Copyright © 2002 Keith Packard 47914d74bSmrg * 57914d74bSmrg * Permission to use, copy, modify, distribute, and sell this software and its 67914d74bSmrg * documentation for any purpose is hereby granted without fee, provided that 77914d74bSmrg * the above copyright notice appear in all copies and that both that 87914d74bSmrg * copyright notice and this permission notice appear in supporting 97914d74bSmrg * documentation, and that the name of Keith Packard not be used in 107914d74bSmrg * advertising or publicity pertaining to distribution of the software without 117914d74bSmrg * specific, written prior permission. Keith Packard makes no 127914d74bSmrg * representations about the suitability of this software for any purpose. It 137914d74bSmrg * is provided "as is" without express or implied warranty. 147914d74bSmrg * 157914d74bSmrg * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 167914d74bSmrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 177914d74bSmrg * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR 187914d74bSmrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 197914d74bSmrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 207914d74bSmrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 217914d74bSmrg * PERFORMANCE OF THIS SOFTWARE. 227914d74bSmrg */ 237914d74bSmrg 247914d74bSmrg#include "xcursorint.h" 257914d74bSmrg#include <X11/Xlibint.h> 267914d74bSmrg#include <X11/Xutil.h> 277914d74bSmrg 287914d74bSmrgXcursorCursors * 297914d74bSmrgXcursorCursorsCreate (Display *dpy, int size) 307914d74bSmrg{ 317914d74bSmrg XcursorCursors *cursors; 327914d74bSmrg 337914d74bSmrg cursors = malloc (sizeof (XcursorCursors) + 344d939ec7Smrg (size_t) size * sizeof (Cursor)); 357914d74bSmrg if (!cursors) 367914d74bSmrg return NULL; 377914d74bSmrg cursors->ref = 1; 387914d74bSmrg cursors->dpy = dpy; 397914d74bSmrg cursors->ncursor = 0; 407914d74bSmrg cursors->cursors = (Cursor *) (cursors + 1); 417914d74bSmrg return cursors; 427914d74bSmrg} 437914d74bSmrg 447914d74bSmrgvoid 457914d74bSmrgXcursorCursorsDestroy (XcursorCursors *cursors) 467914d74bSmrg{ 477914d74bSmrg int n; 487914d74bSmrg 497914d74bSmrg if (!cursors) 507914d74bSmrg return; 517914d74bSmrg 527914d74bSmrg --cursors->ref; 537914d74bSmrg if (cursors->ref > 0) 547914d74bSmrg return; 55e6d5e4e0Smrg 567914d74bSmrg for (n = 0; n < cursors->ncursor; n++) 577914d74bSmrg XFreeCursor (cursors->dpy, cursors->cursors[n]); 587914d74bSmrg free (cursors); 597914d74bSmrg} 607914d74bSmrg 617914d74bSmrgXcursorAnimate * 627914d74bSmrgXcursorAnimateCreate (XcursorCursors *cursors) 637914d74bSmrg{ 647914d74bSmrg XcursorAnimate *animate; 657914d74bSmrg 667914d74bSmrg animate = malloc (sizeof (XcursorAnimate)); 677914d74bSmrg if (!animate) 687914d74bSmrg return NULL; 697914d74bSmrg animate->cursors = cursors; 707914d74bSmrg cursors->ref++; 717914d74bSmrg animate->sequence = 0; 727914d74bSmrg return animate; 737914d74bSmrg} 747914d74bSmrg 757914d74bSmrgvoid 767914d74bSmrgXcursorAnimateDestroy (XcursorAnimate *animate) 777914d74bSmrg{ 787914d74bSmrg if (!animate) 797914d74bSmrg return; 807914d74bSmrg 817914d74bSmrg XcursorCursorsDestroy (animate->cursors); 827914d74bSmrg free (animate); 837914d74bSmrg} 847914d74bSmrg 857914d74bSmrgCursor 867914d74bSmrgXcursorAnimateNext (XcursorAnimate *animate) 877914d74bSmrg{ 887914d74bSmrg Cursor cursor = animate->cursors->cursors[animate->sequence++]; 897914d74bSmrg 907914d74bSmrg if (animate->sequence >= animate->cursors->ncursor) 917914d74bSmrg animate->sequence = 0; 927914d74bSmrg return cursor; 937914d74bSmrg} 947914d74bSmrg 954d939ec7Smrg#if RENDER_MAJOR > 0 || RENDER_MINOR >= 5 967914d74bSmrgstatic int 977914d74bSmrgnativeByteOrder (void) 987914d74bSmrg{ 997914d74bSmrg int x = 1; 1007914d74bSmrg 1017914d74bSmrg return (*((char *) &x) == 1) ? LSBFirst : MSBFirst; 1027914d74bSmrg} 1034d939ec7Smrg#endif 1047914d74bSmrg 1057914d74bSmrgstatic XcursorUInt 1067914d74bSmrg_XcursorPixelBrightness (XcursorPixel p) 1077914d74bSmrg{ 1087914d74bSmrg XcursorPixel alpha = p >> 24; 1097914d74bSmrg XcursorPixel r, g, b; 1107914d74bSmrg 1117914d74bSmrg if (!alpha) 1127914d74bSmrg return 0; 1137914d74bSmrg r = ((p >> 8) & 0xff00) / alpha; 1147914d74bSmrg if (r > 0xff) r = 0xff; 1157914d74bSmrg g = ((p >> 0) & 0xff00) / alpha; 1167914d74bSmrg if (g > 0xff) g = 0xff; 1177914d74bSmrg b = ((p << 8) & 0xff00) / alpha; 1187914d74bSmrg if (b > 0xff) b = 0xff; 1197914d74bSmrg return (r * 153 + g * 301 + b * 58) >> 9; 1207914d74bSmrg} 1217914d74bSmrg 1227914d74bSmrgstatic unsigned short 1237914d74bSmrg_XcursorDivideAlpha (XcursorUInt value, XcursorUInt alpha) 1247914d74bSmrg{ 1257914d74bSmrg if (!alpha) 1267914d74bSmrg return 0; 1277914d74bSmrg value = value * 255 / alpha; 1287914d74bSmrg if (value > 255) 1297914d74bSmrg value = 255; 1304d939ec7Smrg return (unsigned short) (value | (value << 8)); 1317914d74bSmrg} 1327914d74bSmrg 1337914d74bSmrgstatic void 1347914d74bSmrg_XcursorPixelToColor (XcursorPixel p, XColor *color) 1357914d74bSmrg{ 1367914d74bSmrg XcursorPixel alpha = p >> 24; 1377914d74bSmrg 1387914d74bSmrg color->pixel = 0; 1397914d74bSmrg color->red = _XcursorDivideAlpha ((p >> 16) & 0xff, alpha); 1407914d74bSmrg color->green = _XcursorDivideAlpha ((p >> 8) & 0xff, alpha); 1417914d74bSmrg color->blue = _XcursorDivideAlpha ((p >> 0) & 0xff, alpha); 1427914d74bSmrg color->flags = DoRed|DoGreen|DoBlue; 1437914d74bSmrg} 1447914d74bSmrg 1457914d74bSmrg#undef DEBUG_IMAGE 1467914d74bSmrg#ifdef DEBUG_IMAGE 1477914d74bSmrgstatic void 1487914d74bSmrg_XcursorDumpImage (XImage *image) 1497914d74bSmrg{ 1500ea508b1Smrg FILE *f = fopen ("/tmp/images", "a" FOPEN_CLOEXEC); 1517914d74bSmrg int x, y; 1527914d74bSmrg if (!f) 1537914d74bSmrg return; 1547914d74bSmrg fprintf (f, "%d x %x\n", image->width, image->height); 1557914d74bSmrg for (y = 0; y < image->height; y++) 1567914d74bSmrg { 1577914d74bSmrg for (x = 0; x < image->width; x++) 1587914d74bSmrg fprintf (f, "%c", XGetPixel (image, x, y) ? '*' : ' '); 1597914d74bSmrg fprintf (f, "\n"); 1607914d74bSmrg } 1617914d74bSmrg fflush (f); 1627914d74bSmrg fclose (f); 1637914d74bSmrg} 1647914d74bSmrg 1657914d74bSmrgstatic void 1667914d74bSmrg_XcursorDumpColor (XColor *color, char *name) 1677914d74bSmrg{ 1680ea508b1Smrg FILE *f = fopen ("/tmp/images", "a" FOPEN_CLOEXEC); 1697914d74bSmrg fprintf (f, "%s: %x %x %x\n", name, 1707914d74bSmrg color->red, color->green, color->blue); 1717914d74bSmrg fflush (f); 1727914d74bSmrg fclose (f); 1737914d74bSmrg} 1747914d74bSmrg#endif 1757914d74bSmrg 1767914d74bSmrgstatic int 1777914d74bSmrg_XcursorCompareRed (const void *a, const void *b) 1787914d74bSmrg{ 1797914d74bSmrg const XcursorPixel *ap = a, *bp = b; 1807914d74bSmrg 1817914d74bSmrg return (int) (((*ap >> 16) & 0xff) - ((*bp >> 16) & 0xff)); 1827914d74bSmrg} 1837914d74bSmrg 1847914d74bSmrgstatic int 1857914d74bSmrg_XcursorCompareGreen (const void *a, const void *b) 1867914d74bSmrg{ 1877914d74bSmrg const XcursorPixel *ap = a, *bp = b; 1887914d74bSmrg 1897914d74bSmrg return (int) (((*ap >> 8) & 0xff) - ((*bp >> 8) & 0xff)); 1907914d74bSmrg} 1917914d74bSmrg 1927914d74bSmrgstatic int 1937914d74bSmrg_XcursorCompareBlue (const void *a, const void *b) 1947914d74bSmrg{ 1957914d74bSmrg const XcursorPixel *ap = a, *bp = b; 1967914d74bSmrg 1977914d74bSmrg return (int) (((*ap >> 0) & 0xff) - ((*bp >> 0) & 0xff)); 1987914d74bSmrg} 1997914d74bSmrg 2004d939ec7Smrg#define ScaledPixels(c,n) ((c)/(XcursorPixel)(n)) 2014d939ec7Smrg 2027914d74bSmrgstatic XcursorPixel 2037914d74bSmrg_XcursorAverageColor (XcursorPixel *pixels, int npixels) 2047914d74bSmrg{ 2057914d74bSmrg XcursorPixel p; 2067914d74bSmrg XcursorPixel red, green, blue; 2077914d74bSmrg int n = npixels; 2087914d74bSmrg 209c63293b5Smrg if (n < 1) 210c63293b5Smrg return 0; 211c63293b5Smrg 2127914d74bSmrg blue = green = red = 0; 2137914d74bSmrg while (n--) 2147914d74bSmrg { 2157914d74bSmrg p = *pixels++; 2167914d74bSmrg red += (p >> 16) & 0xff; 2177914d74bSmrg green += (p >> 8) & 0xff; 2187914d74bSmrg blue += (p >> 0) & 0xff; 2197914d74bSmrg } 2204d939ec7Smrg return (0xffU << 24) 2214d939ec7Smrg | (ScaledPixels(red, npixels) << 16) 2224d939ec7Smrg | (ScaledPixels(green, npixels) << 8) 2234d939ec7Smrg | ScaledPixels(blue, npixels); 2247914d74bSmrg} 2257914d74bSmrg 2267914d74bSmrgtypedef struct XcursorCoreCursor { 2277914d74bSmrg XImage *src_image; 2287914d74bSmrg XImage *msk_image; 2297914d74bSmrg XColor on_color; 2307914d74bSmrg XColor off_color; 2317914d74bSmrg} XcursorCoreCursor; 2327914d74bSmrg 2337914d74bSmrgstatic Bool 2347914d74bSmrg_XcursorHeckbertMedianCut (const XcursorImage *image, XcursorCoreCursor *core) 2357914d74bSmrg{ 2367914d74bSmrg XImage *src_image = core->src_image, *msk_image = core->msk_image; 2379d0ccd10Smrg unsigned int npixels = image->width * image->height; 2387914d74bSmrg int ncolors; 2397914d74bSmrg int n; 2407914d74bSmrg XcursorPixel *po, *pn, *pc; 2417914d74bSmrg XcursorPixel p; 2427914d74bSmrg XcursorPixel red, green, blue, alpha; 2437914d74bSmrg XcursorPixel max_red, min_red, max_green, min_green, max_blue, min_blue; 2447914d74bSmrg XcursorPixel *temp, *pixels, *colors; 2457914d74bSmrg int split; 2467914d74bSmrg XcursorPixel leftColor, centerColor, rightColor; 2477914d74bSmrg int (*compare) (const void *, const void *); 2487914d74bSmrg int x, y; 249e6d5e4e0Smrg 2507914d74bSmrg /* 2517914d74bSmrg * Temp space for converted image and converted colors 2527914d74bSmrg */ 2537914d74bSmrg temp = malloc (npixels * sizeof (XcursorPixel) * 2); 2547914d74bSmrg if (!temp) 2557914d74bSmrg return False; 256e6d5e4e0Smrg 2577914d74bSmrg pixels = temp; 2587914d74bSmrg colors = pixels + npixels; 259e6d5e4e0Smrg 2607914d74bSmrg /* 2617914d74bSmrg * Convert to 2-value alpha and build 2627914d74bSmrg * array of opaque color values and an 2637914d74bSmrg */ 2647914d74bSmrg po = image->pixels; 2657914d74bSmrg pn = pixels; 2667914d74bSmrg pc = colors; 2677914d74bSmrg max_blue = max_green = max_red = 0; 2687914d74bSmrg min_blue = min_green = min_red = 255; 2694d939ec7Smrg n = (int) npixels; 2707914d74bSmrg while (n--) 2717914d74bSmrg { 2727914d74bSmrg p = *po++; 2737914d74bSmrg alpha = (p >> 24) & 0xff; 2747914d74bSmrg red = (p >> 16) & 0xff; 2757914d74bSmrg green = (p >> 8) & 0xff; 2767914d74bSmrg blue = (p >> 0) & 0xff; 2777914d74bSmrg if (alpha >= 0x80) 2787914d74bSmrg { 2797914d74bSmrg red = red * 255 / alpha; 2807914d74bSmrg green = green * 255 / alpha; 2817914d74bSmrg blue = blue * 255 / alpha; 2827914d74bSmrg if (red < min_red) min_red = red; 2837914d74bSmrg if (red > max_red) max_red = red; 2847914d74bSmrg if (green < min_green) min_green = green; 2857914d74bSmrg if (green > max_green) max_green = green; 2867914d74bSmrg if (blue < min_blue) min_blue = blue; 2877914d74bSmrg if (blue > max_blue) max_blue = blue; 288c63293b5Smrg p = ((0xffU << 24) | (red << 16) | 2897914d74bSmrg (green << 8) | (blue << 0)); 2907914d74bSmrg *pc++ = p; 2917914d74bSmrg } 2927914d74bSmrg else 2937914d74bSmrg p = 0; 2947914d74bSmrg *pn++ = p; 2957914d74bSmrg } 2964d939ec7Smrg ncolors = (int) (pc - colors); 297e6d5e4e0Smrg 2987914d74bSmrg /* 2997914d74bSmrg * Compute longest dimension and sort 3007914d74bSmrg */ 3017914d74bSmrg if ((max_green - min_green) >= (max_red - min_red) && 3027914d74bSmrg (max_green - min_green) >= (max_blue - min_blue)) 3037914d74bSmrg compare = _XcursorCompareGreen; 3047914d74bSmrg else if ((max_red - min_red) >= (max_blue - min_blue)) 3057914d74bSmrg compare = _XcursorCompareRed; 3067914d74bSmrg else 3077914d74bSmrg compare = _XcursorCompareBlue; 3084d939ec7Smrg qsort (colors, (size_t) ncolors, sizeof (XcursorPixel), compare); 3097914d74bSmrg /* 3107914d74bSmrg * Compute average colors on both sides of the cut 3117914d74bSmrg */ 3127914d74bSmrg split = ncolors >> 1; 3137914d74bSmrg leftColor = _XcursorAverageColor (colors, split); 3147914d74bSmrg centerColor = colors[split]; 3157914d74bSmrg rightColor = _XcursorAverageColor (colors + split, ncolors - split); 3167914d74bSmrg /* 3177914d74bSmrg * Select best color for each pixel 3187914d74bSmrg */ 3197914d74bSmrg pn = pixels; 3204d939ec7Smrg for (y = 0; (unsigned) y < image->height; y++) 3214d939ec7Smrg for (x = 0; (unsigned) x < image->width; x++) 3227914d74bSmrg { 3237914d74bSmrg p = *pn++; 3247914d74bSmrg if (p & 0xff000000) 3257914d74bSmrg { 3267914d74bSmrg XPutPixel (msk_image, x, y, 1); 3277914d74bSmrg if ((*compare) (&p, ¢erColor) >= 0) 3287914d74bSmrg XPutPixel (src_image, x, y, 0); 3297914d74bSmrg else 3307914d74bSmrg XPutPixel (src_image, x, y, 1); 3317914d74bSmrg } 3327914d74bSmrg else 3337914d74bSmrg { 3347914d74bSmrg XPutPixel (msk_image, x, y, 0); 3357914d74bSmrg XPutPixel (src_image, x, y, 0); 3367914d74bSmrg } 3377914d74bSmrg } 3387914d74bSmrg free (temp); 3397914d74bSmrg _XcursorPixelToColor (rightColor, &core->off_color); 3407914d74bSmrg _XcursorPixelToColor (leftColor, &core->on_color); 3417914d74bSmrg return True; 3427914d74bSmrg} 3437914d74bSmrg 3447914d74bSmrg#if 0 3457914d74bSmrg#define DITHER_DIM 4 3467914d74bSmrgstatic XcursorPixel orderedDither[4][4] = { 3477914d74bSmrg { 1, 9, 3, 11 }, 3487914d74bSmrg { 13, 5, 15, 7 }, 3497914d74bSmrg { 4, 12, 2, 10 }, 3507914d74bSmrg { 16, 8, 14, 6 } 3517914d74bSmrg}; 3527914d74bSmrg#else 3537914d74bSmrg#define DITHER_DIM 2 3547914d74bSmrgstatic XcursorPixel orderedDither[2][2] = { 3557914d74bSmrg { 1, 3, }, 3567914d74bSmrg { 4, 2, }, 3577914d74bSmrg}; 3587914d74bSmrg#endif 3597914d74bSmrg 3607914d74bSmrg#define DITHER_SIZE ((sizeof orderedDither / sizeof orderedDither[0][0]) + 1) 3617914d74bSmrg 3627914d74bSmrgstatic Bool 3637914d74bSmrg_XcursorBayerOrderedDither (const XcursorImage *image, XcursorCoreCursor *core) 3647914d74bSmrg{ 3657914d74bSmrg int x, y; 3667914d74bSmrg XcursorPixel *pixel, p; 3677914d74bSmrg XcursorPixel a, i, d; 3687914d74bSmrg 3697914d74bSmrg pixel = image->pixels; 3704d939ec7Smrg for (y = 0; (unsigned) y < image->height; y++) 3714d939ec7Smrg for (x = 0; (unsigned) x < image->width; x++) 3727914d74bSmrg { 3737914d74bSmrg p = *pixel++; 3744d939ec7Smrg a = (XcursorPixel) (((p >> 24) * DITHER_SIZE + 127) / 255); 3754d939ec7Smrg i = (XcursorPixel) ((_XcursorPixelBrightness (p) * DITHER_SIZE + 127) / 255); 3767914d74bSmrg d = orderedDither[y&(DITHER_DIM-1)][x&(DITHER_DIM-1)]; 3777914d74bSmrg if (a > d) 3787914d74bSmrg { 3797914d74bSmrg XPutPixel (core->msk_image, x, y, 1); 3807914d74bSmrg if (i > d) 3817914d74bSmrg XPutPixel (core->src_image, x, y, 0); /* white */ 3827914d74bSmrg else 3837914d74bSmrg XPutPixel (core->src_image, x, y, 1); /* black */ 3847914d74bSmrg } 3857914d74bSmrg else 3867914d74bSmrg { 3877914d74bSmrg XPutPixel (core->msk_image, x, y, 0); 3887914d74bSmrg XPutPixel (core->src_image, x, y, 0); 3897914d74bSmrg } 3907914d74bSmrg } 3917914d74bSmrg core->on_color.red = 0; 3927914d74bSmrg core->on_color.green = 0; 3937914d74bSmrg core->on_color.blue = 0; 3947914d74bSmrg core->off_color.red = 0xffff; 3957914d74bSmrg core->off_color.green = 0xffff; 3967914d74bSmrg core->off_color.blue = 0xffff; 3977914d74bSmrg return True; 3987914d74bSmrg} 3997914d74bSmrg 4007914d74bSmrgstatic Bool 4017914d74bSmrg_XcursorFloydSteinberg (const XcursorImage *image, XcursorCoreCursor *core) 4027914d74bSmrg{ 4037914d74bSmrg int *aPicture, *iPicture, *aP, *iP; 4047914d74bSmrg XcursorPixel *pixel, p; 4057914d74bSmrg int aR, iR, aA, iA; 4069d0ccd10Smrg unsigned int npixels = image->width * image->height; 4077914d74bSmrg int n; 4087914d74bSmrg int right = 1; 4094d939ec7Smrg int belowLeft = (int) (image->width - 1); 4104d939ec7Smrg int below = (int) image->width; 4114d939ec7Smrg int belowRight = (int) (image->width + 1); 4127914d74bSmrg int iError, aError; 4137914d74bSmrg int iErrorRight, aErrorRight; 4147914d74bSmrg int iErrorBelowLeft, aErrorBelowLeft; 4157914d74bSmrg int iErrorBelow, aErrorBelow; 4167914d74bSmrg int iErrorBelowRight, aErrorBelowRight; 4177914d74bSmrg int x, y; 4187914d74bSmrg int max_inten, min_inten, mean_inten; 4197914d74bSmrg 4207914d74bSmrg iPicture = malloc (npixels * sizeof (int) * 2); 4217914d74bSmrg if (!iPicture) 4227914d74bSmrg return False; 4237914d74bSmrg aPicture = iPicture + npixels; 4247914d74bSmrg 4257914d74bSmrg /* 4267914d74bSmrg * Compute raw gray and alpha arrays 4277914d74bSmrg */ 4287914d74bSmrg pixel = image->pixels; 4297914d74bSmrg iP = iPicture; 4307914d74bSmrg aP = aPicture; 4314d939ec7Smrg n = (int) npixels; 4327914d74bSmrg max_inten = 0; 4337914d74bSmrg min_inten = 0xff; 4344d939ec7Smrg while (n-- > 0) 4357914d74bSmrg { 4367914d74bSmrg p = *pixel++; 4377914d74bSmrg *aP++ = (int) (p >> 24); 4387914d74bSmrg iR = (int) _XcursorPixelBrightness (p); 4397914d74bSmrg if (iR > max_inten) max_inten = iR; 4407914d74bSmrg if (iR < min_inten) min_inten = iR; 4417914d74bSmrg *iP++ = iR; 4427914d74bSmrg } 4437914d74bSmrg /* 4447914d74bSmrg * Draw the image while diffusing the error 4457914d74bSmrg */ 4467914d74bSmrg iP = iPicture; 4477914d74bSmrg aP = aPicture; 4487914d74bSmrg mean_inten = (max_inten + min_inten + 1) >> 1; 4494d939ec7Smrg for (y = 0; (unsigned) y < image->height; y++) 4504d939ec7Smrg for (x = 0; (unsigned) x < image->width; x++) 4517914d74bSmrg { 4527914d74bSmrg aR = *aP; 4537914d74bSmrg iR = *iP; 4547914d74bSmrg if (aR >= 0x80) 4557914d74bSmrg { 4567914d74bSmrg XPutPixel (core->msk_image, x, y, 1); 4577914d74bSmrg aA = 0xff; 4587914d74bSmrg } 4597914d74bSmrg else 4607914d74bSmrg { 4617914d74bSmrg XPutPixel (core->msk_image, x, y, 0); 4627914d74bSmrg aA = 0x00; 4637914d74bSmrg } 4647914d74bSmrg if (iR >= mean_inten) 4657914d74bSmrg { 4667914d74bSmrg XPutPixel (core->src_image, x, y, 0); 4677914d74bSmrg iA = max_inten; 4687914d74bSmrg } 4697914d74bSmrg else 4707914d74bSmrg { 4717914d74bSmrg XPutPixel (core->src_image, x, y, 1); 4727914d74bSmrg iA = min_inten; 4737914d74bSmrg } 4747914d74bSmrg iError = iR - iA; 4757914d74bSmrg aError = aR - aA; 4767914d74bSmrg iErrorRight = (iError * 7) >> 4; 4777914d74bSmrg iErrorBelowLeft = (iError * 3) >> 4; 4787914d74bSmrg iErrorBelow = (iError * 5) >> 4; 479e6d5e4e0Smrg iErrorBelowRight = (iError - iErrorRight - 4807914d74bSmrg iErrorBelowLeft - iErrorBelow); 4817914d74bSmrg aErrorRight = (aError * 7) >> 4; 4827914d74bSmrg aErrorBelowLeft = (aError * 3) >> 4; 4837914d74bSmrg aErrorBelow = (aError * 5) >> 4; 484e6d5e4e0Smrg aErrorBelowRight = (aError - aErrorRight - 4857914d74bSmrg aErrorBelowLeft - aErrorBelow); 4864d939ec7Smrg if (x < ((int)image->width - 1)) 4877914d74bSmrg { 488e6d5e4e0Smrg iP[right] += iErrorRight; 4897914d74bSmrg aP[right] += aErrorRight; 4907914d74bSmrg } 4914d939ec7Smrg if (y < ((int)image->height - 1)) 4927914d74bSmrg { 4937914d74bSmrg if (x) 4947914d74bSmrg { 4957914d74bSmrg iP[belowLeft] += iErrorBelowLeft; 4967914d74bSmrg aP[belowLeft] += aErrorBelowLeft; 4977914d74bSmrg } 4987914d74bSmrg iP[below] += iErrorBelow; 4997914d74bSmrg aP[below] += aErrorBelow; 5004d939ec7Smrg if (x < ((int)image->width - 1)) 5017914d74bSmrg { 5027914d74bSmrg iP[belowRight] += iErrorBelowRight; 5037914d74bSmrg aP[belowRight] += aErrorBelowRight; 5047914d74bSmrg } 5057914d74bSmrg } 5067914d74bSmrg aP++; 5077914d74bSmrg iP++; 5087914d74bSmrg } 5097914d74bSmrg free (iPicture); 5107914d74bSmrg core->on_color.red = 511e6d5e4e0Smrg core->on_color.green = 5124d939ec7Smrg core->on_color.blue = (unsigned short) (min_inten | min_inten << 8); 513e6d5e4e0Smrg core->off_color.red = 5147914d74bSmrg core->off_color.green = 5154d939ec7Smrg core->off_color.blue = (unsigned short) (max_inten | max_inten << 8); 5167914d74bSmrg return True; 5177914d74bSmrg} 5187914d74bSmrg 5197914d74bSmrgstatic Bool 5207914d74bSmrg_XcursorThreshold (const XcursorImage *image, XcursorCoreCursor *core) 5217914d74bSmrg{ 5227914d74bSmrg XcursorPixel *pixel, p; 5237914d74bSmrg int x, y; 5247914d74bSmrg 5257914d74bSmrg /* 5267914d74bSmrg * Draw the image, picking black for dark pixels and white for light 5277914d74bSmrg */ 5287914d74bSmrg pixel = image->pixels; 5294d939ec7Smrg for (y = 0; (unsigned) y < image->height; y++) 5304d939ec7Smrg for (x = 0; (unsigned) x < image->width; x++) 5317914d74bSmrg { 5327914d74bSmrg p = *pixel++; 5337914d74bSmrg if ((p >> 24) >= 0x80) 5347914d74bSmrg { 5357914d74bSmrg XPutPixel (core->msk_image, x, y, 1); 5367914d74bSmrg if (_XcursorPixelBrightness (p) > 0x80) 5377914d74bSmrg XPutPixel (core->src_image, x, y, 0); 5387914d74bSmrg else 5397914d74bSmrg XPutPixel (core->src_image, x, y, 1); 5407914d74bSmrg } 5417914d74bSmrg else 5427914d74bSmrg { 5437914d74bSmrg XPutPixel (core->msk_image, x, y, 0); 5447914d74bSmrg XPutPixel (core->src_image, x, y, 0); 5457914d74bSmrg } 5467914d74bSmrg } 5477914d74bSmrg core->on_color.red = 548e6d5e4e0Smrg core->on_color.green = 5497914d74bSmrg core->on_color.blue = 0; 550e6d5e4e0Smrg core->off_color.red = 5517914d74bSmrg core->off_color.green = 5527914d74bSmrg core->off_color.blue = 0xffff; 5537914d74bSmrg return True; 5547914d74bSmrg} 5557914d74bSmrg 5567914d74bSmrgCursor 5577914d74bSmrgXcursorImageLoadCursor (Display *dpy, const XcursorImage *image) 5587914d74bSmrg{ 5597914d74bSmrg Cursor cursor; 560e6d5e4e0Smrg 5617914d74bSmrg#if RENDER_MAJOR > 0 || RENDER_MINOR >= 5 5627914d74bSmrg if (XcursorSupportsARGB (dpy)) 5637914d74bSmrg { 5647914d74bSmrg XImage ximage; 5657914d74bSmrg int screen = DefaultScreen (dpy); 5667914d74bSmrg Pixmap pixmap; 5677914d74bSmrg Picture picture; 5687914d74bSmrg GC gc; 5697914d74bSmrg XRenderPictFormat *format; 5707914d74bSmrg 5714d939ec7Smrg ximage.width = (int) image->width; 5724d939ec7Smrg ximage.height = (int) image->height; 5737914d74bSmrg ximage.xoffset = 0; 5747914d74bSmrg ximage.format = ZPixmap; 5757914d74bSmrg ximage.data = (char *) image->pixels; 5767914d74bSmrg ximage.byte_order = nativeByteOrder (); 5777914d74bSmrg ximage.bitmap_unit = 32; 5787914d74bSmrg ximage.bitmap_bit_order = ximage.byte_order; 5797914d74bSmrg ximage.bitmap_pad = 32; 5807914d74bSmrg ximage.depth = 32; 5817914d74bSmrg ximage.bits_per_pixel = 32; 5824d939ec7Smrg ximage.bytes_per_line = (int) (image->width * 4); 5837914d74bSmrg ximage.red_mask = 0xff0000; 5847914d74bSmrg ximage.green_mask = 0x00ff00; 5857914d74bSmrg ximage.blue_mask = 0x0000ff; 5867914d74bSmrg ximage.obdata = NULL; 5877914d74bSmrg if (!XInitImage (&ximage)) 5887914d74bSmrg return None; 5897914d74bSmrg pixmap = XCreatePixmap (dpy, RootWindow (dpy, screen), 5907914d74bSmrg image->width, image->height, 32); 5917914d74bSmrg gc = XCreateGC (dpy, pixmap, 0, NULL); 592e6d5e4e0Smrg XPutImage (dpy, pixmap, gc, &ximage, 5937914d74bSmrg 0, 0, 0, 0, image->width, image->height); 5947914d74bSmrg XFreeGC (dpy, gc); 5957914d74bSmrg format = XRenderFindStandardFormat (dpy, PictStandardARGB32); 5967914d74bSmrg picture = XRenderCreatePicture (dpy, pixmap, format, 0, NULL); 5977914d74bSmrg XFreePixmap (dpy, pixmap); 598e6d5e4e0Smrg cursor = XRenderCreateCursor (dpy, picture, 5997914d74bSmrg image->xhot, image->yhot); 6007914d74bSmrg XRenderFreePicture (dpy, picture); 6017914d74bSmrg } 6027914d74bSmrg else 6037914d74bSmrg#endif 6047914d74bSmrg { 6057914d74bSmrg XcursorDisplayInfo *info = _XcursorGetDisplayInfo (dpy); 6067914d74bSmrg int screen = DefaultScreen (dpy); 6077914d74bSmrg XcursorCoreCursor core; 6087914d74bSmrg Pixmap src_pixmap, msk_pixmap; 6097914d74bSmrg GC gc; 6107914d74bSmrg XGCValues gcv; 6117914d74bSmrg 612e6d5e4e0Smrg if (!info) 613e6d5e4e0Smrg return 0; 614e6d5e4e0Smrg 6157914d74bSmrg core.src_image = XCreateImage (dpy, NULL, 1, ZPixmap, 6167914d74bSmrg 0, NULL, image->width, image->height, 6177914d74bSmrg 32, 0); 618e6d5e4e0Smrg core.src_image->data = Xmalloc (image->height * 6194d939ec7Smrg (unsigned) core.src_image->bytes_per_line); 6207914d74bSmrg core.msk_image = XCreateImage (dpy, NULL, 1, ZPixmap, 6217914d74bSmrg 0, NULL, image->width, image->height, 6227914d74bSmrg 32, 0); 623e6d5e4e0Smrg core.msk_image->data = Xmalloc (image->height * 6244d939ec7Smrg (unsigned) core.msk_image->bytes_per_line); 6257914d74bSmrg 6267914d74bSmrg switch (info->dither) { 6277914d74bSmrg case XcursorDitherThreshold: 6287914d74bSmrg if (!_XcursorThreshold (image, &core)) 6297914d74bSmrg return 0; 6307914d74bSmrg break; 6317914d74bSmrg case XcursorDitherMedian: 6327914d74bSmrg if (!_XcursorHeckbertMedianCut (image, &core)) 6337914d74bSmrg return 0; 6347914d74bSmrg break; 6357914d74bSmrg case XcursorDitherOrdered: 6367914d74bSmrg if (!_XcursorBayerOrderedDither (image, &core)) 6377914d74bSmrg return 0; 6387914d74bSmrg break; 6397914d74bSmrg case XcursorDitherDiffuse: 6407914d74bSmrg if (!_XcursorFloydSteinberg (image, &core)) 6417914d74bSmrg return 0; 6427914d74bSmrg break; 6437914d74bSmrg default: 6447914d74bSmrg return 0; 6457914d74bSmrg } 6467914d74bSmrg 6477914d74bSmrg /* 6487914d74bSmrg * Create the cursor 6497914d74bSmrg */ 6507914d74bSmrg src_pixmap = XCreatePixmap (dpy, RootWindow (dpy, screen), 6517914d74bSmrg image->width, image->height, 1); 6527914d74bSmrg msk_pixmap = XCreatePixmap (dpy, RootWindow (dpy, screen), 6537914d74bSmrg image->width, image->height, 1); 6547914d74bSmrg gcv.foreground = 1; 6557914d74bSmrg gcv.background = 0; 656e6d5e4e0Smrg gc = XCreateGC (dpy, src_pixmap, 6577914d74bSmrg GCForeground|GCBackground, 6587914d74bSmrg &gcv); 6597914d74bSmrg XPutImage (dpy, src_pixmap, gc, core.src_image, 6607914d74bSmrg 0, 0, 0, 0, image->width, image->height); 661e6d5e4e0Smrg 6627914d74bSmrg XPutImage (dpy, msk_pixmap, gc, core.msk_image, 6637914d74bSmrg 0, 0, 0, 0, image->width, image->height); 6647914d74bSmrg XFreeGC (dpy, gc); 665e6d5e4e0Smrg 6667914d74bSmrg#ifdef DEBUG_IMAGE 6677914d74bSmrg _XcursorDumpColor (&core.on_color, "on_color"); 6687914d74bSmrg _XcursorDumpColor (&core.off_color, "off_color"); 6697914d74bSmrg _XcursorDumpImage (core.src_image); 6707914d74bSmrg _XcursorDumpImage (core.msk_image); 6717914d74bSmrg#endif 6727914d74bSmrg XDestroyImage (core.src_image); 6737914d74bSmrg XDestroyImage (core.msk_image); 6747914d74bSmrg 6757914d74bSmrg cursor = XCreatePixmapCursor (dpy, src_pixmap, msk_pixmap, 6767914d74bSmrg &core.on_color, &core.off_color, 6777914d74bSmrg image->xhot, image->yhot); 6787914d74bSmrg XFreePixmap (dpy, src_pixmap); 6797914d74bSmrg XFreePixmap (dpy, msk_pixmap); 6807914d74bSmrg } 6817914d74bSmrg return cursor; 6827914d74bSmrg} 6837914d74bSmrg 6847914d74bSmrgXcursorCursors * 6857914d74bSmrgXcursorImagesLoadCursors (Display *dpy, const XcursorImages *images) 6867914d74bSmrg{ 6877914d74bSmrg XcursorCursors *cursors = XcursorCursorsCreate (dpy, images->nimage); 6887914d74bSmrg int n; 6897914d74bSmrg 6907914d74bSmrg if (!cursors) 6917914d74bSmrg return NULL; 6927914d74bSmrg for (n = 0; n < images->nimage; n++) 6937914d74bSmrg { 6947914d74bSmrg cursors->cursors[n] = XcursorImageLoadCursor (dpy, images->images[n]); 6957914d74bSmrg if (!cursors->cursors[n]) 6967914d74bSmrg { 6977914d74bSmrg XcursorCursorsDestroy (cursors); 6987914d74bSmrg return NULL; 6997914d74bSmrg } 7007914d74bSmrg cursors->ncursor++; 7017914d74bSmrg } 7027914d74bSmrg return cursors; 7037914d74bSmrg} 7047914d74bSmrg 7057914d74bSmrgCursor 7067914d74bSmrgXcursorImagesLoadCursor (Display *dpy, const XcursorImages *images) 7077914d74bSmrg{ 7087914d74bSmrg Cursor cursor; 7097914d74bSmrg if (images->nimage == 1 || !XcursorSupportsAnim (dpy)) 7107914d74bSmrg cursor = XcursorImageLoadCursor (dpy, images->images[0]); 7117914d74bSmrg else 7127914d74bSmrg { 7137914d74bSmrg XcursorCursors *cursors = XcursorImagesLoadCursors (dpy, images); 7147914d74bSmrg XAnimCursor *anim; 7157914d74bSmrg int n; 716e6d5e4e0Smrg 7177914d74bSmrg if (!cursors) 7187914d74bSmrg return 0; 7194d939ec7Smrg anim = malloc ((size_t) cursors->ncursor * sizeof (XAnimCursor)); 7207914d74bSmrg if (!anim) 7217914d74bSmrg { 7227914d74bSmrg XcursorCursorsDestroy (cursors); 7237914d74bSmrg return 0; 7247914d74bSmrg } 7257914d74bSmrg for (n = 0; n < cursors->ncursor; n++) 7267914d74bSmrg { 7277914d74bSmrg anim[n].cursor = cursors->cursors[n]; 7287914d74bSmrg anim[n].delay = images->images[n]->delay; 7297914d74bSmrg } 7307914d74bSmrg cursor = XRenderCreateAnimCursor (dpy, cursors->ncursor, anim); 7317914d74bSmrg XcursorCursorsDestroy(cursors); 7327914d74bSmrg free (anim); 7337914d74bSmrg } 7347914d74bSmrg#if defined HAVE_XFIXES && XFIXES_MAJOR >= 2 7357914d74bSmrg if (images->name) 7367914d74bSmrg XFixesSetCursorName (dpy, cursor, images->name); 7377914d74bSmrg#endif 7387914d74bSmrg return cursor; 7397914d74bSmrg} 7407914d74bSmrg 7417914d74bSmrg 7427914d74bSmrgCursor 7437914d74bSmrgXcursorFilenameLoadCursor (Display *dpy, const char *file) 7447914d74bSmrg{ 7457914d74bSmrg int size = XcursorGetDefaultSize (dpy); 7466b7436aeSmrg XcursorBool resize = XcursorGetResizable (dpy); 7476b7436aeSmrg XcursorImages *images; 7487914d74bSmrg Cursor cursor; 749e6d5e4e0Smrg 7506b7436aeSmrg images = _XcursorFilenameLoadImages (file, size, resize); 7517914d74bSmrg if (!images) 7527914d74bSmrg return None; 7537914d74bSmrg cursor = XcursorImagesLoadCursor (dpy, images); 7547914d74bSmrg XcursorImagesDestroy (images); 7557914d74bSmrg return cursor; 7567914d74bSmrg} 7577914d74bSmrg 7587914d74bSmrgXcursorCursors * 7597914d74bSmrgXcursorFilenameLoadCursors (Display *dpy, const char *file) 7607914d74bSmrg{ 7617914d74bSmrg int size = XcursorGetDefaultSize (dpy); 7626b7436aeSmrg XcursorBool resize = XcursorGetResizable (dpy); 7636b7436aeSmrg XcursorImages *images; 7647914d74bSmrg XcursorCursors *cursors; 765e6d5e4e0Smrg 7666b7436aeSmrg images = _XcursorFilenameLoadImages (file, size, resize); 7677914d74bSmrg if (!images) 7687914d74bSmrg return NULL; 7697914d74bSmrg cursors = XcursorImagesLoadCursors (dpy, images); 7707914d74bSmrg XcursorImagesDestroy (images); 7717914d74bSmrg return cursors; 7727914d74bSmrg} 7737914d74bSmrg 7747914d74bSmrg/* 7757914d74bSmrg * Stolen from XCreateGlyphCursor (which we cruelly override) 7767914d74bSmrg */ 7777914d74bSmrg 7787914d74bSmrgCursor 7797914d74bSmrg_XcursorCreateGlyphCursor(Display *dpy, 7807914d74bSmrg Font source_font, 7817914d74bSmrg Font mask_font, 7827914d74bSmrg unsigned int source_char, 7837914d74bSmrg unsigned int mask_char, 7847914d74bSmrg XColor _Xconst *foreground, 7857914d74bSmrg XColor _Xconst *background) 786e6d5e4e0Smrg{ 7877914d74bSmrg Cursor cid; 7887914d74bSmrg register xCreateGlyphCursorReq *req; 7897914d74bSmrg 7907914d74bSmrg LockDisplay(dpy); 7917914d74bSmrg GetReq(CreateGlyphCursor, req); 7924d939ec7Smrg cid = req->cid = (CARD32) XAllocID(dpy); 7934d939ec7Smrg req->source = (CARD32) source_font; 7944d939ec7Smrg req->mask = (CARD32) mask_font; 7954d939ec7Smrg req->sourceChar = (CARD16) source_char; 7964d939ec7Smrg req->maskChar = (CARD16) mask_char; 7977914d74bSmrg req->foreRed = foreground->red; 7987914d74bSmrg req->foreGreen = foreground->green; 7997914d74bSmrg req->foreBlue = foreground->blue; 8007914d74bSmrg req->backRed = background->red; 8017914d74bSmrg req->backGreen = background->green; 8027914d74bSmrg req->backBlue = background->blue; 8037914d74bSmrg UnlockDisplay(dpy); 8047914d74bSmrg SyncHandle(); 8057914d74bSmrg return (cid); 8067914d74bSmrg} 8077914d74bSmrg 8087914d74bSmrg/* 8097914d74bSmrg * Stolen from XCreateFontCursor (which we cruelly override) 8107914d74bSmrg */ 8117914d74bSmrg 8127914d74bSmrgCursor 8137914d74bSmrg_XcursorCreateFontCursor (Display *dpy, unsigned int shape) 8147914d74bSmrg{ 8154d939ec7Smrg#define DATA(c) { 0UL, c, c, c, 0, 0 } 8164d939ec7Smrg static XColor _Xconst foreground = DATA(0); /* black */ 8174d939ec7Smrg static XColor _Xconst background = DATA(65535); /* white */ 8184d939ec7Smrg#undef DATA 8197914d74bSmrg 820e6d5e4e0Smrg /* 8217914d74bSmrg * the cursor font contains the shape glyph followed by the mask 8227914d74bSmrg * glyph; so character position 0 contains a shape, 1 the mask for 0, 8237914d74bSmrg * 2 a shape, etc. <X11/cursorfont.h> contains hash define names 8247914d74bSmrg * for all of these. 8257914d74bSmrg */ 8267914d74bSmrg 827e6d5e4e0Smrg if (dpy->cursor_font == None) 8287914d74bSmrg { 8297914d74bSmrg dpy->cursor_font = XLoadFont (dpy, CURSORFONT); 8307914d74bSmrg if (dpy->cursor_font == None) 8317914d74bSmrg return None; 8327914d74bSmrg } 8337914d74bSmrg 834e6d5e4e0Smrg return _XcursorCreateGlyphCursor (dpy, dpy->cursor_font, dpy->cursor_font, 8357914d74bSmrg shape, shape + 1, &foreground, &background); 8367914d74bSmrg} 8377914d74bSmrg 838