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