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, &centerColor) >= 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