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

725 lines
16 KiB
C

/*
* Copyright © 2004 David Reveman
*
* 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
* David Reveman not be used in advertising or publicity pertaining to
* distribution of the software without specific, written prior permission.
* David Reveman makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without express or
* implied warranty.
*
* DAVID REVEMAN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
* NO EVENT SHALL DAVID REVEMAN 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"
#include <X11/fonts/fontstruct.h>
#include "dixfontstr.h"
xglDataTypeInfoRec xglGeometryDataTypes[2] = {
{ GLITZ_DATA_TYPE_SHORT, sizeof (glitz_short_t) },
{ GLITZ_DATA_TYPE_FLOAT, sizeof (glitz_float_t) }
};
glitz_buffer_hint_t usageTypes[] = {
GLITZ_BUFFER_HINT_STREAM_DRAW,
GLITZ_BUFFER_HINT_STATIC_DRAW,
GLITZ_BUFFER_HINT_DYNAMIC_DRAW
};
void
xglGeometryResize (ScreenPtr pScreen,
xglGeometryPtr pGeometry,
int size)
{
XGL_SCREEN_PRIV (pScreen);
if (size == pGeometry->size)
return;
if (pGeometry->broken)
return;
if (pGeometry->usage == GEOMETRY_USAGE_SYSMEM)
{
pGeometry->data = xrealloc (pGeometry->data, size);
if (pGeometry->buffer)
glitz_buffer_destroy (pGeometry->buffer);
pGeometry->buffer = NULL;
if (pGeometry->data)
{
pGeometry->buffer = glitz_buffer_create_for_data (pGeometry->data);
if (!pGeometry->buffer)
{
pGeometry->broken = TRUE;
return;
}
}
else if (size)
{
pGeometry->broken = TRUE;
return;
}
}
else
{
glitz_buffer_t *newBuffer;
if (size)
{
newBuffer =
glitz_vertex_buffer_create (pScreenPriv->drawable, NULL, size,
usageTypes[pGeometry->usage]);
if (!newBuffer)
{
pGeometry->broken = TRUE;
return;
}
} else
newBuffer = NULL;
if (pGeometry->buffer && newBuffer)
{
void *oldData, *newData;
oldData = glitz_buffer_map (pGeometry->buffer,
GLITZ_BUFFER_ACCESS_READ_ONLY);
newData = glitz_buffer_map (newBuffer,
GLITZ_BUFFER_ACCESS_WRITE_ONLY);
if (oldData && newData)
memcpy (newData, oldData, MIN (size, pGeometry->size));
glitz_buffer_unmap (pGeometry->buffer);
glitz_buffer_unmap (newBuffer);
glitz_buffer_destroy (pGeometry->buffer);
}
pGeometry->buffer = newBuffer;
}
pGeometry->size = size;
if (pGeometry->endOffset > size)
pGeometry->endOffset = size;
}
#define MAP_GEOMETRY(pScreen, pGeometry, offset, units, ptr, _size) \
if ((pGeometry)->broken) \
return; \
(_size) = (units) * xglGeometryDataTypes[(pGeometry)->dataType].size; \
if (((pGeometry)->size - (offset)) < (_size)) \
{ \
xglGeometryResize (pScreen, pGeometry, \
(pGeometry)->endOffset + (_size) + 500); \
if ((pGeometry)->broken) \
return; \
} \
(ptr) = glitz_buffer_map ((pGeometry)->buffer, \
GLITZ_BUFFER_ACCESS_WRITE_ONLY); \
if (!(ptr)) \
{ \
(pGeometry)->broken = TRUE; \
return; \
} \
(ptr) += (offset)
#define UNMAP_GEOMETRY(pGeometry, offset, _size) \
if (glitz_buffer_unmap ((pGeometry)->buffer)) \
{ \
(pGeometry)->broken = TRUE; \
return; \
} \
if (((offset) + (_size)) > (pGeometry)->endOffset) \
{ \
(pGeometry)->endOffset = (offset) + (_size); \
(pGeometry)->count = (pGeometry)->endOffset / \
(2 * xglGeometryDataTypes[(pGeometry)->dataType].size); \
}
/*
* Adds a number of boxes as GL_QUAD primitives
*/
void
xglGeometryAddBox (ScreenPtr pScreen,
xglGeometryPtr pGeometry,
BoxPtr pBox,
int nBox,
int offset)
{
int size;
char *ptr;
if (nBox < 1)
return;
MAP_GEOMETRY (pScreen, pGeometry, offset, nBox * 8, ptr, size);
switch (pGeometry->dataType) {
case GEOMETRY_DATA_TYPE_SHORT:
{
glitz_short_t *data = (glitz_short_t *) ptr;
while (nBox--)
{
*data++ = (glitz_short_t) pBox->x1;
*data++ = (glitz_short_t) pBox->y1;
*data++ = (glitz_short_t) pBox->x2;
*data++ = (glitz_short_t) pBox->y1;
*data++ = (glitz_short_t) pBox->x2;
*data++ = (glitz_short_t) pBox->y2;
*data++ = (glitz_short_t) pBox->x1;
*data++ = (glitz_short_t) pBox->y2;
pBox++;
}
} break;
case GEOMETRY_DATA_TYPE_FLOAT:
{
glitz_float_t *data = (glitz_float_t *) ptr;
while (nBox--)
{
*data++ = (glitz_float_t) pBox->x1;
*data++ = (glitz_float_t) pBox->y1;
*data++ = (glitz_float_t) pBox->x2;
*data++ = (glitz_float_t) pBox->y1;
*data++ = (glitz_float_t) pBox->x2;
*data++ = (glitz_float_t) pBox->y2;
*data++ = (glitz_float_t) pBox->x1;
*data++ = (glitz_float_t) pBox->y2;
pBox++;
}
} break;
}
UNMAP_GEOMETRY (pGeometry, offset, size);
}
/*
* Adds a number of spans as GL_LINE primitives
*/
void
xglGeometryAddSpan (ScreenPtr pScreen,
xglGeometryPtr pGeometry,
DDXPointPtr ppt,
int *pwidth,
int n,
int offset)
{
int size;
char *ptr;
if (n < 1)
return;
MAP_GEOMETRY (pScreen, pGeometry, offset, n * 4, ptr, size);
switch (pGeometry->dataType) {
case GEOMETRY_DATA_TYPE_SHORT:
{
glitz_short_t *data = (glitz_short_t *) ptr;
while (n--)
{
*data++ = (glitz_short_t) ppt->x;
*data++ = (glitz_short_t) ppt->y;
*data++ = (glitz_short_t) (ppt->x + *pwidth);
*data++ = (glitz_short_t) ppt->y;
ppt++;
pwidth++;
}
} break;
case GEOMETRY_DATA_TYPE_FLOAT:
{
glitz_float_t *data = (glitz_float_t *) ptr;
while (n--)
{
*data++ = (glitz_float_t) ppt->x;
*data++ = (glitz_float_t) ppt->y;
*data++ = (glitz_float_t) (ppt->x + *pwidth);
*data++ = (glitz_float_t) ppt->y;
ppt++;
pwidth++;
}
} break;
}
UNMAP_GEOMETRY (pGeometry, offset, size);
}
/*
* This macro is needed for end pixels to be rasterized correctly using
* OpenGL as OpenGL line segments are half-opened.
*/
#define ADJUST_END_POINT(start, end, isPoint) \
(((end) > (start)) ? (end) + 1: \
((end) < (start)) ? (end) - 1: \
(isPoint) ? (end) + 1: \
(end))
/*
* Adds a number of connected lines as GL_LINE_STRIP primitives
*/
void
xglGeometryAddLine (ScreenPtr pScreen,
xglGeometryPtr pGeometry,
int loop,
int mode,
int npt,
DDXPointPtr ppt,
int offset)
{
DDXPointRec pt;
int size;
char *ptr;
if (npt < 2)
return;
MAP_GEOMETRY (pScreen, pGeometry, offset, npt * 2, ptr, size);
pt.x = 0;
pt.y = 0;
switch (pGeometry->dataType) {
case GEOMETRY_DATA_TYPE_SHORT:
{
glitz_short_t *data = (glitz_short_t *) ptr;
while (npt--)
{
if (mode == CoordModePrevious)
{
pt.x += ppt->x;
pt.y += ppt->y;
}
else
{
pt.x = ppt->x;
pt.y = ppt->y;
}
if (npt || loop)
{
*data++ = (glitz_short_t) pt.x;
*data++ = (glitz_short_t) pt.y;
}
else
{
ppt--;
*data++ = (glitz_short_t)
ADJUST_END_POINT (ppt->x, pt.x, ppt->y == pt.y);
*data++ = (glitz_short_t) ADJUST_END_POINT (ppt->y, pt.y, 0);
}
ppt++;
}
} break;
case GEOMETRY_DATA_TYPE_FLOAT:
{
glitz_float_t *data = (glitz_float_t *) ptr;
while (npt--)
{
if (mode == CoordModePrevious)
{
pt.x += ppt->x;
pt.y += ppt->y;
}
else
{
pt.x = ppt->x;
pt.y = ppt->y;
}
if (npt || loop)
{
*data++ = (glitz_float_t) pt.x;
*data++ = (glitz_float_t) pt.y;
}
else
{
ppt--;
*data++ = (glitz_float_t)
ADJUST_END_POINT (ppt->x, pt.x, ppt->y == pt.y);
*data++ = (glitz_float_t) ADJUST_END_POINT (ppt->y, pt.y, 0);
}
ppt++;
}
} break;
}
UNMAP_GEOMETRY (pGeometry, offset, size);
}
/*
* Adds a number of line segments as GL_LINE primitives
*/
void
xglGeometryAddSegment (ScreenPtr pScreen,
xglGeometryPtr pGeometry,
int nsegInit,
xSegment *pSegInit,
int offset)
{
int size;
char *ptr;
if (nsegInit < 1)
return;
MAP_GEOMETRY (pScreen, pGeometry, offset, nsegInit * 4, ptr, size);
switch (pGeometry->dataType) {
case GEOMETRY_DATA_TYPE_SHORT:
{
glitz_short_t *data = (glitz_short_t *) ptr;
while (nsegInit--)
{
*data++ = (glitz_short_t) pSegInit->x1;
*data++ = (glitz_short_t) pSegInit->y1;
*data++ = (glitz_short_t)
ADJUST_END_POINT (pSegInit->x1, pSegInit->x2,
pSegInit->y1 == pSegInit->y2);
*data++ = (glitz_short_t)
ADJUST_END_POINT (pSegInit->y1, pSegInit->y2, 0);
pSegInit++;
}
} break;
case GEOMETRY_DATA_TYPE_FLOAT:
{
glitz_float_t *data = (glitz_float_t *) ptr;
while (nsegInit--)
{
*data++ = (glitz_float_t) pSegInit->x1;
*data++ = (glitz_float_t) pSegInit->y1;
*data++ = (glitz_float_t)
ADJUST_END_POINT (pSegInit->x1, pSegInit->x2,
pSegInit->y1 == pSegInit->y2);
*data++ = (glitz_float_t)
ADJUST_END_POINT (pSegInit->y1, pSegInit->y2, 0);
pSegInit++;
}
} break;
}
UNMAP_GEOMETRY (pGeometry, offset, size);
}
void
xglGeometryForGlyph (ScreenPtr pScreen,
xglGeometryPtr pGeometry,
unsigned int nGlyph,
CharInfoPtr *ppciInit,
pointer pglyphBase)
{
CharInfoPtr *ppci;
CharInfoPtr pci;
unsigned char *glyphbase = (pointer) ~0;
unsigned char *pglyph;
int x = 0;
int gx, gy;
int gWidth, gHeight;
int n, lastX = 0, lastY = 0;
glitz_multi_array_t *array;
glitz_buffer_t *buffer;
ppci = ppciInit;
n = nGlyph;
while (n--)
{
pglyph = FONTGLYPHBITS (pglyphBase, *ppci++);
if (pglyph < glyphbase)
glyphbase = pglyph;
}
buffer = glitz_buffer_create_for_data (glyphbase);
if (!buffer)
{
pGeometry->broken = TRUE;
return;
}
GEOMETRY_SET_BUFFER (pGeometry, buffer);
array = glitz_multi_array_create (nGlyph);
if (!array)
{
pGeometry->broken = TRUE;
return;
}
GEOMETRY_SET_MULTI_ARRAY (pGeometry, array);
ppci = ppciInit;
while (nGlyph--)
{
pci = *ppci++;
pglyph = FONTGLYPHBITS (pglyphBase, pci);
gWidth = GLYPHWIDTHPIXELS (pci);
gHeight = GLYPHHEIGHTPIXELS (pci);
if (gWidth && gHeight)
{
gx = x + pci->metrics.leftSideBearing;
gy = -pci->metrics.ascent;
glitz_multi_array_add (array,
(pglyph - glyphbase) * 8,
gWidth, gHeight,
(gx - lastX) << 16, (gy - lastY) << 16);
lastX = gx;
lastY = gy;
}
x += pci->metrics.characterWidth;
}
glitz_buffer_destroy (buffer);
glitz_multi_array_destroy (array);
}
#define FIXED_LINE_X_TO_FLOAT(line, v) \
(((glitz_float_t) \
((line).p1.x + (xFixed_16_16) \
(((xFixed_32_32) ((v) - (line).p1.y) * \
((line).p2.x - (line).p1.x)) / \
((line).p2.y - (line).p1.y)))) / 65536)
#define FIXED_LINE_X_CEIL_TO_FLOAT(line, v) \
(((glitz_float_t) \
((line).p1.x + (xFixed_16_16) \
(((((line).p2.y - (line).p1.y) - 1) + \
((xFixed_32_32) ((v) - (line).p1.y) * \
((line).p2.x - (line).p1.x))) / \
((line).p2.y - (line).p1.y)))) / 65536)
/*
* Adds a number of trapezoids as GL_QUAD primitives
*/
void
xglGeometryAddTrapezoid (ScreenPtr pScreen,
xglGeometryPtr pGeometry,
xTrapezoid *pTrap,
int nTrap,
int offset)
{
int size;
char *ptr;
if (nTrap < 1)
return;
MAP_GEOMETRY (pScreen, pGeometry, offset, nTrap * 8, ptr, size);
switch (pGeometry->dataType) {
case GEOMETRY_DATA_TYPE_SHORT:
/* not supported */
pGeometry->broken = TRUE;
break;
case GEOMETRY_DATA_TYPE_FLOAT:
{
glitz_float_t *data = (glitz_float_t *) ptr;
glitz_float_t top, bottom;
while (nTrap--)
{
top = FIXED_TO_FLOAT (pTrap->top);
bottom = FIXED_TO_FLOAT (pTrap->bottom);
*data++ = FIXED_LINE_X_TO_FLOAT (pTrap->left, pTrap->top);
*data++ = top;
*data++ = FIXED_LINE_X_CEIL_TO_FLOAT (pTrap->right, pTrap->top);
*data++ = top;
*data++ = FIXED_LINE_X_CEIL_TO_FLOAT (pTrap->right, pTrap->bottom);
*data++ = bottom;
*data++ = FIXED_LINE_X_TO_FLOAT (pTrap->left, pTrap->bottom);
*data++ = bottom;
pTrap++;
}
} break;
}
UNMAP_GEOMETRY (pGeometry, offset, size);
}
/*
* Adds a number of traps as GL_QUAD primitives
*/
void
xglGeometryAddTrap (ScreenPtr pScreen,
xglGeometryPtr pGeometry,
xTrap *pTrap,
int nTrap,
int offset)
{
int size;
char *ptr;
if (nTrap < 1)
return;
MAP_GEOMETRY (pScreen, pGeometry, offset, nTrap * 8, ptr, size);
switch (pGeometry->dataType) {
case GEOMETRY_DATA_TYPE_SHORT:
/* not supported */
pGeometry->broken = TRUE;
break;
case GEOMETRY_DATA_TYPE_FLOAT:
{
glitz_float_t *data = (glitz_float_t *) ptr;
glitz_float_t top, bottom;
while (nTrap--)
{
top = FIXED_TO_FLOAT (pTrap->top.y);
bottom = FIXED_TO_FLOAT (pTrap->bot.y);
*data++ = FIXED_TO_FLOAT (pTrap->top.l);
*data++ = top;
*data++ = FIXED_TO_FLOAT (pTrap->top.r);
*data++ = top;
*data++ = FIXED_TO_FLOAT (pTrap->bot.r);
*data++ = bottom;
*data++ = FIXED_TO_FLOAT (pTrap->bot.l);
*data++ = bottom;
pTrap++;
}
} break;
}
UNMAP_GEOMETRY (pGeometry, offset, size);
}
/* XXX: scratch geometry size never shrinks, it just gets larger when
required. this is not acceptable. */
xglGeometryPtr
xglGetScratchGeometryWithSize (ScreenPtr pScreen,
int size)
{
xglGeometryPtr pGeometry;
XGL_SCREEN_PRIV (pScreen);
pGeometry = &pScreenPriv->scratchGeometry;
if (pGeometry->broken || pGeometry->size < size)
{
GEOMETRY_UNINIT (pGeometry);
GEOMETRY_INIT (pScreen, pGeometry, pGeometry->type,
pScreenPriv->geometryUsage, size);
}
else
{
if (pGeometry->array)
{
glitz_multi_array_destroy (pGeometry->array);
pGeometry->array = NULL;
}
pGeometry->endOffset = 0;
pGeometry->xOff = 0;
pGeometry->yOff = 0;
pGeometry->first = 0;
pGeometry->count = 0;
pGeometry->width = 2;
}
return pGeometry;
}
xglGeometryPtr
xglGetScratchVertexGeometryWithType (ScreenPtr pScreen,
int type,
int count)
{
xglGeometryPtr pGeometry;
int stride;
stride = 2 * xglGeometryDataTypes[type].size;
pGeometry = xglGetScratchGeometryWithSize (pScreen, count * stride);
pGeometry->type = GLITZ_GEOMETRY_TYPE_VERTEX;
pGeometry->dataType = type;
pGeometry->f.vertex.primitive = GLITZ_PRIMITIVE_QUADS;
pGeometry->f.vertex.type = xglGeometryDataTypes[type].type;
pGeometry->f.vertex.bytes_per_vertex = stride;
pGeometry->f.vertex.attributes = 0;
return pGeometry;
}
xglGeometryPtr
xglGetScratchVertexGeometry (ScreenPtr pScreen,
int count)
{
xglGeometryPtr pGeometry;
int type, stride;
XGL_SCREEN_PRIV (pScreen);
type = pScreenPriv->geometryDataType;
stride = 2 * xglGeometryDataTypes[type].size;
pGeometry = xglGetScratchGeometryWithSize (pScreen, count * stride);
pGeometry->type = GLITZ_GEOMETRY_TYPE_VERTEX;
pGeometry->dataType = type;
pGeometry->f.vertex.primitive = GLITZ_PRIMITIVE_QUADS;
pGeometry->f.vertex.type = xglGeometryDataTypes[type].type;
pGeometry->f.vertex.bytes_per_vertex = stride;
pGeometry->f.vertex.attributes = 0;
return pGeometry;
}
Bool
xglSetGeometry (xglGeometryPtr pGeometry,
glitz_surface_t *surface)
{
if (pGeometry->broken)
return FALSE;
glitz_set_geometry (surface, pGeometry->type, &pGeometry->f,
pGeometry->buffer);
if (pGeometry->array)
glitz_set_multi_array (surface, pGeometry->array,
pGeometry->xOff, pGeometry->yOff);
else
glitz_set_array (surface,
pGeometry->first, pGeometry->width, pGeometry->count,
pGeometry->xOff, pGeometry->yOff);
return TRUE;
}