pdf,ps: Add native mesh pattern support

PS and PDF have native support for mesh patterns, but they have encode
mesh points and colors in an appropriate binary stream.

cairo_pdf_shading_* functions implement the encoding, which is the
same for PDF and PS.
This commit is contained in:
Adrian Johnson 2010-11-17 18:45:22 +01:00 committed by Andrea Canciani
parent 8df122cb4b
commit 30636206b0
5 changed files with 614 additions and 3 deletions

View file

@ -200,8 +200,8 @@ cairo_egl_sources =
cairo_glx_sources =
cairo_wgl_sources =
_cairo_pdf_operators_private = cairo-pdf-operators-private.h
_cairo_pdf_operators_sources = cairo-pdf-operators.c
_cairo_pdf_operators_private = cairo-pdf-operators-private.h cairo-pdf-shading-private.h
_cairo_pdf_operators_sources = cairo-pdf-operators.c cairo-pdf-shading.c
cairo_private += $(_cairo_pdf_operators_private)
cairo_sources += $(_cairo_pdf_operators_sources)

View file

@ -0,0 +1,99 @@
/* -*- 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 Adrian Johnson
*
* 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 Adrian Johnson.
*
* Contributor(s):
* Adrian Johnson <ajohnson@redneon.com>
*/
#ifndef CAIRO_PDF_SHADING_H
#define CAIRO_PDF_SHADING_H
#include "cairo-compiler-private.h"
#include "cairo-types-private.h"
typedef struct _cairo_pdf_shading {
int shading_type;
int bits_per_coordinate;
int bits_per_component;
int bits_per_flag;
double *decode_array;
int decode_array_length;
unsigned char *data;
unsigned long data_length;
} cairo_pdf_shading_t;
/**
* _cairo_pdf_shading_init_color:
* @shading: a #cairo_pdf_shading_t to initialize
* @pattern: the #cairo_mesh_pattern_t to initialize from
*
* Generate the PDF shading dictionary data for the a PDF type 7
* shading from RGB part of the specified mesh pattern.
*
* Return value: %CAIRO_STATUS_SUCCESS if successful, possible errors
* include %CAIRO_STATUS_NO_MEMORY.
**/
cairo_private cairo_status_t
_cairo_pdf_shading_init_color (cairo_pdf_shading_t *shading,
const cairo_mesh_pattern_t *pattern);
/**
* _cairo_pdf_shading_init_alpha:
* @shading: a #cairo_pdf_shading_t to initialize
* @pattern: the #cairo_mesh_pattern_t to initialize from
*
* Generate the PDF shading dictionary data for a PDF type 7
* shading from alpha part of the specified mesh pattern.
*
* Return value: %CAIRO_STATUS_SUCCESS if successful, possible errors
* include %CAIRO_STATUS_NO_MEMORY.
**/
cairo_private cairo_status_t
_cairo_pdf_shading_init_alpha (cairo_pdf_shading_t *shading,
const cairo_mesh_pattern_t *pattern);
/**
* _cairo_pdf_shading_fini:
* @shading: a #cairo_pdf_shading_t
*
* Free all resources associated with @shading. After this call,
* @shading should not be used again without a subsequent call to
* _cairo_pdf_shading_init() again first.
**/
cairo_private void
_cairo_pdf_shading_fini (cairo_pdf_shading_t *shading);
#endif /* CAIRO_PDF_SHADING_H */

277
src/cairo-pdf-shading.c Normal file
View file

