mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-01-19 05:50:23 +01:00
2477 lines
63 KiB
C
2477 lines
63 KiB
C
/* cairo - a vector graphics library with display and print output
|
|
*
|
|
* 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 "cairoint.h"
|
|
#include "cairo-glitz.h"
|
|
#include "cairo-glitz-private.h"
|
|
|
|
typedef struct _cairo_glitz_surface {
|
|
cairo_surface_t base;
|
|
|
|
glitz_surface_t *surface;
|
|
glitz_format_t *format;
|
|
cairo_bool_t has_clip;
|
|
cairo_region_t clip;
|
|
} cairo_glitz_surface_t;
|
|
|
|
static const cairo_surface_backend_t *
|
|
_cairo_glitz_surface_get_backend (void);
|
|
|
|
static cairo_status_t
|
|
_cairo_glitz_surface_finish (void *abstract_surface)
|
|
{
|
|
cairo_glitz_surface_t *surface = abstract_surface;
|
|
|
|
if (surface->has_clip) {
|
|
glitz_surface_set_clip_region (surface->surface, 0, 0, NULL, 0);
|
|
_cairo_region_fini (&surface->clip);
|
|
}
|
|
|
|
glitz_surface_destroy (surface->surface);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static glitz_format_name_t
|
|
_glitz_format_from_content (cairo_content_t content)
|
|
{
|
|
switch (content) {
|
|
case CAIRO_CONTENT_COLOR:
|
|
return GLITZ_STANDARD_RGB24;
|
|
case CAIRO_CONTENT_ALPHA:
|
|
return GLITZ_STANDARD_A8;
|
|
case CAIRO_CONTENT_COLOR_ALPHA:
|
|
return GLITZ_STANDARD_ARGB32;
|
|
}
|
|
|
|
ASSERT_NOT_REACHED;
|
|
return GLITZ_STANDARD_ARGB32;
|
|
}
|
|
|
|
static cairo_surface_t *
|
|
_cairo_glitz_surface_create_similar (void *abstract_src,
|
|
cairo_content_t content,
|
|
int width,
|
|
int height)
|
|
{
|
|
cairo_glitz_surface_t *src = abstract_src;
|
|
cairo_surface_t *crsurface;
|
|
glitz_drawable_t *drawable;
|
|
glitz_surface_t *surface;
|
|
glitz_format_t *gformat;
|
|
|
|
drawable = glitz_surface_get_drawable (src->surface);
|
|
|
|
gformat =
|
|
glitz_find_standard_format (drawable,
|
|
_glitz_format_from_content (content));
|
|
if (!gformat)
|
|
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
|
|
|
surface = glitz_surface_create (drawable, gformat,
|
|
width <= 0 ? 1 : width,
|
|
height <= 0 ? 1 : height,
|
|
0, NULL);
|
|
|
|
if (surface == NULL)
|
|
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
|
|
|
crsurface = cairo_glitz_surface_create (surface);
|
|
|
|
glitz_surface_destroy (surface);
|
|
|
|
return crsurface;
|
|
}
|
|
|
|
static cairo_bool_t
|
|
_CAIRO_MASK_FORMAT (cairo_format_masks_t *masks, cairo_format_t *format)
|
|
{
|
|
switch (masks->bpp) {
|
|
case 32:
|
|
if (masks->alpha_mask == 0xff000000 &&
|
|
masks->red_mask == 0x00ff0000 &&
|
|
masks->green_mask == 0x0000ff00 &&
|
|
masks->blue_mask == 0x000000ff)
|
|
{
|
|
*format = CAIRO_FORMAT_ARGB32;
|
|
return TRUE;
|
|
}
|
|
if (masks->alpha_mask == 0x00000000 &&
|
|
masks->red_mask == 0x00ff0000 &&
|
|
masks->green_mask == 0x0000ff00 &&
|
|
masks->blue_mask == 0x000000ff)
|
|
{
|
|
*format = CAIRO_FORMAT_RGB24;
|
|
return TRUE;
|
|
}
|
|
break;
|
|
case 8:
|
|
if (masks->alpha_mask == 0xff)
|
|
{
|
|
*format = CAIRO_FORMAT_A8;
|
|
return TRUE;
|
|
}
|
|
break;
|
|
case 1:
|
|
if (masks->alpha_mask == 0x1)
|
|
{
|
|
*format = CAIRO_FORMAT_A1;
|
|
return TRUE;
|
|
}
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_glitz_get_boxes_from_region (cairo_region_t *region, glitz_box_t **boxes, int *nboxes)
|
|
{
|
|
cairo_box_int_t *cboxes;
|
|
cairo_status_t status;
|
|
int n, i;
|
|
|
|
status = _cairo_region_get_boxes (region, &n, &cboxes);
|
|
if (status)
|
|
return status;
|
|
|
|
if (n == 0) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto done;
|
|
}
|
|
|
|
*boxes = _cairo_malloc_ab (n, sizeof(glitz_box_t));
|
|
if (*boxes == NULL) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto done;
|
|
}
|
|
|
|
for (i = 0; i < n; i++) {
|
|
(*boxes)[i].x1 = cboxes[i].p1.x;
|
|
(*boxes)[i].y1 = cboxes[i].p1.y;
|
|
(*boxes)[i].x2 = cboxes[i].p2.x;
|
|
(*boxes)[i].y2 = cboxes[i].p2.y;
|
|
}
|
|
|
|
*nboxes = n;
|
|
done:
|
|
_cairo_region_boxes_fini (region, cboxes);
|
|
return status;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_glitz_surface_get_image (cairo_glitz_surface_t *surface,
|
|
cairo_rectangle_int_t *interest,
|
|
cairo_image_surface_t **image_out,
|
|
cairo_rectangle_int_t *rect_out)
|
|
{
|
|
cairo_image_surface_t *image;
|
|
int x1, y1, x2, y2;
|
|
int width, height;
|
|
unsigned char *pixels;
|
|
cairo_format_masks_t masks;
|
|
glitz_buffer_t *buffer;
|
|
glitz_pixel_format_t pf;
|
|
cairo_format_t format;
|
|
|
|
x1 = 0;
|
|
y1 = 0;
|
|
x2 = glitz_surface_get_width (surface->surface);
|
|
y2 = glitz_surface_get_height (surface->surface);
|
|
|
|
if (interest)
|
|
{
|
|
if (interest->x > x1)
|
|
x1 = interest->x;
|
|
if (interest->y > y1)
|
|
y1 = interest->y;
|
|
if (interest->x + interest->width < x2)
|
|
x2 = interest->x + interest->width;
|
|
if (interest->y + interest->height < y2)
|
|
y2 = interest->y + interest->height;
|
|
|
|
if (x1 >= x2 || y1 >= y2)
|
|
{
|
|
*image_out = NULL;
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
width = x2 - x1;
|
|
height = y2 - y1;
|
|
|
|
if (rect_out)
|
|
{
|
|
rect_out->x = x1;
|
|
rect_out->y = y1;
|
|
rect_out->width = width;
|
|
rect_out->height = height;
|
|
}
|
|
|
|
if (surface->format->color.fourcc == GLITZ_FOURCC_RGB) {
|
|
if (surface->format->color.red_size > 0) {
|
|
masks.bpp = 32;
|
|
|
|
if (surface->format->color.alpha_size > 0)
|
|
masks.alpha_mask = 0xff000000;
|
|
else
|
|
masks.alpha_mask = 0x0;
|
|
|
|
masks.red_mask = 0xff0000;
|
|
masks.green_mask = 0xff00;
|
|
masks.blue_mask = 0xff;
|
|
} else {
|
|
masks.bpp = 8;
|
|
masks.blue_mask = masks.green_mask = masks.red_mask = 0x0;
|
|
masks.alpha_mask = 0xff;
|
|
}
|
|
} else {
|
|
masks.bpp = 32;
|
|
masks.alpha_mask = 0xff000000;
|
|
masks.red_mask = 0xff0000;
|
|
masks.green_mask = 0xff00;
|
|
masks.blue_mask = 0xff;
|
|
}
|
|
|
|
pf.fourcc = GLITZ_FOURCC_RGB;
|
|
pf.masks.bpp = masks.bpp;
|
|
pf.masks.alpha_mask = masks.alpha_mask;
|
|
pf.masks.red_mask = masks.red_mask;
|
|
pf.masks.green_mask = masks.green_mask;
|
|
pf.masks.blue_mask = masks.blue_mask;
|
|
pf.xoffset = 0;
|
|
pf.skip_lines = 0;
|
|
|
|
/* XXX: we should eventually return images with negative stride,
|
|
need to verify that libpixman have no problem with this first. */
|
|
pf.bytes_per_line = (((width * masks.bpp) / 8) + 3) & -4;
|
|
pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_TOP_DOWN;
|
|
|
|
pixels = _cairo_malloc_ab (height, pf.bytes_per_line);
|
|
if (!pixels)
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
buffer = glitz_buffer_create_for_data (pixels);
|
|
if (!buffer) {
|
|
free (pixels);
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
}
|
|
|
|
/* clear out the glitz clip; the clip affects glitz_get_pixels */
|
|
if (surface->has_clip)
|
|
glitz_surface_set_clip_region (surface->surface,
|
|
0, 0, NULL, 0);
|
|
|
|
glitz_get_pixels (surface->surface,
|
|
x1, y1,
|
|
width, height,
|
|
&pf,
|
|
buffer);
|
|
|
|
glitz_buffer_destroy (buffer);
|
|
|
|
/* restore the clip, if any */
|
|
if (surface->has_clip) {
|
|
glitz_box_t *box;
|
|
cairo_status_t status;
|
|
int n;
|
|
|
|
status = _cairo_glitz_get_boxes_from_region (&surface->clip, &box, &n);
|
|
if (status) {
|
|
free (pixels);
|
|
return status;
|
|
}
|
|
|
|
glitz_surface_set_clip_region (surface->surface, 0, 0, box, n);
|
|
|
|
free (box);
|
|
}
|
|
|
|
/*
|
|
* Prefer to use a standard pixman format instead of the
|
|
* general masks case.
|
|
*/
|
|
if (_CAIRO_MASK_FORMAT (&masks, &format)) {
|
|
image = (cairo_image_surface_t *)
|
|
cairo_image_surface_create_for_data (pixels,
|
|
format,
|
|
x2 - x1,
|
|
y2 - y1,
|
|
pf.bytes_per_line);
|
|
if (image->base.status)
|
|
goto FAIL;
|
|
} else {
|
|
/*
|
|
* XXX This can't work. We must convert the data to one of the
|
|
* supported pixman formats. Pixman needs another function
|
|
* which takes data in an arbitrary format and converts it
|
|
* to something supported by that library.
|
|
*/
|
|
image = (cairo_image_surface_t *)
|
|
_cairo_image_surface_create_with_masks (pixels,
|
|
&masks,
|
|
x2 - x1,
|
|
y2 - y1,
|
|
pf.bytes_per_line);
|
|
if (image->base.status)
|
|
goto FAIL;
|
|
}
|
|
|
|
_cairo_image_surface_assume_ownership_of_data (image);
|
|
|
|
*image_out = image;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
FAIL:
|
|
free (pixels);
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
}
|
|
|
|
static void
|
|
cairo_format_get_masks (cairo_format_t format,
|
|
uint32_t *bpp,
|
|
uint32_t *alpha,
|
|
uint32_t *red,
|
|
uint32_t *green,
|
|
uint32_t *blue)
|
|
{
|
|
*red = 0x0;
|
|
*green = 0x0;
|
|
*blue = 0x0;
|
|
*alpha = 0x0;
|
|
|
|
switch (format)
|
|
{
|
|
case CAIRO_FORMAT_ARGB32:
|
|
*alpha = 0xff000000;
|
|
case CAIRO_FORMAT_RGB24:
|
|
default:
|
|
*bpp = 32;
|
|
*red = 0x00ff0000;
|
|
*green = 0x0000ff00;
|
|
*blue = 0x000000ff;
|
|
break;
|
|
|
|
case CAIRO_FORMAT_A8:
|
|
*bpp = 8;
|
|
*alpha = 0xff;
|
|
break;
|
|
|
|
case CAIRO_FORMAT_A1:
|
|
*bpp = 1;
|
|
*alpha = 0x1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_glitz_surface_set_image (void *abstract_surface,
|
|
cairo_image_surface_t *image,
|
|
int src_x,
|
|
int src_y,
|
|
int width,
|
|
int height,
|
|
int x_dst,
|
|
int y_dst)
|
|
{
|
|
cairo_glitz_surface_t *surface = abstract_surface;
|
|
glitz_buffer_t *buffer;
|
|
glitz_pixel_format_t pf;
|
|
uint32_t bpp, am, rm, gm, bm;
|
|
char *data;
|
|
|
|
cairo_format_get_masks (image->format, &bpp, &am, &rm, &gm, &bm);
|
|
|
|
pf.fourcc = GLITZ_FOURCC_RGB;
|
|
pf.masks.bpp = bpp;
|
|
pf.masks.alpha_mask = am;
|
|
pf.masks.red_mask = rm;
|
|
pf.masks.green_mask = gm;
|
|
pf.masks.blue_mask = bm;
|
|
pf.xoffset = src_x;
|
|
pf.skip_lines = src_y;
|
|
|
|
/* check for negative stride */
|
|
if (image->stride < 0)
|
|
{
|
|
pf.bytes_per_line = -image->stride;
|
|
pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP;
|
|
data = (char *) image->data + image->stride * (image->height - 1);
|
|
}
|
|
else
|
|
{
|
|
pf.bytes_per_line = image->stride;
|
|
pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_TOP_DOWN;
|
|
data = (char *) image->data;
|
|
}
|
|
|
|
buffer = glitz_buffer_create_for_data (data);
|
|
if (!buffer)
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
glitz_set_pixels (surface->surface,
|
|
x_dst, y_dst,
|
|
width, height,
|
|
&pf,
|
|
buffer);
|
|
|
|
glitz_buffer_destroy (buffer);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_glitz_surface_acquire_source_image (void *abstract_surface,
|
|
cairo_image_surface_t **image_out,
|
|
void **image_extra)
|
|
{
|
|
cairo_glitz_surface_t *surface = abstract_surface;
|
|
|
|
*image_extra = NULL;
|
|
|
|
return _cairo_glitz_surface_get_image (surface, NULL, image_out, NULL);
|
|
}
|
|
|
|
static void
|
|
_cairo_glitz_surface_release_source_image (void *abstract_surface,
|
|
cairo_image_surface_t *image,
|
|
void *image_extra)
|
|
{
|
|
cairo_surface_destroy (&image->base);
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_glitz_surface_acquire_dest_image (void *abstract_surface,
|
|
cairo_rectangle_int_t *interest_rect,
|
|
cairo_image_surface_t **image_out,
|
|
cairo_rectangle_int_t *image_rect_out,
|
|
void **image_extra)
|
|
{
|
|
cairo_glitz_surface_t *surface = abstract_surface;
|
|
cairo_image_surface_t *image;
|
|
cairo_status_t status;
|
|
|
|
status = _cairo_glitz_surface_get_image (surface, interest_rect, &image,
|
|
image_rect_out);
|
|
if (status)
|
|
return status;
|
|
|
|
*image_out = image;
|
|
*image_extra = NULL;
|
|
|
|
return status;
|
|
}
|
|
|
|
static void
|
|
_cairo_glitz_surface_release_dest_image (void *abstract_surface,
|
|
cairo_rectangle_int_t *interest_rect,
|
|
cairo_image_surface_t *image,
|
|
cairo_rectangle_int_t *image_rect,
|
|
void *image_extra)
|
|
{
|
|
cairo_glitz_surface_t *surface = abstract_surface;
|
|
cairo_status_t status;
|
|
|
|
status = _cairo_glitz_surface_set_image (surface, image, 0, 0,
|
|
image->width, image->height,
|
|
image_rect->x, image_rect->y);
|
|
if (status)
|
|
status = _cairo_surface_set_error (&surface->base, status);
|
|
|
|
cairo_surface_destroy (&image->base);
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_glitz_surface_clone_similar (void *abstract_surface,
|
|
cairo_surface_t *src,
|
|
int src_x,
|
|
int src_y,
|
|
int width,
|
|
int height,
|
|
cairo_surface_t **clone_out)
|
|
{
|
|
cairo_glitz_surface_t *surface = abstract_surface;
|
|
cairo_glitz_surface_t *clone;
|
|
cairo_status_t status;
|
|
|
|
if (surface->base.status)
|
|
return surface->base.status;
|
|
|
|
if (src->backend == surface->base.backend)
|
|
{
|
|
*clone_out = cairo_surface_reference (src);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
else if (_cairo_surface_is_image (src))
|
|
{
|
|
cairo_image_surface_t *image_src = (cairo_image_surface_t *) src;
|
|
cairo_content_t content;
|
|
cairo_rectangle_int_t image_extent;
|
|
cairo_rectangle_int_t extent;
|
|
|
|
content = _cairo_content_from_format (image_src->format);
|
|
|
|
clone = (cairo_glitz_surface_t *)
|
|
_cairo_glitz_surface_create_similar (surface, content,
|
|
image_src->width,
|
|
image_src->height);
|
|
if (clone->base.status)
|
|
return clone->base.status;
|
|
|
|
image_extent.x = 0;
|
|
image_extent.y = 0;
|
|
image_extent.width = image_src->width;
|
|
image_extent.height = image_src->height;
|
|
extent.x = src_x;
|
|
extent.y = src_y;
|
|
extent.width = width;
|
|
extent.height = height;
|
|
|
|
_cairo_rectangle_intersect(&extent, &image_extent);
|
|
|
|
status = _cairo_glitz_surface_set_image (clone, image_src,
|
|
extent.x, extent.y,
|
|
extent.width, extent.height,
|
|
extent.x, extent.y);
|
|
if (status) {
|
|
cairo_surface_destroy (&clone->base);
|
|
return status;
|
|
}
|
|
|
|
*clone_out = &clone->base;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
static void
|
|
_cairo_glitz_surface_set_matrix (cairo_glitz_surface_t *surface,
|
|
cairo_matrix_t *matrix)
|
|
{
|
|
glitz_transform_t transform;
|
|
|
|
transform.matrix[0][0] = _cairo_fixed_from_double (matrix->xx);
|
|
transform.matrix[0][1] = _cairo_fixed_from_double (matrix->xy);
|
|
transform.matrix[0][2] = _cairo_fixed_from_double (matrix->x0);
|
|
|
|
transform.matrix[1][0] = _cairo_fixed_from_double (matrix->yx);
|
|
transform.matrix[1][1] = _cairo_fixed_from_double (matrix->yy);
|
|
transform.matrix[1][2] = _cairo_fixed_from_double (matrix->y0);
|
|
|
|
transform.matrix[2][0] = 0;
|
|
transform.matrix[2][1] = 0;
|
|
transform.matrix[2][2] = _cairo_fixed_from_double (1);
|
|
|
|
glitz_surface_set_transform (surface->surface, &transform);
|
|
}
|
|
|
|
static glitz_operator_t
|
|
_glitz_operator (cairo_operator_t op)
|
|
{
|
|
switch (op) {
|
|
case CAIRO_OPERATOR_CLEAR:
|
|
return GLITZ_OPERATOR_CLEAR;
|
|
|
|
case CAIRO_OPERATOR_SOURCE:
|
|
return GLITZ_OPERATOR_SRC;
|
|
case CAIRO_OPERATOR_OVER:
|
|
return GLITZ_OPERATOR_OVER;
|
|
case CAIRO_OPERATOR_IN:
|
|
return GLITZ_OPERATOR_IN;
|
|
case CAIRO_OPERATOR_OUT:
|
|
return GLITZ_OPERATOR_OUT;
|
|
case CAIRO_OPERATOR_ATOP:
|
|
return GLITZ_OPERATOR_ATOP;
|
|
|
|
case CAIRO_OPERATOR_DEST:
|
|
return GLITZ_OPERATOR_DST;
|
|
case CAIRO_OPERATOR_DEST_OVER:
|
|
return GLITZ_OPERATOR_OVER_REVERSE;
|
|
case CAIRO_OPERATOR_DEST_IN:
|
|
return GLITZ_OPERATOR_IN_REVERSE;
|
|
case CAIRO_OPERATOR_DEST_OUT:
|
|
return GLITZ_OPERATOR_OUT_REVERSE;
|
|
case CAIRO_OPERATOR_DEST_ATOP:
|
|
return GLITZ_OPERATOR_ATOP_REVERSE;
|
|
|
|
case CAIRO_OPERATOR_XOR:
|
|
return GLITZ_OPERATOR_XOR;
|
|
case CAIRO_OPERATOR_ADD:
|
|
return GLITZ_OPERATOR_ADD;
|
|
case CAIRO_OPERATOR_SATURATE:
|
|
/* XXX: This line should never be reached. Glitz backend should bail
|
|
out earlier if saturate operator is used. OpenGL can't do saturate
|
|
with pre-multiplied colors. Solid colors can still be done as we
|
|
can just un-pre-multiply them. However, support for that will have
|
|
to be added to glitz. */
|
|
|
|
/* fall-through */
|
|
break;
|
|
}
|
|
|
|
ASSERT_NOT_REACHED;
|
|
|
|
/* Something's very broken if this line of code can be reached, so
|
|
we want to return something that would give a noticeably
|
|
incorrect result. The XOR operator seems so rearely desired
|
|
that it should fit the bill here. */
|
|
return CAIRO_OPERATOR_XOR;
|
|
}
|
|
|
|
#define CAIRO_GLITZ_FEATURE_OK(surface, name) \
|
|
(glitz_drawable_get_features (glitz_surface_get_drawable (surface)) & \
|
|
(GLITZ_FEATURE_ ## name ## _MASK))
|
|
|
|
static glitz_status_t
|
|
_glitz_ensure_target (glitz_surface_t *surface)
|
|
{
|
|
if (!glitz_surface_get_attached_drawable (surface))
|
|
{
|
|
glitz_drawable_format_t *target_format, templ;
|
|
glitz_format_t *format;
|
|
glitz_drawable_t *drawable, *target;
|
|
unsigned int width, height;
|
|
unsigned long mask;
|
|
|
|
drawable = glitz_surface_get_drawable (surface);
|
|
format = glitz_surface_get_format (surface);
|
|
width = glitz_surface_get_width (surface);
|
|
height = glitz_surface_get_height (surface);
|
|
|
|
if (format->color.fourcc != GLITZ_FOURCC_RGB)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
templ.color = format->color;
|
|
templ.depth_size = 0;
|
|
templ.stencil_size = 0;
|
|
templ.doublebuffer = 0;
|
|
templ.samples = 1;
|
|
|
|
mask =
|
|
GLITZ_FORMAT_RED_SIZE_MASK |
|
|
GLITZ_FORMAT_GREEN_SIZE_MASK |
|
|
GLITZ_FORMAT_BLUE_SIZE_MASK |
|
|
GLITZ_FORMAT_ALPHA_SIZE_MASK |
|
|
GLITZ_FORMAT_DEPTH_SIZE_MASK |
|
|
GLITZ_FORMAT_STENCIL_SIZE_MASK |
|
|
GLITZ_FORMAT_DOUBLEBUFFER_MASK |
|
|
GLITZ_FORMAT_SAMPLES_MASK;
|
|
|
|
target_format = glitz_find_drawable_format (drawable, mask, &templ, 0);
|
|
if (!target_format)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
target = glitz_create_drawable (drawable, target_format,
|
|
width, height);
|
|
if (!target)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
glitz_surface_attach (surface, target,
|
|
GLITZ_DRAWABLE_BUFFER_FRONT_COLOR);
|
|
|
|
glitz_drawable_destroy (target);
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
typedef struct _cairo_glitz_surface_attributes {
|
|
cairo_surface_attributes_t base;
|
|
|
|
glitz_fill_t fill;
|
|
glitz_filter_t filter;
|
|
glitz_fixed16_16_t *params;
|
|
int n_params;
|
|
cairo_bool_t acquired;
|
|
} cairo_glitz_surface_attributes_t;
|
|
|
|
static cairo_int_status_t
|
|
_cairo_glitz_pattern_acquire_surface (cairo_pattern_t *pattern,
|
|
cairo_glitz_surface_t *dst,
|
|
int x,
|
|
int y,
|
|
unsigned int width,
|
|
unsigned int height,
|
|
cairo_glitz_surface_t **surface_out,
|
|
cairo_glitz_surface_attributes_t *attr)
|
|
{
|
|
cairo_glitz_surface_t *src = NULL;
|
|
|
|
attr->acquired = FALSE;
|
|
|
|
switch (pattern->type) {
|
|
case CAIRO_PATTERN_TYPE_LINEAR:
|
|
case CAIRO_PATTERN_TYPE_RADIAL: {
|
|
cairo_gradient_pattern_t *gradient =
|
|
(cairo_gradient_pattern_t *) pattern;
|
|
char *data;
|
|
glitz_fixed16_16_t *params;
|
|
unsigned int n_params;
|
|
unsigned int *pixels;
|
|
unsigned int i, n_base_params;
|
|
glitz_buffer_t *buffer;
|
|
static const glitz_pixel_format_t format = {
|
|
GLITZ_FOURCC_RGB,
|
|
{
|
|
32,
|
|
0xff000000,
|
|
0x00ff0000,
|
|
0x0000ff00,
|
|
0x000000ff
|
|
},
|
|
0, 0, 0,
|
|
GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP
|
|
};
|
|
|
|
/* XXX: the current color gradient acceleration provided by glitz is
|
|
* experimental, it's been proven inappropriate in a number of ways,
|
|
* most importantly, it's currently implemented as filters and
|
|
* gradients are not filters. eventually, it will be replaced with
|
|
* something more appropriate.
|
|
*/
|
|
|
|
if (gradient->n_stops < 2)
|
|
break;
|
|
|
|
if (!CAIRO_GLITZ_FEATURE_OK (dst->surface, FRAGMENT_PROGRAM))
|
|
break;
|
|
|
|
if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL)
|
|
n_base_params = 6;
|
|
else
|
|
n_base_params = 4;
|
|
|
|
n_params = gradient->n_stops * 3 + n_base_params;
|
|
|
|
/* check for int overflow */
|
|
{
|
|
int size1, size2;
|
|
if (n_params >= INT32_MAX / sizeof (glitz_fixed16_16_t) ||
|
|
gradient->n_stops >= INT32_MAX / sizeof (unsigned int))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
size1 = n_params * sizeof (glitz_fixed16_16_t);
|
|
size2 = gradient->n_stops * sizeof (unsigned int);
|
|
|
|
if (size1 >= INT32_MAX - size2)
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
data = malloc (size1 + size2);
|
|
}
|
|
|
|
if (!data)
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
params = (glitz_fixed16_16_t *) data;
|
|
pixels = (unsigned int *)
|
|
(data + sizeof (glitz_fixed16_16_t) * n_params);
|
|
|
|
buffer = glitz_buffer_create_for_data (pixels);
|
|
if (!buffer) {
|
|
free (data);
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
}
|
|
|
|
src = (cairo_glitz_surface_t *)
|
|
_cairo_surface_create_similar_scratch (&dst->base,
|
|
CAIRO_CONTENT_COLOR_ALPHA,
|
|
gradient->n_stops, 1);
|
|
if (src->base.status)
|
|
{
|
|
glitz_buffer_destroy (buffer);
|
|
free (data);
|
|
return src->base.status;
|
|
}
|
|
|
|
for (i = 0; i < gradient->n_stops; i++)
|
|
{
|
|
pixels[i] =
|
|
(((int) (gradient->stops[i].color.alpha_short >> 8)) << 24) |
|
|
(((int) (gradient->stops[i].color.red_short >> 8)) << 16) |
|
|
(((int) (gradient->stops[i].color.green_short >> 8)) << 8) |
|
|
(((int) (gradient->stops[i].color.blue_short >> 8)));
|
|
|
|
params[n_base_params + 3 * i + 0] = gradient->stops[i].x;
|
|
params[n_base_params + 3 * i + 1] = i << 16;
|
|
params[n_base_params + 3 * i + 2] = 0;
|
|
}
|
|
|
|
glitz_set_pixels (src->surface, 0, 0, gradient->n_stops, 1,
|
|
(glitz_pixel_format_t *)&format, buffer);
|
|
|
|
glitz_buffer_destroy (buffer);
|
|
|
|
if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR)
|
|
{
|
|
cairo_linear_pattern_t *grad = (cairo_linear_pattern_t *) pattern;
|
|
|
|
params[0] = grad->p1.x;
|
|
params[1] = grad->p1.y;
|
|
params[2] = grad->p2.x;
|
|
params[3] = grad->p2.y;
|
|
attr->filter = GLITZ_FILTER_LINEAR_GRADIENT;
|
|
}
|
|
else
|
|
{
|
|
cairo_radial_pattern_t *grad = (cairo_radial_pattern_t *) pattern;
|
|
|
|
params[0] = grad->c1.x;
|
|
params[1] = grad->c1.y;
|
|
params[2] = grad->r1;
|
|
params[3] = grad->c2.x;
|
|
params[4] = grad->c2.y;
|
|
params[5] = grad->r2;
|
|
attr->filter = GLITZ_FILTER_RADIAL_GRADIENT;
|
|
}
|
|
|
|
switch (pattern->extend) {
|
|
case CAIRO_EXTEND_NONE:
|
|
attr->fill = GLITZ_FILL_TRANSPARENT;
|
|
break;
|
|
case CAIRO_EXTEND_REPEAT:
|
|
attr->fill = GLITZ_FILL_REPEAT;
|
|
break;
|
|
case CAIRO_EXTEND_REFLECT:
|
|
attr->fill = GLITZ_FILL_REFLECT;
|
|
break;
|
|
case CAIRO_EXTEND_PAD:
|
|
attr->fill = GLITZ_FILL_NEAREST;
|
|
break;
|
|
}
|
|
|
|
attr->params = params;
|
|
attr->n_params = n_params;
|
|
attr->base.matrix = pattern->matrix;
|
|
attr->base.x_offset = 0;
|
|
attr->base.y_offset = 0;
|
|
} break;
|
|
case CAIRO_PATTERN_TYPE_SOLID:
|
|
case CAIRO_PATTERN_TYPE_SURFACE:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!src)
|
|
{
|
|
cairo_int_status_t status;
|
|
|
|
status = _cairo_pattern_acquire_surface (pattern, &dst->base,
|
|
x, y, width, height,
|
|
(cairo_surface_t **) &src,
|
|
&attr->base);
|
|
if (status)
|
|
return status;
|
|
|
|
if (src)
|
|
{
|
|
switch (attr->base.extend) {
|
|
case CAIRO_EXTEND_NONE:
|
|
attr->fill = GLITZ_FILL_TRANSPARENT;
|
|
break;
|
|
case CAIRO_EXTEND_REPEAT:
|
|
attr->fill = GLITZ_FILL_REPEAT;
|
|
break;
|
|
case CAIRO_EXTEND_REFLECT:
|
|
attr->fill = GLITZ_FILL_REFLECT;
|
|
break;
|
|
case CAIRO_EXTEND_PAD:
|
|
default:
|
|
attr->fill = GLITZ_FILL_NEAREST;
|
|
break;
|
|
}
|
|
|
|
switch (attr->base.filter) {
|
|
case CAIRO_FILTER_FAST:
|
|
case CAIRO_FILTER_NEAREST:
|
|
attr->filter = GLITZ_FILTER_NEAREST;
|
|
break;
|
|
case CAIRO_FILTER_GOOD:
|
|
case CAIRO_FILTER_BEST:
|
|
case CAIRO_FILTER_BILINEAR:
|
|
case CAIRO_FILTER_GAUSSIAN:
|
|
default:
|
|
attr->filter = GLITZ_FILTER_BILINEAR;
|
|
break;
|
|
}
|
|
|
|
attr->params = NULL;
|
|
attr->n_params = 0;
|
|
attr->acquired = TRUE;
|
|
}
|
|
}
|
|
|
|
*surface_out = src;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
_cairo_glitz_pattern_release_surface (cairo_pattern_t *pattern,
|
|
cairo_glitz_surface_t *surface,
|
|
cairo_glitz_surface_attributes_t *attr)
|
|
{
|
|
if (attr->acquired)
|
|
_cairo_pattern_release_surface (pattern, &surface->base, &attr->base);
|
|
else
|
|
cairo_surface_destroy (&surface->base);
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_glitz_pattern_acquire_surfaces (cairo_pattern_t *src,
|
|
cairo_pattern_t *mask,
|
|
cairo_glitz_surface_t *dst,
|
|
int src_x,
|
|
int src_y,
|
|
int mask_x,
|
|
int mask_y,
|
|
unsigned int width,
|
|
unsigned int height,
|
|
cairo_glitz_surface_t **src_out,
|
|
cairo_glitz_surface_t **mask_out,
|
|
cairo_glitz_surface_attributes_t *sattr,
|
|
cairo_glitz_surface_attributes_t *mattr)
|
|
{
|
|
cairo_int_status_t status;
|
|
cairo_pattern_union_t tmp;
|
|
|
|
/* If src and mask are both solid, then the mask alpha can be
|
|
* combined into src and mask can be ignored. */
|
|
|
|
/* XXX: This optimization assumes that there is no color
|
|
* information in mask, so this will need to change when we
|
|
* support RENDER-style 4-channel masks. */
|
|
|
|
if (src->type == CAIRO_PATTERN_TYPE_SOLID &&
|
|
mask->type == CAIRO_PATTERN_TYPE_SOLID)
|
|
{
|
|
cairo_color_t combined;
|
|
cairo_solid_pattern_t *src_solid = (cairo_solid_pattern_t *) src;
|
|
cairo_solid_pattern_t *mask_solid = (cairo_solid_pattern_t *) mask;
|
|
|
|
combined = src_solid->color;
|
|
_cairo_color_multiply_alpha (&combined, mask_solid->color.alpha);
|
|
|
|
_cairo_pattern_init_solid (&tmp.solid, &combined,
|
|
CAIRO_COLOR_IS_OPAQUE (&combined) ?
|
|
CAIRO_CONTENT_COLOR :
|
|
CAIRO_CONTENT_COLOR_ALPHA);
|
|
|
|
mask = NULL;
|
|
} else {
|
|
status = _cairo_pattern_init_copy (&tmp.base, src);
|
|
if (status)
|
|
return status;
|
|
}
|
|
|
|
status = _cairo_glitz_pattern_acquire_surface (&tmp.base, dst,
|
|
src_x, src_y,
|
|
width, height,
|
|
src_out, sattr);
|
|
|
|
_cairo_pattern_fini (&tmp.base);
|
|
|
|
if (status)
|
|
return status;
|
|
|
|
if (mask)
|
|
{
|
|
status = _cairo_pattern_init_copy (&tmp.base, mask);
|
|
if (status)
|
|
return status;
|
|
|
|
status = _cairo_glitz_pattern_acquire_surface (&tmp.base, dst,
|
|
mask_x, mask_y,
|
|
width, height,
|
|
mask_out, mattr);
|
|
|
|
if (status)
|
|
_cairo_glitz_pattern_release_surface (&tmp.base, *src_out, sattr);
|
|
|
|
_cairo_pattern_fini (&tmp.base);
|
|
|
|
return status;
|
|
}
|
|
else
|
|
{
|
|
*mask_out = NULL;
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
_cairo_glitz_surface_set_attributes (cairo_glitz_surface_t *surface,
|
|
cairo_glitz_surface_attributes_t *a)
|
|
{
|
|
_cairo_glitz_surface_set_matrix (surface, &a->base.matrix);
|
|
glitz_surface_set_fill (surface->surface, a->fill);
|
|
glitz_surface_set_filter (surface->surface, a->filter,
|
|
a->params, a->n_params);
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_glitz_surface_composite (cairo_operator_t op,
|
|
cairo_pattern_t *src_pattern,
|
|
cairo_pattern_t *mask_pattern,
|
|
void *abstract_dst,
|
|
int src_x,
|
|
int src_y,
|
|
int mask_x,
|
|
int mask_y,
|
|
int dst_x,
|
|
int dst_y,
|
|
unsigned int width,
|
|
unsigned int height)
|
|
{
|
|
cairo_glitz_surface_attributes_t src_attr, mask_attr;
|
|
cairo_glitz_surface_t *dst = abstract_dst;
|
|
cairo_glitz_surface_t *src;
|
|
cairo_glitz_surface_t *mask;
|
|
cairo_int_status_t status;
|
|
|
|
if (op == CAIRO_OPERATOR_SATURATE)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
if (_glitz_ensure_target (dst->surface))
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
status = _cairo_glitz_pattern_acquire_surfaces (src_pattern, mask_pattern,
|
|
dst,
|
|
src_x, src_y,
|
|
mask_x, mask_y,
|
|
width, height,
|
|
&src, &mask,
|
|
&src_attr, &mask_attr);
|
|
if (status)
|
|
return status;
|
|
|
|
_cairo_glitz_surface_set_attributes (src, &src_attr);
|
|
if (mask)
|
|
{
|
|
_cairo_glitz_surface_set_attributes (mask, &mask_attr);
|
|
glitz_composite (_glitz_operator (op),
|
|
src->surface,
|
|
mask->surface,
|
|
dst->surface,
|
|
src_x + src_attr.base.x_offset,
|
|
src_y + src_attr.base.y_offset,
|
|
mask_x + mask_attr.base.x_offset,
|
|
mask_y + mask_attr.base.y_offset,
|
|
dst_x, dst_y,
|
|
width, height);
|
|
|
|
if (mask_attr.n_params)
|
|
free (mask_attr.params);
|
|
|
|
_cairo_glitz_pattern_release_surface (mask_pattern, mask, &mask_attr);
|
|
}
|
|
else
|
|
{
|
|
glitz_composite (_glitz_operator (op),
|
|
src->surface,
|
|
NULL,
|
|
dst->surface,
|
|
src_x + src_attr.base.x_offset,
|
|
src_y + src_attr.base.y_offset,
|
|
0, 0,
|
|
dst_x, dst_y,
|
|
width, height);
|
|
}
|
|
|
|
if (src_attr.n_params)
|
|
free (src_attr.params);
|
|
|
|
_cairo_glitz_pattern_release_surface (src_pattern, src, &src_attr);
|
|
|
|
if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_glitz_surface_fill_rectangles (void *abstract_dst,
|
|
cairo_operator_t op,
|
|
const cairo_color_t *color,
|
|
cairo_rectangle_int_t *rects,
|
|
int n_rects)
|
|
{
|
|
cairo_glitz_surface_t *dst = abstract_dst;
|
|
cairo_glitz_surface_t *src;
|
|
|
|
switch (op) {
|
|
case CAIRO_OPERATOR_SOURCE: {
|
|
glitz_color_t glitz_color;
|
|
|
|
glitz_color.red = color->red_short;
|
|
glitz_color.green = color->green_short;
|
|
glitz_color.blue = color->blue_short;
|
|
glitz_color.alpha = color->alpha_short;
|
|
|
|
glitz_set_rectangles (dst->surface, &glitz_color,
|
|
(glitz_rectangle_t *) rects, n_rects);
|
|
} break;
|
|
case CAIRO_OPERATOR_CLEAR: {
|
|
static const glitz_color_t glitz_color = { 0, 0, 0, 0 };
|
|
|
|
glitz_set_rectangles (dst->surface, &glitz_color,
|
|
(glitz_rectangle_t *) rects, n_rects);
|
|
} break;
|
|
case CAIRO_OPERATOR_SATURATE:
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
case CAIRO_OPERATOR_OVER:
|
|
case CAIRO_OPERATOR_IN:
|
|
case CAIRO_OPERATOR_OUT:
|
|
case CAIRO_OPERATOR_ATOP:
|
|
case CAIRO_OPERATOR_DEST:
|
|
case CAIRO_OPERATOR_DEST_OVER:
|
|
case CAIRO_OPERATOR_DEST_IN:
|
|
case CAIRO_OPERATOR_DEST_OUT:
|
|
case CAIRO_OPERATOR_DEST_ATOP:
|
|
case CAIRO_OPERATOR_XOR:
|
|
case CAIRO_OPERATOR_ADD:
|
|
default:
|
|
if (_glitz_ensure_target (dst->surface))
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
src = (cairo_glitz_surface_t *)
|
|
_cairo_surface_create_similar_solid (&dst->base,
|
|
CAIRO_CONTENT_COLOR_ALPHA,
|
|
1, 1,
|
|
(cairo_color_t *) color,
|
|
NULL);
|
|
if (src->base.status)
|
|
return src->base.status;
|
|
|
|
glitz_surface_set_fill (src->surface, GLITZ_FILL_REPEAT);
|
|
|
|
while (n_rects--)
|
|
{
|
|
glitz_composite (_glitz_operator (op),
|
|
src->surface,
|
|
NULL,
|
|
dst->surface,
|
|
0, 0,
|
|
0, 0,
|
|
rects->x, rects->y,
|
|
rects->width, rects->height);
|
|
rects++;
|
|
}
|
|
|
|
cairo_surface_destroy (&src->base);
|
|
break;
|
|
}
|
|
|
|
if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_glitz_surface_composite_trapezoids (cairo_operator_t op,
|
|
cairo_pattern_t *pattern,
|
|
void *abstract_dst,
|
|
cairo_antialias_t antialias,
|
|
int src_x,
|
|
int src_y,
|
|
int dst_x,
|
|
int dst_y,
|
|
unsigned int width,
|
|
unsigned int height,
|
|
cairo_trapezoid_t *traps,
|
|
int n_traps)
|
|
{
|
|
cairo_pattern_union_t tmp_src_pattern;
|
|
cairo_pattern_t *src_pattern;
|
|
cairo_glitz_surface_attributes_t attributes;
|
|
cairo_glitz_surface_t *dst = abstract_dst;
|
|
cairo_glitz_surface_t *src;
|
|
cairo_glitz_surface_t *mask = NULL;
|
|
glitz_buffer_t *buffer = NULL;
|
|
void *data = NULL;
|
|
cairo_int_status_t status;
|
|
unsigned short alpha;
|
|
|
|
if (antialias != CAIRO_ANTIALIAS_DEFAULT &&
|
|
antialias != CAIRO_ANTIALIAS_GRAY)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
if (dst->base.status)
|
|
return dst->base.status;
|
|
|
|
if (op == CAIRO_OPERATOR_SATURATE)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
if (_glitz_ensure_target (dst->surface))
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE)
|
|
{
|
|
status = _cairo_pattern_init_copy (&tmp_src_pattern.base, pattern);
|
|
if (status)
|
|
return status;
|
|
|
|
status = _cairo_glitz_pattern_acquire_surface (&tmp_src_pattern.base,
|
|
dst,
|
|
src_x, src_y,
|
|
width, height,
|
|
&src, &attributes);
|
|
src_pattern = &tmp_src_pattern.base;
|
|
}
|
|
else
|
|
{
|
|
status = _cairo_glitz_pattern_acquire_surface (pattern, dst,
|
|
src_x, src_y,
|
|
width, height,
|
|
&src, &attributes);
|
|
src_pattern = pattern;
|
|
}
|
|
alpha = 0xffff;
|
|
|
|
if (status)
|
|
return status;
|
|
|
|
if (op == CAIRO_OPERATOR_ADD || n_traps <= 1)
|
|
{
|
|
static const glitz_color_t clear_black = { 0, 0, 0, 0 };
|
|
glitz_color_t color;
|
|
glitz_geometry_format_t format;
|
|
int n_trap_added;
|
|
int offset = 0;
|
|
int data_size = 0;
|
|
int size = 30 * n_traps; /* just a guess */
|
|
|
|
format.vertex.primitive = GLITZ_PRIMITIVE_QUADS;
|
|
format.vertex.type = GLITZ_DATA_TYPE_FLOAT;
|
|
format.vertex.bytes_per_vertex = 3 * sizeof (glitz_float_t);
|
|
format.vertex.attributes = GLITZ_VERTEX_ATTRIBUTE_MASK_COORD_MASK;
|
|
format.vertex.mask.type = GLITZ_DATA_TYPE_FLOAT;
|
|
format.vertex.mask.size = GLITZ_COORDINATE_SIZE_X;
|
|
format.vertex.mask.offset = 2 * sizeof (glitz_float_t);
|
|
|
|
mask = (cairo_glitz_surface_t *)
|
|
_cairo_glitz_surface_create_similar (&dst->base,
|
|
CAIRO_CONTENT_ALPHA,
|
|
2, 1);
|
|
if (mask->base.status)
|
|
{
|
|
_cairo_glitz_pattern_release_surface (src_pattern, src, &attributes);
|
|
if (src_pattern == &tmp_src_pattern.base)
|
|
_cairo_pattern_fini (&tmp_src_pattern.base);
|
|
|
|
return mask->base.status;
|
|
}
|
|
|
|
color.red = color.green = color.blue = color.alpha = 0xffff;
|
|
|
|
glitz_set_rectangle (mask->surface, &clear_black, 0, 0, 1, 1);
|
|
glitz_set_rectangle (mask->surface, &color, 1, 0, 1, 1);
|
|
|
|
glitz_surface_set_fill (mask->surface, GLITZ_FILL_NEAREST);
|
|
glitz_surface_set_filter (mask->surface,
|
|
GLITZ_FILTER_BILINEAR,
|
|
NULL, 0);
|
|
|
|
size *= format.vertex.bytes_per_vertex;
|
|
|
|
while (n_traps)
|
|
{
|
|
if (data_size < size)
|
|
{
|
|
void *p;
|
|
data_size = size;
|
|
p = realloc (data, data_size);
|
|
if (!p)
|
|
{
|
|
_cairo_glitz_pattern_release_surface (src_pattern, src,
|
|
&attributes);
|
|
if (src_pattern == &tmp_src_pattern.base)
|
|
_cairo_pattern_fini (&tmp_src_pattern.base);
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
}
|
|
data = p;
|
|
|
|
if (buffer)
|
|
glitz_buffer_destroy (buffer);
|
|
|
|
buffer = glitz_buffer_create_for_data (data);
|
|
if (!buffer) {
|
|
free (data);
|
|
_cairo_glitz_pattern_release_surface (src_pattern, src,
|
|
&attributes);
|
|
if (src_pattern == &tmp_src_pattern.base)
|
|
_cairo_pattern_fini (&tmp_src_pattern.base);
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
}
|
|
}
|
|
|
|
offset +=
|
|
glitz_add_trapezoids (buffer,
|
|
offset, size - offset,
|
|
format.vertex.type, mask->surface,
|
|
(glitz_trapezoid_t *) traps, n_traps,
|
|
&n_trap_added);
|
|
|
|
n_traps -= n_trap_added;
|
|
traps += n_trap_added;
|
|
size *= 2;
|
|
}
|
|
|
|
glitz_set_geometry (dst->surface,
|
|
GLITZ_GEOMETRY_TYPE_VERTEX,
|
|
&format, buffer);
|
|
glitz_set_array (dst->surface, 0, 3,
|
|
offset / format.vertex.bytes_per_vertex,
|
|
0, 0);
|
|
}
|
|
else
|
|
{
|
|
cairo_image_surface_t *image;
|
|
unsigned char *ptr;
|
|
int stride;
|
|
|
|
stride = (width + 3) & -4;
|
|
data = calloc (stride, height);
|
|
if (!data)
|
|
{
|
|
_cairo_glitz_pattern_release_surface (src_pattern, src, &attributes);
|
|
if (src_pattern == &tmp_src_pattern.base)
|
|
_cairo_pattern_fini (&tmp_src_pattern.base);
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
}
|
|
|
|
/* using negative stride */
|
|
ptr = (unsigned char *) data + stride * (height - 1);
|
|
|
|
image = (cairo_image_surface_t *)
|
|
cairo_image_surface_create_for_data (ptr,
|
|
CAIRO_FORMAT_A8,
|
|
width, height,
|
|
-stride);
|
|
if (image->base.status)
|
|
{
|
|
cairo_surface_destroy (&src->base);
|
|
free (data);
|
|
return image->base.status;
|
|
}
|
|
|
|
pixman_add_trapezoids (image->pixman_image, -dst_x, -dst_y,
|
|
n_traps, (pixman_trapezoid_t *) traps);
|
|
|
|
mask = (cairo_glitz_surface_t *)
|
|
_cairo_surface_create_similar_scratch (&dst->base,
|
|
CAIRO_CONTENT_ALPHA,
|
|
width, height);
|
|
if (mask->base.status)
|
|
{
|
|
_cairo_glitz_pattern_release_surface (src_pattern, src, &attributes);
|
|
free (data);
|
|
cairo_surface_destroy (&image->base);
|
|
return mask->base.status;
|
|
}
|
|
|
|
status = _cairo_glitz_surface_set_image (mask, image,
|
|
0, 0, width, height, 0, 0);
|
|
|
|
cairo_surface_destroy(&image->base);
|
|
|
|
if (status)
|
|
return status;
|
|
}
|
|
|
|
_cairo_glitz_surface_set_attributes (src, &attributes);
|
|
|
|
glitz_composite (_glitz_operator (op),
|
|
src->surface,
|
|
mask->surface,
|
|
dst->surface,
|
|
src_x + attributes.base.x_offset,
|
|
src_y + attributes.base.y_offset,
|
|
0, 0,
|
|
dst_x, dst_y,
|
|
width, height);
|
|
|
|
if (attributes.n_params)
|
|
free (attributes.params);
|
|
|
|
glitz_set_geometry (dst->surface,
|
|
GLITZ_GEOMETRY_TYPE_NONE,
|
|
NULL, NULL);
|
|
|
|
if (buffer)
|
|
glitz_buffer_destroy (buffer);
|
|
|
|
free (data);
|
|
|
|
_cairo_glitz_pattern_release_surface (src_pattern, src, &attributes);
|
|
if (src_pattern == &tmp_src_pattern.base)
|
|
_cairo_pattern_fini (&tmp_src_pattern.base);
|
|
|
|
if (mask)
|
|
cairo_surface_destroy (&mask->base);
|
|
|
|
if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_glitz_surface_set_clip_region (void *abstract_surface,
|
|
cairo_region_t *region)
|
|
{
|
|
cairo_glitz_surface_t *surface = abstract_surface;
|
|
|
|
if (region)
|
|
{
|
|
glitz_box_t *box;
|
|
int n;
|
|
cairo_status_t status;
|
|
|
|
if (!surface->has_clip) {
|
|
_cairo_region_init (&surface->clip);
|
|
surface->has_clip = TRUE;
|
|
}
|
|
|
|
status = _cairo_region_copy (&surface->clip, region);
|
|
if (status) {
|
|
_cairo_region_fini (&surface->clip);
|
|
surface->has_clip = FALSE;
|
|
return status;
|
|
}
|
|
|
|
status = _cairo_glitz_get_boxes_from_region (&surface->clip, &box, &n);
|
|
if (status) {
|
|
_cairo_region_fini (&surface->clip);
|
|
surface->has_clip = FALSE;
|
|
return status;
|
|
}
|
|
|
|
glitz_surface_set_clip_region (surface->surface, 0, 0, box, n);
|
|
|
|
free (box);
|
|
}
|
|
else
|
|
{
|
|
glitz_surface_set_clip_region (surface->surface, 0, 0, NULL, 0);
|
|
|
|
if (surface->has_clip) {
|
|
_cairo_region_fini (&surface->clip);
|
|
surface->has_clip = FALSE;
|
|
}
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_glitz_surface_get_extents (void *abstract_surface,
|
|
cairo_rectangle_int_t *rectangle)
|
|
{
|
|
cairo_glitz_surface_t *surface = abstract_surface;
|
|
|
|
rectangle->x = 0;
|
|
rectangle->y = 0;
|
|
rectangle->width = glitz_surface_get_width (surface->surface);
|
|
rectangle->height = glitz_surface_get_height (surface->surface);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
#define CAIRO_GLITZ_AREA_AVAILABLE 0
|
|
#define CAIRO_GLITZ_AREA_DIVIDED 1
|
|
#define CAIRO_GLITZ_AREA_OCCUPIED 2
|
|
|
|
typedef struct _cairo_glitz_root_area cairo_glitz_root_area_t;
|
|
|
|
typedef struct _cairo_glitz_area {
|
|
int state;
|
|
int level;
|
|
int x, y;
|
|
int width, height;
|
|
struct _cairo_glitz_area *area[4];
|
|
cairo_glitz_root_area_t *root;
|
|
void *closure;
|
|
} cairo_glitz_area_t;
|
|
|
|
static cairo_glitz_area_t _empty_area = {
|
|
0, 0, 0, 0, 0, 0,
|
|
{ NULL, NULL, NULL, NULL },
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
typedef struct _cairo_glitz_area_funcs {
|
|
cairo_status_t (*move_in) (cairo_glitz_area_t *area,
|
|
void *closure);
|
|
|
|
void (*move_out) (cairo_glitz_area_t *area,
|
|
void *closure);
|
|
|
|
int (*compare_score) (cairo_glitz_area_t *area,
|
|
void *closure1,
|
|
void *closure2);
|
|
} cairo_glitz_area_funcs_t;
|
|
|
|
struct _cairo_glitz_root_area {
|
|
int max_level;
|
|
int width, height;
|
|
cairo_glitz_area_t *area;
|
|
const cairo_glitz_area_funcs_t *funcs;
|
|
};
|
|
|
|
static cairo_status_t
|
|
_cairo_glitz_area_move_in (cairo_glitz_area_t *area,
|
|
void *closure)
|
|
{
|
|
area->closure = closure;
|
|
area->state = CAIRO_GLITZ_AREA_OCCUPIED;
|
|
|
|
return (*area->root->funcs->move_in) (area, area->closure);
|
|
}
|
|
|
|
static void
|
|
_cairo_glitz_area_move_out (cairo_glitz_area_t *area)
|
|
{
|
|
if (area->root)
|
|
{
|
|
(*area->root->funcs->move_out) (area, area->closure);
|
|
|
|
area->closure = NULL;
|
|
area->state = CAIRO_GLITZ_AREA_AVAILABLE;
|
|
}
|
|
}
|
|
|
|
static cairo_glitz_area_t *
|
|
_cairo_glitz_area_create (cairo_glitz_root_area_t *root,
|
|
int level,
|
|
int x,
|
|
int y,
|
|
int width,
|
|
int height)
|
|
{
|
|
cairo_glitz_area_t *area;
|
|
int n = 4;
|
|
|
|
area = malloc (sizeof (cairo_glitz_area_t));
|
|
if (!area) {
|
|
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
|
|
return NULL;
|
|
}
|
|
|
|
area->level = level;
|
|
area->x = x;
|
|
area->y = y;
|
|
area->width = width;
|
|
area->height = height;
|
|
area->root = root;
|
|
area->closure = NULL;
|
|
area->state = CAIRO_GLITZ_AREA_AVAILABLE;
|
|
|
|
while (n--)
|
|
area->area[n] = NULL;
|
|
|
|
return area;
|
|
}
|
|
|
|
static void
|
|
_cairo_glitz_area_destroy (cairo_glitz_area_t *area)
|
|
{
|
|
if (area == NULL)
|
|
return;
|
|
|
|
if (area->state == CAIRO_GLITZ_AREA_OCCUPIED)
|
|
{
|
|
_cairo_glitz_area_move_out (area);
|
|
}
|
|
else
|
|
{
|
|
int n = 4;
|
|
|
|
while (n--)
|
|
_cairo_glitz_area_destroy (area->area[n]);
|
|
}
|
|
|
|
free (area);
|
|
}
|
|
|
|
static cairo_glitz_area_t *
|
|
_cairo_glitz_area_get_top_scored_sub_area (cairo_glitz_area_t *area)
|
|
{
|
|
if (!area)
|
|
return NULL;
|
|
|
|
switch (area->state) {
|
|
case CAIRO_GLITZ_AREA_OCCUPIED:
|
|
return area;
|
|
case CAIRO_GLITZ_AREA_AVAILABLE:
|
|
break;
|
|
case CAIRO_GLITZ_AREA_DIVIDED: {
|
|
cairo_glitz_area_t *tmp, *top = NULL;
|
|
int i;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
tmp = _cairo_glitz_area_get_top_scored_sub_area (area->area[i]);
|
|
if (tmp && top)
|
|
{
|
|
if ((*area->root->funcs->compare_score) (tmp,
|
|
tmp->closure,
|
|
top->closure) > 0)
|
|
top = tmp;
|
|
}
|
|
else if (tmp)
|
|
{
|
|
top = tmp;
|
|
}
|
|
}
|
|
return top;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_glitz_area_find (cairo_glitz_area_t *area,
|
|
int width,
|
|
int height,
|
|
cairo_bool_t kick_out,
|
|
void *closure)
|
|
{
|
|
cairo_status_t status;
|
|
|
|
if (area->width < width || area->height < height)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
switch (area->state) {
|
|
case CAIRO_GLITZ_AREA_OCCUPIED:
|
|
if (kick_out)
|
|
{
|
|
if ((*area->root->funcs->compare_score) (area,
|
|
area->closure,
|
|
closure) >= 0)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
_cairo_glitz_area_move_out (area);
|
|
} else {
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
/* fall-through */
|
|
case CAIRO_GLITZ_AREA_AVAILABLE: {
|
|
if (area->level == area->root->max_level ||
|
|
(area->width == width && area->height == height))
|
|
{
|
|
return _cairo_glitz_area_move_in (area, closure);
|
|
}
|
|
else
|
|
{
|
|
int dx[4], dy[4], w[4], h[4], i;
|
|
|
|
dx[0] = dx[2] = dy[0] = dy[1] = 0;
|
|
|
|
w[0] = w[2] = dx[1] = dx[3] = width;
|
|
h[0] = h[1] = dy[2] = dy[3] = height;
|
|
|
|
w[1] = w[3] = area->width - width;
|
|
h[2] = h[3] = area->height - height;
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
if (w[i])
|
|
area->area[i] =
|
|
_cairo_glitz_area_create (area->root,
|
|
area->level + 1,
|
|
area->x + dx[i],
|
|
area->y + dy[i],
|
|
w[i], h[i]);
|
|
}
|
|
|
|
for (; i < 4; i++)
|
|
{
|
|
if (w[i] && h[i])
|
|
area->area[i] =
|
|
_cairo_glitz_area_create (area->root,
|
|
area->level + 1,
|
|
area->x + dx[i],
|
|
area->y + dy[i],
|
|
w[i], h[i]);
|
|
}
|
|
|
|
area->state = CAIRO_GLITZ_AREA_DIVIDED;
|
|
|
|
status = _cairo_glitz_area_find (area->area[0],
|
|
width, height,
|
|
kick_out, closure);
|
|
if (status == CAIRO_STATUS_SUCCESS)
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
} break;
|
|
case CAIRO_GLITZ_AREA_DIVIDED: {
|
|
cairo_glitz_area_t *to_area;
|
|
int i, rejected = FALSE;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
if (area->area[i])
|
|
{
|
|
if (area->area[i]->width >= width &&
|
|
area->area[i]->height >= height)
|
|
{
|
|
status = _cairo_glitz_area_find (area->area[i],
|
|
width, height,
|
|
kick_out, closure);
|
|
if (status == CAIRO_STATUS_SUCCESS)
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
rejected = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rejected)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
to_area = _cairo_glitz_area_get_top_scored_sub_area (area);
|
|
if (to_area)
|
|
{
|
|
if (kick_out)
|
|
{
|
|
if ((*area->root->funcs->compare_score) (to_area,
|
|
to_area->closure,
|
|
closure) >= 0)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
} else {
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
_cairo_glitz_area_destroy (area->area[i]);
|
|
area->area[i] = NULL;
|
|
}
|
|
|
|
area->closure = NULL;
|
|
area->state = CAIRO_GLITZ_AREA_AVAILABLE;
|
|
|
|
status = _cairo_glitz_area_find (area, width, height,
|
|
TRUE, closure);
|
|
if (status == CAIRO_STATUS_SUCCESS)
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
} break;
|
|
}
|
|
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_glitz_root_area_init (cairo_glitz_root_area_t *root,
|
|
int max_level,
|
|
int width,
|
|
int height,
|
|
const cairo_glitz_area_funcs_t *funcs)
|
|
{
|
|
root->max_level = max_level;
|
|
root->funcs = funcs;
|
|
|
|
root->area = _cairo_glitz_area_create (root, 0, 0, 0, width, height);
|
|
if (!root->area)
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
_cairo_glitz_root_area_fini (cairo_glitz_root_area_t *root)
|
|
{
|
|
_cairo_glitz_area_destroy (root->area);
|
|
}
|
|
|
|
typedef struct _cairo_glitz_surface_font_private {
|
|
cairo_glitz_root_area_t root;
|
|
glitz_surface_t *surface;
|
|
} cairo_glitz_surface_font_private_t;
|
|
|
|
typedef struct _cairo_glitz_surface_glyph_private {
|
|
cairo_glitz_area_t *area;
|
|
cairo_bool_t locked;
|
|
cairo_point_double_t p1, p2;
|
|
} cairo_glitz_surface_glyph_private_t;
|
|
|
|
static cairo_status_t
|
|
_cairo_glitz_glyph_move_in (cairo_glitz_area_t *area,
|
|
void *closure)
|
|
{
|
|
cairo_glitz_surface_glyph_private_t *glyph_private = closure;
|
|
|
|
glyph_private->area = area;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
_cairo_glitz_glyph_move_out (cairo_glitz_area_t *area,
|
|
void *closure)
|
|
{
|
|
cairo_glitz_surface_glyph_private_t *glyph_private = closure;
|
|
|
|
glyph_private->area = NULL;
|
|
}
|
|
|
|
static int
|
|
_cairo_glitz_glyph_compare (cairo_glitz_area_t *area,
|
|
void *closure1,
|
|
void *closure2)
|
|
{
|
|
cairo_glitz_surface_glyph_private_t *glyph_private = closure1;
|
|
|
|
if (glyph_private->locked)
|
|
return 1;
|
|
|
|
return -1;
|
|
}
|
|
|
|
static const cairo_glitz_area_funcs_t _cairo_glitz_area_funcs = {
|
|
_cairo_glitz_glyph_move_in,
|
|
_cairo_glitz_glyph_move_out,
|
|
_cairo_glitz_glyph_compare
|
|
};
|
|
|
|
#define GLYPH_CACHE_TEXTURE_SIZE 512
|
|
#define GLYPH_CACHE_MAX_LEVEL 64
|
|
#define GLYPH_CACHE_MAX_HEIGHT 96
|
|
#define GLYPH_CACHE_MAX_WIDTH 96
|
|
|
|
#define WRITE_VEC2(ptr, _x, _y) \
|
|
*(ptr)++ = (_x); \
|
|
*(ptr)++ = (_y)
|
|
|
|
#define WRITE_BOX(ptr, _vx1, _vy1, _vx2, _vy2, p1, p2) \
|
|
WRITE_VEC2 (ptr, _vx1, _vy1); \
|
|
WRITE_VEC2 (ptr, (p1)->x, (p2)->y); \
|
|
WRITE_VEC2 (ptr, _vx2, _vy1); \
|
|
WRITE_VEC2 (ptr, (p2)->x, (p2)->y); \
|
|
WRITE_VEC2 (ptr, _vx2, _vy2); \
|
|
WRITE_VEC2 (ptr, (p2)->x, (p1)->y); \
|
|
WRITE_VEC2 (ptr, _vx1, _vy2); \
|
|
WRITE_VEC2 (ptr, (p1)->x, (p1)->y)
|
|
|
|
static cairo_status_t
|
|
_cairo_glitz_surface_font_init (cairo_glitz_surface_t *surface,
|
|
cairo_scaled_font_t *scaled_font,
|
|
cairo_format_t format)
|
|
{
|
|
cairo_glitz_surface_font_private_t *font_private;
|
|
glitz_drawable_t *drawable;
|
|
glitz_format_t *surface_format = NULL;
|
|
cairo_int_status_t status;
|
|
|
|
drawable = glitz_surface_get_drawable (surface->surface);
|
|
|
|
switch (format) {
|
|
case CAIRO_FORMAT_A1:
|
|
case CAIRO_FORMAT_A8:
|
|
surface_format =
|
|
glitz_find_standard_format (drawable, GLITZ_STANDARD_A8);
|
|
break;
|
|
case CAIRO_FORMAT_RGB24:
|
|
ASSERT_NOT_REACHED;
|
|
break;
|
|
case CAIRO_FORMAT_ARGB32:
|
|
surface_format =
|
|
glitz_find_standard_format (drawable, GLITZ_STANDARD_ARGB32);
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!surface_format)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
font_private = malloc (sizeof (cairo_glitz_surface_font_private_t));
|
|
if (!font_private)
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
font_private->surface = glitz_surface_create (drawable, surface_format,
|
|
GLYPH_CACHE_TEXTURE_SIZE,
|
|
GLYPH_CACHE_TEXTURE_SIZE,
|
|
0, NULL);
|
|
if (font_private->surface == NULL)
|
|
{
|
|
free (font_private);
|
|
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
if (format == CAIRO_FORMAT_ARGB32)
|
|
glitz_surface_set_component_alpha (font_private->surface, 1);
|
|
|
|
status = _cairo_glitz_root_area_init (&font_private->root,
|
|
GLYPH_CACHE_MAX_LEVEL,
|
|
GLYPH_CACHE_TEXTURE_SIZE,
|
|
GLYPH_CACHE_TEXTURE_SIZE,
|
|
&_cairo_glitz_area_funcs);
|
|
if (status != CAIRO_STATUS_SUCCESS)
|
|
{
|
|
glitz_surface_destroy (font_private->surface);
|
|
free (font_private);
|
|
|
|
return status;
|
|
}
|
|
|
|
scaled_font->surface_private = font_private;
|
|
scaled_font->surface_backend = _cairo_glitz_surface_get_backend ();
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
_cairo_glitz_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font)
|
|
{
|
|
cairo_glitz_surface_font_private_t *font_private;
|
|
|
|
font_private = scaled_font->surface_private;
|
|
if (font_private)
|
|
{
|
|
_cairo_glitz_root_area_fini (&font_private->root);
|
|
glitz_surface_destroy (font_private->surface);
|
|
free (font_private);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_cairo_glitz_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
|
|
cairo_scaled_font_t *scaled_font)
|
|
{
|
|
cairo_glitz_surface_glyph_private_t *glyph_private;
|
|
|
|
glyph_private = scaled_glyph->surface_private;
|
|
if (glyph_private)
|
|
{
|
|
if (glyph_private->area)
|
|
_cairo_glitz_area_move_out (glyph_private->area);
|
|
|
|
free (glyph_private);
|
|
}
|
|
}
|
|
|
|
#define FIXED_TO_FLOAT(f) (((glitz_float_t) (f)) / 65536)
|
|
|
|
static cairo_status_t
|
|
_cairo_glitz_surface_add_glyph (cairo_glitz_surface_t *surface,
|
|
cairo_scaled_font_t *scaled_font,
|
|
cairo_scaled_glyph_t *scaled_glyph)
|
|
{
|
|
cairo_image_surface_t *glyph_surface = scaled_glyph->surface;
|
|
cairo_glitz_surface_font_private_t *font_private;
|
|
cairo_glitz_surface_glyph_private_t *glyph_private;
|
|
glitz_point_fixed_t p1, p2;
|
|
glitz_pixel_format_t pf;
|
|
glitz_buffer_t *buffer;
|
|
unsigned int bpp, am, rm, gm, bm;
|
|
cairo_int_status_t status;
|
|
|
|
glyph_private = scaled_glyph->surface_private;
|
|
if (glyph_private == NULL)
|
|
{
|
|
glyph_private = malloc (sizeof (cairo_glitz_surface_glyph_private_t));
|
|
if (!glyph_private)
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
glyph_private->area = NULL;
|
|
glyph_private->locked = FALSE;
|
|
|
|
scaled_glyph->surface_private = (void *) glyph_private;
|
|
}
|
|
|
|
if (glyph_surface->width > GLYPH_CACHE_MAX_WIDTH ||
|
|
glyph_surface->height > GLYPH_CACHE_MAX_HEIGHT)
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
if (scaled_font->surface_private == NULL)
|
|
{
|
|
status = _cairo_glitz_surface_font_init (surface, scaled_font,
|
|
glyph_surface->format);
|
|
if (status)
|
|
return status;
|
|
}
|
|
|
|
font_private = scaled_font->surface_private;
|
|
|
|
if (glyph_surface->width == 0 || glyph_surface->height == 0)
|
|
{
|
|
glyph_private->area = &_empty_area;
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
if (_cairo_glitz_area_find (font_private->root.area,
|
|
glyph_surface->width,
|
|
glyph_surface->height,
|
|
FALSE, glyph_private))
|
|
{
|
|
if (_cairo_glitz_area_find (font_private->root.area,
|
|
glyph_surface->width,
|
|
glyph_surface->height,
|
|
TRUE, glyph_private))
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
buffer = glitz_buffer_create_for_data (glyph_surface->data);
|
|
if (!buffer)
|
|
{
|
|
_cairo_glitz_area_move_out (glyph_private->area);
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
}
|
|
|
|
cairo_format_get_masks (glyph_surface->format, &bpp, &am, &rm, &gm, &bm);
|
|
|
|
pf.fourcc = GLITZ_FOURCC_RGB;
|
|
pf.masks.bpp = bpp;
|
|
pf.masks.alpha_mask = am;
|
|
pf.masks.red_mask = rm;
|
|
pf.masks.green_mask = gm;
|
|
pf.masks.blue_mask = bm;
|
|
|
|
pf.bytes_per_line = glyph_surface->stride;
|
|
pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP;
|
|
pf.xoffset = 0;
|
|
pf.skip_lines = 0;
|
|
|
|
glitz_set_pixels (font_private->surface,
|
|
glyph_private->area->x,
|
|
glyph_private->area->y,
|
|
glyph_surface->width,
|
|
glyph_surface->height,
|
|
&pf, buffer);
|
|
|
|
glitz_buffer_destroy (buffer);
|
|
|
|
p1.x = glyph_private->area->x << 16;
|
|
p1.y = glyph_private->area->y << 16;
|
|
p2.x = (glyph_private->area->x + glyph_surface->width) << 16;
|
|
p2.y = (glyph_private->area->y + glyph_surface->height) << 16;
|
|
|
|
glitz_surface_translate_point (font_private->surface, &p1, &p1);
|
|
glitz_surface_translate_point (font_private->surface, &p2, &p2);
|
|
|
|
glyph_private->p1.x = FIXED_TO_FLOAT (p1.x);
|
|
glyph_private->p1.y = FIXED_TO_FLOAT (p1.y);
|
|
glyph_private->p2.x = FIXED_TO_FLOAT (p2.x);
|
|
glyph_private->p2.y = FIXED_TO_FLOAT (p2.y);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
#define N_STACK_BUF 256
|
|
|
|
static cairo_int_status_t
|
|
_cairo_glitz_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font,
|
|
cairo_operator_t op,
|
|
cairo_pattern_t *pattern,
|
|
void *abstract_surface,
|
|
int src_x,
|
|
int src_y,
|
|
int dst_x,
|
|
int dst_y,
|
|
unsigned int width,
|
|
unsigned int height,
|
|
cairo_glyph_t *glyphs,
|
|
int num_glyphs)
|
|
{
|
|
cairo_glitz_surface_attributes_t attributes;
|
|
cairo_glitz_surface_glyph_private_t *glyph_private;
|
|
cairo_glitz_surface_t *dst = abstract_surface;
|
|
cairo_glitz_surface_t *src;
|
|
cairo_scaled_glyph_t *stack_scaled_glyphs[N_STACK_BUF];
|
|
cairo_scaled_glyph_t **scaled_glyphs;
|
|
glitz_float_t stack_vertices[N_STACK_BUF * 16];
|
|
glitz_float_t *vertices;
|
|
glitz_buffer_t *buffer;
|
|
cairo_int_status_t status;
|
|
int x_offset, y_offset;
|
|
int i, cached_glyphs = 0;
|
|
int remaining_glyps = num_glyphs;
|
|
glitz_float_t x1, y1, x2, y2;
|
|
static const glitz_vertex_format_t format = {
|
|
GLITZ_PRIMITIVE_QUADS,
|
|
GLITZ_DATA_TYPE_FLOAT,
|
|
sizeof (glitz_float_t) * 4,
|
|
GLITZ_VERTEX_ATTRIBUTE_MASK_COORD_MASK,
|
|
{ 0 },
|
|
{
|
|
GLITZ_DATA_TYPE_FLOAT,
|
|
GLITZ_COORDINATE_SIZE_XY,
|
|
sizeof (glitz_float_t) * 2,
|
|
}
|
|
};
|
|
|
|
if (scaled_font->surface_backend != NULL &&
|
|
scaled_font->surface_backend != _cairo_glitz_surface_get_backend ())
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
if (op == CAIRO_OPERATOR_SATURATE)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
if (_glitz_ensure_target (dst->surface))
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
status = _cairo_glitz_pattern_acquire_surface (pattern, dst,
|
|
src_x, src_y,
|
|
width, height,
|
|
&src, &attributes);
|
|
if (status)
|
|
return status;
|
|
|
|
_cairo_glitz_surface_set_attributes (src, &attributes);
|
|
|
|
if (num_glyphs > N_STACK_BUF)
|
|
{
|
|
char *data;
|
|
size_t size1, size2;
|
|
|
|
if ((size_t)num_glyphs >= INT32_MAX / sizeof(void*) ||
|
|
(size_t)num_glyphs >= INT32_MAX / sizeof(glitz_float_t) ||
|
|
((size_t)num_glyphs * sizeof(glitz_float_t)) >= INT32_MAX / 16)
|
|
goto FAIL1;
|
|
|
|
size1 = num_glyphs * sizeof(void *);
|
|
size2 = num_glyphs * sizeof(glitz_float_t) * 16;
|
|
if (size1 >= INT32_MAX - size2)
|
|
goto FAIL1;
|
|
|
|
data = malloc (size1 + size2);
|
|
if (!data) {
|
|
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
|
|
goto FAIL1;
|
|
}
|
|
|
|
scaled_glyphs = (cairo_scaled_glyph_t **) data;
|
|
vertices = (glitz_float_t *) (data + num_glyphs * sizeof (void *));
|
|
}
|
|
else
|
|
{
|
|
scaled_glyphs = stack_scaled_glyphs;
|
|
vertices = stack_vertices;
|
|
}
|
|
|
|
buffer = glitz_buffer_create_for_data (vertices);
|
|
if (!buffer)
|
|
goto FAIL2;
|
|
|
|
_cairo_scaled_font_freeze_cache (scaled_font);
|
|
|
|
for (i = 0; i < num_glyphs; i++)
|
|
{
|
|
status = _cairo_scaled_glyph_lookup (scaled_font,
|
|
glyphs[i].index,
|
|
CAIRO_SCALED_GLYPH_INFO_SURFACE,
|
|
&scaled_glyphs[i]);
|
|
if (status != CAIRO_STATUS_SUCCESS)
|
|
{
|
|
num_glyphs = i;
|
|
goto UNLOCK;
|
|
}
|
|
|
|
glyph_private = scaled_glyphs[i]->surface_private;
|
|
if (!glyph_private || !glyph_private->area)
|
|
{
|
|
status = _cairo_glitz_surface_add_glyph (dst,
|
|
scaled_font,
|
|
scaled_glyphs[i]);
|
|
if (status != CAIRO_STATUS_SUCCESS) {
|
|
num_glyphs = i;
|
|
goto UNLOCK;
|
|
}
|
|
}
|
|
glyph_private = scaled_glyphs[i]->surface_private;
|
|
if (glyph_private && glyph_private->area)
|
|
{
|
|
remaining_glyps--;
|
|
|
|
if (glyph_private->area->width)
|
|
{
|
|
x_offset = scaled_glyphs[i]->surface->base.device_transform.x0;
|
|
y_offset = scaled_glyphs[i]->surface->base.device_transform.y0;
|
|
|
|
x1 = _cairo_lround (glyphs[i].x - x_offset);
|
|
y1 = _cairo_lround (glyphs[i].y - y_offset);
|
|
x2 = x1 + glyph_private->area->width;
|
|
y2 = y1 + glyph_private->area->height;
|
|
|
|
WRITE_BOX (vertices, x1, y1, x2, y2,
|
|
&glyph_private->p1, &glyph_private->p2);
|
|
|
|
glyph_private->locked = TRUE;
|
|
|
|
cached_glyphs++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (remaining_glyps)
|
|
{
|
|
cairo_surface_t *image;
|
|
cairo_glitz_surface_t *clone;
|
|
|
|
for (i = 0; i < num_glyphs; i++)
|
|
{
|
|
glyph_private = scaled_glyphs[i]->surface_private;
|
|
if (!glyph_private || !glyph_private->area)
|
|
{
|
|
int glyph_width, glyph_height;
|
|
|
|
image = &scaled_glyphs[i]->surface->base;
|
|
glyph_width = scaled_glyphs[i]->surface->width;
|
|
glyph_height = scaled_glyphs[i]->surface->height;
|
|
status =
|
|
_cairo_glitz_surface_clone_similar (abstract_surface,
|
|
image,
|
|
0,
|
|
0,
|
|
glyph_width,
|
|
glyph_height,
|
|
(cairo_surface_t **)
|
|
&clone);
|
|
if (status)
|
|
goto UNLOCK;
|
|
|
|
x_offset = scaled_glyphs[i]->surface->base.device_transform.x0;
|
|
y_offset = scaled_glyphs[i]->surface->base.device_transform.y0;
|
|
x1 = _cairo_lround (glyphs[i].x - x_offset);
|
|
y1 = _cairo_lround (glyphs[i].y - y_offset);
|
|
|
|
glitz_composite (_glitz_operator (op),
|
|
src->surface,
|
|
clone->surface,
|
|
dst->surface,
|
|
src_x + attributes.base.x_offset + x1,
|
|
src_y + attributes.base.y_offset + y1,
|
|
0, 0,
|
|
x1, y1,
|
|
glyph_width,
|
|
glyph_height);
|
|
|
|
cairo_surface_destroy (&clone->base);
|
|
|
|
if (glitz_surface_get_status (dst->surface) ==
|
|
GLITZ_STATUS_NOT_SUPPORTED)
|
|
{
|
|
status = CAIRO_INT_STATUS_UNSUPPORTED;
|
|
goto UNLOCK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cached_glyphs)
|
|
{
|
|
cairo_glitz_surface_font_private_t *font_private;
|
|
|
|
glitz_set_geometry (dst->surface,
|
|
GLITZ_GEOMETRY_TYPE_VERTEX,
|
|
(glitz_geometry_format_t *) &format,
|
|
buffer);
|
|
|
|
glitz_set_array (dst->surface, 0, 4, cached_glyphs * 4, 0, 0);
|
|
|
|
font_private = scaled_font->surface_private;
|
|
|
|
glitz_composite (_glitz_operator (op),
|
|
src->surface,
|
|
font_private->surface,
|
|
dst->surface,
|
|
src_x + attributes.base.x_offset,
|
|
src_y + attributes.base.y_offset,
|
|
0, 0,
|
|
dst_x, dst_y,
|
|
width, height);
|
|
|
|
glitz_set_geometry (dst->surface,
|
|
GLITZ_GEOMETRY_TYPE_NONE,
|
|
NULL, NULL);
|
|
}
|
|
|
|
UNLOCK:
|
|
if (cached_glyphs)
|
|
{
|
|
for (i = 0; i < num_glyphs; i++)
|
|
{
|
|
glyph_private = scaled_glyphs[i]->surface_private;
|
|
if (glyph_private)
|
|
glyph_private->locked = FALSE;
|
|
}
|
|
}
|
|
|
|
_cairo_scaled_font_thaw_cache (scaled_font);
|
|
|
|
glitz_buffer_destroy (buffer);
|
|
|
|
FAIL2:
|
|
if (num_glyphs > N_STACK_BUF)
|
|
free (scaled_glyphs);
|
|
|
|
FAIL1:
|
|
if (attributes.n_params)
|
|
free (attributes.params);
|
|
|
|
_cairo_glitz_pattern_release_surface (pattern, src, &attributes);
|
|
|
|
if (status)
|
|
return status;
|
|
|
|
if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_glitz_surface_flush (void *abstract_surface)
|
|
{
|
|
cairo_glitz_surface_t *surface = abstract_surface;
|
|
|
|
glitz_surface_flush (surface->surface);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_bool_t
|
|
_cairo_glitz_surface_is_similar (void *surface_a,
|
|
void *surface_b,
|
|
cairo_content_t content)
|
|
{
|
|
cairo_glitz_surface_t *a = (cairo_glitz_surface_t *) surface_a;
|
|
cairo_glitz_surface_t *b = (cairo_glitz_surface_t *) surface_b;
|
|
|
|
glitz_drawable_t *drawable_a = glitz_surface_get_drawable (a->surface);
|
|
glitz_drawable_t *drawable_b = glitz_surface_get_drawable (b->surface);
|
|
|
|
return drawable_a == drawable_b;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_glitz_surface_reset (void *abstract_surface)
|
|
{
|
|
cairo_glitz_surface_t *surface = abstract_surface;
|
|
cairo_status_t status;
|
|
|
|
status = _cairo_glitz_surface_set_clip_region (surface, NULL);
|
|
if (status)
|
|
return status;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static const cairo_surface_backend_t cairo_glitz_surface_backend = {
|
|
CAIRO_SURFACE_TYPE_GLITZ,
|
|
_cairo_glitz_surface_create_similar,
|
|
_cairo_glitz_surface_finish,
|
|
_cairo_glitz_surface_acquire_source_image,
|
|
_cairo_glitz_surface_release_source_image,
|
|
_cairo_glitz_surface_acquire_dest_image,
|
|
_cairo_glitz_surface_release_dest_image,
|
|
_cairo_glitz_surface_clone_similar,
|
|
_cairo_glitz_surface_composite,
|
|
_cairo_glitz_surface_fill_rectangles,
|
|
_cairo_glitz_surface_composite_trapezoids,
|
|
NULL, /* copy_page */
|
|
NULL, /* show_page */
|
|
_cairo_glitz_surface_set_clip_region,
|
|
NULL, /* intersect_clip_path */
|
|
_cairo_glitz_surface_get_extents,
|
|
_cairo_glitz_surface_old_show_glyphs,
|
|
NULL, /* get_font_options */
|
|
_cairo_glitz_surface_flush,
|
|
NULL, /* mark_dirty_rectangle */
|
|
_cairo_glitz_surface_scaled_font_fini,
|
|
_cairo_glitz_surface_scaled_glyph_fini,
|
|
|
|
NULL, /* paint */
|
|
NULL, /* mask */
|
|
NULL, /* stroke */
|
|
NULL, /* fill */
|
|
NULL, /* show_glyphs */
|
|
|
|
NULL, /* snapshot */
|
|
_cairo_glitz_surface_is_similar,
|
|
|
|
_cairo_glitz_surface_reset
|
|
};
|
|
|
|
static const cairo_surface_backend_t *
|
|
_cairo_glitz_surface_get_backend (void)
|
|
{
|
|
return &cairo_glitz_surface_backend;
|
|
}
|
|
|
|
static cairo_content_t
|
|
_glitz_format_to_content (glitz_format_t * format)
|
|
{
|
|
assert (format->color.fourcc == GLITZ_FOURCC_RGB);
|
|
|
|
if (format->color.alpha_size != 0) {
|
|
if (format->color.red_size != 0 &&
|
|
format->color.green_size != 0 &&
|
|
format->color.blue_size != 0)
|
|
return CAIRO_CONTENT_COLOR_ALPHA;
|
|
else
|
|
return CAIRO_CONTENT_ALPHA;
|
|
}
|
|
return CAIRO_CONTENT_COLOR;
|
|
}
|
|
|
|
cairo_surface_t *
|
|
cairo_glitz_surface_create (glitz_surface_t *surface)
|
|
{
|
|
cairo_glitz_surface_t *crsurface;
|
|
glitz_format_t *format;
|
|
|
|
if (surface == NULL)
|
|
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER));
|
|
|
|
crsurface = malloc (sizeof (cairo_glitz_surface_t));
|
|
if (crsurface == NULL)
|
|
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
|
|
|
format = glitz_surface_get_format (surface);
|
|
_cairo_surface_init (&crsurface->base, &cairo_glitz_surface_backend,
|
|
_glitz_format_to_content(format));
|
|
|
|
glitz_surface_reference (surface);
|
|
|
|
crsurface->surface = surface;
|
|
crsurface->format = format;
|
|
crsurface->has_clip = FALSE;
|
|
|
|
return (cairo_surface_t *) crsurface;
|
|
}
|
|
slim_hidden_def (cairo_glitz_surface_create);
|