xenocara/xserver/hw/xgl/xglglyph.c
2006-11-26 18:13:41 +00:00

1170 lines
26 KiB
C

/*
* 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);
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