forked from KolibriOS/kolibrios
754f9336f0
git-svn-id: svn://kolibrios.org@4349 a494cfbc-eb01-0410-851d-a64ba20cac60
941 lines
29 KiB
C
941 lines
29 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 Andrea Canciani
|
|
*
|
|
* 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 Andrea Canciani.
|
|
*
|
|
* Contributor(s):
|
|
* Andrea Canciani <ranma42@gmail.com>
|
|
*/
|
|
|
|
#include "cairoint.h"
|
|
|
|
#include "cairo-array-private.h"
|
|
#include "cairo-pattern-private.h"
|
|
|
|
/*
|
|
* Rasterizer for mesh patterns.
|
|
*
|
|
* This implementation is based on techniques derived from several
|
|
* papers (available from ACM):
|
|
*
|
|
* - Lien, Shantz and Pratt "Adaptive Forward Differencing for
|
|
* Rendering Curves and Surfaces" (discussion of the AFD technique,
|
|
* bound of 1/sqrt(2) on step length without proof)
|
|
*
|
|
* - Popescu and Rosen, "Forward rasterization" (description of
|
|
* forward rasterization, proof of the previous bound)
|
|
*
|
|
* - Klassen, "Integer Forward Differencing of Cubic Polynomials:
|
|
* Analysis and Algorithms"
|
|
*
|
|
* - Klassen, "Exact Integer Hybrid Subdivision and Forward
|
|
* Differencing of Cubics" (improving the bound on the minimum
|
|
* number of steps)
|
|
*
|
|
* - Chang, Shantz and Rocchetti, "Rendering Cubic Curves and Surfaces
|
|
* with Integer Adaptive Forward Differencing" (analysis of forward
|
|
* differencing applied to Bezier patches)
|
|
*
|
|
* Notes:
|
|
* - Poor performance expected in degenerate cases
|
|
*
|
|
* - Patches mostly outside the drawing area are drawn completely (and
|
|
* clipped), wasting time
|
|
*
|
|
* - Both previous problems are greatly reduced by splitting until a
|
|
* reasonably small size and clipping the new tiles: execution time
|
|
* is quadratic in the convex-hull diameter instead than linear to
|
|
* the painted area. Splitting the tiles doesn't change the painted
|
|
* area but (usually) reduces the bounding box area (bbox area can
|
|
* remain the same after splitting, but cannot grow)
|
|
*
|
|
* - The initial implementation used adaptive forward differencing,
|
|
* but simple forward differencing scored better in benchmarks
|
|
*
|
|
* Idea:
|
|
*
|
|
* We do a sampling over the cubic patch with step du and dv (in the
|
|
* two parameters) that guarantees that any point of our sampling will
|
|
* be at most at 1/sqrt(2) from its adjacent points. In formulae
|
|
* (assuming B is the patch):
|
|
*
|
|
* |B(u,v) - B(u+du,v)| < 1/sqrt(2)
|
|
* |B(u,v) - B(u,v+dv)| < 1/sqrt(2)
|
|
*
|
|
* This means that every pixel covered by the patch will contain at
|
|
* least one of the samples, thus forward rasterization can be
|
|
* performed. Sketch of proof (from Popescu and Rosen):
|
|
*
|
|
* Let's take the P pixel we're interested into. If we assume it to be
|
|
* square, its boundaries define 9 regions on the plane:
|
|
*
|
|
* 1|2|3
|
|
* -+-+-
|
|
* 8|P|4
|
|
* -+-+-
|
|
* 7|6|5
|
|
*
|
|
* Let's check that the pixel P will contain at least one point
|
|
* assuming that it is covered by the patch.
|
|
*
|
|
* Since the pixel is covered by the patch, its center will belong to
|
|
* (at least) one of the quads:
|
|
*
|
|
* {(B(u,v), B(u+du,v), B(u,v+dv), B(u+du,v+dv)) for u,v in [0,1]}
|
|
*
|
|
* If P doesn't contain any of the corners of the quad:
|
|
*
|
|
* - if one of the corners is in 1,3,5 or 7, other two of them have to
|
|
* be in 2,4,6 or 8, thus if the last corner is not in P, the length
|
|
* of one of the edges will be > 1/sqrt(2)
|
|
*
|
|
* - if none of the corners is in 1,3,5 or 7, all of them are in 2,4,6
|
|
* and/or 8. If they are all in different regions, they can't
|
|
* satisfy the distance constraint. If two of them are in the same
|
|
* region (let's say 2), no point is in 6 and again it is impossible
|
|
* to have the center of P in the quad respecting the distance
|
|
* constraint (both these assertions can be checked by continuity
|
|
* considering the length of the edges of a quad with the vertices
|
|
* on the edges of P)
|
|
*
|
|
* Each of the cases led to a contradiction, so P contains at least
|
|
* one of the corners of the quad.
|
|
*/
|
|
|
|
/*
|
|
* Make sure that errors are less than 1 in fixed point math if you
|
|
* change these values.
|
|
*
|
|
* The error is amplified by about steps^3/4 times.
|
|
* The rasterizer always uses a number of steps that is a power of 2.
|
|
*
|
|
* 256 is the maximum allowed number of steps (to have error < 1)
|
|
* using 8.24 for the differences.
|
|
*/
|
|
#define STEPS_MAX_V 256.0
|
|
#define STEPS_MAX_U 256.0
|
|
|
|
/*
|
|
* If the patch/curve is only partially visible, split it to a finer
|
|
* resolution to get higher chances to clip (part of) it.
|
|
*
|
|
* These values have not been computed, but simply obtained
|
|
* empirically (by benchmarking some patches). They should never be
|
|
* greater than STEPS_MAX_V (or STEPS_MAX_U), but they can be as small
|
|
* as 1 (depending on how much you want to spend time in splitting the
|
|
* patch/curve when trying to save some rasterization time).
|
|
*/
|
|
#define STEPS_CLIP_V 64.0
|
|
#define STEPS_CLIP_U 64.0
|
|
|
|
|
|
/* Utils */
|
|
static inline double
|
|
sqlen (cairo_point_double_t p0, cairo_point_double_t p1)
|
|
{
|
|
cairo_point_double_t delta;
|
|
|
|
delta.x = p0.x - p1.x;
|
|
delta.y = p0.y - p1.y;
|
|
|
|
return delta.x * delta.x + delta.y * delta.y;
|
|
}
|
|
|
|
static inline int16_t
|
|
_color_delta_to_shifted_short (int32_t from, int32_t to, int shift)
|
|
{
|
|
int32_t delta = to - from;
|
|
|
|
/* We need to round toward zero, because otherwise adding the
|
|
* delta 2^shift times can overflow */
|
|
if (delta >= 0)
|
|
return delta >> shift;
|
|
else
|
|
return -((-delta) >> shift);
|
|
}
|
|
|
|
/*
|
|
* Convert a number of steps to the equivalent shift.
|
|
*
|
|
* Input: the square of the minimum number of steps
|
|
*
|
|
* Output: the smallest integer x such that 2^x > steps
|
|
*/
|
|
static inline int
|
|
sqsteps2shift (double steps_sq)
|
|
{
|
|
int r;
|
|
frexp (MAX (1.0, steps_sq), &r);
|
|
return (r + 1) >> 1;
|
|
}
|
|
|
|
/*
|
|
* FD functions
|
|
*
|
|
* A Bezier curve is defined (with respect to a parameter t in
|
|
* [0,1]) from its nodes (x,y,z,w) like this:
|
|
*
|
|
* B(t) = x(1-t)^3 + 3yt(1-t)^2 + 3zt^2(1-t) + wt^3
|
|
*
|
|
* To efficiently evaluate a Bezier curve, the rasterizer uses forward
|
|
* differences. Given x, y, z, w (the 4 nodes of the Bezier curve), it
|
|
* is possible to convert them to forward differences form and walk
|
|
* over the curve using fd_init (), fd_down () and fd_fwd ().
|
|
*
|
|
* f[0] is always the value of the Bezier curve for "current" t.
|
|
*/
|
|
|
|
/*
|
|
* Initialize the coefficient for forward differences.
|
|
*
|
|
* Input: x,y,z,w are the 4 nodes of the Bezier curve
|
|
*
|
|
* Output: f[i] is the i-th difference of the curve
|
|
*
|
|
* f[0] is the value of the curve for t==0, i.e. f[0]==x.
|
|
*
|
|
* The initial step is 1; this means that each step increases t by 1
|
|
* (so fd_init () immediately followed by fd_fwd (f) n times makes
|
|
* f[0] be the value of the curve for t==n).
|
|
*/
|
|
static inline void
|
|
fd_init (double x, double y, double z, double w, double f[4])
|
|
{
|
|
f[0] = x;
|
|
f[1] = w - x;
|
|
f[2] = 6. * (w - 2. * z + y);
|
|
f[3] = 6. * (w - 3. * z + 3. * y - x);
|
|
}
|
|
|
|
/*
|
|
* Halve the step of the coefficients for forward differences.
|
|
*
|
|
* Input: f[i] is the i-th difference of the curve
|
|
*
|
|
* Output: f[i] is the i-th difference of the curve with half the
|
|
* original step
|
|
*
|
|
* f[0] is not affected, so the current t is not changed.
|
|
*
|
|
* The other coefficients are changed so that the step is half the
|
|
* original step. This means that doing fd_fwd (f) n times with the
|
|
* input f results in the same f[0] as doing fd_fwd (f) 2n times with
|
|
* the output f.
|
|
*/
|
|
static inline void
|
|
fd_down (double f[4])
|
|
{
|
|
f[3] *= 0.125;
|
|
f[2] = f[2] * 0.25 - f[3];
|
|
f[1] = (f[1] - f[2]) * 0.5;
|
|
}
|
|
|
|
/*
|
|
* Perform one step of forward differences along the curve.
|
|
*
|
|
* Input: f[i] is the i-th difference of the curve
|
|
*
|
|
* Output: f[i] is the i-th difference of the curve after one step
|
|
*/
|
|
static inline void
|
|
fd_fwd (double f[4])
|
|
{
|
|
f[0] += f[1];
|
|
f[1] += f[2];
|
|
f[2] += f[3];
|
|
}
|
|
|
|
/*
|
|
* Transform to integer forward differences.
|
|
*
|
|
* Input: d[n] is the n-th difference (in double precision)
|
|
*
|
|
* Output: i[n] is the n-th difference (in fixed point precision)
|
|
*
|
|
* i[0] is 9.23 fixed point, other differences are 4.28 fixed point.
|
|
*/
|
|
static inline void
|
|
fd_fixed (double d[4], int32_t i[4])
|
|
{
|
|
i[0] = _cairo_fixed_16_16_from_double (256 * 2 * d[0]);
|
|
i[1] = _cairo_fixed_16_16_from_double (256 * 16 * d[1]);
|
|
i[2] = _cairo_fixed_16_16_from_double (256 * 16 * d[2]);
|
|
i[3] = _cairo_fixed_16_16_from_double (256 * 16 * d[3]);
|
|
}
|
|
|
|
/*
|
|
* Perform one step of integer forward differences along the curve.
|
|
*
|
|
* Input: f[n] is the n-th difference
|
|
*
|
|
* Output: f[n] is the n-th difference
|
|
*
|
|
* f[0] is 9.23 fixed point, other differences are 4.28 fixed point.
|
|
*/
|
|
static inline void
|
|
fd_fixed_fwd (int32_t f[4])
|
|
{
|
|
f[0] += (f[1] >> 5) + ((f[1] >> 4) & 1);
|
|
f[1] += f[2];
|
|
f[2] += f[3];
|
|
}
|
|
|
|
/*
|
|
* Compute the minimum number of steps that guarantee that walking
|
|
* over a curve will leave no holes.
|
|
*
|
|
* Input: p[0..3] the nodes of the Bezier curve
|
|
*
|
|
* Returns: the square of the number of steps
|
|
*
|
|
* Idea:
|
|
*
|
|
* We want to make sure that at every step we move by less than
|
|
* 1/sqrt(2).
|
|
*
|
|
* The derivative of the cubic Bezier with nodes (p0, p1, p2, p3) is
|
|
* the quadratic Bezier with nodes (p1-p0, p2-p1, p3-p2) scaled by 3,
|
|
* so (since a Bezier curve is always bounded by its convex hull), we
|
|
* can say that:
|
|
*
|
|
* max(|B'(t)|) <= 3 max (|p1-p0|, |p2-p1|, |p3-p2|)
|
|
*
|
|
* We can improve this by noticing that a quadratic Bezier (a,b,c) is
|
|
* bounded by the quad (a,lerp(a,b,t),lerp(b,c,t),c) for any t, so
|
|
* (substituting the previous values, using t=0.5 and simplifying):
|
|
*
|
|
* max(|B'(t)|) <= 3 max (|p1-p0|, |p2-p0|/2, |p3-p1|/2, |p3-p2|)
|
|
*
|
|
* So, to guarantee a maximum step length of 1/sqrt(2) we must do:
|
|
*
|
|
* 3 max (|p1-p0|, |p2-p0|/2, |p3-p1|/2, |p3-p2|) sqrt(2) steps
|
|
*/
|
|
static inline double
|
|
bezier_steps_sq (cairo_point_double_t p[4])
|
|
{
|
|
double tmp = sqlen (p[0], p[1]);
|
|
tmp = MAX (tmp, sqlen (p[2], p[3]));
|
|
tmp = MAX (tmp, sqlen (p[0], p[2]) * .25);
|
|
tmp = MAX (tmp, sqlen (p[1], p[3]) * .25);
|
|
return 18.0 * tmp;
|
|
}
|
|
|
|
/*
|
|
* Split a 1D Bezier cubic using de Casteljau's algorithm.
|
|
*
|
|
* Input: x,y,z,w the nodes of the Bezier curve
|
|
*
|
|
* Output: x0,y0,z0,w0 and x1,y1,z1,w1 are respectively the nodes of
|
|
* the first half and of the second half of the curve
|
|
*
|
|
* The output control nodes have to be distinct.
|
|
*/
|
|
static inline void
|
|
split_bezier_1D (double x, double y, double z, double w,
|
|
double *x0, double *y0, double *z0, double *w0,
|
|
double *x1, double *y1, double *z1, double *w1)
|
|
{
|
|
double tmp;
|
|
|
|
*x0 = x;
|
|
*w1 = w;
|
|
|
|
tmp = 0.5 * (y + z);
|
|
*y0 = 0.5 * (x + y);
|
|
*z1 = 0.5 * (z + w);
|
|
|
|
*z0 = 0.5 * (*y0 + tmp);
|
|
*y1 = 0.5 * (tmp + *z1);
|
|
|
|
*w0 = *x1 = 0.5 * (*z0 + *y1);
|
|
}
|
|
|
|
/*
|
|
* Split a Bezier curve using de Casteljau's algorithm.
|
|
*
|
|
* Input: p[0..3] the nodes of the Bezier curve
|
|
*
|
|
* Output: fst_half[0..3] and snd_half[0..3] are respectively the
|
|
* nodes of the first and of the second half of the curve
|
|
*
|
|
* fst_half and snd_half must be different, but they can be the same as
|
|
* nodes.
|
|
*/
|
|
static void
|
|
split_bezier (cairo_point_double_t p[4],
|
|
cairo_point_double_t fst_half[4],
|
|
cairo_point_double_t snd_half[4])
|
|
{
|
|
split_bezier_1D (p[0].x, p[1].x, p[2].x, p[3].x,
|
|
&fst_half[0].x, &fst_half[1].x, &fst_half[2].x, &fst_half[3].x,
|
|
&snd_half[0].x, &snd_half[1].x, &snd_half[2].x, &snd_half[3].x);
|
|
|
|
split_bezier_1D (p[0].y, p[1].y, p[2].y, p[3].y,
|
|
&fst_half[0].y, &fst_half[1].y, &fst_half[2].y, &fst_half[3].y,
|
|
&snd_half[0].y, &snd_half[1].y, &snd_half[2].y, &snd_half[3].y);
|
|
}
|
|
|
|
|
|
typedef enum _intersection {
|
|
INSIDE = -1, /* the interval is entirely contained in the reference interval */
|
|
OUTSIDE = 0, /* the interval has no intersection with the reference interval */
|
|
PARTIAL = 1 /* the interval intersects the reference interval (but is not fully inside it) */
|
|
} intersection_t;
|
|
|
|
/*
|
|
* Check if an interval if inside another.
|
|
*
|
|
* Input: a,b are the extrema of the first interval
|
|
* c,d are the extrema of the second interval
|
|
*
|
|
* Returns: INSIDE iff [a,b) intersection [c,d) = [a,b)
|
|
* OUTSIDE iff [a,b) intersection [c,d) = {}
|
|
* PARTIAL otherwise
|
|
*
|
|
* The function assumes a < b and c < d
|
|
*
|
|
* Note: Bitwise-anding the results along each component gives the
|
|
* expected result for [a,b) x [A,B) intersection [c,d) x [C,D).
|
|
*/
|
|
static inline int
|
|
intersect_interval (double a, double b, double c, double d)
|
|
{
|
|
if (c <= a && b <= d)
|
|
return INSIDE;
|
|
else if (a >= d || b <= c)
|
|
return OUTSIDE;
|
|
else
|
|
return PARTIAL;
|
|
}
|
|
|
|
/*
|
|
* Set the color of a pixel.
|
|
*
|
|
* Input: data is the base pointer of the image
|
|
* width, height are the dimensions of the image
|
|
* stride is the stride in bytes between adjacent rows
|
|
* x, y are the coordinates of the pixel to be colored
|
|
* r,g,b,a are the color components of the color to be set
|
|
*
|
|
* Output: the (x,y) pixel in data has the (r,g,b,a) color
|
|
*
|
|
* The input color components are not premultiplied, but the data
|
|
* stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc,
|
|
* premultiplied).
|
|
*
|
|
* If the pixel to be set is outside the image, this function does
|
|
* nothing.
|
|
*/
|
|
static inline void
|
|
draw_pixel (unsigned char *data, int width, int height, int stride,
|
|
int x, int y, uint16_t r, uint16_t g, uint16_t b, uint16_t a)
|
|
{
|
|
if (likely (0 <= x && 0 <= y && x < width && y < height)) {
|
|
uint32_t tr, tg, tb, ta;
|
|
|
|
/* Premultiply and round */
|
|
ta = a;
|
|
tr = r * ta + 0x8000;
|
|
tg = g * ta + 0x8000;
|
|
tb = b * ta + 0x8000;
|
|
|
|
tr += tr >> 16;
|
|
tg += tg >> 16;
|
|
tb += tb >> 16;
|
|
|
|
*((uint32_t*) (data + y*stride + 4*x)) = ((ta << 16) & 0xff000000) |
|
|
((tr >> 8) & 0xff0000) | ((tg >> 16) & 0xff00) | (tb >> 24);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Forward-rasterize a cubic curve using forward differences.
|
|
*
|
|
* Input: data is the base pointer of the image
|
|
* width, height are the dimensions of the image
|
|
* stride is the stride in bytes between adjacent rows
|
|
* ushift is log2(n) if n is the number of desired steps
|
|
* dxu[i], dyu[i] are the x,y forward differences of the curve
|
|
* r0,g0,b0,a0 are the color components of the start point
|
|
* r3,g3,b3,a3 are the color components of the end point
|
|
*
|
|
* Output: data will be changed to have the requested curve drawn in
|
|
* the specified colors
|
|
*
|
|
* The input color components are not premultiplied, but the data
|
|
* stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc,
|
|
* premultiplied).
|
|
*
|
|
* The function draws n+1 pixels, that is from the point at step 0 to
|
|
* the point at step n, both included. This is the discrete equivalent
|
|
* to drawing the curve for values of the interpolation parameter in
|
|
* [0,1] (including both extremes).
|
|
*/
|
|
static inline void
|
|
rasterize_bezier_curve (unsigned char *data, int width, int height, int stride,
|
|
int ushift, double dxu[4], double dyu[4],
|
|
uint16_t r0, uint16_t g0, uint16_t b0, uint16_t a0,
|
|
uint16_t r3, uint16_t g3, uint16_t b3, uint16_t a3)
|
|
{
|
|
int32_t xu[4], yu[4];
|
|
int x0, y0, u, usteps = 1 << ushift;
|
|
|
|
uint16_t r = r0, g = g0, b = b0, a = a0;
|
|
int16_t dr = _color_delta_to_shifted_short (r0, r3, ushift);
|
|
int16_t dg = _color_delta_to_shifted_short (g0, g3, ushift);
|
|
int16_t db = _color_delta_to_shifted_short (b0, b3, ushift);
|
|
int16_t da = _color_delta_to_shifted_short (a0, a3, ushift);
|
|
|
|
fd_fixed (dxu, xu);
|
|
fd_fixed (dyu, yu);
|
|
|
|
/*
|
|
* Use (dxu[0],dyu[0]) as origin for the forward differences.
|
|
*
|
|
* This makes it possible to handle much larger coordinates (the
|
|
* ones that can be represented as cairo_fixed_t)
|
|
*/
|
|
x0 = _cairo_fixed_from_double (dxu[0]);
|
|
y0 = _cairo_fixed_from_double (dyu[0]);
|
|
xu[0] = 0;
|
|
yu[0] = 0;
|
|
|
|
for (u = 0; u <= usteps; ++u) {
|
|
/*
|
|
* This rasterizer assumes that pixels are integer aligned
|
|
* squares, so a generic (x,y) point belongs to the pixel with
|
|
* top-left coordinates (floor(x), floor(y))
|
|
*/
|
|
|
|
int x = _cairo_fixed_integer_floor (x0 + (xu[0] >> 15) + ((xu[0] >> 14) & 1));
|
|
int y = _cairo_fixed_integer_floor (y0 + (yu[0] >> 15) + ((yu[0] >> 14) & 1));
|
|
|
|
draw_pixel (data, width, height, stride, x, y, r, g, b, a);
|
|
|
|
fd_fixed_fwd (xu);
|
|
fd_fixed_fwd (yu);
|
|
r += dr;
|
|
g += dg;
|
|
b += db;
|
|
a += da;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Clip, split and rasterize a Bezier curve.
|
|
*
|
|
* Input: data is the base pointer of the image
|
|
* width, height are the dimensions of the image
|
|
* stride is the stride in bytes between adjacent rows
|
|
* p[i] is the i-th node of the Bezier curve
|
|
* c0[i] is the i-th color component at the start point
|
|
* c3[i] is the i-th color component at the end point
|
|
*
|
|
* Output: data will be changed to have the requested curve drawn in
|
|
* the specified colors
|
|
*
|
|
* The input color components are not premultiplied, but the data
|
|
* stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc,
|
|
* premultiplied).
|
|
*
|
|
* The color components are red, green, blue and alpha, in this order.
|
|
*
|
|
* The function guarantees that it will draw the curve with a step
|
|
* small enough to never have a distance above 1/sqrt(2) between two
|
|
* consecutive points (which is needed to ensure that no hole can
|
|
* appear when using this function to rasterize a patch).
|
|
*/
|
|
static void
|
|
draw_bezier_curve (unsigned char *data, int width, int height, int stride,
|
|
cairo_point_double_t p[4], double c0[4], double c3[4])
|
|
{
|
|
double top, bottom, left, right, steps_sq;
|
|
int i, v;
|
|
|
|
top = bottom = p[0].y;
|
|
for (i = 1; i < 4; ++i) {
|
|
top = MIN (top, p[i].y);
|
|
bottom = MAX (bottom, p[i].y);
|
|
}
|
|
|
|
/* Check visibility */
|
|
v = intersect_interval (top, bottom, 0, height);
|
|
if (v == OUTSIDE)
|
|
return;
|
|
|
|
left = right = p[0].x;
|
|
for (i = 1; i < 4; ++i) {
|
|
left = MIN (left, p[i].x);
|
|
right = MAX (right, p[i].x);
|
|
}
|
|
|
|
v &= intersect_interval (left, right, 0, width);
|
|
if (v == OUTSIDE)
|
|
return;
|
|
|
|
steps_sq = bezier_steps_sq (p);
|
|
if (steps_sq >= (v == INSIDE ? STEPS_MAX_U * STEPS_MAX_U : STEPS_CLIP_U * STEPS_CLIP_U)) {
|
|
/*
|
|
* The number of steps is greater than the threshold. This
|
|
* means that either the error would become too big if we
|
|
* directly rasterized it or that we can probably save some
|
|
* time by splitting the curve and clipping part of it
|
|
*/
|
|
cairo_point_double_t first[4], second[4];
|
|
double midc[4];
|
|
split_bezier (p, first, second);
|
|
midc[0] = (c0[0] + c3[0]) * 0.5;
|
|
midc[1] = (c0[1] + c3[1]) * 0.5;
|
|
midc[2] = (c0[2] + c3[2]) * 0.5;
|
|
midc[3] = (c0[3] + c3[3]) * 0.5;
|
|
draw_bezier_curve (data, width, height, stride, first, c0, midc);
|
|
draw_bezier_curve (data, width, height, stride, second, midc, c3);
|
|
} else {
|
|
double xu[4], yu[4];
|
|
int ushift = sqsteps2shift (steps_sq), k;
|
|
|
|
fd_init (p[0].x, p[1].x, p[2].x, p[3].x, xu);
|
|
fd_init (p[0].y, p[1].y, p[2].y, p[3].y, yu);
|
|
|
|
for (k = 0; k < ushift; ++k) {
|
|
fd_down (xu);
|
|
fd_down (yu);
|
|
}
|
|
|
|
rasterize_bezier_curve (data, width, height, stride, ushift,
|
|
xu, yu,
|
|
_cairo_color_double_to_short (c0[0]),
|
|
_cairo_color_double_to_short (c0[1]),
|
|
_cairo_color_double_to_short (c0[2]),
|
|
_cairo_color_double_to_short (c0[3]),
|
|
_cairo_color_double_to_short (c3[0]),
|
|
_cairo_color_double_to_short (c3[1]),
|
|
_cairo_color_double_to_short (c3[2]),
|
|
_cairo_color_double_to_short (c3[3]));
|
|
|
|
/* Draw the end point, to make sure that we didn't leave it
|
|
* out because of rounding */
|
|
draw_pixel (data, width, height, stride,
|
|
_cairo_fixed_integer_floor (_cairo_fixed_from_double (p[3].x)),
|
|
_cairo_fixed_integer_floor (_cairo_fixed_from_double (p[3].y)),
|
|
_cairo_color_double_to_short (c3[0]),
|
|
_cairo_color_double_to_short (c3[1]),
|
|
_cairo_color_double_to_short (c3[2]),
|
|
_cairo_color_double_to_short (c3[3]));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Forward-rasterize a cubic Bezier patch using forward differences.
|
|
*
|
|
* Input: data is the base pointer of the image
|
|
* width, height are the dimensions of the image
|
|
* stride is the stride in bytes between adjacent rows
|
|
* vshift is log2(n) if n is the number of desired steps
|
|
* p[i][j], p[i][j] are the the nodes of the Bezier patch
|
|
* col[i][j] is the j-th color component of the i-th corner
|
|
*
|
|
* Output: data will be changed to have the requested patch drawn in
|
|
* the specified colors
|
|
*
|
|
* The nodes of the patch are as follows:
|
|
*
|
|
* u\v 0 - > 1
|
|
* 0 p00 p01 p02 p03
|
|
* | p10 p11 p12 p13
|
|
* v p20 p21 p22 p23
|
|
* 1 p30 p31 p32 p33
|
|
*
|
|
* i.e. u varies along the first component (rows), v varies along the
|
|
* second one (columns).
|
|
*
|
|
* The color components are red, green, blue and alpha, in this order.
|
|
* c[0..3] are the colors in p00, p30, p03, p33 respectively
|
|
*
|
|
* The input color components are not premultiplied, but the data
|
|
* stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc,
|
|
* premultiplied).
|
|
*
|
|
* If the patch folds over itself, the part with the highest v
|
|
* parameter is considered above. If both have the same v, the one
|
|
* with the highest u parameter is above.
|
|
*
|
|
* The function draws n+1 curves, that is from the curve at step 0 to
|
|
* the curve at step n, both included. This is the discrete equivalent
|
|
* to drawing the patch for values of the interpolation parameter in
|
|
* [0,1] (including both extremes).
|
|
*/
|
|
static inline void
|
|
rasterize_bezier_patch (unsigned char *data, int width, int height, int stride, int vshift,
|
|
cairo_point_double_t p[4][4], double col[4][4])
|
|
{
|
|
double pv[4][2][4], cstart[4], cend[4], dcstart[4], dcend[4];
|
|
int vsteps, v, i, k;
|
|
|
|
vsteps = 1 << vshift;
|
|
|
|
/*
|
|
* pv[i][0] is the function (represented using forward
|
|
* differences) mapping v to the x coordinate of the i-th node of
|
|
* the Bezier curve with parameter u.
|
|
* (Likewise p[i][0] gives the y coordinate).
|
|
*
|
|
* This means that (pv[0][0][0],pv[0][1][0]),
|
|
* (pv[1][0][0],pv[1][1][0]), (pv[2][0][0],pv[2][1][0]) and
|
|
* (pv[3][0][0],pv[3][1][0]) are the nodes of the Bezier curve for
|
|
* the "current" v value (see the FD comments for more details).
|
|
*/
|
|
for (i = 0; i < 4; ++i) {
|
|
fd_init (p[i][0].x, p[i][1].x, p[i][2].x, p[i][3].x, pv[i][0]);
|
|
fd_init (p[i][0].y, p[i][1].y, p[i][2].y, p[i][3].y, pv[i][1]);
|
|
for (k = 0; k < vshift; ++k) {
|
|
fd_down (pv[i][0]);
|
|
fd_down (pv[i][1]);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < 4; ++i) {
|
|
cstart[i] = col[0][i];
|
|
cend[i] = col[1][i];
|
|
dcstart[i] = (col[2][i] - col[0][i]) / vsteps;
|
|
dcend[i] = (col[3][i] - col[1][i]) / vsteps;
|
|
}
|
|
|
|
for (v = 0; v <= vsteps; ++v) {
|
|
cairo_point_double_t nodes[4];
|
|
for (i = 0; i < 4; ++i) {
|
|
nodes[i].x = pv[i][0][0];
|
|
nodes[i].y = pv[i][1][0];
|
|
}
|
|
|
|
draw_bezier_curve (data, width, height, stride, nodes, cstart, cend);
|
|
|
|
for (i = 0; i < 4; ++i) {
|
|
fd_fwd (pv[i][0]);
|
|
fd_fwd (pv[i][1]);
|
|
cstart[i] += dcstart[i];
|
|
cend[i] += dcend[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Clip, split and rasterize a Bezier cubic patch.
|
|
*
|
|
* Input: data is the base pointer of the image
|
|
* width, height are the dimensions of the image
|
|
* stride is the stride in bytes between adjacent rows
|
|
* p[i][j], p[i][j] are the nodes of the patch
|
|
* col[i][j] is the j-th color component of the i-th corner
|
|
*
|
|
* Output: data will be changed to have the requested patch drawn in
|
|
* the specified colors
|
|
*
|
|
* The nodes of the patch are as follows:
|
|
*
|
|
* u\v 0 - > 1
|
|
* 0 p00 p01 p02 p03
|
|
* | p10 p11 p12 p13
|
|
* v p20 p21 p22 p23
|
|
* 1 p30 p31 p32 p33
|
|
*
|
|
* i.e. u varies along the first component (rows), v varies along the
|
|
* second one (columns).
|
|
*
|
|
* The color components are red, green, blue and alpha, in this order.
|
|
* c[0..3] are the colors in p00, p30, p03, p33 respectively
|
|
*
|
|
* The input color components are not premultiplied, but the data
|
|
* stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc,
|
|
* premultiplied).
|
|
*
|
|
* If the patch folds over itself, the part with the highest v
|
|
* parameter is considered above. If both have the same v, the one
|
|
* with the highest u parameter is above.
|
|
*
|
|
* The function guarantees that it will draw the patch with a step
|
|
* small enough to never have a distance above 1/sqrt(2) between two
|
|
* adjacent points (which guarantees that no hole can appear).
|
|
*
|
|
* This function can be used to rasterize a tile of PDF type 7
|
|
* shadings (see http://www.adobe.com/devnet/pdf/pdf_reference.html).
|
|
*/
|
|
static void
|
|
draw_bezier_patch (unsigned char *data, int width, int height, int stride,
|
|
cairo_point_double_t p[4][4], double c[4][4])
|
|
{
|
|
double top, bottom, left, right, steps_sq;
|
|
int i, j, v;
|
|
|
|
top = bottom = p[0][0].y;
|
|
for (i = 0; i < 4; ++i) {
|
|
for (j= 0; j < 4; ++j) {
|
|
top = MIN (top, p[i][j].y);
|
|
bottom = MAX (bottom, p[i][j].y);
|
|
}
|
|
}
|
|
|
|
v = intersect_interval (top, bottom, 0, height);
|
|
if (v == OUTSIDE)
|
|
return;
|
|
|
|
left = right = p[0][0].x;
|
|
for (i = 0; i < 4; ++i) {
|
|
for (j= 0; j < 4; ++j) {
|
|
left = MIN (left, p[i][j].x);
|
|
right = MAX (right, p[i][j].x);
|
|
}
|
|
}
|
|
|
|
v &= intersect_interval (left, right, 0, width);
|
|
if (v == OUTSIDE)
|
|
return;
|
|
|
|
steps_sq = 0;
|
|
for (i = 0; i < 4; ++i)
|
|
steps_sq = MAX (steps_sq, bezier_steps_sq (p[i]));
|
|
|
|
if (steps_sq >= (v == INSIDE ? STEPS_MAX_V * STEPS_MAX_V : STEPS_CLIP_V * STEPS_CLIP_V)) {
|
|
/* The number of steps is greater than the threshold. This
|
|
* means that either the error would become too big if we
|
|
* directly rasterized it or that we can probably save some
|
|
* time by splitting the curve and clipping part of it. The
|
|
* patch is only split in the v direction to guarantee that
|
|
* rasterizing each part will overwrite parts with low v with
|
|
* overlapping parts with higher v. */
|
|
|
|
cairo_point_double_t first[4][4], second[4][4];
|
|
double subc[4][4];
|
|
|
|
for (i = 0; i < 4; ++i)
|
|
split_bezier (p[i], first[i], second[i]);
|
|
|
|
for (i = 0; i < 4; ++i) {
|
|
subc[0][i] = c[0][i];
|
|
subc[1][i] = c[1][i];
|
|
subc[2][i] = 0.5 * (c[0][i] + c[2][i]);
|
|
subc[3][i] = 0.5 * (c[1][i] + c[3][i]);
|
|
}
|
|
|
|
draw_bezier_patch (data, width, height, stride, first, subc);
|
|
|
|
for (i = 0; i < 4; ++i) {
|
|
subc[0][i] = subc[2][i];
|
|
subc[1][i] = subc[3][i];
|
|
subc[2][i] = c[2][i];
|
|
subc[3][i] = c[3][i];
|
|
}
|
|
draw_bezier_patch (data, width, height, stride, second, subc);
|
|
} else {
|
|
rasterize_bezier_patch (data, width, height, stride, sqsteps2shift (steps_sq), p, c);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Draw a tensor product shading pattern.
|
|
*
|
|
* Input: mesh is the mesh pattern
|
|
* data is the base pointer of the image
|
|
* width, height are the dimensions of the image
|
|
* stride is the stride in bytes between adjacent rows
|
|
*
|
|
* Output: data will be changed to have the pattern drawn on it
|
|
*
|
|
* data is assumed to be clear and its content is assumed to be in
|
|
* CAIRO_FORMAT_ARGB32 (8 bpc, premultiplied).
|
|
*
|
|
* This function can be used to rasterize a PDF type 7 shading (see
|
|
* http://www.adobe.com/devnet/pdf/pdf_reference.html).
|
|
*/
|
|
void
|
|
_cairo_mesh_pattern_rasterize (const cairo_mesh_pattern_t *mesh,
|
|
void *data,
|
|
int width,
|
|
int height,
|
|
int stride,
|
|
double x_offset,
|
|
double y_offset)
|
|
{
|
|
cairo_point_double_t nodes[4][4];
|
|
double colors[4][4];
|
|
cairo_matrix_t p2u;
|
|
unsigned int i, j, k, n;
|
|
cairo_status_t status;
|
|
const cairo_mesh_patch_t *patch;
|
|
const cairo_color_t *c;
|
|
|
|
assert (mesh->base.status == CAIRO_STATUS_SUCCESS);
|
|
assert (mesh->current_patch == NULL);
|
|
|
|
p2u = mesh->base.matrix;
|
|
status = cairo_matrix_invert (&p2u);
|
|
assert (status == CAIRO_STATUS_SUCCESS);
|
|
|
|
n = _cairo_array_num_elements (&mesh->patches);
|
|
patch = _cairo_array_index_const (&mesh->patches, 0);
|
|
for (i = 0; i < n; i++) {
|
|
for (j = 0; j < 4; j++) {
|
|
for (k = 0; k < 4; k++) {
|
|
nodes[j][k] = patch->points[j][k];
|
|
cairo_matrix_transform_point (&p2u, &nodes[j][k].x, &nodes[j][k].y);
|
|
nodes[j][k].x += x_offset;
|
|
nodes[j][k].y += y_offset;
|
|
}
|
|
}
|
|
|
|
c = &patch->colors[0];
|
|
colors[0][0] = c->red;
|
|
colors[0][1] = c->green;
|
|
colors[0][2] = c->blue;
|
|
colors[0][3] = c->alpha;
|
|
|
|
c = &patch->colors[3];
|
|
colors[1][0] = c->red;
|
|
colors[1][1] = c->green;
|
|
colors[1][2] = c->blue;
|
|
colors[1][3] = c->alpha;
|
|
|
|
c = &patch->colors[1];
|
|
colors[2][0] = c->red;
|
|
colors[2][1] = c->green;
|
|
colors[2][2] = c->blue;
|
|
colors[2][3] = c->alpha;
|
|
|
|
c = &patch->colors[2];
|
|
colors[3][0] = c->red;
|
|
colors[3][1] = c->green;
|
|
colors[3][2] = c->blue;
|
|
colors[3][3] = c->alpha;
|
|
|
|
draw_bezier_patch (data, width, height, stride, nodes, colors);
|
|
patch++;
|
|
}
|
|
}
|