/* * $Id: xftdraw.c,v 1.1 2006/11/25 17:21:38 matthieu Exp $ * * Copyright © 2000 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "xftint.h" /* * Ok, this is a pain. To share source pictures across multiple destinations, * the screen for each drawable must be discovered. */ static int _XftDrawScreen (Display *dpy, Drawable drawable, Visual *visual) { int s; Window root; int x, y; unsigned int width, height, borderWidth, depth; /* Special case the most common environment */ if (ScreenCount (dpy) == 1) return 0; /* * If we've got a visual, look for the screen that points at it. * This requires no round trip. */ if (visual) { for (s = 0; s < ScreenCount (dpy); s++) { XVisualInfo template, *ret; int nret; template.visualid = visual->visualid; template.screen = s; ret = XGetVisualInfo (dpy, VisualIDMask|VisualScreenMask, &template, &nret); if (ret) { XFree (ret); return s; } } } /* * Otherwise, as the server for the drawable geometry and find * the screen from the root window. * This takes a round trip. */ if (XGetGeometry (dpy, drawable, &root, &x, &y, &width, &height, &borderWidth, &depth)) { for (s = 0; s < ScreenCount (dpy); s++) { if (RootWindow (dpy, s) == root) return s; } } /* * Make a guess -- it's probably wrong, but then the app probably * handed us a bogus drawable in this case */ return 0; } _X_HIDDEN unsigned int XftDrawDepth (XftDraw *draw) { if (!draw->depth) { Window root; int x, y; unsigned int width, height, borderWidth, depth; if (XGetGeometry (draw->dpy, draw->drawable, &root, &x, &y, &width, &height, &borderWidth, &depth)) draw->depth = depth; } return draw->depth; } _X_HIDDEN unsigned int XftDrawBitsPerPixel (XftDraw *draw) { if (!draw->bits_per_pixel) { XPixmapFormatValues *formats; int nformats; unsigned int depth; if ((depth = XftDrawDepth (draw)) && (formats = XListPixmapFormats (draw->dpy, &nformats))) { int i; for (i = 0; i < nformats; i++) { if (formats[i].depth == depth) { draw->bits_per_pixel = formats[i].bits_per_pixel; break; } } XFree (formats); } } return draw->bits_per_pixel; } _X_EXPORT XftDraw * XftDrawCreate (Display *dpy, Drawable drawable, Visual *visual, Colormap colormap) { XftDraw *draw; draw = (XftDraw *) malloc (sizeof (XftDraw)); if (!draw) return 0; draw->dpy = dpy; draw->drawable = drawable; draw->screen = _XftDrawScreen (dpy, drawable, visual); draw->depth = 0; /* don't find out unless we need to know */ draw->bits_per_pixel = 0; /* don't find out unless we need to know */ draw->visual = visual; draw->colormap = colormap; draw->render.pict = 0; draw->core.gc = 0; draw->core.use_pixmap = 0; draw->clip_type = XftClipTypeNone; draw->subwindow_mode = ClipByChildren; XftMemAlloc (XFT_MEM_DRAW, sizeof (XftDraw)); return draw; } _X_EXPORT XftDraw * XftDrawCreateBitmap (Display *dpy, Pixmap bitmap) { XftDraw *draw; draw = (XftDraw *) malloc (sizeof (XftDraw)); if (!draw) return 0; draw->dpy = dpy; draw->drawable = (Drawable) bitmap; draw->screen = _XftDrawScreen (dpy, bitmap, 0); draw->depth = 1; draw->bits_per_pixel = 1; draw->visual = 0; draw->colormap = 0; draw->render.pict = 0; draw->core.gc = 0; draw->core.use_pixmap = 0; draw->clip_type = XftClipTypeNone; draw->subwindow_mode = ClipByChildren; XftMemAlloc (XFT_MEM_DRAW, sizeof (XftDraw)); return draw; } _X_EXPORT XftDraw * XftDrawCreateAlpha (Display *dpy, Pixmap pixmap, int depth) { XftDraw *draw; draw = (XftDraw *) malloc (sizeof (XftDraw)); if (!draw) return 0; draw->dpy = dpy; draw->drawable = (Drawable) pixmap; draw->screen = _XftDrawScreen (dpy, pixmap, 0); draw->depth = depth; draw->bits_per_pixel = 0; /* don't find out until we need it */ draw->visual = 0; draw->colormap = 0; draw->render.pict = 0; draw->core.gc = 0; draw->core.use_pixmap = 0; draw->clip_type = XftClipTypeNone; draw->subwindow_mode = ClipByChildren; XftMemAlloc (XFT_MEM_DRAW, sizeof (XftDraw)); return draw; } static XRenderPictFormat * _XftDrawFormat (XftDraw *draw) { XftDisplayInfo *info = _XftDisplayInfoGet (draw->dpy, True); if (!info || !info->hasRender) return 0; if (draw->visual == 0) { XRenderPictFormat pf; pf.type = PictTypeDirect; pf.depth = XftDrawDepth (draw); pf.direct.alpha = 0; pf.direct.alphaMask = (1 << pf.depth) - 1; return XRenderFindFormat (draw->dpy, (PictFormatType| PictFormatDepth| PictFormatAlpha| PictFormatAlphaMask), &pf, 0); } else return XRenderFindVisualFormat (draw->dpy, draw->visual); } _X_EXPORT void XftDrawChange (XftDraw *draw, Drawable drawable) { draw->drawable = drawable; if (draw->render.pict) { XRenderFreePicture (draw->dpy, draw->render.pict); draw->render.pict = 0; } if (draw->core.gc) { XFreeGC (draw->dpy, draw->core.gc); draw->core.gc = 0; } } _X_EXPORT Display * XftDrawDisplay (XftDraw *draw) { return draw->dpy; } _X_EXPORT Drawable XftDrawDrawable (XftDraw *draw) { return draw->drawable; } _X_EXPORT Colormap XftDrawColormap (XftDraw *draw) { return draw->colormap; } _X_EXPORT Visual * XftDrawVisual (XftDraw *draw) { return draw->visual; } _X_EXPORT void XftDrawDestroy (XftDraw *draw) { if (draw->render.pict) XRenderFreePicture (draw->dpy, draw->render.pict); if (draw->core.gc) XFreeGC (draw->dpy, draw->core.gc); switch (draw->clip_type) { case XftClipTypeRegion: XDestroyRegion (draw->clip.region); break; case XftClipTypeRectangles: free (draw->clip.rect); break; case XftClipTypeNone: break; } XftMemFree (XFT_MEM_DRAW, sizeof (XftDraw)); free (draw); } _X_EXPORT Picture XftDrawSrcPicture (XftDraw *draw, _Xconst XftColor *color) { Display *dpy = draw->dpy; XftDisplayInfo *info = _XftDisplayInfoGet (dpy, True); int i; XftColor bitmapColor; if (!info) return 0; /* * Monochrome targets require special handling; the PictOp controls * the color, and the color must be opaque */ if (!draw->visual && draw->depth == 1) { bitmapColor.color.alpha = 0xffff; bitmapColor.color.red = 0xffff; bitmapColor.color.green = 0xffff; bitmapColor.color.blue = 0xffff; color = &bitmapColor; } /* * See if there's one already available */ for (i = 0; i < XFT_NUM_SOLID_COLOR; i++) { if (info->colors[i].pict && info->colors[i].screen == draw->screen && !memcmp ((void *) &color->color, (void *) &info->colors[i].color, sizeof (XRenderColor))) return info->colors[i].pict; } /* * Pick one to replace at random */ i = (unsigned int) rand () % XFT_NUM_SOLID_COLOR; /* * Recreate if it was for the wrong screen */ if (info->colors[i].screen != draw->screen && info->colors[i].pict) { XRenderFreePicture (dpy, info->colors[i].pict); info->colors[i].pict = 0; } /* * Create picture if necessary */ if (!info->colors[i].pict) { Pixmap pix; XRenderPictureAttributes pa; pix = XCreatePixmap (dpy, RootWindow (dpy, draw->screen), 1, 1, info->solidFormat->depth); pa.repeat = True; info->colors[i].pict = XRenderCreatePicture (draw->dpy, pix, info->solidFormat, CPRepeat, &pa); XFreePixmap (dpy, pix); } /* * Set to the new color */ info->colors[i].color = color->color; info->colors[i].screen = draw->screen; XRenderFillRectangle (dpy, PictOpSrc, info->colors[i].pict, &color->color, 0, 0, 1, 1); return info->colors[i].pict; } static int _XftDrawOp (_Xconst XftDraw *draw, _Xconst XftColor *color) { if (draw->visual || draw->depth != 1) return PictOpOver; if (color->color.alpha >= 0x8000) return PictOpOver; return PictOpOutReverse; } static FcBool _XftDrawRenderPrepare (XftDraw *draw) { if (!draw->render.pict) { XRenderPictFormat *format; XRenderPictureAttributes pa; unsigned long mask = 0; format = _XftDrawFormat (draw); if (!format) return FcFalse; if (draw->subwindow_mode == IncludeInferiors) { pa.subwindow_mode = IncludeInferiors; mask |= CPSubwindowMode; } draw->render.pict = XRenderCreatePicture (draw->dpy, draw->drawable, format, mask, &pa); if (!draw->render.pict) return FcFalse; switch (draw->clip_type) { case XftClipTypeRegion: XRenderSetPictureClipRegion (draw->dpy, draw->render.pict, draw->clip.region); break; case XftClipTypeRectangles: XRenderSetPictureClipRectangles (draw->dpy, draw->render.pict, draw->clip.rect->xOrigin, draw->clip.rect->yOrigin, XftClipRects(draw->clip.rect), draw->clip.rect->n); break; case XftClipTypeNone: break; } } return FcTrue; } static FcBool _XftDrawCorePrepare (XftDraw *draw, _Xconst XftColor *color) { if (!draw->core.gc) { XGCValues gcv; unsigned long mask = 0; if (draw->subwindow_mode == IncludeInferiors) { gcv.subwindow_mode = IncludeInferiors; mask |= GCSubwindowMode; } draw->core.gc = XCreateGC (draw->dpy, draw->drawable, mask, &gcv); if (!draw->core.gc) return FcFalse; switch (draw->clip_type) { case XftClipTypeRegion: XSetRegion (draw->dpy, draw->core.gc, draw->clip.region); break; case XftClipTypeRectangles: XSetClipRectangles (draw->dpy, draw->core.gc, draw->clip.rect->xOrigin, draw->clip.rect->yOrigin, XftClipRects (draw->clip.rect), draw->clip.rect->n, Unsorted); break; case XftClipTypeNone: break; } } XSetForeground (draw->dpy, draw->core.gc, color->pixel); return FcTrue; } _X_EXPORT Picture XftDrawPicture (XftDraw *draw) { if (!_XftDrawRenderPrepare (draw)) return 0; return draw->render.pict; } #define NUM_LOCAL 1024 _X_EXPORT void XftDrawGlyphs (XftDraw *draw, _Xconst XftColor *color, XftFont *pub, int x, int y, _Xconst FT_UInt *glyphs, int nglyphs) { XftFontInt *font = (XftFontInt *) pub; if (font->format) { Picture src; if (_XftDrawRenderPrepare (draw) && (src = XftDrawSrcPicture (draw, color))) XftGlyphRender (draw->dpy, _XftDrawOp (draw, color), src, pub, draw->render.pict, 0, 0, x, y, glyphs, nglyphs); } else { if (_XftDrawCorePrepare (draw, color)) XftGlyphCore (draw, color, pub, x, y, glyphs, nglyphs); } } _X_EXPORT void XftDrawString8 (XftDraw *draw, _Xconst XftColor *color, XftFont *pub, int x, int y, _Xconst FcChar8 *string, int len) { FT_UInt *glyphs, glyphs_local[NUM_LOCAL]; int i; if (XftDebug () & XFT_DBG_DRAW) printf ("DrawString \"%*.*s\"\n", len, len, string); if (len <= NUM_LOCAL) glyphs = glyphs_local; else { glyphs = malloc (len * sizeof (FT_UInt)); if (!glyphs) return; } for (i = 0; i < len; i++) glyphs[i] = XftCharIndex (draw->dpy, pub, string[i]); XftDrawGlyphs (draw, color, pub, x, y, glyphs, len); if (glyphs != glyphs_local) free (glyphs); } _X_EXPORT void XftDrawString16 (XftDraw *draw, _Xconst XftColor *color, XftFont *pub, int x, int y, _Xconst FcChar16 *string, int len) { FT_UInt *glyphs, glyphs_local[NUM_LOCAL]; int i; if (len <= NUM_LOCAL) glyphs = glyphs_local; else { glyphs = malloc (len * sizeof (FT_UInt)); if (!glyphs) return; } for (i = 0; i < len; i++) glyphs[i] = XftCharIndex (draw->dpy, pub, string[i]); XftDrawGlyphs (draw, color, pub, x, y, glyphs, len); if (glyphs != glyphs_local) free (glyphs); } _X_EXPORT void XftDrawString32 (XftDraw *draw, _Xconst XftColor *color, XftFont *pub, int x, int y, _Xconst FcChar32 *string, int len) { FT_UInt *glyphs, glyphs_local[NUM_LOCAL]; int i; if (len <= NUM_LOCAL) glyphs = glyphs_local; else { glyphs = malloc (len * sizeof (FT_UInt)); if (!glyphs) return; } for (i = 0; i < len; i++) glyphs[i] = XftCharIndex (draw->dpy, pub, string[i]); XftDrawGlyphs (draw, color, pub, x, y, glyphs, len); if (glyphs != glyphs_local) free (glyphs); } _X_EXPORT void XftDrawStringUtf8 (XftDraw *draw, _Xconst XftColor *color, XftFont *pub, int x, int y, _Xconst FcChar8 *string, int len) { FT_UInt *glyphs, *glyphs_new, glyphs_local[NUM_LOCAL]; FcChar32 ucs4; int i; int l; int size; i = 0; glyphs = glyphs_local; size = NUM_LOCAL; while (len && (l = FcUtf8ToUcs4 (string, &ucs4, len)) > 0) { if (i == size) { glyphs_new = malloc (size * 2 * sizeof (FT_UInt)); if (!glyphs_new) { if (glyphs != glyphs_local) free (glyphs); return; } memcpy (glyphs_new, glyphs, size * sizeof (FT_UInt)); size *= 2; if (glyphs != glyphs_local) free (glyphs); glyphs = glyphs_new; } glyphs[i++] = XftCharIndex (draw->dpy, pub, ucs4); string += l; len -= l; } XftDrawGlyphs (draw, color, pub, x, y, glyphs, i); if (glyphs != glyphs_local) free (glyphs); } _X_EXPORT void XftDrawStringUtf16 (XftDraw *draw, _Xconst XftColor *color, XftFont *pub, int x, int y, _Xconst FcChar8 *string, FcEndian endian, int len) { FT_UInt *glyphs, *glyphs_new, glyphs_local[NUM_LOCAL]; FcChar32 ucs4; int i; int l; int size; i = 0; glyphs = glyphs_local; size = NUM_LOCAL; while (len && (l = FcUtf16ToUcs4 (string, endian, &ucs4, len)) > 0) { if (i == size) { glyphs_new = malloc (size * 2 * sizeof (FT_UInt)); if (!glyphs_new) { if (glyphs != glyphs_local) free (glyphs); return; } memcpy (glyphs_new, glyphs, size * sizeof (FT_UInt)); size *= 2; if (glyphs != glyphs_local) free (glyphs); glyphs = glyphs_new; } glyphs[i++] = XftCharIndex (draw->dpy, pub, ucs4); string += l; len -= l; } XftDrawGlyphs (draw, color, pub, x, y, glyphs, i); if (glyphs != glyphs_local) free (glyphs); } _X_EXPORT void XftDrawGlyphSpec (XftDraw *draw, _Xconst XftColor *color, XftFont *pub, _Xconst XftGlyphSpec *glyphs, int len) { XftFontInt *font = (XftFontInt *) pub; if (font->format) { Picture src; if (_XftDrawRenderPrepare (draw) && (src = XftDrawSrcPicture (draw, color))) { XftGlyphSpecRender (draw->dpy, _XftDrawOp (draw, color), src, pub, draw->render.pict, 0, 0, glyphs, len); } } else { if (_XftDrawCorePrepare (draw, color)) XftGlyphSpecCore (draw, color, pub, glyphs, len); } } _X_EXPORT void XftDrawGlyphFontSpec (XftDraw *draw, _Xconst XftColor *color, _Xconst XftGlyphFontSpec *glyphs, int len) { int i; int start; i = 0; while (i < len) { start = i; if (((XftFontInt *) glyphs[i].font)->format) { Picture src; while (i < len && ((XftFontInt *) glyphs[i].font)->format) i++; if (_XftDrawRenderPrepare (draw) && (src = XftDrawSrcPicture (draw, color))) { XftGlyphFontSpecRender (draw->dpy, _XftDrawOp (draw, color), src, draw->render.pict, 0, 0, glyphs + start , i - start); } } else { while (i < len && !((XftFontInt *) glyphs[i].font)->format) i++; if (_XftDrawCorePrepare (draw, color)) XftGlyphFontSpecCore (draw, color, glyphs + start, i - start); } } } _X_EXPORT void XftDrawCharSpec (XftDraw *draw, _Xconst XftColor *color, XftFont *pub, _Xconst XftCharSpec *chars, int len) { XftGlyphSpec *glyphs, glyphs_local[NUM_LOCAL]; int i; if (len <= NUM_LOCAL) glyphs = glyphs_local; else { glyphs = malloc (len * sizeof (XftGlyphSpec)); if (!glyphs) return; } for (i = 0; i < len; i++) { glyphs[i].glyph = XftCharIndex(draw->dpy, pub, chars[i].ucs4); glyphs[i].x = chars[i].x; glyphs[i].y = chars[i].y; } XftDrawGlyphSpec (draw, color, pub, glyphs, len); if (glyphs != glyphs_local) free (glyphs); } _X_EXPORT void XftDrawCharFontSpec (XftDraw *draw, _Xconst XftColor *color, _Xconst XftCharFontSpec *chars, int len) { XftGlyphFontSpec *glyphs, glyphs_local[NUM_LOCAL]; int i; if (len <= NUM_LOCAL) glyphs = glyphs_local; else { glyphs = malloc (len * sizeof (XftGlyphFontSpec)); if (!glyphs) return; } for (i = 0; i < len; i++) { glyphs[i].font = chars[i].font; glyphs[i].glyph = XftCharIndex(draw->dpy, glyphs[i].font, chars[i].ucs4); glyphs[i].x = chars[i].x; glyphs[i].y = chars[i].y; } XftDrawGlyphFontSpec (draw, color, glyphs, len); if (glyphs != glyphs_local) free (glyphs); } _X_EXPORT void XftDrawRect (XftDraw *draw, _Xconst XftColor *color, int x, int y, unsigned int width, unsigned int height) { if (_XftDrawRenderPrepare (draw)) { XRenderFillRectangle (draw->dpy, PictOpSrc, draw->render.pict, &color->color, x, y, width, height); } else if (_XftDrawCorePrepare (draw, color)) { XftRectCore (draw, color, x, y, width, height); } } _X_EXPORT Bool XftDrawSetClip (XftDraw *draw, Region r) { Region n = 0; /* * Check for quick exits */ if (!r && draw->clip_type == XftClipTypeNone) return True; if (r && draw->clip_type == XftClipTypeRegion && XEqualRegion (r, draw->clip.region)) { return True; } /* * Duplicate the region so future changes can be short circuited */ if (r) { n = XCreateRegion (); if (n) { if (!XUnionRegion (n, r, n)) { XDestroyRegion (n); return False; } } } /* * Destroy existing clip */ switch (draw->clip_type) { case XftClipTypeRegion: XDestroyRegion (draw->clip.region); break; case XftClipTypeRectangles: free (draw->clip.rect); break; case XftClipTypeNone: break; } /* * Set the clip */ if (n) { draw->clip_type = XftClipTypeRegion; draw->clip.region = n; } else { draw->clip_type = XftClipTypeNone; } /* * Apply new clip to existing objects */ if (draw->render.pict) { if (n) XRenderSetPictureClipRegion (draw->dpy, draw->render.pict, n); else { XRenderPictureAttributes pa; pa.clip_mask = None; XRenderChangePicture (draw->dpy, draw->render.pict, CPClipMask, &pa); } } if (draw->core.gc) { if (n) XSetRegion (draw->dpy, draw->core.gc, draw->clip.region); else XSetClipMask (draw->dpy, draw->core.gc, None); } return True; } _X_EXPORT Bool XftDrawSetClipRectangles (XftDraw *draw, int xOrigin, int yOrigin, _Xconst XRectangle *rects, int n) { XftClipRect *new = 0; /* * Check for quick exit */ if (draw->clip_type == XftClipTypeRectangles && draw->clip.rect->n == n && (n == 0 || (draw->clip.rect->xOrigin == xOrigin && draw->clip.rect->yOrigin == yOrigin)) && !memcmp (XftClipRects (draw->clip.rect), rects, n * sizeof (XRectangle))) { return True; } /* * Duplicate the region so future changes can be short circuited */ new = malloc (sizeof (XftClipRect) + n * sizeof (XRectangle)); if (!new) return False; new->n = n; new->xOrigin = xOrigin; new->yOrigin = yOrigin; memcpy (XftClipRects (new), rects, n * sizeof (XRectangle)); /* * Destroy existing clip */ switch (draw->clip_type) { case XftClipTypeRegion: XDestroyRegion (draw->clip.region); break; case XftClipTypeRectangles: free (draw->clip.rect); break; case XftClipTypeNone: break; } /* * Set the clip */ draw->clip_type = XftClipTypeRectangles; draw->clip.rect = new; /* * Apply new clip to existing objects */ if (draw->render.pict) { XRenderSetPictureClipRectangles (draw->dpy, draw->render.pict, new->xOrigin, new->yOrigin, XftClipRects(new), new->n); } if (draw->core.gc) { XSetClipRectangles (draw->dpy, draw->core.gc, new->xOrigin, new->yOrigin, XftClipRects (new), new->n, Unsorted); } return True; } _X_EXPORT void XftDrawSetSubwindowMode (XftDraw *draw, int mode) { if (mode == draw->subwindow_mode) return; draw->subwindow_mode = mode; if (draw->render.pict) { XRenderPictureAttributes pa; pa.subwindow_mode = mode; XRenderChangePicture (draw->dpy, draw->render.pict, CPSubwindowMode, &pa); } if (draw->core.gc) XSetSubwindowMode (draw->dpy, draw->core.gc, mode); }