1170 lines
26 KiB
C
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
|