mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-01-23 11:40:29 +01:00
A common requirement is the fast upload of pixel data. In order to allocate the most appropriate image buffer, we need knowledge of the destination. The most obvious example is that we could use a shared-memory region for the image to avoid the transfer cost of uploading the pixels to the X server. Similarly, gl, win32, quartz... The other side of the equation is that for manual modification of a remote surface, it would be more efficient if we can create a similar image to reduce the transfer costs. This strategy is already followed for the destination fallbacks and this merely exposes the same capability for the application fallbacks. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
1153 lines
31 KiB
C
1153 lines
31 KiB
C
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
|
|
/* cairo - a vector graphics library with display and print output
|
|
*
|
|
* Copyright © 2009 Chris Wilson
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it either under the terms of the GNU Lesser General Public
|
|
* License version 2.1 as published by the Free Software Foundation
|
|
* (the "LGPL") or, at your option, under the terms of the Mozilla
|
|
* Public License Version 1.1 (the "MPL"). If you do not alter this
|
|
* notice, a recipient may use your version of this file under either
|
|
* the MPL or the LGPL.
|
|
*
|
|
* You should have received a copy of the LGPL along with this library
|
|
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
|
|
* You should have received a copy of the MPL along with this library
|
|
* in the file COPYING-MPL-1.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License
|
|
* Version 1.1 (the "License"); you may not use this file except in
|
|
* compliance with the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
|
|
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
|
|
* the specific language governing rights and limitations.
|
|
*
|
|
* The Original Code is the cairo graphics library.
|
|
*
|
|
* The Initial Developer of the Original Code is Chris Wilson.
|
|
*
|
|
* Contributor(s):
|
|
* Chris Wilson <chris@chris-wilson.co.uk>
|
|
*/
|
|
|
|
/* This surface is intended to produce a verbose, hierarchical, DAG XML file
|
|
* representing a single surface. It is intended to be used by debuggers,
|
|
* such as cairo-sphinx, or by application test-suites that what a log of
|
|
* operations.
|
|
*/
|
|
|
|
#include "cairoint.h"
|
|
|
|
#include "cairo-xml.h"
|
|
|
|
#include "cairo-clip-private.h"
|
|
#include "cairo-device-private.h"
|
|
#include "cairo-default-context-private.h"
|
|
#include "cairo-error-private.h"
|
|
#include "cairo-output-stream-private.h"
|
|
#include "cairo-recording-surface-private.h"
|
|
|
|
#define static cairo_warn static
|
|
|
|
typedef struct _cairo_xml_surface cairo_xml_surface_t;
|
|
|
|
typedef struct _cairo_xml {
|
|
cairo_device_t base;
|
|
|
|
cairo_output_stream_t *stream;
|
|
int indent;
|
|
} cairo_xml_t;
|
|
|
|
struct _cairo_xml_surface {
|
|
cairo_surface_t base;
|
|
|
|
double width, height;
|
|
};
|
|
|
|
slim_hidden_proto (cairo_xml_for_recording_surface);
|
|
|
|
static const cairo_surface_backend_t _cairo_xml_surface_backend;
|
|
|
|
static const char *
|
|
_operator_to_string (cairo_operator_t op)
|
|
{
|
|
static const char *names[] = {
|
|
"CLEAR", /* CAIRO_OPERATOR_CLEAR */
|
|
|
|
"SOURCE", /* CAIRO_OPERATOR_SOURCE */
|
|
"OVER", /* CAIRO_OPERATOR_OVER */
|
|
"IN", /* CAIRO_OPERATOR_IN */
|
|
"OUT", /* CAIRO_OPERATOR_OUT */
|
|
"ATOP", /* CAIRO_OPERATOR_ATOP */
|
|
|
|
"DEST", /* CAIRO_OPERATOR_DEST */
|
|
"DEST_OVER", /* CAIRO_OPERATOR_DEST_OVER */
|
|
"DEST_IN", /* CAIRO_OPERATOR_DEST_IN */
|
|
"DEST_OUT", /* CAIRO_OPERATOR_DEST_OUT */
|
|
"DEST_ATOP", /* CAIRO_OPERATOR_DEST_ATOP */
|
|
|
|
"XOR", /* CAIRO_OPERATOR_XOR */
|
|
"ADD", /* CAIRO_OPERATOR_ADD */
|
|
"SATURATE", /* CAIRO_OPERATOR_SATURATE */
|
|
|
|
"MULTIPLY", /* CAIRO_OPERATOR_MULTIPLY */
|
|
"SCREEN", /* CAIRO_OPERATOR_SCREEN */
|
|
"OVERLAY", /* CAIRO_OPERATOR_OVERLAY */
|
|
"DARKEN", /* CAIRO_OPERATOR_DARKEN */
|
|
"LIGHTEN", /* CAIRO_OPERATOR_LIGHTEN */
|
|
"DODGE", /* CAIRO_OPERATOR_COLOR_DODGE */
|
|
"BURN", /* CAIRO_OPERATOR_COLOR_BURN */
|
|
"HARD_LIGHT", /* CAIRO_OPERATOR_HARD_LIGHT */
|
|
"SOFT_LIGHT", /* CAIRO_OPERATOR_SOFT_LIGHT */
|
|
"DIFFERENCE", /* CAIRO_OPERATOR_DIFFERENCE */
|
|
"EXCLUSION", /* CAIRO_OPERATOR_EXCLUSION */
|
|
"HSL_HUE", /* CAIRO_OPERATOR_HSL_HUE */
|
|
"HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */
|
|
"HSL_COLOR", /* CAIRO_OPERATOR_HSL_COLOR */
|
|
"HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */
|
|
};
|
|
assert (op < ARRAY_LENGTH (names));
|
|
return names[op];
|
|
}
|
|
|
|
static const char *
|
|
_extend_to_string (cairo_extend_t extend)
|
|
{
|
|
static const char *names[] = {
|
|
"EXTEND_NONE", /* CAIRO_EXTEND_NONE */
|
|
"EXTEND_REPEAT", /* CAIRO_EXTEND_REPEAT */
|
|
"EXTEND_REFLECT", /* CAIRO_EXTEND_REFLECT */
|
|
"EXTEND_PAD" /* CAIRO_EXTEND_PAD */
|
|
};
|
|
assert (extend < ARRAY_LENGTH (names));
|
|
return names[extend];
|
|
}
|
|
|
|
static const char *
|
|
_filter_to_string (cairo_filter_t filter)
|
|
{
|
|
static const char *names[] = {
|
|
"FILTER_FAST", /* CAIRO_FILTER_FAST */
|
|
"FILTER_GOOD", /* CAIRO_FILTER_GOOD */
|
|
"FILTER_BEST", /* CAIRO_FILTER_BEST */
|
|
"FILTER_NEAREST", /* CAIRO_FILTER_NEAREST */
|
|
"FILTER_BILINEAR", /* CAIRO_FILTER_BILINEAR */
|
|
"FILTER_GAUSSIAN", /* CAIRO_FILTER_GAUSSIAN */
|
|
};
|
|
assert (filter < ARRAY_LENGTH (names));
|
|
return names[filter];
|
|
}
|
|
|
|
static const char *
|
|
_fill_rule_to_string (cairo_fill_rule_t rule)
|
|
{
|
|
static const char *names[] = {
|
|
"WINDING", /* CAIRO_FILL_RULE_WINDING */
|
|
"EVEN_ODD" /* CAIRO_FILL_RILE_EVEN_ODD */
|
|
};
|
|
assert (rule < ARRAY_LENGTH (names));
|
|
return names[rule];
|
|
}
|
|
|
|
static const char *
|
|
_antialias_to_string (cairo_antialias_t antialias)
|
|
{
|
|
static const char *names[] = {
|
|
"ANTIALIAS_DEFAULT", /* CAIRO_ANTIALIAS_DEFAULT */
|
|
"ANTIALIAS_NONE", /* CAIRO_ANTIALIAS_NONE */
|
|
"ANTIALIAS_GRAY", /* CAIRO_ANTIALIAS_GRAY */
|
|
"ANTIALIAS_SUBPIXEL" /* CAIRO_ANTIALIAS_SUBPIXEL */
|
|
};
|
|
assert (antialias < ARRAY_LENGTH (names));
|
|
return names[antialias];
|
|
}
|
|
|
|
static const char *
|
|
_line_cap_to_string (cairo_line_cap_t line_cap)
|
|
{
|
|
static const char *names[] = {
|
|
"LINE_CAP_BUTT", /* CAIRO_LINE_CAP_BUTT */
|
|
"LINE_CAP_ROUND", /* CAIRO_LINE_CAP_ROUND */
|
|
"LINE_CAP_SQUARE" /* CAIRO_LINE_CAP_SQUARE */
|
|
};
|
|
assert (line_cap < ARRAY_LENGTH (names));
|
|
return names[line_cap];
|
|
}
|
|
|
|
static const char *
|
|
_line_join_to_string (cairo_line_join_t line_join)
|
|
{
|
|
static const char *names[] = {
|
|
"LINE_JOIN_MITER", /* CAIRO_LINE_JOIN_MITER */
|
|
"LINE_JOIN_ROUND", /* CAIRO_LINE_JOIN_ROUND */
|
|
"LINE_JOIN_BEVEL", /* CAIRO_LINE_JOIN_BEVEL */
|
|
};
|
|
assert (line_join < ARRAY_LENGTH (names));
|
|
return names[line_join];
|
|
}
|
|
|
|
static const char *
|
|
_content_to_string (cairo_content_t content)
|
|
{
|
|
switch (content) {
|
|
case CAIRO_CONTENT_ALPHA: return "ALPHA";
|
|
case CAIRO_CONTENT_COLOR: return "COLOR";
|
|
default:
|
|
case CAIRO_CONTENT_COLOR_ALPHA: return "COLOR_ALPHA";
|
|
}
|
|
}
|
|
|
|
static const char *
|
|
_format_to_string (cairo_format_t format)
|
|
{
|
|
switch (format) {
|
|
case CAIRO_FORMAT_ARGB32: return "ARGB32";
|
|
case CAIRO_FORMAT_RGB30: return "RGB30";
|
|
case CAIRO_FORMAT_RGB24: return "RGB24";
|
|
case CAIRO_FORMAT_RGB16_565: return "RGB16_565";
|
|
case CAIRO_FORMAT_A8: return "A8";
|
|
case CAIRO_FORMAT_A1: return "A1";
|
|
case CAIRO_FORMAT_INVALID: return "INVALID";
|
|
}
|
|
ASSERT_NOT_REACHED;
|
|
return "INVALID";
|
|
}
|
|
|
|
static cairo_status_t
|
|
_device_flush (void *abstract_device)
|
|
{
|
|
cairo_xml_t *xml = abstract_device;
|
|
cairo_status_t status;
|
|
|
|
status = _cairo_output_stream_flush (xml->stream);
|
|
|
|
return status;
|
|
}
|
|
|
|
static void
|
|
_device_destroy (void *abstract_device)
|
|
{
|
|
cairo_xml_t *xml = abstract_device;
|
|
cairo_status_t status;
|
|
|
|
status = _cairo_output_stream_destroy (xml->stream);
|
|
|
|
free (xml);
|
|
}
|
|
|
|
static const cairo_device_backend_t _cairo_xml_device_backend = {
|
|
CAIRO_DEVICE_TYPE_XML,
|
|
|
|
NULL, NULL, /* lock, unlock */
|
|
|
|
_device_flush,
|
|
NULL, /* finish */
|
|
_device_destroy
|
|
};
|
|
|
|
static cairo_device_t *
|
|
_cairo_xml_create_internal (cairo_output_stream_t *stream)
|
|
{
|
|
cairo_xml_t *xml;
|
|
|
|
xml = malloc (sizeof (cairo_xml_t));
|
|
if (unlikely (xml == NULL))
|
|
return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
memset (xml, 0, sizeof (cairo_xml_t));
|
|
|
|
_cairo_device_init (&xml->base, &_cairo_xml_device_backend);
|
|
|
|
xml->indent = 0;
|
|
xml->stream = stream;
|
|
|
|
return &xml->base;
|
|
}
|
|
|
|
static void
|
|
_cairo_xml_indent (cairo_xml_t *xml, int indent)
|
|
{
|
|
xml->indent += indent;
|
|
assert (xml->indent >= 0);
|
|
}
|
|
|
|
static void CAIRO_PRINTF_FORMAT (2, 3)
|
|
_cairo_xml_printf (cairo_xml_t *xml, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
char indent[80];
|
|
int len;
|
|
|
|
len = MIN (xml->indent, ARRAY_LENGTH (indent));
|
|
memset (indent, ' ', len);
|
|
_cairo_output_stream_write (xml->stream, indent, len);
|
|
|
|
va_start (ap, fmt);
|
|
_cairo_output_stream_vprintf (xml->stream, fmt, ap);
|
|
va_end (ap);
|
|
|
|
_cairo_output_stream_write (xml->stream, "\n", 1);
|
|
}
|
|
|
|
static void CAIRO_PRINTF_FORMAT (2, 3)
|
|
_cairo_xml_printf_start (cairo_xml_t *xml, const char *fmt, ...)
|
|
{
|
|
char indent[80];
|
|
int len;
|
|
|
|
len = MIN (xml->indent, ARRAY_LENGTH (indent));
|
|
memset (indent, ' ', len);
|
|
_cairo_output_stream_write (xml->stream, indent, len);
|
|
|
|
if (fmt != NULL) {
|
|
va_list ap;
|
|
|
|
va_start (ap, fmt);
|
|
_cairo_output_stream_vprintf (xml->stream, fmt, ap);
|
|
va_end (ap);
|
|
}
|
|
}
|
|
|
|
static void CAIRO_PRINTF_FORMAT (2, 3)
|
|
_cairo_xml_printf_continue (cairo_xml_t *xml, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start (ap, fmt);
|
|
_cairo_output_stream_vprintf (xml->stream, fmt, ap);
|
|
va_end (ap);
|
|
}
|
|
|
|
static void CAIRO_PRINTF_FORMAT (2, 3)
|
|
_cairo_xml_printf_end (cairo_xml_t *xml, const char *fmt, ...)
|
|
{
|
|
if (fmt != NULL) {
|
|
va_list ap;
|
|
|
|
va_start (ap, fmt);
|
|
_cairo_output_stream_vprintf (xml->stream, fmt, ap);
|
|
va_end (ap);
|
|
}
|
|
|
|
_cairo_output_stream_write (xml->stream, "\n", 1);
|
|
}
|
|
|
|
static cairo_surface_t *
|
|
_cairo_xml_surface_create_similar (void *abstract_surface,
|
|
cairo_content_t content,
|
|
int width,
|
|
int height)
|
|
{
|
|
cairo_rectangle_t extents;
|
|
|
|
extents.x = extents.y = 0;
|
|
extents.width = width;
|
|
extents.height = height;
|
|
|
|
return cairo_recording_surface_create (content, &extents);
|
|
}
|
|
|
|
static cairo_bool_t
|
|
_cairo_xml_surface_get_extents (void *abstract_surface,
|
|
cairo_rectangle_int_t *rectangle)
|
|
{
|
|
cairo_xml_surface_t *surface = abstract_surface;
|
|
|
|
if (surface->width < 0 || surface->height < 0)
|
|
return FALSE;
|
|
|
|
rectangle->x = 0;
|
|
rectangle->y = 0;
|
|
rectangle->width = surface->width;
|
|
rectangle->height = surface->height;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xml_move_to (void *closure,
|
|
const cairo_point_t *p1)
|
|
{
|
|
_cairo_xml_printf_continue (closure, " %f %f m",
|
|
_cairo_fixed_to_double (p1->x),
|
|
_cairo_fixed_to_double (p1->y));
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xml_line_to (void *closure,
|
|
const cairo_point_t *p1)
|
|
{
|
|
_cairo_xml_printf_continue (closure, " %f %f l",
|
|
_cairo_fixed_to_double (p1->x),
|
|
_cairo_fixed_to_double (p1->y));
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xml_curve_to (void *closure,
|
|
const cairo_point_t *p1,
|
|
const cairo_point_t *p2,
|
|
const cairo_point_t *p3)
|
|
{
|
|
_cairo_xml_printf_continue (closure, " %f %f %f %f %f %f c",
|
|
_cairo_fixed_to_double (p1->x),
|
|
_cairo_fixed_to_double (p1->y),
|
|
_cairo_fixed_to_double (p2->x),
|
|
_cairo_fixed_to_double (p2->y),
|
|
_cairo_fixed_to_double (p3->x),
|
|
_cairo_fixed_to_double (p3->y));
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xml_close_path (void *closure)
|
|
{
|
|
_cairo_xml_printf_continue (closure, " h");
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
_cairo_xml_emit_path (cairo_xml_t *xml,
|
|
const cairo_path_fixed_t *path)
|
|
{
|
|
cairo_status_t status;
|
|
|
|
_cairo_xml_printf_start (xml, "<path>");
|
|
status = _cairo_path_fixed_interpret (path,
|
|
_cairo_xml_move_to,
|
|
_cairo_xml_line_to,
|
|
_cairo_xml_curve_to,
|
|
_cairo_xml_close_path,
|
|
xml);
|
|
assert (status == CAIRO_STATUS_SUCCESS);
|
|
_cairo_xml_printf_end (xml, "</path>");
|
|
}
|
|
|
|
static void
|
|
_cairo_xml_emit_string (cairo_xml_t *xml,
|
|
const char *node,
|
|
const char *data)
|
|
{
|
|
_cairo_xml_printf (xml, "<%s>%s</%s>", node, data, node);
|
|
}
|
|
|
|
static void
|
|
_cairo_xml_emit_double (cairo_xml_t *xml,
|
|
const char *node,
|
|
double data)
|
|
{
|
|
_cairo_xml_printf (xml, "<%s>%f</%s>", node, data, node);
|
|
}
|
|
|
|
static cairo_xml_t *
|
|
to_xml (cairo_xml_surface_t *surface)
|
|
{
|
|
return (cairo_xml_t *) surface->base.device;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xml_surface_emit_clip_path (cairo_xml_surface_t *surface,
|
|
cairo_clip_path_t *clip_path)
|
|
{
|
|
cairo_box_t box;
|
|
cairo_status_t status;
|
|
cairo_xml_t *xml;
|
|
|
|
if (clip_path->prev != NULL) {
|
|
status = _cairo_xml_surface_emit_clip_path (surface, clip_path->prev);
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
|
|
|
|
/* skip the trivial clip covering the surface extents */
|
|
if (surface->width >= 0 && surface->height >= 0 &&
|
|
_cairo_path_fixed_is_box (&clip_path->path, &box))
|
|
{
|
|
if (box.p1.x <= 0 && box.p1.y <= 0 &&
|
|
box.p2.x - box.p1.x >= _cairo_fixed_from_double (surface->width) &&
|
|
box.p2.y - box.p1.y >= _cairo_fixed_from_double (surface->height))
|
|
{
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
xml = to_xml (surface);
|
|
|
|
_cairo_xml_printf_start (xml, "<clip>");
|
|
_cairo_xml_indent (xml, 2);
|
|
|
|
_cairo_xml_emit_path (xml, &clip_path->path);
|
|
_cairo_xml_emit_double (xml, "tolerance", clip_path->tolerance);
|
|
_cairo_xml_emit_string (xml, "antialias",
|
|
_antialias_to_string (clip_path->antialias));
|
|
_cairo_xml_emit_string (xml, "fill-rule",
|
|
_fill_rule_to_string (clip_path->fill_rule));
|
|
|
|
_cairo_xml_indent (xml, -2);
|
|
_cairo_xml_printf_end (xml, "</clip>");
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xml_surface_emit_clip (cairo_xml_surface_t *surface,
|
|
const cairo_clip_t *clip)
|
|
{
|
|
if (clip == NULL)
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
return _cairo_xml_surface_emit_clip_path (surface, clip->path);
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xml_emit_solid (cairo_xml_t *xml,
|
|
const cairo_solid_pattern_t *solid)
|
|
{
|
|
_cairo_xml_printf (xml, "<solid>%f %f %f %f</solid>",
|
|
solid->color.red,
|
|
solid->color.green,
|
|
solid->color.blue,
|
|
solid->color.alpha);
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
_cairo_xml_emit_matrix (cairo_xml_t *xml,
|
|
const cairo_matrix_t *matrix)
|
|
{
|
|
if (! _cairo_matrix_is_identity (matrix)) {
|
|
_cairo_xml_printf (xml, "<matrix>%f %f %f %f %f %f</matrix>",
|
|
matrix->xx, matrix->yx,
|
|
matrix->xy, matrix->yy,
|
|
matrix->x0, matrix->y0);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_cairo_xml_emit_gradient (cairo_xml_t *xml,
|
|
const cairo_gradient_pattern_t *gradient)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < gradient->n_stops; i++) {
|
|
_cairo_xml_printf (xml,
|
|
"<color-stop>%f %f %f %f %f</color-stop>",
|
|
gradient->stops[i].offset,
|
|
gradient->stops[i].color.red,
|
|
gradient->stops[i].color.green,
|
|
gradient->stops[i].color.blue,
|
|
gradient->stops[i].color.alpha);
|
|
}
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xml_emit_linear (cairo_xml_t *xml,
|
|
const cairo_linear_pattern_t *linear)
|
|
{
|
|
_cairo_xml_printf (xml,
|
|
"<linear x1='%f' y1='%f' x2='%f' y2='%f'>",
|
|
linear->pd1.x, linear->pd1.y,
|
|
linear->pd2.x, linear->pd2.y);
|
|
_cairo_xml_indent (xml, 2);
|
|
_cairo_xml_emit_gradient (xml, &linear->base);
|
|
_cairo_xml_indent (xml, -2);
|
|
_cairo_xml_printf (xml, "</linear>");
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xml_emit_radial (cairo_xml_t *xml,
|
|
const cairo_radial_pattern_t *radial)
|
|
{
|
|
_cairo_xml_printf (xml,
|
|
"<radial x1='%f' y1='%f' r1='%f' x2='%f' y2='%f' r2='%f'>",
|
|
radial->cd1.center.x, radial->cd1.center.y, radial->cd1.radius,
|
|
radial->cd2.center.x, radial->cd2.center.y, radial->cd2.radius);
|
|
_cairo_xml_indent (xml, 2);
|
|
_cairo_xml_emit_gradient (xml, &radial->base);
|
|
_cairo_xml_indent (xml, -2);
|
|
_cairo_xml_printf (xml, "</radial>");
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_write_func (void *closure, const unsigned char *data, unsigned len)
|
|
{
|
|
_cairo_output_stream_write (closure, data, len);
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xml_emit_image (cairo_xml_t *xml,
|
|
cairo_image_surface_t *image)
|
|
{
|
|
cairo_output_stream_t *stream;
|
|
cairo_status_t status;
|
|
|
|
_cairo_xml_printf_start (xml,
|
|
"<image width='%d' height='%d' format='%s'>",
|
|
image->width, image->height,
|
|
_format_to_string (image->format));
|
|
|
|
stream = _cairo_base64_stream_create (xml->stream);
|
|
status = cairo_surface_write_to_png_stream (&image->base,
|
|
_write_func, stream);
|
|
assert (status == CAIRO_STATUS_SUCCESS);
|
|
status = _cairo_output_stream_destroy (stream);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
_cairo_xml_printf_end (xml, "</image>");
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xml_emit_surface (cairo_xml_t *xml,
|
|
const cairo_surface_pattern_t *pattern)
|
|
{
|
|
cairo_surface_t *source = pattern->surface;
|
|
cairo_status_t status;
|
|
|
|
if (_cairo_surface_is_recording (source)) {
|
|
status = cairo_xml_for_recording_surface (&xml->base, source);
|
|
} else {
|
|
cairo_image_surface_t *image;
|
|
void *image_extra;
|
|
|
|
status = _cairo_surface_acquire_source_image (source,
|
|
&image, &image_extra);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
status = _cairo_xml_emit_image (xml, image);
|
|
|
|
_cairo_surface_release_source_image (source, image, image_extra);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xml_emit_pattern (cairo_xml_t *xml,
|
|
const char *source_or_mask,
|
|
const cairo_pattern_t *pattern)
|
|
{
|
|
cairo_status_t status;
|
|
|
|
_cairo_xml_printf (xml, "<%s-pattern>", source_or_mask);
|
|
_cairo_xml_indent (xml, 2);
|
|
|
|
switch (pattern->type) {
|
|
case CAIRO_PATTERN_TYPE_SOLID:
|
|
status = _cairo_xml_emit_solid (xml, (cairo_solid_pattern_t *) pattern);
|
|
break;
|
|
case CAIRO_PATTERN_TYPE_LINEAR:
|
|
status = _cairo_xml_emit_linear (xml, (cairo_linear_pattern_t *) pattern);
|
|
break;
|
|
case CAIRO_PATTERN_TYPE_RADIAL:
|
|
status = _cairo_xml_emit_radial (xml, (cairo_radial_pattern_t *) pattern);
|
|
break;
|
|
case CAIRO_PATTERN_TYPE_SURFACE:
|
|
status = _cairo_xml_emit_surface (xml, (cairo_surface_pattern_t *) pattern);
|
|
break;
|
|
default:
|
|
ASSERT_NOT_REACHED;
|
|
status = CAIRO_INT_STATUS_UNSUPPORTED;
|
|
break;
|
|
}
|
|
|
|
if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) {
|
|
_cairo_xml_emit_matrix (xml, &pattern->matrix);
|
|
_cairo_xml_printf (xml,
|
|
"<extend>%s</extend>",
|
|
_extend_to_string (pattern->extend));
|
|
_cairo_xml_printf (xml,
|
|
"<filter>%s</filter>",
|
|
_filter_to_string (pattern->filter));
|
|
}
|
|
|
|
_cairo_xml_indent (xml, -2);
|
|
_cairo_xml_printf (xml, "</%s-pattern>", source_or_mask);
|
|
|
|
return status;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_xml_surface_paint (void *abstract_surface,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
const cairo_clip_t *clip)
|
|
{
|
|
cairo_xml_surface_t *surface = abstract_surface;
|
|
cairo_xml_t *xml = to_xml (surface);
|
|
cairo_status_t status;
|
|
|
|
_cairo_xml_printf (xml, "<paint>");
|
|
_cairo_xml_indent (xml, 2);
|
|
|
|
_cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
|
|
|
|
status = _cairo_xml_surface_emit_clip (surface, clip);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
status = _cairo_xml_emit_pattern (xml, "source", source);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
_cairo_xml_indent (xml, -2);
|
|
_cairo_xml_printf (xml, "</paint>");
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_xml_surface_mask (void *abstract_surface,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
const cairo_pattern_t *mask,
|
|
const cairo_clip_t *clip)
|
|
{
|
|
cairo_xml_surface_t *surface = abstract_surface;
|
|
cairo_xml_t *xml = to_xml (surface);
|
|
cairo_status_t status;
|
|
|
|
_cairo_xml_printf (xml, "<mask>");
|
|
_cairo_xml_indent (xml, 2);
|
|
|
|
_cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
|
|
|
|
status = _cairo_xml_surface_emit_clip (surface, clip);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
status = _cairo_xml_emit_pattern (xml, "source", source);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
status = _cairo_xml_emit_pattern (xml, "mask", mask);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
_cairo_xml_indent (xml, -2);
|
|
_cairo_xml_printf (xml, "</mask>");
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_xml_surface_stroke (void *abstract_surface,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
const cairo_path_fixed_t *path,
|
|
const cairo_stroke_style_t *style,
|
|
const cairo_matrix_t *ctm,
|
|
const cairo_matrix_t *ctm_inverse,
|
|
double tolerance,
|
|
cairo_antialias_t antialias,
|
|
const cairo_clip_t *clip)
|
|
{
|
|
cairo_xml_surface_t *surface = abstract_surface;
|
|
cairo_xml_t *xml = to_xml (surface);
|
|
cairo_status_t status;
|
|
|
|
_cairo_xml_printf (xml, "<stroke>");
|
|
_cairo_xml_indent (xml, 2);
|
|
|
|
_cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
|
|
_cairo_xml_emit_double (xml, "line-width", style->line_width);
|
|
_cairo_xml_emit_double (xml, "miter-limit", style->miter_limit);
|
|
_cairo_xml_emit_string (xml, "line-cap", _line_cap_to_string (style->line_cap));
|
|
_cairo_xml_emit_string (xml, "line-join", _line_join_to_string (style->line_join));
|
|
|
|
status = _cairo_xml_surface_emit_clip (surface, clip);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
status = _cairo_xml_emit_pattern (xml, "source", source);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
if (style->num_dashes) {
|
|
unsigned int i;
|
|
|
|
_cairo_xml_printf_start (xml, "<dash offset='%f'>",
|
|
style->dash_offset);
|
|
for (i = 0; i < style->num_dashes; i++)
|
|
_cairo_xml_printf_continue (xml, "%f ", style->dash[i]);
|
|
|
|
_cairo_xml_printf_end (xml, "</dash>");
|
|
}
|
|
|
|
_cairo_xml_emit_path (xml, path);
|
|
_cairo_xml_emit_double (xml, "tolerance", tolerance);
|
|
_cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias));
|
|
|
|
_cairo_xml_emit_matrix (xml, ctm);
|
|
|
|
_cairo_xml_indent (xml, -2);
|
|
_cairo_xml_printf (xml, "</stroke>");
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_xml_surface_fill (void *abstract_surface,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
const cairo_path_fixed_t*path,
|
|
cairo_fill_rule_t fill_rule,
|
|
double tolerance,
|
|
cairo_antialias_t antialias,
|
|
const cairo_clip_t *clip)
|
|
{
|
|
cairo_xml_surface_t *surface = abstract_surface;
|
|
cairo_xml_t *xml = to_xml (surface);
|
|
cairo_status_t status;
|
|
|
|
_cairo_xml_printf (xml, "<fill>");
|
|
_cairo_xml_indent (xml, 2);
|
|
|
|
_cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
|
|
|
|
status = _cairo_xml_surface_emit_clip (surface, clip);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
status = _cairo_xml_emit_pattern (xml, "source", source);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
_cairo_xml_emit_path (xml, path);
|
|
_cairo_xml_emit_double (xml, "tolerance", tolerance);
|
|
_cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias));
|
|
_cairo_xml_emit_string (xml, "fill-rule", _fill_rule_to_string (fill_rule));
|
|
|
|
_cairo_xml_indent (xml, -2);
|
|
_cairo_xml_printf (xml, "</fill>");
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
#if CAIRO_HAS_FT_FONT
|
|
#include "cairo-ft-private.h"
|
|
static cairo_status_t
|
|
_cairo_xml_emit_type42_font (cairo_xml_t *xml,
|
|
cairo_scaled_font_t *scaled_font)
|
|
{
|
|
const cairo_scaled_font_backend_t *backend;
|
|
cairo_output_stream_t *base64_stream;
|
|
cairo_output_stream_t *zlib_stream;
|
|
cairo_status_t status, status2;
|
|
unsigned long size;
|
|
uint32_t len;
|
|
uint8_t *buf;
|
|
|
|
backend = scaled_font->backend;
|
|
if (backend->load_truetype_table == NULL)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
size = 0;
|
|
status = backend->load_truetype_table (scaled_font, 0, 0, NULL, &size);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
buf = malloc (size);
|
|
if (unlikely (buf == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
status = backend->load_truetype_table (scaled_font, 0, 0, buf, &size);
|
|
if (unlikely (status)) {
|
|
free (buf);
|
|
return status;
|
|
}
|
|
|
|
_cairo_xml_printf_start (xml, "<font type='42' flags='%d' index='0'>",
|
|
_cairo_ft_scaled_font_get_load_flags (scaled_font));
|
|
|
|
|
|
base64_stream = _cairo_base64_stream_create (xml->stream);
|
|
len = size;
|
|
_cairo_output_stream_write (base64_stream, &len, sizeof (len));
|
|
|
|
zlib_stream = _cairo_deflate_stream_create (base64_stream);
|
|
|
|
_cairo_output_stream_write (zlib_stream, buf, size);
|
|
free (buf);
|
|
|
|
status2 = _cairo_output_stream_destroy (zlib_stream);
|
|
if (status == CAIRO_STATUS_SUCCESS)
|
|
status = status2;
|
|
|
|
status2 = _cairo_output_stream_destroy (base64_stream);
|
|
if (status == CAIRO_STATUS_SUCCESS)
|
|
status = status2;
|
|
|
|
_cairo_xml_printf_end (xml, "</font>");
|
|
|
|
return status;
|
|
}
|
|
#else
|
|
static cairo_status_t
|
|
_cairo_xml_emit_type42_font (cairo_xml_t *xml,
|
|
cairo_scaled_font_t *scaled_font)
|
|
{
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
}
|
|
#endif
|
|
|
|
static cairo_status_t
|
|
_cairo_xml_emit_type3_font (cairo_xml_t *xml,
|
|
cairo_scaled_font_t *scaled_font,
|
|
cairo_glyph_t *glyphs,
|
|
int num_glyphs)
|
|
{
|
|
_cairo_xml_printf_start (xml, "<font type='3'>");
|
|
_cairo_xml_printf_end (xml, "</font>");
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xml_emit_scaled_font (cairo_xml_t *xml,
|
|
cairo_scaled_font_t *scaled_font,
|
|
cairo_glyph_t *glyphs,
|
|
int num_glyphs)
|
|
{
|
|
cairo_int_status_t status;
|
|
|
|
_cairo_xml_printf (xml, "<scaled-font>");
|
|
_cairo_xml_indent (xml, 2);
|
|
|
|
status = _cairo_xml_emit_type42_font (xml, scaled_font);
|
|
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
|
|
status = _cairo_xml_emit_type3_font (xml, scaled_font,
|
|
glyphs, num_glyphs);
|
|
}
|
|
|
|
_cairo_xml_indent (xml, -2);
|
|
_cairo_xml_printf (xml, "<scaled-font>");
|
|
|
|
return status;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_xml_surface_glyphs (void *abstract_surface,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
cairo_glyph_t *glyphs,
|
|
int num_glyphs,
|
|
cairo_scaled_font_t *scaled_font,
|
|
const cairo_clip_t *clip,
|
|
int *remaining_glyphs)
|
|
{
|
|
cairo_xml_surface_t *surface = abstract_surface;
|
|
cairo_xml_t *xml = to_xml (surface);
|
|
cairo_status_t status;
|
|
int i;
|
|
|
|
_cairo_xml_printf (xml, "<glyphs>");
|
|
_cairo_xml_indent (xml, 2);
|
|
|
|
_cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
|
|
|
|
status = _cairo_xml_surface_emit_clip (surface, clip);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
status = _cairo_xml_emit_pattern (xml, "source", source);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
status = _cairo_xml_emit_scaled_font (xml, scaled_font, glyphs, num_glyphs);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
for (i = 0; i < num_glyphs; i++) {
|
|
_cairo_xml_printf (xml, "<glyph index='%lu'>%f %f</glyph>",
|
|
glyphs[i].index,
|
|
glyphs[i].x,
|
|
glyphs[i].y);
|
|
}
|
|
|
|
_cairo_xml_indent (xml, -2);
|
|
_cairo_xml_printf (xml, "</glyphs>");
|
|
|
|
*remaining_glyphs = 0;
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static const cairo_surface_backend_t
|
|
_cairo_xml_surface_backend = {
|
|
CAIRO_SURFACE_TYPE_XML,
|
|
NULL,
|
|
_cairo_default_context_create,
|
|
|
|
_cairo_xml_surface_create_similar,
|
|
NULL, /* create_similar_image */
|
|
NULL, /* map_to_image */
|
|
NULL, /* unmap_image */
|
|
|
|
NULL, NULL, /* source image */
|
|
NULL, NULL, /* dst image */
|
|
NULL, /* clone_similar */
|
|
NULL, /* composite */
|
|
NULL, /* fill_rectangles */
|
|
NULL, /* composite_trapezoids */
|
|
NULL, /* create_span_renderer */
|
|
NULL, /* check_span_renderer */
|
|
NULL, NULL, /* copy/show page */
|
|
_cairo_xml_surface_get_extents,
|
|
NULL, /* old_show_glyphs */
|
|
NULL, /* get_font_options */
|
|
NULL, /* flush */
|
|
NULL, /* mark_dirty_rectangle */
|
|
NULL, /* font fini */
|
|
NULL, /* scaled_glyph_fini */
|
|
|
|
/* The 5 high level operations */
|
|
_cairo_xml_surface_paint,
|
|
_cairo_xml_surface_mask,
|
|
_cairo_xml_surface_stroke,
|
|
_cairo_xml_surface_fill,
|
|
_cairo_xml_surface_glyphs,
|
|
|
|
NULL, /* snapshot */
|
|
|
|
NULL, /* is_similar */
|
|
NULL, /* fill_stroke */
|
|
NULL, /* create_solid_pattern_surface */
|
|
NULL, /* can_repaint_solid_pattern_surface */
|
|
|
|
/* The alternate high-level text operation */
|
|
NULL, NULL, /* has, show_text_glyphs */
|
|
};
|
|
|
|
static cairo_surface_t *
|
|
_cairo_xml_surface_create_internal (cairo_device_t *device,
|
|
cairo_content_t content,
|
|
double width,
|
|
double height)
|
|
{
|
|
cairo_xml_surface_t *surface;
|
|
|
|
surface = malloc (sizeof (cairo_xml_surface_t));
|
|
if (unlikely (surface == NULL))
|
|
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
|
|
|
_cairo_surface_init (&surface->base,
|
|
&_cairo_xml_surface_backend,
|
|
device,
|
|
content);
|
|
|
|
surface->width = width;
|
|
surface->height = height;
|
|
|
|
return &surface->base;
|
|
}
|
|
|
|
cairo_device_t *
|
|
cairo_xml_create (const char *filename)
|
|
{
|
|
cairo_output_stream_t *stream;
|
|
cairo_status_t status;
|
|
|
|
stream = _cairo_output_stream_create_for_filename (filename);
|
|
if ((status = _cairo_output_stream_get_status (stream)))
|
|
return _cairo_device_create_in_error (status);
|
|
|
|
return _cairo_xml_create_internal (stream);
|
|
}
|
|
|
|
cairo_device_t *
|
|
cairo_xml_create_for_stream (cairo_write_func_t write_func,
|
|
void *closure)
|
|
{
|
|
cairo_output_stream_t *stream;
|
|
cairo_status_t status;
|
|
|
|
stream = _cairo_output_stream_create (write_func, NULL, closure);
|
|
if ((status = _cairo_output_stream_get_status (stream)))
|
|
return _cairo_device_create_in_error (status);
|
|
|
|
return _cairo_xml_create_internal (stream);
|
|
}
|
|
|
|
cairo_surface_t *
|
|
cairo_xml_surface_create (cairo_device_t *device,
|
|
cairo_content_t content,
|
|
double width, double height)
|
|
{
|
|
if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_XML))
|
|
return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
|
|
|
|
if (unlikely (device->status))
|
|
return _cairo_surface_create_in_error (device->status);
|
|
|
|
return _cairo_xml_surface_create_internal (device, content, width, height);
|
|
}
|
|
|
|
cairo_status_t
|
|
cairo_xml_for_recording_surface (cairo_device_t *device,
|
|
cairo_surface_t *recording_surface)
|
|
{
|
|
cairo_box_t bbox;
|
|
cairo_rectangle_int_t extents;
|
|
cairo_surface_t *surface;
|
|
cairo_xml_t *xml;
|
|
cairo_status_t status;
|
|
|
|
if (unlikely (device->status))
|
|
return device->status;
|
|
|
|
if (unlikely (recording_surface->status))
|
|
return recording_surface->status;
|
|
|
|
if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_XML))
|
|
return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
|
|
|
|
if (unlikely (! _cairo_surface_is_recording (recording_surface)))
|
|
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
|
|
status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface,
|
|
&bbox, NULL);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
_cairo_box_round_to_rectangle (&bbox, &extents);
|
|
surface = _cairo_xml_surface_create_internal (device,
|
|
recording_surface->content,
|
|
extents.width,
|
|
extents.height);
|
|
if (unlikely (surface->status))
|
|
return surface->status;
|
|
|
|
xml = (cairo_xml_t *) device;
|
|
|
|
_cairo_xml_printf (xml,
|
|
"<surface content='%s' width='%d' height='%d'>",
|
|
_content_to_string (recording_surface->content),
|
|
extents.width, extents.height);
|
|
_cairo_xml_indent (xml, 2);
|
|
|
|
cairo_surface_set_device_offset (surface, -extents.x, -extents.y);
|
|
status = _cairo_recording_surface_replay (recording_surface, surface);
|
|
cairo_surface_destroy (surface);
|
|
|
|
_cairo_xml_indent (xml, -2);
|
|
_cairo_xml_printf (xml, "</surface>");
|
|
|
|
return status;
|
|
}
|
|
slim_hidden_def (cairo_xml_for_recording_surface);
|