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, ¢erColor) >= 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