Bug 288498 - Prevent SVG DOS with dasharray

This commit is contained in:
tor%cs.brown.edu 2005-03-31 20:14:05 +00:00
Родитель 84a42982ba
Коммит 79a8633670
6 изменённых файлов: 352 добавлений и 3800 удалений

Просмотреть файл

@ -19,3 +19,5 @@ PATCHES:
xlib.diff - include X11/xlib.h in cairo-xlib.h - needed for Solaris.
stroke-extent.diff - fix calculation of stroke extents.
dash-dos.diff - prevent DOS attack with dasharray.

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,645 +0,0 @@
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 University of Southern
* California.
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <math.h>
#include "cairoint.h"
static cairo_matrix_t const CAIRO_MATRIX_IDENTITY = {
{
{1, 0},
{0, 1},
{0, 0}
}
};
static void
_cairo_matrix_scalar_multiply (cairo_matrix_t *matrix, double scalar);
static void
_cairo_matrix_compute_adjoint (cairo_matrix_t *matrix);
/**
* cairo_matrix_create:
*
* Creates a new identity matrix.
*
* Return value: a newly created matrix; free with cairo_matrix_destroy(),
* or %NULL if memory couldn't be allocated.
**/
cairo_matrix_t *
cairo_matrix_create (void)
{
cairo_matrix_t *matrix;
matrix = malloc (sizeof (cairo_matrix_t));
if (matrix == NULL)
return NULL;
_cairo_matrix_init (matrix);
return matrix;
}
void
_cairo_matrix_init (cairo_matrix_t *matrix)
{
cairo_matrix_set_identity (matrix);
}
void
_cairo_matrix_fini (cairo_matrix_t *matrix)
{
/* nothing to do here */
}
/**
* cairo_matrix_destroy:
* @matrix: a #cairo_matrix_t
*
* Frees a matrix created with cairo_matrix_create.
**/
void
cairo_matrix_destroy (cairo_matrix_t *matrix)
{
_cairo_matrix_fini (matrix);
free (matrix);
}
/**
* cairo_matrix_copy:
* @matrix: a #cairo_matrix_t
* @other: another #cairo_
*
* Modifies @matrix to be identical to @other.
*
* Return value: %CAIRO_STATUS_SUCCESS, always.
**/
cairo_status_t
cairo_matrix_copy (cairo_matrix_t *matrix, const cairo_matrix_t *other)
{
*matrix = *other;
return CAIRO_STATUS_SUCCESS;
}
slim_hidden_def(cairo_matrix_copy);
/**
* cairo_matrix_set_identity:
* @matrix: a #cairo_matrix_t
*
* Modifies @matrix to be an identity transformation.
*
* Return value: %CAIRO_STATUS_SUCCESS, always.
**/
cairo_status_t
cairo_matrix_set_identity (cairo_matrix_t *matrix)
{
*matrix = CAIRO_MATRIX_IDENTITY;
return CAIRO_STATUS_SUCCESS;
}
slim_hidden_def(cairo_matrix_set_identity);
/**
* cairo_matrix_set_affine:
* @matrix: a cairo_matrix_t
* @a: a component of the affine transformation
* @b: b component of the affine transformation
* @c: c component of the affine transformation
* @d: d component of the affine transformation
* @tx: X translation component of the affine transformation
* @ty: Y translation component of the affine transformation
*
* Sets @matrix to be the affine transformation given by
* @a, b, @c, @d, @tx, @ty. The transformation is given
* by:
* <programlisting>
* x_new = x * a + y * c + tx;
* y_new = x * b + y * d + ty;
* </programlisting>
*
* Return value: %CAIRO_STATUS_SUCCESS, always.
**/
cairo_status_t
cairo_matrix_set_affine (cairo_matrix_t *matrix,
double a, double b,
double c, double d,
double tx, double ty)
{
matrix->m[0][0] = a; matrix->m[0][1] = b;
matrix->m[1][0] = c; matrix->m[1][1] = d;
matrix->m[2][0] = tx; matrix->m[2][1] = ty;
return CAIRO_STATUS_SUCCESS;
}
slim_hidden_def(cairo_matrix_set_affine);
/**
* cairo_matrix_get_affine:
* @matrix: a @cairo_matrix_t
* @a: location to store a component of affine transformation, or %NULL
* @b: location to store b component of affine transformation, or %NULL
* @c: location to store c component of affine transformation, or %NULL
* @d: location to store d component of affine transformation, or %NULL
* @tx: location to store X-translation component of affine transformation, or %NULL
* @ty: location to store Y-translation component of affine transformation, or %NULL
*
* Gets the matrix values for the affine tranformation that @matrix represents.
* See cairo_matrix_set_affine().
*
* Return value: %CAIRO_STATUS_SUCCESS, always.
**/
cairo_status_t
cairo_matrix_get_affine (cairo_matrix_t *matrix,
double *a, double *b,
double *c, double *d,
double *tx, double *ty)
{
if (a)
*a = matrix->m[0][0];
if (b)
*b = matrix->m[0][1];
if (c)
*c = matrix->m[1][0];
if (d)
*d = matrix->m[1][1];
if (tx)
*tx = matrix->m[2][0];
if (ty)
*ty = matrix->m[2][1];
return CAIRO_STATUS_SUCCESS;
}
cairo_status_t
_cairo_matrix_set_translate (cairo_matrix_t *matrix,
double tx, double ty)
{
return cairo_matrix_set_affine (matrix,
1, 0,
0, 1,
tx, ty);
}
/**
* cairo_matrix_translate:
* @matrix: a cairo_matrix_t
* @tx: amount to rotate in the X direction
* @ty: amount to rotate in the Y direction
*
* Applies a translation by @tx, @ty to the transformation in
* @matrix. The new transformation is given by first translating by
* @tx, @ty then applying the original transformation
*
* Return value: %CAIRO_STATUS_SUCCESS, always.
**/
cairo_status_t
cairo_matrix_translate (cairo_matrix_t *matrix, double tx, double ty)
{
cairo_matrix_t tmp;
_cairo_matrix_set_translate (&tmp, tx, ty);
return cairo_matrix_multiply (matrix, &tmp, matrix);
}
cairo_status_t
_cairo_matrix_set_scale (cairo_matrix_t *matrix,
double sx, double sy)
{
return cairo_matrix_set_affine (matrix,
sx, 0,
0, sy,
0, 0);
}
/**
* cairo_matrix_scale:
* @matrix: a #cairo_matrix_t
* @sx: Scale factor in the X direction
* @sy: Scale factor in the Y direction
*
* Applies scaling by @tx, @ty to the transformation in
* @matrix. The new transformation is given by first scaling by @sx
* and @sy then applying the original transformation
*
* Return value: %CAIRO_STATUS_SUCCESS, always.
**/
cairo_status_t
cairo_matrix_scale (cairo_matrix_t *matrix, double sx, double sy)
{
cairo_matrix_t tmp;
_cairo_matrix_set_scale (&tmp, sx, sy);
return cairo_matrix_multiply (matrix, &tmp, matrix);
}
slim_hidden_def(cairo_matrix_scale);
cairo_status_t
_cairo_matrix_set_rotate (cairo_matrix_t *matrix,
double radians)
{
double s;
double c;
#if HAVE_SINCOS
sincos (radians, &s, &c);
#else
s = sin (radians);
c = cos (radians);
#endif
return cairo_matrix_set_affine (matrix,
c, s,
-s, c,
0, 0);
}
/**
* cairo_matrix_rotate:
* @matrix: a @cairo_matrix_t
* @radians: angle of rotation, in radians. Angles are defined
* so that an angle of 90 degrees (%M_PI radians) rotates the
* positive X axis into the positive Y axis. With the default
* Cairo choice of axis orientation, positive rotations are
* clockwise.
*
* Applies rotation by @radians to the transformation in
* @matrix. The new transformation is given by first rotating by
* @radians then applying the original transformation
*
* Return value: %CAIRO_STATUS_SUCCESS, always.
**/
cairo_status_t
cairo_matrix_rotate (cairo_matrix_t *matrix, double radians)
{
cairo_matrix_t tmp;
_cairo_matrix_set_rotate (&tmp, radians);
return cairo_matrix_multiply (matrix, &tmp, matrix);
}
/**
* cairo_matrix_multiply:
* @result: a @cairo_matrix_t in which to store the result
* @a: a @cairo_matrix_t
* @b: a @cairo_matrix_t
*
* Multiplies the affine transformations in @a and @b together
* and stores the result in @result. The resulting transformation
* is given by first applying the transformation in @b then
* applying the transformation in @a.
*
* Return value: %CAIRO_STATUS_SUCCESS, always.
**/
cairo_status_t
cairo_matrix_multiply (cairo_matrix_t *result, const cairo_matrix_t *a, const cairo_matrix_t *b)
{
cairo_matrix_t r;
int row, col, n;
double t;
for (row = 0; row < 3; row++) {
for (col = 0; col < 2; col++) {
if (row == 2)
t = b->m[2][col];
else
t = 0;
for (n = 0; n < 2; n++) {
t += a->m[row][n] * b->m[n][col];
}
r.m[row][col] = t;
}
}
*result = r;
return CAIRO_STATUS_SUCCESS;
}
slim_hidden_def(cairo_matrix_multiply);
/**
* cairo_matrix_transform_distance:
* @matrix: a @cairo_matrix_t
* @dx: a distance in the X direction. An in/out parameter
* @dy: a distance in the Y direction. An in/out parameter
*
* Transforms the vector (@dx,@dy) by @matrix. Translation is
* ignored. In terms of the components of the affine transformation:
*
* <programlisting>
* dx2 = dx1 * a + dy1 * c;
* dy2 = dx1 * b + dy1 * d;
* </programlisting>
*
* Affine transformations are position invariant, so the same vector
* always transforms to the same vector. If (@x1,@y1) transforms
* to (@x2,@y2) then (@x1+@dx1,@y1+@dy1) will transform to
* (@x1+@dx2,@y1+@dy2) for all values of @x1 and @x2.
*
* Return value: %CAIRO_STATUS_SUCCESS, always.
**/
cairo_status_t
cairo_matrix_transform_distance (cairo_matrix_t *matrix, double *dx, double *dy)
{
double new_x, new_y;
new_x = (matrix->m[0][0] * *dx
+ matrix->m[1][0] * *dy);
new_y = (matrix->m[0][1] * *dx
+ matrix->m[1][1] * *dy);
*dx = new_x;
*dy = new_y;
return CAIRO_STATUS_SUCCESS;
}
slim_hidden_def(cairo_matrix_transform_distance);
/**
* cairo_matrix_transform_point:
* @matrix: a @cairo_matrix_t
* @x: X position. An in/out parameter
* @y: Y position. An in/out parameter
*
* Transforms the point (@x, @y) by @matrix.
*
* Return value: %CAIRO_STATUS_SUCCESS, always.
**/
cairo_status_t
cairo_matrix_transform_point (cairo_matrix_t *matrix, double *x, double *y)
{
cairo_matrix_transform_distance (matrix, x, y);
*x += matrix->m[2][0];
*y += matrix->m[2][1];
return CAIRO_STATUS_SUCCESS;
}
slim_hidden_def(cairo_matrix_transform_point);
cairo_status_t
_cairo_matrix_transform_bounding_box (cairo_matrix_t *matrix,
double *x, double *y,
double *width, double *height)
{
int i;
double quad_x[4], quad_y[4];
double dx1, dy1;
double dx2, dy2;
double min_x, max_x;
double min_y, max_y;
quad_x[0] = *x;
quad_y[0] = *y;
cairo_matrix_transform_point (matrix, &quad_x[0], &quad_y[0]);
dx1 = *width;
dy1 = 0;
cairo_matrix_transform_distance (matrix, &dx1, &dy1);
quad_x[1] = quad_x[0] + dx1;
quad_y[1] = quad_y[0] + dy1;
dx2 = 0;
dy2 = *height;
cairo_matrix_transform_distance (matrix, &dx2, &dy2);
quad_x[2] = quad_x[0] + dx2;
quad_y[2] = quad_y[0] + dy2;
quad_x[3] = quad_x[0] + dx1 + dx2;
quad_y[3] = quad_y[0] + dy1 + dy2;
min_x = max_x = quad_x[0];
min_y = max_y = quad_y[0];
for (i=1; i < 4; i++) {
if (quad_x[i] < min_x)
min_x = quad_x[i];
if (quad_x[i] > max_x)
max_x = quad_x[i];
if (quad_y[i] < min_y)
min_y = quad_y[i];
if (quad_y[i] > max_y)
max_y = quad_y[i];
}
*x = min_x;
*y = min_y;
*width = max_x - min_x;
*height = max_y - min_y;
return CAIRO_STATUS_SUCCESS;
}
static void
_cairo_matrix_scalar_multiply (cairo_matrix_t *matrix, double scalar)
{
int row, col;
for (row = 0; row < 3; row++)
for (col = 0; col < 2; col++)
matrix->m[row][col] *= scalar;
}
/* This function isn't a correct adjoint in that the implicit 1 in the
homogeneous result should actually be ad-bc instead. But, since this
adjoint is only used in the computation of the inverse, which
divides by det (A)=ad-bc anyway, everything works out in the end. */
static void
_cairo_matrix_compute_adjoint (cairo_matrix_t *matrix)
{
/* adj (A) = transpose (C:cofactor (A,i,j)) */
double a, b, c, d, tx, ty;
a = matrix->m[0][0]; b = matrix->m[0][1];
c = matrix->m[1][0]; d = matrix->m[1][1];
tx = matrix->m[2][0]; ty = matrix->m[2][1];
cairo_matrix_set_affine (matrix,
d, -b,
-c, a,
c*ty - d*tx, b*tx - a*ty);
}
/**
* cairo_matrix_invert:
* @matrix: a @cairo_matrix_t
*
* Changes @matrix to be the inverse of it's original value. Not
* all transformation matrices have inverses; if the matrix
* collapses points together (it is <firstterm>degenerate</firstterm>),
* then it has no inverse and this function will fail.
*
* Returns: If @matrix has an inverse, modifies @matrix to
* be the inverse matrix and returns %CAIRO_STATUS_SUCCESS. Otherwise,
* returns %CAIRO_STATUS_INVALID_MATRIX.
**/
cairo_status_t
cairo_matrix_invert (cairo_matrix_t *matrix)
{
/* inv (A) = 1/det (A) * adj (A) */
double det;
_cairo_matrix_compute_determinant (matrix, &det);
if (det == 0)
return CAIRO_STATUS_INVALID_MATRIX;
_cairo_matrix_compute_adjoint (matrix);
_cairo_matrix_scalar_multiply (matrix, 1 / det);
return CAIRO_STATUS_SUCCESS;
}
slim_hidden_def(cairo_matrix_invert);
cairo_status_t
_cairo_matrix_compute_determinant (cairo_matrix_t *matrix, double *det)
{
double a, b, c, d;
a = matrix->m[0][0]; b = matrix->m[0][1];
c = matrix->m[1][0]; d = matrix->m[1][1];
*det = a*d - b*c;
return CAIRO_STATUS_SUCCESS;
}
cairo_status_t
_cairo_matrix_compute_eigen_values (cairo_matrix_t *matrix, double *lambda1, double *lambda2)
{
/* The eigenvalues of an NxN matrix M are found by solving the polynomial:
det (M - lI) = 0
The zeros in our homogeneous 3x3 matrix make this equation equal
to that formed by the sub-matrix:
M = a b
c d
by which:
l^2 - (a+d)l + (ad - bc) = 0
l = (a+d +/- sqrt (a^2 + 2ad + d^2 - 4 (ad-bc))) / 2;
*/
double a, b, c, d, rad;
a = matrix->m[0][0];
b = matrix->m[0][1];
c = matrix->m[1][0];
d = matrix->m[1][1];
rad = sqrt (a*a + 2*a*d + d*d - 4*(a*d - b*c));
*lambda1 = (a + d + rad) / 2.0;
*lambda2 = (a + d - rad) / 2.0;
return CAIRO_STATUS_SUCCESS;
}
/* Compute the amount that each basis vector is scaled by. */
cairo_status_t
_cairo_matrix_compute_scale_factors (cairo_matrix_t *matrix, double *sx, double *sy, int x_major)
{
double det;
_cairo_matrix_compute_determinant (matrix, &det);
if (det == 0)
*sx = *sy = 0;
else
{
double x = x_major != 0;
double y = x == 0;
double major, minor;
cairo_matrix_transform_distance (matrix, &x, &y);
major = sqrt(x*x + y*y);
/*
* ignore mirroring
*/
if (det < 0)
det = -det;
if (major)
minor = det / major;
else
minor = 0.0;
if (x_major)
{
*sx = major;
*sy = minor;
}
else
{
*sx = minor;
*sy = major;
}
}
return CAIRO_STATUS_SUCCESS;
}
cairo_bool_t
_cairo_matrix_is_integer_translation(cairo_matrix_t *mat,
int *itx, int *ity)
{
double a, b, c, d, tx, ty;
int ttx, tty;
int ok = 0;
cairo_matrix_get_affine (mat, &a, &b, &c, &d, &tx, &ty);
ttx = _cairo_fixed_from_double (tx);
tty = _cairo_fixed_from_double (ty);
ok = ((a == 1.0)
&& (b == 0.0)
&& (c == 0.0)
&& (d == 1.0)
&& (_cairo_fixed_is_integer(ttx))
&& (_cairo_fixed_is_integer(tty)));
if (ok) {
*itx = _cairo_fixed_integer_part(ttx);
*ity = _cairo_fixed_integer_part(tty);
return TRUE;
}
return FALSE;
}

