diff options
Diffstat (limited to 'xorg-server/hw/xgl/xglglyph.c')
-rw-r--r-- | xorg-server/hw/xgl/xglglyph.c | 1170 |
1 files changed, 1170 insertions, 0 deletions
diff --git a/xorg-server/hw/xgl/xglglyph.c b/xorg-server/hw/xgl/xglglyph.c new file mode 100644 index 000000000..c1a484ac0 --- /dev/null +++ b/xorg-server/hw/xgl/xglglyph.c @@ -0,0 +1,1170 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * 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 + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. 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. + * + * Author: David Reveman <davidr@novell.com> + */ + +#include "xgl.h" + +#ifdef RENDER +#include "gcstruct.h" +#include "picturestr.h" + +#define BITMAP_CACHE_SIZE 256000 +#define BITMAP_CACHE_MAX_LEVEL ~0 +#define BITMAP_CACHE_MAX_SIZE 512 + +#define TEXTURE_CACHE_SIZE 512 +#define TEXTURE_CACHE_MAX_LEVEL 64 +#define TEXTURE_CACHE_MAX_HEIGHT 72 +#define TEXTURE_CACHE_MAX_WIDTH 72 + +#define NEXT_GLYPH_SERIAL_NUMBER ((++glyphSerialNumber) > MAX_SERIAL_NUM ? \ + (glyphSerialNumber = 1): glyphSerialNumber) + +#define GLYPH_GET_AREA_PRIV(pArea) \ + ((xglGlyphAreaPtr) (pArea)->devPrivate.ptr) + +#define GLYPH_AREA_PRIV(pArea) \ + xglGlyphAreaPtr pAreaPriv = GLYPH_GET_AREA_PRIV (pArea) + +#define NEEDS_COMPONENT(f) (PICT_FORMAT_A (f) != 0 && PICT_FORMAT_RGB (f) != 0) + +#define WRITE_VEC2(ptr, _x, _y) \ + *(ptr)++ = (_x); \ + *(ptr)++ = (_y) + +#define WRITE_BOX(ptr, _vx1, _vy1, _vx2, _vy2, box) \ + WRITE_VEC2 (ptr, _vx1, _vy1); \ + WRITE_VEC2 (ptr, (box).x1, (box).y2); \ + WRITE_VEC2 (ptr, _vx2, _vy1); \ + WRITE_VEC2 (ptr, (box).x2, (box).y2); \ + WRITE_VEC2 (ptr, _vx2, _vy2); \ + WRITE_VEC2 (ptr, (box).x2, (box).y1); \ + WRITE_VEC2 (ptr, _vx1, _vy2); \ + WRITE_VEC2 (ptr, (box).x1, (box).y1) + +typedef union _xglGlyphList { + glitz_short_t *s; + glitz_float_t *f; +} xglGlyphListRec, *xglGlyphListPtr; + +typedef struct _xglGlyphArray { + int lastX, lastY; +} xglGlyphArrayRec, *xglGlyphArrayPtr; + +typedef union _xglGlyphVertexData { + xglGlyphArrayRec array; + xglGlyphListRec list; +} xglGlyphVertexDataRec, *xglGlyphVertexDataPtr; + +typedef struct _xglGlyphOp { + GlyphListPtr pLists; + int listLen; + GlyphPtr *ppGlyphs; + int nGlyphs; + int xOff; + int yOff; + Bool noCache; +} xglGlyphOpRec, *xglGlyphOpPtr; + +unsigned long glyphSerialNumber = 0; + +xglAreaRec zeroSizeArea = { + 0, 0, + 0, 0, + 0, 0, + { NULL, NULL, NULL, NULL }, NULL, + (pointer) 0, + { 0 } +}; + +static Bool +xglGlyphCreate (xglAreaPtr pArea) +{ + return TRUE; +} + +static Bool +xglGlyphMoveIn (xglAreaPtr pArea, + pointer closure) +{ + xglGlyphCachePtr pCache = (xglGlyphCachePtr) pArea->pRoot->closure; + GlyphPtr pGlyph = (GlyphPtr) closure; + + XGL_GLYPH_PRIV (pCache->pScreen, pGlyph); + + pGlyphPriv->pArea = pArea; + + return TRUE; +} + +static void +xglGlyphMoveOut (xglAreaPtr pArea, + pointer closure) +{ + xglGlyphCachePtr pCache = (xglGlyphCachePtr) pArea->pRoot->closure; + GlyphPtr pGlyph = (GlyphPtr) closure; + + XGL_GLYPH_PRIV (pCache->pScreen, pGlyph); + + pGlyphPriv->pArea = NULL; +} + +static int +xglGlyphCompareScore (xglAreaPtr pArea, + pointer closure1, + pointer closure2) +{ + GLYPH_AREA_PRIV (pArea); + + if (pAreaPriv->serial == glyphSerialNumber) + return 1; + + return -1; +} + +static const xglAreaFuncsRec xglGlyphAreaFuncs = { + xglGlyphCreate, + xglGlyphMoveIn, + xglGlyphMoveOut, + xglGlyphCompareScore +}; + +Bool +xglRealizeGlyph (ScreenPtr pScreen, + GlyphPtr pGlyph) +{ + PictureScreenPtr pPictureScreen = GetPictureScreen (pScreen); + Bool ret; + + XGL_SCREEN_PRIV (pScreen); + XGL_GLYPH_PRIV (pScreen, pGlyph); + + XGL_PICTURE_SCREEN_UNWRAP (RealizeGlyph); + ret = (*pPictureScreen->RealizeGlyph) (pScreen, pGlyph); + XGL_PICTURE_SCREEN_WRAP (RealizeGlyph, xglRealizeGlyph); + + pGlyphPriv->pArea = NULL; + + return ret; +} + +void +xglUnrealizeGlyph (ScreenPtr pScreen, + GlyphPtr pGlyph) +{ + PictureScreenPtr pPictureScreen = GetPictureScreen (pScreen); + + XGL_SCREEN_PRIV (pScreen); + XGL_GLYPH_PRIV (pScreen, pGlyph); + + XGL_PICTURE_SCREEN_UNWRAP (UnrealizeGlyph); + (*pPictureScreen->UnrealizeGlyph) (pScreen, pGlyph); + XGL_PICTURE_SCREEN_WRAP (UnrealizeGlyph, xglUnrealizeGlyph); + + if (pGlyphPriv->pArea && pGlyphPriv->pArea->width) + xglWithdrawArea (pGlyphPriv->pArea); +} + +Bool +xglInitGlyphCache (xglGlyphCachePtr pCache, + ScreenPtr pScreen, + PictFormatPtr format) +{ + XGL_SCREEN_PRIV (pScreen); + + pCache->depth = format->depth; + + if (!pScreenPriv->pSolidAlpha) + { + xglCreateSolidAlphaPicture (pScreen); + if (!pScreenPriv->pSolidAlpha) + return FALSE; + } + + if (pCache->depth == 1) + { + int stride; + + GEOMETRY_INIT (pScreen, &pCache->u.geometry, + GLITZ_GEOMETRY_TYPE_VERTEX, + GEOMETRY_USAGE_STATIC, BITMAP_CACHE_SIZE); + GEOMETRY_SET_VERTEX_DATA_TYPE (&pCache->u.geometry, + pScreenPriv->geometryDataType); + + stride = pCache->u.geometry.f.vertex.bytes_per_vertex; + if (!xglRootAreaInit (&pCache->rootArea, + BITMAP_CACHE_MAX_LEVEL, + BITMAP_CACHE_SIZE / (stride * 4), + 0, sizeof (xglGlyphAreaRec), + (xglAreaFuncsPtr) &xglGlyphAreaFuncs, + (pointer) pCache)) + { + GEOMETRY_UNINIT (&pCache->u.geometry); + return FALSE; + } + } + else + { + + xglGlyphTexturePtr pTexture = &pCache->u.texture; + glitz_surface_t *mask; + glitz_surface_attributes_t attr; + glitz_vertex_format_t *vertex; + xglVisualPtr pVisual; + + pVisual = xglFindVisualWithDepth (pScreen, format->depth); + if (!pVisual) + return FALSE; + + if (!xglRootAreaInit (&pCache->rootArea, + TEXTURE_CACHE_MAX_LEVEL, + TEXTURE_CACHE_SIZE, TEXTURE_CACHE_SIZE, + sizeof (xglGlyphAreaRec), + (xglAreaFuncsPtr) &xglGlyphAreaFuncs, + (pointer) pCache)) + return FALSE; + + if (pScreenPriv->geometryDataType == GEOMETRY_DATA_TYPE_SHORT) + { + attr.unnormalized = 1; + mask = glitz_surface_create (pScreenPriv->drawable, + pVisual->format.surface, + TEXTURE_CACHE_SIZE, + TEXTURE_CACHE_SIZE, + GLITZ_SURFACE_UNNORMALIZED_MASK, + &attr); + } + else + mask = NULL; + + if (!mask) + { + mask = glitz_surface_create (pScreenPriv->drawable, + pVisual->format.surface, + TEXTURE_CACHE_SIZE, + TEXTURE_CACHE_SIZE, + 0, NULL); + if (!mask) + return FALSE; + + pTexture->geometryDataType = GEOMETRY_DATA_TYPE_FLOAT; + } + else + pTexture->geometryDataType = GEOMETRY_DATA_TYPE_SHORT; + + if (NEEDS_COMPONENT (format->format)) + glitz_surface_set_component_alpha (mask, 1); + + pTexture->pMask = xglCreateDevicePicture (mask); + if (!pTexture->pMask) + return FALSE; + + vertex = &pCache->u.texture.format.vertex; + vertex->primitive = GLITZ_PRIMITIVE_QUADS; + vertex->mask.size = GLITZ_COORDINATE_SIZE_XY; + vertex->attributes = GLITZ_VERTEX_ATTRIBUTE_MASK_COORD_MASK; + + if (pTexture->geometryDataType == GEOMETRY_DATA_TYPE_FLOAT) + { + vertex->type = GLITZ_DATA_TYPE_FLOAT; + vertex->bytes_per_vertex = sizeof (glitz_float_t) * 4; + vertex->mask.offset = sizeof (glitz_float_t) * 2; + vertex->mask.type = GLITZ_DATA_TYPE_FLOAT; + } + else + { + vertex->type = GLITZ_DATA_TYPE_SHORT; + vertex->bytes_per_vertex = sizeof (glitz_short_t) * 4; + vertex->mask.offset = sizeof (glitz_short_t) * 2; + vertex->mask.type = GLITZ_DATA_TYPE_SHORT; + } + + pTexture->pixel.fourcc = GLITZ_FOURCC_RGB; + pTexture->pixel.masks = pVisual->pPixel->masks; + pTexture->pixel.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP; + pTexture->pixel.bytes_per_line = 0; + pTexture->pixel.xoffset = 0; + pTexture->pixel.skip_lines = 0; + } + + pCache->pScreen = pScreen; + + return TRUE; +} + +void +xglFiniGlyphCache (xglGlyphCachePtr pCache) +{ + if (pCache->pScreen) + { + xglRootAreaFini (&pCache->rootArea); + + if (pCache->depth == 1) + { + GEOMETRY_UNINIT (&pCache->u.geometry); + } + else + { + if (pCache->u.texture.pMask) + FreePicture ((pointer) pCache->u.texture.pMask, 0); + } + + pCache->pScreen = NULL; + } +} + +static xglAreaPtr +xglCacheGlyph (xglGlyphCachePtr pCache, + GlyphPtr pGlyph) +{ + ScreenPtr pScreen = pCache->pScreen; + + XGL_GLYPH_PRIV (pScreen, pGlyph); + + if (pCache->depth == 1) + { + PixmapPtr pPixmap; + RegionPtr pRegion; + int nBox; + + pPixmap = GetScratchPixmapHeader (pScreen, + pGlyph->info.width, + pGlyph->info.height, + pCache->depth, pCache->depth, 0, + (pointer) (pGlyph + 1)); + if (!pPixmap) + return NULL; + + (*pScreen->ModifyPixmapHeader) (pPixmap, + pGlyph->info.width, + pGlyph->info.height, + 0, 0, -1, (pointer) (pGlyph + 1)); + + pRegion = (*pScreen->BitmapToRegion) (pPixmap); + FreeScratchPixmapHeader (pPixmap); + + if (!pRegion) + return NULL; + + nBox = REGION_NUM_RECTS (pRegion); + if (nBox > BITMAP_CACHE_MAX_SIZE) + { + REGION_DESTROY (pScreen, pRegion); + return NULL; + } + + if (nBox > 0) + { + /* Find available area */ + if (!xglFindArea (pCache->rootArea.pArea, nBox, 0, + FALSE, (pointer) pGlyph)) + { + /* Kicking out area with lower score */ + xglFindArea (pCache->rootArea.pArea, nBox, 0, + TRUE, (pointer) pGlyph); + } + + if (pGlyphPriv->pArea) + { + int stride; + + GLYPH_AREA_PRIV (pGlyphPriv->pArea); + + pAreaPriv->serial = glyphSerialNumber; + pAreaPriv->u.range.first = pGlyphPriv->pArea->x * 4; + pAreaPriv->u.range.count = nBox * 4; + + stride = pCache->u.geometry.f.vertex.bytes_per_vertex; + GEOMETRY_ADD_REGION_AT (pScreen, &pCache->u.geometry, pRegion, + pGlyphPriv->pArea->x * stride * 4); + } + } else + pGlyphPriv->pArea = &zeroSizeArea; + + REGION_DESTROY (pScreen, pRegion); + } + else + { + xglGlyphTexturePtr pTexture = &pCache->u.texture; + + if (pGlyph->info.width > TEXTURE_CACHE_MAX_WIDTH || + pGlyph->info.height > TEXTURE_CACHE_MAX_HEIGHT) + return NULL; + + if (pGlyph->info.width > 0 && pGlyph->info.height > 0) + { + glitz_buffer_t *buffer; + + buffer = glitz_buffer_create_for_data (pGlyph + 1); + if (!buffer) + return NULL; + + /* Find available area */ + if (!xglFindArea (pCache->rootArea.pArea, + pGlyph->info.width, pGlyph->info.height, + FALSE, (pointer) pGlyph)) + { + /* Kicking out area with lower score */ + xglFindArea (pCache->rootArea.pArea, + pGlyph->info.width, pGlyph->info.height, + TRUE, (pointer) pGlyph); + } + + if (pGlyphPriv->pArea) + { + glitz_surface_t *surface; + glitz_point_fixed_t p1, p2; + glitz_pixel_format_t pixel; + + GLYPH_AREA_PRIV (pGlyphPriv->pArea); + + pixel = pTexture->pixel; + pixel.bytes_per_line = + PixmapBytePad (pGlyph->info.width, pCache->depth); + + surface = pTexture->pMask->pSourcePict->source.devPrivate.ptr; + + glitz_set_pixels (surface, + pGlyphPriv->pArea->x, + pGlyphPriv->pArea->y, + pGlyph->info.width, + pGlyph->info.height, + &pixel, + buffer); + + p1.x = pGlyphPriv->pArea->x << 16; + p1.y = pGlyphPriv->pArea->y << 16; + p2.x = (pGlyphPriv->pArea->x + pGlyph->info.width) << 16; + p2.y = (pGlyphPriv->pArea->y + pGlyph->info.height) << 16; + + glitz_surface_translate_point (surface, &p1, &p1); + glitz_surface_translate_point (surface, &p2, &p2); + + pAreaPriv->serial = glyphSerialNumber; + if (pTexture->geometryDataType) + { + pAreaPriv->u.box.fBox.x1 = FIXED_TO_FLOAT (p1.x); + pAreaPriv->u.box.fBox.y1 = FIXED_TO_FLOAT (p1.y); + pAreaPriv->u.box.fBox.x2 = FIXED_TO_FLOAT (p2.x); + pAreaPriv->u.box.fBox.y2 = FIXED_TO_FLOAT (p2.y); + } + else + { + pAreaPriv->u.box.sBox.x1 = p1.x >> 16; + pAreaPriv->u.box.sBox.y1 = p1.y >> 16; + pAreaPriv->u.box.sBox.x2 = p2.x >> 16; + pAreaPriv->u.box.sBox.y2 = p2.y >> 16; + } + } + glitz_buffer_destroy (buffer); + } else + pGlyphPriv->pArea = &zeroSizeArea; + } + + return pGlyphPriv->pArea; +} + +static void +xglUncachedGlyphs (CARD8 op, + PicturePtr pSrc, + PicturePtr pDst, + INT16 xSrc, + INT16 ySrc, + xglGlyphOpPtr pOp) +{ + ScreenPtr pScreen = pDst->pDrawable->pScreen; + PicturePtr pPicture = NULL; + PixmapPtr pPixmap = NULL; + xglGlyphCachePtr pCache; + int depth = pOp->pLists->format->depth; + GlyphPtr glyph; + INT16 xOff, yOff; + xglGlyphPtr pGlyphPriv; + xglAreaPtr pArea; + Bool usingCache = !pOp->noCache; + + XGL_SCREEN_PRIV (pScreen); + + pCache = &pScreenPriv->glyphCache[depth]; + if (usingCache) + { + if (!pCache->pScreen) + { + if (!xglInitGlyphCache (pCache, pScreen, pOp->pLists->format)) + usingCache = FALSE; + } + } + + while (pOp->nGlyphs) + { + glyph = *pOp->ppGlyphs; + + if (!pOp->listLen) + { + pOp->pLists++; + pOp->listLen = pOp->pLists->len; + pOp->xOff += pOp->pLists->xOff; + pOp->yOff += pOp->pLists->yOff; + } + + xOff = pOp->xOff; + yOff = pOp->yOff; + + if (usingCache) + { + pGlyphPriv = XGL_GET_GLYPH_PRIV (pScreen, glyph); + pArea = pGlyphPriv->pArea; + if (pSrc) + { + if (!pArea) + pArea = xglCacheGlyph (pCache, glyph); + + if (pArea) + break; + } + } else + pArea = NULL; + + pOp->listLen--; + pOp->nGlyphs--; + pOp->ppGlyphs++; + + pOp->xOff += glyph->info.xOff; + pOp->yOff += glyph->info.yOff; + + if (pArea) + continue; + + if (!pPicture) + { + XID componentAlpha; + int error; + + pPixmap = GetScratchPixmapHeader (pScreen, + glyph->info.width, + glyph->info.height, + depth, depth, + 0, (pointer) (glyph + 1)); + if (!pPixmap) + return; + + componentAlpha = NEEDS_COMPONENT (pOp->pLists->format->format); + pPicture = CreatePicture (0, &pPixmap->drawable, + pOp->pLists->format, + CPComponentAlpha, &componentAlpha, + serverClient, &error); + if (!pPicture) + { + FreeScratchPixmapHeader (pPixmap); + return; + } + } + + (*pScreen->ModifyPixmapHeader) (pPixmap, + glyph->info.width, glyph->info.height, + 0, 0, -1, (pointer) (glyph + 1)); + pPixmap->drawable.serialNumber = NEXT_SERIAL_NUMBER; + + if (pSrc) + CompositePicture (op, + pSrc, + pPicture, + pDst, + xSrc + (xOff - glyph->info.x), + ySrc + (yOff - glyph->info.y), + 0, 0, + xOff - glyph->info.x, + yOff - glyph->info.y, + glyph->info.width, + glyph->info.height); + else + CompositePicture (PictOpAdd, + pPicture, + NULL, + pDst, + 0, 0, + 0, 0, + xOff - glyph->info.x, + yOff - glyph->info.y, + glyph->info.width, + glyph->info.height); + } + + if (pPicture) + { + FreeScratchPixmapHeader (pPixmap); + FreePicture ((pointer) pPicture, 0); + } +} + +static Bool +xglCachedGlyphs (CARD8 op, + PicturePtr pSrc, + PicturePtr pDst, + INT16 xSrc, + INT16 ySrc, + xglGlyphOpPtr pOp) +{ + ScreenPtr pScreen = pDst->pDrawable->pScreen; + xglGlyphOpRec opSave = *pOp; + xglGlyphCachePtr pCache; + xglGlyphVertexDataRec vData; + xglGeometryPtr pGeometry; + GlyphPtr glyph; + xglGlyphPtr pGlyphPriv; + xglAreaPtr pArea; + xglGlyphAreaPtr pGlyphArea; + BoxRec extents; + INT16 xOff, yOff, x1, x2, y1, y2; + int depth = pOp->pLists->format->depth; + int i, remaining = pOp->nGlyphs; + int nGlyph = 0; + PicturePtr pMaskPicture = NULL; + + XGL_SCREEN_PRIV (pScreen); + + pCache = &pScreenPriv->glyphCache[depth]; + if (!pCache->pScreen) + { + if (!xglInitGlyphCache (pCache, pScreen, pOp->pLists->format)) + { + pOp->noCache = TRUE; + return 1; + } + } + + /* update serial number for all glyphs already in cache so that + we don't accidentally replace one. */ + for (i = 0; i < pOp->nGlyphs; i++) + { + pGlyphPriv = XGL_GET_GLYPH_PRIV (pScreen, pOp->ppGlyphs[i]); + pArea = pGlyphPriv->pArea; + if (pArea && pArea->width) + GLYPH_GET_AREA_PRIV (pArea)->serial = glyphSerialNumber; + } + + for (i = 0; i < pOp->nGlyphs; i++) + { + pGlyphPriv = XGL_GET_GLYPH_PRIV (pScreen, pOp->ppGlyphs[i]); + pArea = pGlyphPriv->pArea; + if (!pArea) + pArea = xglCacheGlyph (pCache, pOp->ppGlyphs[i]); + + if (pArea) + { + if (pArea->width) + nGlyph++; + } + else if (pSrc) + break; + } + + if (nGlyph) + { + if (depth == 1) + { + glitz_multi_array_t *multiArray; + + pGeometry = &pCache->u.geometry; + pGeometry->xOff = pGeometry->yOff = 0; + + multiArray = glitz_multi_array_create (nGlyph); + if (!multiArray) + return 1; + + GEOMETRY_SET_MULTI_ARRAY (pGeometry, multiArray); + glitz_multi_array_destroy (multiArray); + + vData.array.lastX = 0; + vData.array.lastY = 0; + } + else + { + i = 4 * pCache->u.texture.format.vertex.bytes_per_vertex * nGlyph; + pGeometry = xglGetScratchGeometryWithSize (pScreen, i); + + pGeometry->f = pCache->u.texture.format; + pGeometry->type = GLITZ_GEOMETRY_TYPE_VERTEX; + pMaskPicture = pCache->u.texture.pMask; + + vData.list.s = glitz_buffer_map (pGeometry->buffer, + GLITZ_BUFFER_ACCESS_WRITE_ONLY); + } + } else + pGeometry = NULL; + + extents.x1 = MAXSHORT; + extents.y1 = MAXSHORT; + extents.x2 = MINSHORT; + extents.y2 = MINSHORT; + + while (pOp->nGlyphs) + { + glyph = *pOp->ppGlyphs; + + if (!pOp->listLen) + { + pOp->pLists++; + pOp->listLen = pOp->pLists->len; + pOp->xOff += pOp->pLists->xOff; + pOp->yOff += pOp->pLists->yOff; + } + + xOff = pOp->xOff; + yOff = pOp->yOff; + + pGlyphPriv = XGL_GET_GLYPH_PRIV (pScreen, glyph); + pArea = pGlyphPriv->pArea; + if (!pArea && pSrc) + break; + + pOp->listLen--; + pOp->nGlyphs--; + pOp->ppGlyphs++; + + pOp->xOff += glyph->info.xOff; + pOp->yOff += glyph->info.yOff; + + if (!pArea) + continue; + + x1 = xOff - glyph->info.x; + x2 = x1 + glyph->info.width; + if (x1 < extents.x1) + extents.x1 = x1; + if (x2 > extents.x2) + extents.x2 = x2; + + y1 = yOff - glyph->info.y; + y2 = y1 + glyph->info.height; + if (y1 < extents.y1) + extents.y1 = y1; + if (y2 > extents.y2) + extents.y2 = y2; + + if (pArea->width) + { + pGlyphArea = GLYPH_GET_AREA_PRIV (pArea); + if (depth == 1) + { + glitz_multi_array_add (pGeometry->array, + pGlyphArea->u.range.first, 2, + pGlyphArea->u.range.count, + (x1 - vData.array.lastX) << 16, + (y1 - vData.array.lastY) << 16); + vData.array.lastX = x1; + vData.array.lastY = y1; + } + else + { + if (pCache->u.texture.geometryDataType) + { + WRITE_BOX (vData.list.f, x1, y1, x2, y2, + pGlyphArea->u.box.fBox); + } + else + { + WRITE_BOX (vData.list.s, x1, y1, x2, y2, + pGlyphArea->u.box.sBox); + } + } + } + remaining--; + } + + NEXT_GLYPH_SERIAL_NUMBER; + + if (nGlyph) + { + if (depth != 1) + { + glitz_buffer_unmap (pGeometry->buffer); + pGeometry->count = nGlyph * 4; + } + + xSrc += extents.x1; + ySrc += extents.y1; + + if (!pSrc) + { + op = PictOpAdd; + pSrc = pScreenPriv->pSolidAlpha; + + if (remaining) + *pOp = opSave; + } + + GEOMETRY_TRANSLATE (pGeometry, + pDst->pDrawable->x, + pDst->pDrawable->y); + + if (xglCompositeGeneral (op, + pSrc, + pMaskPicture, + pDst, + pGeometry, + xSrc, ySrc, + 0, 0, + pDst->pDrawable->x + extents.x1, + pDst->pDrawable->y + extents.y1, + extents.x2 - extents.x1, + extents.y2 - extents.y1)) + { + xglAddCurrentBitDamage (pDst->pDrawable); + return remaining; + } + + remaining = ~0; + *pOp = opSave; + pOp->noCache = TRUE; + } + else + { + if (remaining) + { + *pOp = opSave; + pOp->noCache = TRUE; + } + } + + return remaining; +} + +static Bool +xglGlyphExtents (PicturePtr pDst, + int nlist, + GlyphListPtr list, + GlyphPtr *glyphs, + BoxPtr extents) +{ + GlyphPtr glyph; + BoxRec line; + int x1, x2, y1, y2; + int n; + int x; + int y; + Bool overlap = FALSE; + + x = 0; + y = 0; + + extents->x1 = MAXSHORT; + extents->x2 = MINSHORT; + extents->y1 = MAXSHORT; + extents->y2 = MINSHORT; + + while (!list->len) + { + if (--nlist) + { + x += list->xOff; + y += list->yOff; + list++; + } + else + { + return FALSE; + } + } + + glyph = *glyphs; + x1 = (x + list->xOff) - glyph->info.x; + if (x1 < MINSHORT) + x1 = MINSHORT; + y1 = (y + list->yOff) - glyph->info.y; + if (y1 < MINSHORT) + y1 = MINSHORT; + + line.x1 = x1; + line.x2 = x1; + line.y1 = y1; + line.y2 = y1; + + while (nlist--) + { + x += list->xOff; + y += list->yOff; + n = list->len; + list++; + + while (n--) + { + glyph = *glyphs++; + x1 = x - glyph->info.x; + if (x1 < MINSHORT) + x1 = MINSHORT; + y1 = y - glyph->info.y; + if (y1 < MINSHORT) + y1 = MINSHORT; + x2 = x1 + glyph->info.width; + if (x2 > MAXSHORT) + x2 = MAXSHORT; + y2 = y1 + glyph->info.height; + if (y2 > MAXSHORT) + y2 = MAXSHORT; + + if (x1 >= line.x2) + { + line.x2 = x2; + if (y1 < line.y1) + line.y1 = y1; + if (y2 > line.y2) + line.y2 = y2; + } + else if (x2 <= line.x1) + { + line.x1 = x1; + if (y1 < line.y1) + line.y1 = y1; + if (y2 > line.y2) + line.y2 = y2; + } + else + { + if (line.y1 >= extents->y2) + { + extents->y2 = line.y2; + if (line.y1 < extents->y1) + extents->y1 = line.y1; + } + else if (line.y2 <= extents->y1) + { + extents->y1 = line.y1; + if (line.y2 > extents->y2) + extents->y2 = line.y2; + } + else + { + if (line.y1 < extents->y1) + extents->y1 = line.y1; + if (line.y2 > extents->y2) + extents->y2 = line.y2; + + overlap = TRUE; + } + + if (line.x1 < extents->x1) + extents->x1 = line.x1; + if (line.x2 > extents->x2) + extents->x2 = line.x2; + + line.x1 = x1; + line.y1 = y1; + line.x2 = x2; + line.y2 = y2; + } + + x += glyph->info.xOff; + y += glyph->info.yOff; + } + } + + if (line.y1 >= extents->y2) + { + extents->y2 = line.y2; + if (line.y1 < extents->y1) + extents->y1 = line.y1; + } + else if (line.y2 <= extents->y1) + { + extents->y1 = line.y1; + if (line.y2 > extents->y2) + extents->y2 = line.y2; + } + else + { + if (line.y1 < extents->y1) + extents->y1 = line.y1; + if (line.y2 > extents->y2) + extents->y2 = line.y2; + + overlap = TRUE; + } + + if (line.x1 < extents->x1) + extents->x1 = line.x1; + if (line.x2 > extents->x2) + extents->x2 = line.x2; + + xglPictureClipExtents (pDst, extents); + + return overlap; +} + +/* returns 0 if all glyph lists don't have the same format */ +static CARD32 +xglGlyphListFormatId (GlyphListPtr list, + int nlist) +{ + CARD32 id = list->format->id; + + nlist--; + list++; + + while (nlist--) + { + if (list->format->id != id) + return 0; + + list++; + } + + return id; +} + +void +xglGlyphs (CARD8 op, + PicturePtr pSrc, + PicturePtr pDst, + PictFormatPtr maskFormat, + INT16 xSrc, + INT16 ySrc, + int nlist, + GlyphListPtr list, + GlyphPtr *glyphs) +{ + ScreenPtr pScreen = pDst->pDrawable->pScreen; + PicturePtr pMask = NULL, pSrcPicture, pDstPicture; + BoxRec extents; + xglGlyphOpRec glyphOp; + int xDst = list->xOff, yDst = list->yOff; + int overlap; + int target; + + overlap = xglGlyphExtents (pDst, nlist, list, glyphs, &extents); + if (extents.x2 <= extents.x1 || extents.y2 <= extents.y1) + return; + + target = xglPrepareTarget (pDst->pDrawable); + + if (op != PictOpAdd && maskFormat && + (!target || overlap || op != PictOpOver || + xglGlyphListFormatId (list, nlist) != maskFormat->id)) + { + PixmapPtr pPixmap; + XID componentAlpha; + GCPtr pGC; + xRectangle rect; + int error; + + rect.x = 0; + rect.y = 0; + rect.width = extents.x2 - extents.x1; + rect.height = extents.y2 - extents.y1; + + pPixmap = (*pScreen->CreatePixmap) (pScreen, + rect.width, rect.height, + maskFormat->depth, + CREATE_PIXMAP_USAGE_SCRATCH); + if (!pPixmap) + return; + + componentAlpha = NEEDS_COMPONENT (maskFormat->format); + pMask = CreatePicture (0, &pPixmap->drawable, + maskFormat, CPComponentAlpha, &componentAlpha, + serverClient, &error); + if (!pMask) + { + (*pScreen->DestroyPixmap) (pPixmap); + return; + } + + if (!target) + { + /* make sure we don't do accelerated drawing to mask */ + xglSetPixmapVisual (pPixmap, NULL); + } + + ValidatePicture (pMask); + pGC = GetScratchGC (pPixmap->drawable.depth, pScreen); + ValidateGC (&pPixmap->drawable, pGC); + (*pGC->ops->PolyFillRect) (&pPixmap->drawable, pGC, 1, &rect); + FreeScratchGC (pGC); + + (*pScreen->DestroyPixmap) (pPixmap); + + target = xglPrepareTarget (pMask->pDrawable); + + glyphOp.xOff = -extents.x1; + glyphOp.yOff = -extents.y1; + pSrcPicture = NULL; + pDstPicture = pMask; + } + else + { + glyphOp.xOff = 0; + glyphOp.yOff = 0; + pSrcPicture = pSrc; + pDstPicture = pDst; + } + + glyphOp.ppGlyphs = glyphs; + glyphOp.noCache = !target; + + while (nlist--) + { + glyphOp.xOff += list->xOff; + glyphOp.yOff += list->yOff; + glyphOp.listLen = list->len; + glyphOp.nGlyphs = list->len; + glyphOp.pLists = list++; + + for (; nlist; nlist--, list++) + { + if (list->format->id != glyphOp.pLists->format->id) + break; + + glyphOp.nGlyphs += list->len; + } + + while (glyphOp.nGlyphs) + { + if (glyphOp.noCache || xglCachedGlyphs (op, + pSrcPicture, + pDstPicture, + xSrc - xDst, ySrc - yDst, + &glyphOp)) + xglUncachedGlyphs (op, + pSrcPicture, + pDstPicture, + xSrc - xDst, ySrc - yDst, + &glyphOp); + } + } + + if (pMask) + { + CompositePicture (op, pSrc, pMask, pDst, + xSrc + extents.x1 - xDst, + ySrc + extents.y1 - yDst, + 0, 0, + extents.x1, extents.y1, + extents.x2 - extents.x1, + extents.y2 - extents.y1); + + FreePicture ((pointer) pMask, (XID) 0); + } +} + +#endif |