cairo/src/cairo-surface.c

757 lines
20 KiB
C
Raw Normal View History

2003-07-18 11:34:19 +00:00
/*
* Copyright <EFBFBD> 2002 USC, Information Sciences Institute
*
* 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 the
* University of Southern California not be used in advertising or
* publicity pertaining to distribution of the software without
* specific, written prior permission. The University of Southern
* California makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without express
* or implied warranty.
*
* THE UNIVERSITY OF SOUTHERN CALIFORNIA DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF
* SOUTHERN CALIFORNIA 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: Carl D. Worth <cworth@isi.edu>
*/
#include <stdlib.h>
#include "cairoint.h"
static const XTransform CAIRO_XTRANSFORM_IDENTITY = {
{
{65536, 0, 0},
{ 0, 65536, 0},
{ 0, 0, 65536}
}
};
#define CAIRO_SURFACE_RENDER_AT_LEAST(surface, major, minor) \
(((surface)->render_major > major) ? 1 \
: ((surface)->render_major == major) ? ((surface)->render_minor >= minor) : 0)
#define CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0)
#define CAIRO_SURFACE_RENDER_HAS_COMPOSITE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0)
#define CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 1)
#define CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 1)
#define CAIRO_SURFACE_RENDER_HAS_DISJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2)
#define CAIRO_SURFACE_RENDER_HAS_CONJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2)
#define CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4)
#define CAIRO_SURFACE_RENDER_HAS_TRIANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4)
#define CAIRO_SURFACE_RENDER_HAS_TRISTRIP(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4)
#define CAIRO_SURFACE_RENDER_HAS_TRIFAN(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4)
#define CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6)
static IcFormat *
_create_icformat_for_visual (Visual *visual)
{
return IcFormatCreateMasks (32, 0,
visual->red_mask,
visual->green_mask,
visual->blue_mask);
}
static IcFormat *
_create_icformat_for_format (cairo_format_t format)
{
switch (format) {
case CAIRO_FORMAT_ARGB32:
return IcFormatCreate (IcFormatNameARGB32);
break;
case CAIRO_FORMAT_RGB24:
return IcFormatCreate (IcFormatNameRGB24);
break;
case CAIRO_FORMAT_A8:
return IcFormatCreate (IcFormatNameA8);
break;
case CAIRO_FORMAT_A1:
return IcFormatCreate (IcFormatNameA1);
break;
default:
return NULL;
}
}
2003-07-18 11:34:19 +00:00
cairo_surface_t *
cairo_surface_create_for_drawable (Display *dpy,
Drawable drawable,
Visual *visual,
cairo_format_t format,
Colormap colormap)
{
cairo_surface_t *surface;
surface = malloc (sizeof (cairo_surface_t));
if (surface == NULL)
return NULL;
/* XXX: We should really get this value from somewhere like Xft.dpy */
surface->ppm = 3780;
surface->ref_count = 1;
surface->repeat = 0;
2003-07-18 11:34:19 +00:00
surface->dpy = dpy;
surface->image_data = NULL;
surface->icimage = NULL;
2003-07-18 11:34:19 +00:00
surface->type = CAIRO_SURFACE_TYPE_DRAWABLE;
surface->xtransform = CAIRO_XTRANSFORM_IDENTITY;
2003-07-18 11:34:19 +00:00
surface->gc = 0;
surface->drawable = drawable;
surface->visual = visual;
2003-07-18 11:34:19 +00:00
if (! XRenderQueryVersion (dpy, &surface->render_major, &surface->render_minor)) {
surface->render_major = -1;
surface->render_minor = -1;
}
if (visual)
surface->icformat = _create_icformat_for_visual (visual);
else
surface->icformat = _create_icformat_for_format (format);
/* XXX: I'm currently ignoring the colormap. Is that bad? */
if (CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (surface))
surface->picture = XRenderCreatePicture (dpy, drawable,
visual ?
XRenderFindVisualFormat (dpy, visual) :
XRenderFindStandardFormat (dpy, format),
0, NULL);
else
surface->picture = 0;
surface->ximage = NULL;
/* XXX: How to get the proper width/height? Force a roundtrip? And
how can we track the width/height properly? Shall we give up on
supporting Windows and only allow drawing to pixmaps? */
surface->width = 0;
surface->height = 0;
2003-07-18 11:34:19 +00:00
return surface;
}
slim_hidden_def(cairo_surface_create_for_drawable);
2003-07-18 11:34:19 +00:00
static int
cairo_format_bpp (cairo_format_t format)
{
switch (format) {
case CAIRO_FORMAT_A1:
return 1;
break;
case CAIRO_FORMAT_A8:
return 8;
break;
case CAIRO_FORMAT_RGB24:
case CAIRO_FORMAT_ARGB32:
default:
return 32;
break;
}
}
cairo_surface_t *
cairo_surface_create_for_image (char *data,
cairo_format_t format,
int width,
int height,
int stride)
{
cairo_surface_t *surface;
surface = malloc (sizeof (cairo_surface_t));
if (surface == NULL)
return NULL;
surface->icformat = _create_icformat_for_format (format);
/* Assume a default until the user lets us know otherwise */
surface->ppm = 3780;
surface->ref_count = 1;
surface->repeat = 0;
2003-07-18 11:34:19 +00:00
surface->dpy = NULL;
surface->image_data = NULL;
surface->width = width;
surface->height = height;
surface->icimage = IcImageCreateForData ((IcBits *) data,
surface->icformat,
width, height,
cairo_format_bpp (format),
stride);
if (surface->icimage == NULL) {
2003-07-18 11:34:19 +00:00
free (surface);
return NULL;
}
surface->type = CAIRO_SURFACE_TYPE_ICIMAGE;
surface->xtransform = CAIRO_XTRANSFORM_IDENTITY;
2003-07-18 11:34:19 +00:00
surface->gc = 0;
surface->drawable = 0;
surface->visual = NULL;
surface->render_major = -1;
surface->render_minor = -1;
2003-07-18 11:34:19 +00:00
surface->picture = 0;
surface->ximage = NULL;
2003-07-18 11:34:19 +00:00
return surface;
}
slim_hidden_def(cairo_surface_create_for_image);
2003-07-18 11:34:19 +00:00
cairo_surface_t *
cairo_surface_create_similar (cairo_surface_t *other,
cairo_format_t format,
int width,
int height)
{
return cairo_surface_create_similar_solid (other, format, width, height, 0, 0, 0, 0);
}
static int
_CAIRO_FORMAT_DEPTH (cairo_format_t format)
{
switch (format) {
case CAIRO_FORMAT_A1:
return 1;
case CAIRO_FORMAT_A8:
return 8;
case CAIRO_FORMAT_RGB24:
return 24;
case CAIRO_FORMAT_ARGB32:
default:
return 32;
}
}
cairo_surface_t *
cairo_surface_create_similar_solid (cairo_surface_t *other,
cairo_format_t format,
int width,
int height,
double red,
double green,
double blue,
double alpha)
{
cairo_surface_t *surface = NULL;
cairo_color_t color;
/* XXX: There's a pretty lame heuristic here. This assumes that
* all non-Render X servers do not support depth-32 pixmaps, (and
* that they do support depths 1, 8, and 24). Obviously, it would
* be much better to check the depths that are actually
* supported. */
if (other->dpy
&& (CAIRO_SURFACE_RENDER_HAS_COMPOSITE (other)
|| format != CAIRO_FORMAT_ARGB32)) {
2003-07-18 11:34:19 +00:00
Display *dpy = other->dpy;
int scr = DefaultScreen (dpy);
Pixmap pix = XCreatePixmap (dpy,
DefaultRootWindow (dpy),
width, height,
_CAIRO_FORMAT_DEPTH (format));
surface = cairo_surface_create_for_drawable (dpy, pix,
NULL,
format,
DefaultColormap (dpy, scr));
/* XXX: huh? This should be fine since we already created a picture
from the pixmap, right?? (Somehow, it seems to be causing some
breakage).
XFreePixmap (surface->dpy, pix);
*/
} else {
char *data;
int stride;
stride = ((width * cairo_format_bpp (format)) + 7) >> 3;
data = malloc (stride * height);
if (data == NULL)
return NULL;
surface = cairo_surface_create_for_image (data, format,
width, height, stride);
/* lodge data in the surface structure to be freed with the surface */
surface->image_data = data;
}
/* XXX: Initializing the color in this way assumes
non-pre-multiplied alpha. I'm not sure that that's what I want
to do or not. */
_cairo_color_init (&color);
_cairo_color_set_rgb (&color, red, green, blue);
_cairo_color_set_alpha (&color, alpha);
_cairo_surface_fill_rectangle (surface, CAIRO_OPERATOR_SRC, &color, 0, 0, width, height);
return surface;
}
slim_hidden_def(cairo_surface_create_similar_solid);
2003-07-18 11:34:19 +00:00
void
_cairo_surface_reference (cairo_surface_t *surface)
{
if (surface == NULL)
return;
surface->ref_count++;
}
void
cairo_surface_destroy (cairo_surface_t *surface)
{
if (surface == NULL)
return;
surface->ref_count--;
if (surface->ref_count)
return;
if (surface->picture)
XRenderFreePicture (surface->dpy, surface->picture);
if (surface->icformat)
IcFormatDestroy (surface->icformat);
if (surface->icimage)
IcImageDestroy (surface->icimage);
2003-07-18 11:34:19 +00:00
if (surface->image_data)
free (surface->image_data);
surface->image_data = NULL;
surface->dpy = 0;
2003-07-18 11:34:19 +00:00
free (surface);
}
slim_hidden_def(cairo_surface_destroy);
2003-07-18 11:34:19 +00:00
static void
_cairo_surface_ensure_gc (cairo_surface_t *surface)
{
if (surface->gc)
return;
surface->gc = XCreateGC (surface->dpy, surface->drawable, 0, NULL);
}
static cairo_status_t
_cairo_x11_surface_put_image (cairo_surface_t *surface,
char *data,
int width,
int height,
int stride)
2003-07-18 11:34:19 +00:00
{
if (surface->picture) {
XImage *image;
unsigned bitmap_pad;
/* XXX: This is obviously bogus. depth needs to be figured out for real */
int depth = 32;
if (depth > 16)
bitmap_pad = 32;
else if (depth > 8)
bitmap_pad = 16;
else
bitmap_pad = 8;
image = XCreateImage(surface->dpy,
DefaultVisual(surface->dpy, DefaultScreen(surface->dpy)),
depth, ZPixmap, 0,
data, width, height,
bitmap_pad,
stride);
if (image == NULL)
return CAIRO_STATUS_NO_MEMORY;
_cairo_surface_ensure_gc (surface);
XPutImage(surface->dpy, surface->drawable, surface->gc,
image, 0, 0, 0, 0, width, height);
/* Foolish XDestroyImage thinks it can free my data, but I won't
stand for it. */
image->data = NULL;
XDestroyImage(image);
} else {
/* XXX: Need to implement the IcImage method of setting a picture. memcpy? */
}
2003-07-18 11:34:19 +00:00
return CAIRO_STATUS_SUCCESS;
}
void
_cairo_surface_pull_image (cairo_surface_t *surface)
{
Window root_ignore;
int x_ignore, y_ignore, bwidth_ignore, depth_ignore;
if (surface == NULL)
return;
if (surface->type == CAIRO_SURFACE_TYPE_ICIMAGE)
return;
if (surface->icimage) {
IcImageDestroy (surface->icimage);
surface->icimage = NULL;
}
XGetGeometry(surface->dpy,
surface->drawable,
&root_ignore, &x_ignore, &y_ignore,
&surface->width, &surface->height,
&bwidth_ignore, &depth_ignore);
surface->ximage = XGetImage (surface->dpy,
surface->drawable,
0, 0,
surface->width, surface->height,
AllPlanes, ZPixmap);
surface->icimage = IcImageCreateForData ((IcBits *)(surface->ximage->data),
surface->icformat,
surface->ximage->width,
surface->ximage->height,
surface->ximage->bits_per_pixel,
surface->ximage->bytes_per_line);
IcImageSetRepeat (surface->icimage, surface->repeat);
/* XXX: Evil cast here... */
IcImageSetTransform (surface->icimage, (IcTransform *) &(surface->xtransform));
/* XXX: Add support here for pictures with external alpha. */
}
void
_cairo_surface_push_image (cairo_surface_t *surface)
{
if (surface == NULL)
return;
if (surface->type == CAIRO_SURFACE_TYPE_ICIMAGE)
return;
if (surface->ximage == NULL)
return;
_cairo_surface_ensure_gc (surface);
XPutImage (surface->dpy,
surface->drawable,
surface->gc,
surface->ximage,
0, 0,
0, 0,
surface->width,
surface->height);
XDestroyImage(surface->ximage);
surface->ximage = NULL;
}
2003-07-18 11:34:19 +00:00
/* XXX: We may want to move to projective matrices at some point. If
nothing else, that would eliminate the two different transform data
structures we have here. */
cairo_status_t
cairo_surface_set_matrix (cairo_surface_t *surface, cairo_matrix_t *matrix)
{
XTransform *xtransform = &surface->xtransform;
xtransform->matrix[0][0] = XDoubleToFixed (matrix->m[0][0]);
xtransform->matrix[0][1] = XDoubleToFixed (matrix->m[1][0]);
xtransform->matrix[0][2] = XDoubleToFixed (matrix->m[2][0]);
2003-07-18 11:34:19 +00:00
xtransform->matrix[1][0] = XDoubleToFixed (matrix->m[0][1]);
xtransform->matrix[1][1] = XDoubleToFixed (matrix->m[1][1]);
xtransform->matrix[1][2] = XDoubleToFixed (matrix->m[2][1]);
2003-07-18 11:34:19 +00:00
xtransform->matrix[2][0] = 0;
xtransform->matrix[2][1] = 0;
xtransform->matrix[2][2] = XDoubleToFixed (1);
2003-07-18 11:34:19 +00:00
if (surface->picture) {
if (CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface))
XRenderSetPictureTransform (surface->dpy, surface->picture, xtransform);
/* XXX: Need support here if using an old RENDER without support
for SetPictureTransform */
}
2003-07-18 11:34:19 +00:00
/* XXX: This cast should only occur with a #define hint from libic that it is OK */
if (surface->icimage) {
IcImageSetTransform (surface->icimage, (IcTransform *) xtransform);
}
2003-07-18 11:34:19 +00:00
return CAIRO_STATUS_SUCCESS;
}
slim_hidden_def(cairo_surface_set_matrix);
2003-07-18 11:34:19 +00:00
cairo_status_t
cairo_surface_get_matrix (cairo_surface_t *surface, cairo_matrix_t *matrix)
{
XTransform *xtransform = &surface->xtransform;
2003-07-18 11:34:19 +00:00
matrix->m[0][0] = _cairo_fixed_to_double (xtransform->matrix[0][0]);
matrix->m[1][0] = _cairo_fixed_to_double (xtransform->matrix[0][1]);
matrix->m[2][0] = _cairo_fixed_to_double (xtransform->matrix[0][2]);
2003-07-18 11:34:19 +00:00
matrix->m[0][1] = _cairo_fixed_to_double (xtransform->matrix[1][0]);
matrix->m[1][1] = _cairo_fixed_to_double (xtransform->matrix[1][1]);
matrix->m[2][1] = _cairo_fixed_to_double (xtransform->matrix[1][2]);
2003-07-18 11:34:19 +00:00
return CAIRO_STATUS_SUCCESS;
}
slim_hidden_def(cairo_surface_get_matrix);
2003-07-18 11:34:19 +00:00
/* XXX: The Render specification has capitalized versions of these
strings. However, the current implementation is case-sensitive and
expects lowercase versions. */
static char *
_render_filter_name (cairo_filter_t filter)
{
switch (filter) {
case CAIRO_FILTER_FAST:
return "fast";
case CAIRO_FILTER_GOOD:
return "good";
case CAIRO_FILTER_BEST:
return "best";
case CAIRO_FILTER_NEAREST:
return "nearest";
case CAIRO_FILTER_BILINEAR:
return "bilinear";
default:
return "best";
}
}
2003-07-18 11:34:19 +00:00
cairo_status_t
cairo_surface_set_filter (cairo_surface_t *surface, cairo_filter_t filter)
{
if (surface->picture) {
XRenderSetPictureFilter (surface->dpy, surface->picture,
_render_filter_name (filter), NULL, 0);
}
if (surface->icimage) {
IcImageSetFilter (surface->icimage, filter);
}
2003-07-18 11:34:19 +00:00
return CAIRO_STATUS_SUCCESS;
}
/* XXX: NYI
2003-07-18 11:34:19 +00:00
cairo_status_t
cairo_surface_clip_rectangle (cairo_surface_t *surface,
int x, int y,
int width, int height)
2003-07-18 11:34:19 +00:00
{
}
*/
/* XXX: NYI
cairo_status_t
cairo_surface_clip_restore (cairo_surface_t *surface);
*/
2003-07-18 11:34:19 +00:00
cairo_status_t
cairo_surface_set_repeat (cairo_surface_t *surface, int repeat)
{
surface->repeat = repeat;
if (surface->picture) {
unsigned long mask;
XRenderPictureAttributes pa;
mask = CPRepeat;
pa.repeat = repeat;
XRenderChangePicture (surface->dpy, surface->picture, mask, &pa);
}
if (surface->icimage) {
IcImageSetRepeat (surface->icimage, repeat);
}
2003-07-18 11:34:19 +00:00
return CAIRO_STATUS_SUCCESS;
}
slim_hidden_def(cairo_surface_set_repeat);
2003-07-18 11:34:19 +00:00
void
_cairo_surface_composite (cairo_operator_t operator,
cairo_surface_t *src,
cairo_surface_t *mask,
cairo_surface_t *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)
2003-07-18 11:34:19 +00:00
{
if (dst->type == CAIRO_SURFACE_TYPE_DRAWABLE
&& CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst)
&& (mask == NULL || mask->dpy == dst->dpy)
&& (src->type == CAIRO_SURFACE_TYPE_ICIMAGE || src->dpy == dst->dpy)) {
cairo_surface_t *src_on_server = NULL;
if (src->type == CAIRO_SURFACE_TYPE_ICIMAGE) {
cairo_matrix_t matrix;
src_on_server = cairo_surface_create_similar (dst, CAIRO_FORMAT_ARGB32,
IcImageGetWidth (src->icimage),
IcImageGetWidth (src->icimage));
if (src_on_server == NULL)
return;
cairo_surface_get_matrix (src, &matrix);
cairo_surface_set_matrix (src_on_server, &matrix);
_cairo_x11_surface_put_image (src_on_server,
(char *) IcImageGetData (src->icimage),
IcImageGetWidth (src->icimage),
IcImageGetHeight (src->icimage),
IcImageGetStride (src->icimage));
}
XRenderComposite (dst->dpy, operator,
src_on_server ? src_on_server->picture : src->picture,
mask ? mask->picture : 0,
dst->picture,
src_x, src_y,
mask_x, mask_y,
dst_x, dst_y,
width, height);
} else {
_cairo_surface_pull_image (src);
if (mask)
_cairo_surface_pull_image (mask);
_cairo_surface_pull_image (dst);
IcComposite (operator,
src->icimage,
mask ? mask->icimage : NULL,
dst->icimage,
src_x, src_y,
mask_x, mask_y,
dst_x, dst_y,
width, height);
_cairo_surface_push_image (dst);
}
2003-07-18 11:34:19 +00:00
}
void
_cairo_surface_fill_rectangle (cairo_surface_t *surface,
cairo_operator_t operator,
cairo_color_t *color,
int x,
int y,
int width,
int height)
{
cairo_rectangle_t rect;
rect.x = x;
rect.y = y;
rect.width = width;
rect.height = height;
_cairo_surface_fill_rectangles (surface, operator, color, &rect, 1);
}
void
_cairo_surface_fill_rectangles (cairo_surface_t *surface,
cairo_operator_t operator,
const cairo_color_t *color,
cairo_rectangle_t *rects,
int num_rects)
{
if (num_rects == 0)
return;
if (surface->type == CAIRO_SURFACE_TYPE_DRAWABLE
&& CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLE (surface)) {
XRenderColor render_color;
render_color.red = color->red_short;
render_color.green = color->green_short;
render_color.blue = color->blue_short;
render_color.alpha = color->alpha_short;
/* XXX: This XRectangle cast is evil... it needs to go away somehow. */
XRenderFillRectangles (surface->dpy, operator, surface->picture,
&render_color, (XRectangle *) rects, num_rects);
} else {
IcColor ic_color;
ic_color.red = color->red_short;
ic_color.green = color->green_short;
ic_color.blue = color->blue_short;
ic_color.alpha = color->alpha_short;
_cairo_surface_pull_image (surface);
/* XXX: The IcRectangle cast is evil... it needs to go away somehow. */
IcFillRectangles (operator, surface->icimage,
&ic_color, (IcRectangle *) rects, num_rects);
_cairo_surface_push_image (surface);
}
}
void
_cairo_surface_composite_trapezoids (cairo_operator_t operator,
cairo_surface_t *src,
cairo_surface_t *dst,
int xSrc,
int ySrc,
cairo_trapezoid_t *traps,
int num_traps)
{
if (dst->type == CAIRO_SURFACE_TYPE_DRAWABLE
&& CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS (dst)
&& src->dpy == dst->dpy) {
/* XXX: The XTrapezoid cast is evil and needs to go away somehow. */
XRenderCompositeTrapezoids (dst->dpy, operator, src->picture, dst->picture,
XRenderFindStandardFormat (dst->dpy, PictStandardA8),
xSrc, ySrc, (XTrapezoid *) traps, num_traps);
} else {
_cairo_surface_pull_image (src);
_cairo_surface_pull_image (dst);
/* XXX: The IcTrapezoid cast is evil and needs to go away somehow. */
IcCompositeTrapezoids (operator, src->icimage, dst->icimage,
xSrc, ySrc, (IcTrapezoid *) traps, num_traps);
_cairo_surface_push_image (dst);
}
2003-07-18 11:34:19 +00:00
}