Просмотреть файл

@ -1,587 +0,0 @@
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 University of Southern
* California.
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
*/
#include "cairoint.h"
static int
_cairo_pen_vertices_needed (double tolerance, double radius, cairo_matrix_t *matrix);
static void
_cairo_pen_compute_slopes (cairo_pen_t *pen);
static cairo_status_t
_cairo_pen_stroke_spline_half (cairo_pen_t *pen, cairo_spline_t *spline, cairo_direction_t dir, cairo_polygon_t *polygon);
cairo_status_t
_cairo_pen_init_empty (cairo_pen_t *pen)
{
pen->radius = 0;
pen->tolerance = 0;
pen->vertices = NULL;
pen->num_vertices = 0;
return CAIRO_STATUS_SUCCESS;
}
cairo_status_t
_cairo_pen_init (cairo_pen_t *pen, double radius, cairo_gstate_t *gstate)
{
int i;
int reflect;
double det;
if (pen->num_vertices) {
/* XXX: It would be nice to notice that the pen is already properly constructed.
However, this test would also have to account for possible changes in the transformation
matrix.
if (pen->radius == radius && pen->tolerance == tolerance)
return CAIRO_STATUS_SUCCESS;
*/
_cairo_pen_fini (pen);
}
pen->radius = radius;
pen->tolerance = gstate->tolerance;
_cairo_matrix_compute_determinant (&gstate->ctm, &det);
if (det >= 0) {
reflect = 0;
} else {
reflect = 1;
}
pen->num_vertices = _cairo_pen_vertices_needed (gstate->tolerance,
radius,
&gstate->ctm);
pen->vertices = malloc (pen->num_vertices * sizeof (cairo_pen_vertex_t));
if (pen->vertices == NULL) {
return CAIRO_STATUS_NO_MEMORY;
}
/*
* Compute pen coordinates. To generate the right ellipse, compute points around
* a circle in user space and transform them to device space. To get a consistent
* orientation in device space, flip the pen if the transformation matrix
* is reflecting
*/
for (i=0; i < pen->num_vertices; i++) {
double theta = 2 * M_PI * i / (double) pen->num_vertices;
double dx = radius * cos (reflect ? -theta : theta);
double dy = radius * sin (reflect ? -theta : theta);
cairo_pen_vertex_t *v = &pen->vertices[i];
cairo_matrix_transform_distance (&gstate->ctm, &dx, &dy);
v->point.x = _cairo_fixed_from_double (dx);
v->point.y = _cairo_fixed_from_double (dy);
}
_cairo_pen_compute_slopes (pen);
return CAIRO_STATUS_SUCCESS;
}
void
_cairo_pen_fini (cairo_pen_t *pen)
{
free (pen->vertices);
pen->vertices = NULL;
_cairo_pen_init_empty (pen);
}
cairo_status_t
_cairo_pen_init_copy (cairo_pen_t *pen, cairo_pen_t *other)
{
*pen = *other;
if (pen->num_vertices) {
pen->vertices = malloc (pen->num_vertices * sizeof (cairo_pen_vertex_t));
if (pen->vertices == NULL) {
return CAIRO_STATUS_NO_MEMORY;
}
memcpy (pen->vertices, other->vertices, pen->num_vertices * sizeof (cairo_pen_vertex_t));
}
return CAIRO_STATUS_SUCCESS;
}
cairo_status_t
_cairo_pen_add_points (cairo_pen_t *pen, cairo_point_t *point, int num_points)
{
cairo_pen_vertex_t *vertices;
int num_vertices;
int i;
num_vertices = pen->num_vertices + num_points;
vertices = realloc (pen->vertices, num_vertices * sizeof (cairo_pen_vertex_t));
if (vertices == NULL)
return CAIRO_STATUS_NO_MEMORY;
pen->vertices = vertices;
pen->num_vertices = num_vertices;
/* initialize new vertices */
for (i=0; i < num_points; i++)
pen->vertices[pen->num_vertices-num_points+i].point = point[i];
_cairo_hull_compute (pen->vertices, &pen->num_vertices);
_cairo_pen_compute_slopes (pen);
return CAIRO_STATUS_SUCCESS;
}
/*
The circular pen in user space is transformed into an ellipse in
device space.
We construct the pen by computing points along the circumference
using equally spaced angles.
We show below that this approximation to the ellipse has
maximum error at the major axis of the ellipse.
So, we need to compute the length of the major axis and then
use that to compute the number of sides needed in our pen.
Thanks to Walter Brisken <wbrisken@aoc.nrao.edu> for this
derivation:
1. First some notation:
All capital letters represent vectors in two dimensions. A prime '
represents a transformed coordinate. Matrices are written in underlined
form, ie _R_. Lowercase letters represent scalar real values.
The letter t is used to represent the greek letter theta.
2. The question has been posed: What is the maximum expansion factor
achieved by the linear transformation
X' = _R_ X
where _R_ is a real-valued 2x2 matrix with entries:
_R_ = [a b]
[c d] .
In other words, what is the maximum radius, MAX[ |X'| ], reached for any
X on the unit circle ( |X| = 1 ) ?
3. Some useful formulae
(A) through (C) below are standard double-angle formulae. (D) is a lesser
known result and is derived below:
(A) sin^2(t) = (1 - cos(2*t))/2
(B) cos^2(t) = (1 + cos(2*t))/2
(C) sin(t)*cos(t) = sin(2*t)/2
(D) MAX[a*cos(t) + b*sin(t)] = sqrt(a^2 + b^2)
Proof of (D):
find the maximum of the function by setting the derivative to zero:
-a*sin(t)+b*cos(t) = 0
From this it follows that
tan(t) = b/a
and hence
sin(t) = b/sqrt(a^2 + b^2)
and
cos(t) = a/sqrt(a^2 + b^2)
Thus the maximum value is
MAX[a*cos(t) + b*sin(t)] = (a^2 + b^2)/sqrt(a^2 + b^2)
= sqrt(a^2 + b^2)
4. Derivation of maximum expansion
To find MAX[ |X'| ] we search brute force method using calculus. The unit
circle on which X is constrained is to be parameterized by t:
X(t) = (cos(t), sin(t))
Thus
X'(t) = (a*cos(t) + b*sin(t), c*cos(t) + d*sin(t)) .
Define
r(t) = |X'(t)|
Thus
r^2(t) = (a*cos(t) + b*sin(t))^2 + (c*cos(t) + d*sin(t))^2
= (a^2 + c^2)*cos^2(t) + (b^2 + d^2)*sin^2(t)
+ 2*(a*b + c*d)*cos(t)*sin(t)
Now apply the double angle formulae (A) to (C) from above:
r^2(t) = (a^2 + b^2 + c^2 + d^2)/2
+ (a^2 - b^2 + c^2 - d^2)*cos(2*t)/2
+ (a*b + c*d)*sin(2*t)
= f + g*cos(u) + h*sin(u)
Where
f = (a^2 + b^2 + c^2 + d^2)/2
g = (a^2 - b^2 + c^2 - d^2)/2
h = (a*b + c*d)
u = 2*t
It is clear that MAX[ |X'| ] = sqrt(MAX[ r^2 ]). Here we determine MAX[ r^2 ]
using (D) from above:
MAX[ r^2 ] = f + sqrt(g^2 + h^2)
And finally
MAX[ |X'| ] = sqrt( f + sqrt(g^2 + h^2) )
Which is the solution to this problem.
Walter Brisken
2004/10/08
(Note that the minor axis length is at the minimum of the above solution,
which is just sqrt (f - sqrt (g^2 + h^2)) given the symmetry of (D)).
Now to compute how many sides to use for the pen formed by
a regular polygon.
Set
M = major axis length (computed by above formula)
m = minor axis length (computed by above formula)
Align 'M' along the X axis and 'm' along the Y axis and draw
an ellipse parameterized by angle 't':
x = M cos t y = m sin t
Perturb t by ± d and compute two new points (x+,y+), (x-,y-).
The distance from the average of these two points to (x,y) represents
the maximum error in approximating the ellipse with a polygon formed
from vertices 2 radians apart.
x+ = M cos (t+) y+ = m sin (t+)
x- = M cos (t-) y- = m sin (t-)
Now compute the approximation error, E:
Ex = (x - (x+ + x-) / 2)
Ex = (M cos(t) - (Mcos(t+) + Mcos(t-))/2)
= M (cos(t) - (cos(t)cos() + sin(t)sin() +
cos(t)cos() - sin(t)sin())/2)
= M(cos(t) - cos(t)cos())
= M cos(t) (1 - cos())
Ey = y - (y+ - y-) / 2
= m sin (t) - (m sin(t+) + m sin(t-)) / 2
= m (sin(t) - (sin(t)cos() + cos(t)sin() +
sin(t)cos() - cos(t)sin())/2)
= m (sin(t) - sin(t)cos())
= m sin(t) (1 - cos())
E² = Ex² + Ey²
= (M cos(t) (1 - cos ()))² + (m sin(t) (1-cos()))²
= (1 - cos())² (M² cos²(t) + m² sin²(t))
= (1 - cos())² ((m² + M² - m²) cos² (t) + m² sin²(t))
= (1 - cos())² (M² - m²) cos² (t) + (1 - cos())² m²
Find the extremum by differentiation wrt t and setting that to zero
(E²)/(t) = (1-cos())² (M² - m²) (-2 cos(t) sin(t))
0 = 2 cos (t) sin (t)
0 = sin (2t)
t =
Which is to say that the maximum and minimum errors occur on the
axes of the ellipse at 0 and π radians:
E²(0) = (1-cos())² (M² - m²) + (1-cos())² m²
= (1-cos())² M²
E²(π) = (1-cos())² m²
maximum error = M (1-cos())
minimum error = m (1-cos())
We must make maximum error tolerance, so compute the needed:
tolerance = M (1-cos())
tolerance / M = 1 - cos ()
cos() = 1 - tolerance/M
= acos (1 - tolerance / M);
Remembering that is half of our angle between vertices,
the number of vertices is then
vertices = ceil(2π/2).
= ceil(π/).
Note that this also equation works for M == m (a circle) as it
doesn't matter where on the circle the error is computed.
*/
static int
_cairo_pen_vertices_needed (double tolerance,
double radius,
cairo_matrix_t *matrix)
{
double a = matrix->m[0][0], c = matrix->m[0][1];
double b = matrix->m[1][0], d = matrix->m[1][1];
double i = a*a + c*c;
double j = b*b + d*d;
double f = 0.5 * (i + j);
double g = 0.5 * (i - j);
double h = a*b + c*d;
/*
* compute major and minor axes lengths for
* a pen with the specified radius
*/
double major_axis = radius * sqrt (f + sqrt (g*g+h*h));
/*
* we don't need the minor axis length, which is
* double min = radius * sqrt (f - sqrt (g*g+h*h));
*/
/*
* compute number of vertices needed
*/
int num_vertices;
/* Where tolerance / M is > 1, we use 4 points */
if (tolerance >= major_axis) {
num_vertices = 4;
} else {
double delta = acos (1 - tolerance / major_axis);
num_vertices = ceil (M_PI / delta);
/* number of vertices must be even */
if (num_vertices % 2)
num_vertices++;
}
return num_vertices;
}
static void
_cairo_pen_compute_slopes (cairo_pen_t *pen)
{
int i, i_prev;
cairo_pen_vertex_t *prev, *v, *next;
for (i=0, i_prev = pen->num_vertices - 1;
i < pen->num_vertices;
i_prev = i++) {
prev = &pen->vertices[i_prev];
v = &pen->vertices[i];
next = &pen->vertices[(i + 1) % pen->num_vertices];
_cairo_slope_init (&v->slope_cw, &prev->point, &v->point);
_cairo_slope_init (&v->slope_ccw, &v->point, &next->point);
}
}
/*
* Find active pen vertex for clockwise edge of stroke at the given slope.
*
* NOTE: The behavior of this function is sensitive to the sense of
* the inequality within _cairo_slope_clockwise/_cairo_slope_counter_clockwise.
*
* The issue is that the slope_ccw member of one pen vertex will be
* equivalent to the slope_cw member of the next pen vertex in a
* counterclockwise order. However, for this function, we care
* strongly about which vertex is returned.
*/
cairo_status_t
_cairo_pen_find_active_cw_vertex_index (cairo_pen_t *pen,
cairo_slope_t *slope,
int *active)
{
int i;
for (i=0; i < pen->num_vertices; i++) {
if (_cairo_slope_clockwise (slope, &pen->vertices[i].slope_ccw)
&& _cairo_slope_counter_clockwise (slope, &pen->vertices[i].slope_cw))
break;
}
*active = i;
return CAIRO_STATUS_SUCCESS;
}
/* Find active pen vertex for counterclockwise edge of stroke at the given slope.
*
* NOTE: The behavior of this function is sensitive to the sense of
* the inequality within _cairo_slope_clockwise/_cairo_slope_counter_clockwise.
*/
cairo_status_t
_cairo_pen_find_active_ccw_vertex_index (cairo_pen_t *pen,
cairo_slope_t *slope,
int *active)
{
int i;
cairo_slope_t slope_reverse;
slope_reverse = *slope;
slope_reverse.dx = -slope_reverse.dx;
slope_reverse.dy = -slope_reverse.dy;
for (i=pen->num_vertices-1; i >= 0; i--) {
if (_cairo_slope_counter_clockwise (&pen->vertices[i].slope_ccw, &slope_reverse)
&& _cairo_slope_clockwise (&pen->vertices[i].slope_cw, &slope_reverse))
break;
}
*active = i;
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_pen_stroke_spline_half (cairo_pen_t *pen,
cairo_spline_t *spline,
cairo_direction_t dir,
cairo_polygon_t *polygon)
{
int i;
cairo_status_t status;
int start, stop, step;
int active = 0;
cairo_point_t hull_point;
cairo_slope_t slope, initial_slope, final_slope;
cairo_point_t *point = spline->points;
int num_points = spline->num_points;
if (dir == CAIRO_DIRECTION_FORWARD) {
start = 0;
stop = num_points;
step = 1;
initial_slope = spline->initial_slope;
final_slope = spline->final_slope;
} else {
start = num_points - 1;
stop = -1;
step = -1;
initial_slope = spline->final_slope;
initial_slope.dx = -initial_slope.dx;
initial_slope.dy = -initial_slope.dy;
final_slope = spline->initial_slope;
final_slope.dx = -final_slope.dx;
final_slope.dy = -final_slope.dy;
}
_cairo_pen_find_active_cw_vertex_index (pen, &initial_slope, &active);
i = start;
while (i != stop) {
hull_point.x = point[i].x + pen->vertices[active].point.x;
hull_point.y = point[i].y + pen->vertices[active].point.y;
status = _cairo_polygon_line_to (polygon, &hull_point);
if (status)
return status;
if (i + step == stop)
slope = final_slope;
else
_cairo_slope_init (&slope, &point[i], &point[i+step]);
if (_cairo_slope_counter_clockwise (&slope, &pen->vertices[active].slope_ccw)) {
if (++active == pen->num_vertices)
active = 0;
} else if (_cairo_slope_clockwise (&slope, &pen->vertices[active].slope_cw)) {
if (--active == -1)
active = pen->num_vertices - 1;
} else {
i += step;
}
}
return CAIRO_STATUS_SUCCESS;
}
/* Compute outline of a given spline using the pen.
The trapezoids needed to fill that outline will be added to traps
*/
cairo_status_t
_cairo_pen_stroke_spline (cairo_pen_t *pen,
cairo_spline_t *spline,
double tolerance,
cairo_traps_t *traps)
{
cairo_status_t status;
cairo_polygon_t polygon;
/* If the line width is so small that the pen is reduced to a
single point, then we have nothing to do. */
if (pen->num_vertices <= 1)
return CAIRO_STATUS_SUCCESS;
_cairo_polygon_init (&polygon);
status = _cairo_spline_decompose (spline, tolerance);
if (status)
return status;
status = _cairo_pen_stroke_spline_half (pen, spline, CAIRO_DIRECTION_FORWARD, &polygon);
if (status)
return status;
status = _cairo_pen_stroke_spline_half (pen, spline, CAIRO_DIRECTION_REVERSE, &polygon);
if (status)
return status;
_cairo_polygon_close (&polygon);
_cairo_traps_tessellate_polygon (traps, &polygon, CAIRO_FILL_RULE_WINDING);
_cairo_polygon_fini (&polygon);
return CAIRO_STATUS_SUCCESS;
}

Просмотреть файл

@ -858,6 +858,8 @@ typedef struct _cairo_gstate {
double *dash;
int num_dashes;
double dash_offset;
double max_dash_length;
double fraction_dash_lit;
char *font_family; /* NULL means CAIRO_FONT_FAMILY_DEFAULT; */
cairo_font_slant_t font_slant;
@ -1645,6 +1647,9 @@ _cairo_matrix_compute_eigen_values (cairo_matrix_t *matrix, double *lambda1, dou
cairo_private cairo_status_t
_cairo_matrix_compute_scale_factors (cairo_matrix_t *matrix, double *sx, double *sy, int x_major);
cairo_private cairo_status_t
_cairo_matrix_compute_expansion_factors (cairo_matrix_t *matrix, double *min, double *max);
cairo_private cairo_bool_t
_cairo_matrix_is_integer_translation(cairo_matrix_t *matrix, int *itx, int *ity);

345
gfx/cairo/dash-dos.diff Normal file
Просмотреть файл

@ -0,0 +1,345 @@
Index: cairo_gstate.c
===================================================================
RCS file: /cvsroot/mozilla/gfx/cairo/cairo/src/cairo_gstate.c,v
retrieving revision 1.3
diff -u -8 -p -r1.3 cairo_gstate.c
--- cairo_gstate.c 30 Mar 2005 17:32:16 -0000 1.3
+++ cairo_gstate.c 31 Mar 2005 19:53:08 -0000
@@ -84,16 +84,18 @@ _cairo_gstate_init (cairo_gstate_t *gsta
gstate->line_join = CAIRO_GSTATE_LINE_JOIN_DEFAULT;
gstate->miter_limit = CAIRO_GSTATE_MITER_LIMIT_DEFAULT;
gstate->fill_rule = CAIRO_GSTATE_FILL_RULE_DEFAULT;
gstate->dash = NULL;
gstate->num_dashes = 0;
gstate->dash_offset = 0.0;
+ gstate->max_dash_length = 0.0;
+ gstate->fraction_dash_lit = 0.0;
gstate->font_family = NULL;
gstate->font_slant = CAIRO_FONT_SLANT_DEFAULT;
gstate->font_weight = CAIRO_FONT_WEIGHT_DEFAULT;
gstate->font = NULL;
gstate->surface = NULL;
@@ -538,28 +540,41 @@ cairo_line_join_t
_cairo_gstate_current_line_join (cairo_gstate_t *gstate)
{
return gstate->line_join;
}
cairo_status_t
_cairo_gstate_set_dash (cairo_gstate_t *gstate, double *dash, int num_dashes, double offset)
{
+ double length = 0.0, lit = 0.0;
+ int i;
+
if (gstate->dash) {
free (gstate->dash);
gstate->dash = NULL;
}
gstate->num_dashes = num_dashes;
if (gstate->num_dashes) {
gstate->dash = malloc (gstate->num_dashes * sizeof (double));
if (gstate->dash == NULL) {
gstate->num_dashes = 0;
return CAIRO_STATUS_NO_MEMORY;
}
+
+ gstate->max_dash_length = 0.0;
+ for (i = 0; i < num_dashes; i++) {
+ gstate->max_dash_length = MAX(dash[i], gstate->max_dash_length);
+
+ if (!(i & 1))
+ lit += dash[i];
+ length += dash[i];
+ }
+ gstate->fraction_dash_lit = lit/length;
}
memcpy (gstate->dash, dash, gstate->num_dashes * sizeof (double));
gstate->dash_offset = offset;
return CAIRO_STATUS_SUCCESS;
}
@@ -1317,46 +1332,77 @@ _cairo_gstate_pattern_init_copy (cairo_g
cairo_pattern_union_t *pattern,
cairo_pattern_t *src)
{
_cairo_pattern_init_copy (&pattern->base, src);
_cairo_pattern_transform (&pattern->base, &gstate->ctm_inverse);
_cairo_pattern_set_alpha (&pattern->base, gstate->alpha);
}
+static cairo_bool_t
+_dashes_invisible (cairo_gstate_t *gstate)
+{
+ if (gstate->dash) {
+ double min, max;
+
+ _cairo_matrix_compute_expansion_factors (&gstate->ctm, &min, &max);
+
+ /* Quick and dirty applicaton of Nyquist sampling limit */
+
+ if (min * gstate->max_dash_length < 0.5f)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
cairo_status_t
_cairo_gstate_stroke (cairo_gstate_t *gstate)
{
cairo_status_t status;
cairo_traps_t traps;
+ double *dash = NULL;
+ double alpha = 0.0;
if (gstate->line_width <= 0.0)
return CAIRO_STATUS_SUCCESS;
+ if (_dashes_invisible(gstate)) {
+ dash = gstate->dash;
+ gstate->dash = NULL;
+
+ alpha = gstate->alpha;
+ gstate->alpha *= gstate->fraction_dash_lit;
+ }
+
_cairo_pen_init (&gstate->pen_regular, gstate->line_width / 2.0, gstate);
_cairo_traps_init (&traps);
status = _cairo_path_stroke_to_traps (&gstate->path, gstate, &traps);
- if (status) {
- _cairo_traps_fini (&traps);
- return status;
- }
+ if (status)
+ goto BAIL;
_cairo_gstate_clip_and_composite_trapezoids (gstate,
gstate->pattern,
gstate->operator,
gstate->surface,
&traps);
+ BAIL:
_cairo_traps_fini (&traps);
_cairo_gstate_new_path (gstate);
- return CAIRO_STATUS_SUCCESS;
+ if (dash) {
+ gstate->dash = dash;
+ gstate->alpha = alpha;
+ }
+
+ return status;
}
cairo_status_t
_cairo_gstate_in_stroke (cairo_gstate_t *gstate,
double x,
double y,
cairo_bool_t *inside_ret)
{
@@ -1695,19 +1741,25 @@ _cairo_gstate_show_page (cairo_gstate_t
cairo_status_t
_cairo_gstate_stroke_extents (cairo_gstate_t *gstate,
double *x1, double *y1,
double *x2, double *y2)
{
cairo_status_t status;
cairo_traps_t traps;
cairo_box_t extents;
+ double *dash = NULL;
+
+ if (_dashes_invisible(gstate)) {
+ dash = gstate->dash;
+ gstate->dash = NULL;
+ }
_cairo_pen_init (&gstate->pen_regular, gstate->line_width / 2.0, gstate);
-
+
_cairo_traps_init (&traps);
status = _cairo_path_stroke_to_traps (&gstate->path, gstate, &traps);
if (status)
goto BAIL;
_cairo_traps_extents (&traps, &extents);
@@ -1716,16 +1768,19 @@ _cairo_gstate_stroke_extents (cairo_gsta
*x2 = _cairo_fixed_to_double (extents.p2.x);
*y2 = _cairo_fixed_to_double (extents.p2.y);
cairo_matrix_transform_point (&gstate->ctm_inverse, x1, y1);
cairo_matrix_transform_point (&gstate->ctm_inverse, x2, y2);
BAIL:
_cairo_traps_fini (&traps);
+
+ if (dash)
+ gstate->dash = dash;
return status;
}
cairo_status_t
_cairo_gstate_fill_extents (cairo_gstate_t *gstate,
double *x1, double *y1,
double *x2, double *y2)
Index: cairo_matrix.c
===================================================================
RCS file: /cvsroot/mozilla/gfx/cairo/cairo/src/cairo_matrix.c,v
retrieving revision 1.2
diff -u -8 -p -r1.2 cairo_matrix.c
--- cairo_matrix.c 23 Mar 2005 19:53:39 -0000 1.2
+++ cairo_matrix.c 31 Mar 2005 19:53:08 -0000
@@ -615,16 +615,39 @@ _cairo_matrix_compute_scale_factors (cai
*sx = minor;
*sy = major;
}
}
return CAIRO_STATUS_SUCCESS;
}
+/* Compute the min/max expansion factors. See the comment in
+ * cairo-pen.c for the derivation */
+cairo_status_t
+_cairo_matrix_compute_expansion_factors (cairo_matrix_t *matrix,
+ double *min, double *max)
+{
+ double a = matrix->m[0][0], c = matrix->m[0][1];
+ double b = matrix->m[1][0], d = matrix->m[1][1];
+
+ double i = a*a + c*c;
+ double j = b*b + d*d;
+
+ double f = 0.5 * (i + j);
+ double g = 0.5 * (i - j);
+ double h = a*b + c*d;
+
+ *max = sqrt (f + sqrt (g*g+h*h));
+
+ *min = sqrt (f - sqrt (g*g+h*h));
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
cairo_bool_t
_cairo_matrix_is_integer_translation(cairo_matrix_t *mat,
int *itx, int *ity)
{
double a, b, c, d, tx, ty;
int ttx, tty;
int ok = 0;
cairo_matrix_get_affine (mat, &a, &b, &c, &d, &tx, &ty);
Index: cairo_pen.c
===================================================================
RCS file: /cvsroot/mozilla/gfx/cairo/cairo/src/cairo_pen.c,v
retrieving revision 1.2
diff -u -8 -p -r1.2 cairo_pen.c
--- cairo_pen.c 23 Mar 2005 19:53:39 -0000 1.2
+++ cairo_pen.c 31 Mar 2005 19:53:08 -0000
@@ -367,42 +367,24 @@ doesn't matter where on the circle the e
*/
static int
_cairo_pen_vertices_needed (double tolerance,
double radius,
cairo_matrix_t *matrix)
{
- double a = matrix->m[0][0], c = matrix->m[0][1];
- double b = matrix->m[1][0], d = matrix->m[1][1];
+ double min, max, major_axis;
+ int num_vertices;
- double i = a*a + c*c;
- double j = b*b + d*d;
-
- double f = 0.5 * (i + j);
- double g = 0.5 * (i - j);
- double h = a*b + c*d;
-
- /*
- * compute major and minor axes lengths for
- * a pen with the specified radius
- */
-
- double major_axis = radius * sqrt (f + sqrt (g*g+h*h));
-
- /*
- * we don't need the minor axis length, which is
- * double min = radius * sqrt (f - sqrt (g*g+h*h));
- */
-
+ _cairo_matrix_compute_expansion_factors (matrix, &min, &max);
+ major_axis = radius * max;
/*
* compute number of vertices needed
*/
- int num_vertices;
/* Where tolerance / M is > 1, we use 4 points */
if (tolerance >= major_axis) {
num_vertices = 4;
} else {
double delta = acos (1 - tolerance / major_axis);
num_vertices = ceil (M_PI / delta);
Index: cairoint.h
===================================================================
RCS file: /cvsroot/mozilla/gfx/cairo/cairo/src/cairoint.h,v
retrieving revision 1.4
diff -u -8 -p -r1.4 cairoint.h
--- cairoint.h 29 Mar 2005 20:53:07 -0000 1.4
+++ cairoint.h 31 Mar 2005 19:53:08 -0000
@@ -853,16 +853,18 @@ typedef struct _cairo_gstate {
cairo_line_join_t line_join;
double miter_limit;
cairo_fill_rule_t fill_rule;
double *dash;
int num_dashes;
double dash_offset;
+ double max_dash_length;
+ double fraction_dash_lit;
char *font_family; /* NULL means CAIRO_FONT_FAMILY_DEFAULT; */
cairo_font_slant_t font_slant;
cairo_font_weight_t font_weight;
cairo_font_t *font; /* Specific to the current CTM */
cairo_surface_t *surface;
@@ -1640,16 +1642,19 @@ cairo_private cairo_status_t
_cairo_matrix_compute_determinant (cairo_matrix_t *matrix, double *det);
cairo_private cairo_status_t
_cairo_matrix_compute_eigen_values (cairo_matrix_t *matrix, double *lambda1, double *lambda2);
cairo_private cairo_status_t
_cairo_matrix_compute_scale_factors (cairo_matrix_t *matrix, double *sx, double *sy, int x_major);
+cairo_private cairo_status_t
+_cairo_matrix_compute_expansion_factors (cairo_matrix_t *matrix, double *min, double *max);
+
cairo_private cairo_bool_t
_cairo_matrix_is_integer_translation(cairo_matrix_t *matrix, int *itx, int *ity);
/* cairo_traps.c */
cairo_private void
_cairo_traps_init (cairo_traps_t *traps);
cairo_private void