@ -0,0 +1,277 @@
/* -*- 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 Adrian Johnson
*
* 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 Adrian Johnson.
*
* Contributor(s):
* Adrian Johnson <ajohnson@redneon.com>
*/
#include "cairoint.h"
#if CAIRO_HAS_PDF_OPERATORS
#include "cairo-pdf-shading-private.h"
#include "cairo-error-private.h"
#include <float.h>
static unsigned char *
encode_coordinate (unsigned char *p, double c)
{
uint32_t f;
f = c;
*p++ = f >> 24;
*p++ = (f >> 16) & 0xff;
*p++ = (f >> 8) & 0xff;
*p++ = f & 0xff;
return p;
}
static unsigned char *
encode_point (unsigned char *p, const cairo_point_double_t *point)
{
p = encode_coordinate (p, point->x);
p = encode_coordinate (p, point->y);
return p;
}
static unsigned char *
encode_color_component (unsigned char *p, double color)
{
uint16_t c;
c = _cairo_color_double_to_short (color);
*p++ = c >> 8;
*p++ = c & 0xff;
return p;
}
static unsigned char *
encode_color (unsigned char *p, const cairo_color_t *color)
{
p = encode_color_component (p, color->red);
p = encode_color_component (p, color->green);
p = encode_color_component (p, color->blue);
return p;
}
static unsigned char *
encode_alpha (unsigned char *p, const cairo_color_t *color)
{
p = encode_color_component (p, color->alpha);
return p;
}
static cairo_status_t
_cairo_pdf_shading_generate_decode_array (cairo_pdf_shading_t *shading,
const cairo_mesh_pattern_t *mesh,
cairo_bool_t is_alpha)
{
unsigned int num_color_components, i;
cairo_bool_t is_valid;
if (is_alpha)
num_color_components = 1;
else
num_color_components = 3;
shading->decode_array_length = 4 + num_color_components * 2;
shading->decode_array = _cairo_malloc_ab (shading->decode_array_length,
sizeof (double));
if (unlikely (shading->decode_array == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
is_valid = _cairo_mesh_pattern_coord_box (mesh,
&shading->decode_array[0],
&shading->decode_array[2],
&shading->decode_array[1],
&shading->decode_array[3]);
assert (is_valid);
assert (shading->decode_array[1] - shading->decode_array[0] >= DBL_EPSILON);
assert (shading->decode_array[3] - shading->decode_array[2] >= DBL_EPSILON);
for (i = 0; i < num_color_components; i++) {
shading->decode_array[4 + 2*i] = 0;
shading->decode_array[5 + 2*i] = 1;
}
return CAIRO_STATUS_SUCCESS;
}
/* The ISO32000 specification mandates this order for the points which
* define the patch. */
static const int pdf_points_order_i[16] = {
0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 2, 1, 1, 1, 2, 2 };
static const int pdf_points_order_j[16] = {
0, 1, 2, 3, 3, 3, 3, 2, 1, 0, 0, 0, 1, 2, 2, 1 };
static cairo_status_t
_cairo_pdf_shading_generate_data (cairo_pdf_shading_t *shading,
const cairo_mesh_pattern_t *mesh,
cairo_bool_t is_alpha)
{
const cairo_mesh_patch_t *patch;
double x_off, y_off, x_scale, y_scale;
unsigned int num_patches;
unsigned int num_color_components;
unsigned char *p;
unsigned int i, j;
if (is_alpha)
num_color_components = 1;
else
num_color_components = 3;
num_patches = _cairo_array_num_elements (&mesh->patches);
patch = _cairo_array_index_const (&mesh->patches, 0);
/* Each patch requires:
*
* 1 flag - 1 byte
* 16 points. Each point is 2 coordinates. Each coordinate is
* stored in 4 bytes.
*
* 4 colors. Each color is stored in 2 bytes * num_color_components.
*/
shading->data_length = num_patches * (1 + 16 * 2 * 4 + 4 * 2 * num_color_components);
shading->data = malloc (shading->data_length);
if (unlikely (shading->data == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
x_off = shading->decode_array[0];
y_off = shading->decode_array[2];
x_scale = UINT32_MAX / (shading->decode_array[1] - x_off);
y_scale = UINT32_MAX / (shading->decode_array[3] - y_off);
p = shading->data;
for (i = 0; i < num_patches; i++) {
/* edge flag */
*p++ = 0;
/* 16 points */
for (j = 0; j < 16; j++) {
cairo_point_double_t point;
int pi, pj;
pi = pdf_points_order_i[j];
pj = pdf_points_order_j[j];
point = patch[i].points[pi][pj];
/* Transform the point as specified in the decode array */
point.x -= x_off;
point.y -= y_off;
point.x *= x_scale;
point.y *= y_scale;
/* Make sure that rounding errors don't cause
* wraparounds */
point.x = _cairo_restrict_value (point.x, 0, UINT32_MAX);
point.y = _cairo_restrict_value (point.y, 0, UINT32_MAX);
p = encode_point (p, &point);
}
/* 4 colors */
for (j = 0; j < 4; j++) {
if (is_alpha)
p = encode_alpha (p, &patch[i].colors[j]);
else
p = encode_color (p, &patch[i].colors[j]);
}
}
assert (p == shading->data + shading->data_length);
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_pdf_shading_init (cairo_pdf_shading_t *shading,
const cairo_mesh_pattern_t *mesh,
cairo_bool_t is_alpha)
{
cairo_status_t status;
assert (mesh->base.status == CAIRO_STATUS_SUCCESS);
assert (mesh->current_patch == NULL);
shading->shading_type = 7;
/*
* Coordinates from the minimum to the maximum value of the mesh
* map to the [0..UINT32_MAX] range and are represented as
* uint32_t values.
*
* Color components are represented as uint16_t (in a 0.16 fixed
* point format, as in the rest of cairo).
*/
shading->bits_per_coordinate = 32;
shading->bits_per_component = 16;
shading->bits_per_flag = 8;
shading->decode_array = NULL;
shading->data = NULL;
status = _cairo_pdf_shading_generate_decode_array (shading, mesh, is_alpha);
if (unlikely (status))
return status;
return _cairo_pdf_shading_generate_data (shading, mesh, is_alpha);
}
cairo_status_t
_cairo_pdf_shading_init_color (cairo_pdf_shading_t *shading,
const cairo_mesh_pattern_t *pattern)
{
return _cairo_pdf_shading_init (shading, pattern, FALSE);
}
cairo_status_t
_cairo_pdf_shading_init_alpha (cairo_pdf_shading_t *shading,
const cairo_mesh_pattern_t *pattern)
{
return _cairo_pdf_shading_init (shading, pattern, TRUE);
}
void
_cairo_pdf_shading_fini (cairo_pdf_shading_t *shading)
{
free (shading->data);
free (shading->decode_array);
}
#endif /* CAIRO_HAS_PDF_OPERATORS */

View file

@ -44,6 +44,7 @@
#include "cairo-pdf.h"
#include "cairo-pdf-surface-private.h"
#include "cairo-pdf-operators-private.h"
#include "cairo-pdf-shading-private.h"
#include "cairo-analysis-surface-private.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-error-private.h"
@ -1275,7 +1276,8 @@ _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface,
/* gradient patterns require an smask object to implement transparency */
if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR ||
pattern->type == CAIRO_PATTERN_TYPE_RADIAL)
pattern->type == CAIRO_PATTERN_TYPE_RADIAL ||
pattern->type == CAIRO_PATTERN_TYPE_MESH)
{
double min_alpha;
@ -3339,6 +3341,150 @@ _cairo_pdf_surface_emit_gradient (cairo_pdf_surface_t *surface,
return _cairo_output_stream_get_status (surface->output);
}
static cairo_status_t
_cairo_pdf_surface_emit_mesh_pattern (cairo_pdf_surface_t *surface,
cairo_pdf_pattern_t *pdf_pattern)
{
cairo_matrix_t pat_to_pdf;
cairo_status_t status;
cairo_pattern_t *pattern = pdf_pattern->pattern;
cairo_pdf_shading_t shading;
int i;
cairo_pdf_resource_t res;
pat_to_pdf = pattern->matrix;
status = cairo_matrix_invert (&pat_to_pdf);
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_STATUS_SUCCESS);
cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf);
status = _cairo_pdf_shading_init_color (&shading, (cairo_mesh_pattern_t *) pattern);
if (unlikely (status))
return status;
res = _cairo_pdf_surface_new_object (surface);
if (unlikely (res.id == 0))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /ShadingType %d\n"
" /ColorSpace /DeviceRGB\n"
" /BitsPerCoordinate %d\n"
" /BitsPerComponent %d\n"
" /BitsPerFlag %d\n"
" /Decode [",
res.id,
shading.shading_type,
shading.bits_per_coordinate,
shading.bits_per_component,
shading.bits_per_flag);
for (i = 0; i < shading.decode_array_length; i++)
_cairo_output_stream_printf (surface->output, "%f ", shading.decode_array[i]);
_cairo_output_stream_printf (surface->output,
"]\n"
" /Length %ld\n"
">>\n"
"stream\n",
shading.data_length);
_cairo_output_stream_write (surface->output, shading.data, shading.data_length);
_cairo_output_stream_printf (surface->output,
"\nendstream\n"
"endobj\n");
_cairo_pdf_shading_fini (&shading);
_cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res);
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Type /Pattern\n"
" /PatternType 2\n"
" /Matrix [ %f %f %f %f %f %f ]\n"
" /Shading %d 0 R\n"
">>\n"
"endobj\n",
pdf_pattern->pattern_res.id,
pat_to_pdf.xx, pat_to_pdf.yx,
pat_to_pdf.xy, pat_to_pdf.yy,
pat_to_pdf.x0, pat_to_pdf.y0,
res.id);
if (pdf_pattern->gstate_res.id != 0) {
cairo_pdf_resource_t mask_resource;
/* Create pattern for SMask. */
res = _cairo_pdf_surface_new_object (surface);
if (unlikely (res.id == 0))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
status = _cairo_pdf_shading_init_alpha (&shading, (cairo_mesh_pattern_t *) pattern);
if (unlikely (status))
return status;
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /ShadingType %d\n"
" /ColorSpace /DeviceGray\n"
" /BitsPerCoordinate %d\n"
" /BitsPerComponent %d\n"
" /BitsPerFlag %d\n"
" /Decode [",
res.id,
shading.shading_type,
shading.bits_per_coordinate,
shading.bits_per_component,
shading.bits_per_flag);
for (i = 0; i < shading.decode_array_length; i++)
_cairo_output_stream_printf (surface->output, "%f ", shading.decode_array[i]);
_cairo_output_stream_printf (surface->output,
"]\n"
" /Length %ld\n"
">>\n"
"stream\n",
shading.data_length);
_cairo_output_stream_write (surface->output, shading.data, shading.data_length);
_cairo_output_stream_printf (surface->output,
"\nendstream\n"
"endobj\n");
_cairo_pdf_shading_fini (&shading);
mask_resource = _cairo_pdf_surface_new_object (surface);
if (unlikely (mask_resource.id == 0))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Type /Pattern\n"
" /PatternType 2\n"
" /Matrix [ %f %f %f %f %f %f ]\n"
" /Shading %d 0 R\n"
">>\n"
"endobj\n",
mask_resource.id,
pat_to_pdf.xx, pat_to_pdf.yx,
pat_to_pdf.xy, pat_to_pdf.yy,
pat_to_pdf.x0, pat_to_pdf.y0,
res.id);
status = cairo_pdf_surface_emit_transparency_group (surface,
pdf_pattern->gstate_res,
mask_resource);
if (unlikely (status))
return status;
}
return _cairo_output_stream_get_status (surface->output);
}
static cairo_status_t
_cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern_t *pdf_pattern)
{
@ -3366,6 +3512,10 @@ _cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern
status = _cairo_pdf_surface_emit_gradient (surface, pdf_pattern);
break;
case CAIRO_PATTERN_TYPE_MESH:
status = _cairo_pdf_surface_emit_mesh_pattern (surface, pdf_pattern);
break;
default:
ASSERT_NOT_REACHED;
status = _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
@ -5379,6 +5529,7 @@ _pattern_supported (const cairo_pattern_t *pattern)
case CAIRO_PATTERN_TYPE_SOLID:
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
case CAIRO_PATTERN_TYPE_MESH:
return TRUE;
case CAIRO_PATTERN_TYPE_SURFACE:

View file

@ -58,6 +58,7 @@
#include "cairo-ps.h"
#include "cairo-ps-surface-private.h"
#include "cairo-pdf-operators-private.h"
#include "cairo-pdf-shading-private.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-error-private.h"
#include "cairo-scaled-font-subsets-private.h"
@ -1750,6 +1751,7 @@ pattern_supported (cairo_ps_surface_t *surface, const cairo_pattern_t *pattern)
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
case CAIRO_PATTERN_TYPE_MESH:
return _gradient_pattern_supported (surface, pattern);
case CAIRO_PATTERN_TYPE_SURFACE:
@ -3372,6 +3374,81 @@ _cairo_ps_surface_emit_gradient (cairo_ps_surface_t *surface,
return status;
}
static cairo_status_t
_cairo_ps_surface_emit_mesh_pattern (cairo_ps_surface_t *surface,
cairo_mesh_pattern_t *pattern)
{
cairo_matrix_t pat_to_ps;
cairo_status_t status;
cairo_pdf_shading_t shading;
unsigned char *data_compressed;
unsigned long data_compressed_size;
int i;
if (_cairo_array_num_elements (&pattern->patches) == 0)
return CAIRO_INT_STATUS_NOTHING_TO_DO;
pat_to_ps = pattern->base.matrix;
status = cairo_matrix_invert (&pat_to_ps);
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_STATUS_SUCCESS);
cairo_matrix_multiply (&pat_to_ps, &pat_to_ps, &surface->cairo_to_ps);
status = _cairo_pdf_shading_init_color (&shading, pattern);
if (unlikely (status))
return status;
_cairo_output_stream_printf (surface->stream,
"<< /PatternType 2\n"
" /Shading\n"
" << /ShadingType %d\n"
" /ColorSpace /DeviceRGB\n"
" /DataSource currentfile /ASCII85Decode filter /LZWDecode filter\n"
" /BitsPerCoordinate %d\n"
" /BitsPerComponent %d\n"
" /BitsPerFlag %d\n"
" /Decode [",
shading.shading_type,
shading.bits_per_coordinate,
shading.bits_per_component,
shading.bits_per_flag);
for (i = 0; i < shading.decode_array_length; i++)
_cairo_output_stream_printf (surface->stream, "%f ", shading.decode_array[i]);
_cairo_output_stream_printf (surface->stream,
"]\n"
" >>\n"
">>\n");
_cairo_output_stream_printf (surface->stream,
"[ %f %f %f %f %f %f ]\n",
pat_to_ps.xx, pat_to_ps.yx,
pat_to_ps.xy, pat_to_ps.yy,
pat_to_ps.x0, pat_to_ps.y0);
_cairo_output_stream_printf (surface->stream,
"makepattern\n");
data_compressed_size = shading.data_length;
data_compressed = _cairo_lzw_compress (shading.data, &data_compressed_size);
if (unlikely (data_compressed == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
status = _cairo_ps_surface_emit_base85_string (surface,
data_compressed,
data_compressed_size,
FALSE);
_cairo_output_stream_printf (surface->stream,
"\n"
"setpattern\n");
free (data_compressed);
_cairo_pdf_shading_fini (&shading);
return status;
}
static cairo_status_t
_cairo_ps_surface_emit_pattern (cairo_ps_surface_t *surface,
const cairo_pattern_t *pattern,
@ -3430,6 +3507,13 @@ _cairo_ps_surface_emit_pattern (cairo_ps_surface_t *surface,
if (unlikely (status))
return status;
break;
case CAIRO_PATTERN_TYPE_MESH:
status = _cairo_ps_surface_emit_mesh_pattern (surface,
(cairo_mesh_pattern_t *) pattern);
if (unlikely (status))
return status;
break;
}
return CAIRO_STATUS_SUCCESS;