Created
December 4, 2015 11:18
-
-
Save hiroyuki-sato/bee0d26d8353588df50a to your computer and use it in GitHub Desktop.
poppler debug
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
UPDATE BLEND DEBUG CairoOutputDev.cc:299 | |
DEBUG CairoOutputDev.cc:570 | |
blend mode: 0 | |
CairoOutputDev.cc:311 | |
restore | |
UPDATE BLEND DEBUG CairoOutputDev.cc:299 | |
DEBUG CairoOutputDev.cc:570 | |
blend mode: 0 | |
CairoOutputDev.cc:311 | |
restore | |
UPDATE BLEND DEBUG CairoOutputDev.cc:299 | |
DEBUG CairoOutputDev.cc:570 | |
blend mode: 0 | |
CairoOutputDev.cc:311 | |
save | |
close | |
clip | |
fill color: 3598 8224 12336 | |
save | |
line width: 1.000000 | |
updateFont() font=PHSZHE+HiraginoSans-W6 | |
font matrix: 18.000000 0.000000 -0.000000 18.000000 | |
DEBUG CairoOutputDev.cc:1224 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1282 | |
fill string | |
restore | |
UPDATE BLEND DEBUG CairoOutputDev.cc:299 | |
DEBUG CairoOutputDev.cc:570 | |
blend mode: 0 | |
CairoOutputDev.cc:311 | |
save | |
line width: 1.000000 | |
updateFont() font=PHSZHE+HiraginoSans-W6 | |
font matrix: 18.000000 0.000000 -0.000000 18.000000 | |
DEBUG CairoOutputDev.cc:1224 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1282 | |
fill string | |
restore | |
UPDATE BLEND DEBUG CairoOutputDev.cc:299 | |
DEBUG CairoOutputDev.cc:570 | |
blend mode: 0 | |
CairoOutputDev.cc:311 | |
save | |
line width: 1.000000 | |
updateFont() font=PHSZHE+HiraginoSans-W6 | |
font matrix: 18.000000 0.000000 -0.000000 18.000000 | |
DEBUG CairoOutputDev.cc:1224 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1282 | |
fill string | |
DEBUG CairoOutputDev.cc:1224 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1282 | |
fill string | |
restore | |
UPDATE BLEND DEBUG CairoOutputDev.cc:299 | |
DEBUG CairoOutputDev.cc:570 | |
blend mode: 0 | |
CairoOutputDev.cc:311 | |
save | |
line width: 1.000000 | |
updateFont() font=PHSZHE+HiraginoSans-W6 | |
font matrix: 18.000000 0.000000 -0.000000 18.000000 | |
DEBUG CairoOutputDev.cc:1224 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1282 | |
fill string | |
restore | |
UPDATE BLEND DEBUG CairoOutputDev.cc:299 | |
DEBUG CairoOutputDev.cc:570 | |
blend mode: 0 | |
CairoOutputDev.cc:311 | |
save | |
line width: 1.000000 | |
updateFont() font=PHSZHE+HiraginoSans-W6 | |
font matrix: 18.000000 0.000000 -0.000000 18.000000 | |
DEBUG CairoOutputDev.cc:1224 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1282 | |
fill string | |
restore | |
UPDATE BLEND DEBUG CairoOutputDev.cc:299 | |
DEBUG CairoOutputDev.cc:570 | |
blend mode: 0 | |
CairoOutputDev.cc:311 | |
save | |
line width: 1.000000 | |
updateFont() font=PHSZHE+HiraginoSans-W6 | |
font matrix: 18.000000 0.000000 -0.000000 18.000000 | |
DEBUG CairoOutputDev.cc:1224 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1247 | |
DEBUG CairoOutputDev.cc:1282 | |
fill string | |
restore | |
UPDATE BLEND DEBUG CairoOutputDev.cc:299 | |
DEBUG CairoOutputDev.cc:570 | |
blend mode: 0 | |
CairoOutputDev.cc:311 | |
fill color: 3855 38294 42663 | |
close | |
fill | |
line width: 3.000000 | |
stroke color: 2827 21588 23387 | |
save | |
line width: 3.000000 | |
close | |
stroke | |
restore | |
UPDATE BLEND DEBUG CairoOutputDev.cc:299 | |
DEBUG CairoOutputDev.cc:570 | |
blend mode: 0 | |
CairoOutputDev.cc:311 | |
restore | |
fill color: 0 0 0 | |
stroke color: 0 0 0 | |
UPDATE BLEND DEBUG CairoOutputDev.cc:299 | |
DEBUG CairoOutputDev.cc:570 | |
blend mode: 0 | |
CairoOutputDev.cc:311 | |
restore | |
UPDATE BLEND DEBUG CairoOutputDev.cc:299 | |
DEBUG CairoOutputDev.cc:570 | |
blend mode: 0 | |
CairoOutputDev.cc:311 | |
DEBUG CairoOutputDev.cc:196 | |
/tmp/ruby-gnome2/poppler/lib/poppler.rb: line 112 | |
Poppler-INFO **:Internal error at position -1: cairo context error: out of memory<0a> | |
DEBUG CairoOutputDev.cc:218 | |
[:draw, :end] | |
/path/to/home/GitHub/rabbit/lib/rabbit/renderer/engine/cairo.rb:93:in `restore': out of memory | |
from /path/to/home/GitHub/rabbit/lib/rabbit/renderer/engine/cairo.rb:93:in `restore_context' | |
from /path/to/home/GitHub/rabbit/lib/rabbit/renderer/kernel.rb:35:in `ensure in save_context' | |
from /path/to/home/GitHub/rabbit/lib/rabbit/renderer/kernel.rb:35:in `save_context' | |
from /path/to/home/GitHub/rabbit/lib/rabbit/renderer/engine/cairo.rb:89:in `save_context' | |
from /path/to/home/GitHub/rabbit/lib/rabbit/renderer/display/base.rb:103:in `draw_slide' | |
from /path/to/home/GitHub/rabbit/lib/rabbit/renderer/display/drawing-area-base.rb:306:in `draw_slide' | |
from /path/to/home/GitHub/rabbit/lib/rabbit/element/slide-element.rb:37:in `draw' | |
from /path/to/home/GitHub/rabbit/lib/rabbit/renderer/display/drawing-area-primitive.rb:166:in `draw_current_slide' | |
from /path/to/home/GitHub/rabbit/lib/rabbit/renderer/display/drawing-area-primitive.rb:158:in `draw' | |
from /path/to/home/GitHub/rabbit/lib/rabbit/renderer/display/drawing-area-base.rb:298:in `draw' | |
from /path/to/home/GitHub/rabbit/lib/rabbit/renderer/display/drawing-area-primitive.rb:150:in `block in set_draw' | |
from /path/to/home/GitHub/rabbit/lib/rabbit/gtk.rb:106:in `call' | |
from /path/to/home/GitHub/rabbit/lib/rabbit/gtk.rb:106:in `main' | |
from /path/to/home/GitHub/rabbit/lib/rabbit/gtk.rb:106:in `block in initialize' | |
from /path/to/home/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/gobject-introspection-3.0.7/lib/gobject-introspection/loader.rb:551:in `call' | |
from /path/to/home/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/gobject-introspection-3.0.7/lib/gobject-introspection/loader.rb:551:in `invoke' | |
from /path/to/home/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/gobject-introspection-3.0.7/lib/gobject-introspection/loader.rb:551:in `block in define_method' | |
from /path/to/home/GitHub/rabbit/lib/rabbit/command/rabbit.rb:54:in `block in run' | |
from /path/to/home/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/gobject-introspection-3.0.7/lib/gobject-introspection/loader.rb:551:in `call' | |
from /path/to/home/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/gobject-introspection-3.0.7/lib/gobject-introspection/loader.rb:551:in `invoke' | |
from /path/to/home/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/gobject-introspection-3.0.7/lib/gobject-introspection/loader.rb:551:in `block in define_method' | |
from /path/to/home/GitHub/rabbit/lib/rabbit/command/rabbit.rb:64:in `run' | |
from /path/to/home/GitHub/rabbit/lib/rabbit/command/rabbit.rb:29:in `run' | |
from ../rabbit/bin/rabbit:24:in `<main>' | |
/path/to/home/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/glib2-3.0.7/lib/glib2.rb:41:in `exit': exit | |
from /path/to/home/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/glib2-3.0.7/lib/glib2.rb:41:in `exit_application' | |
from /path/to/home/GitHub/rabbit/lib/rabbit/gtk.rb:106:in `main' | |
from /path/to/home/GitHub/rabbit/lib/rabbit/gtk.rb:106:in `block in initialize' | |
from /path/to/home/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/gobject-introspection-3.0.7/lib/gobject-introspection/loader.rb:551:in `call' | |
from /path/to/home/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/gobject-introspection-3.0.7/lib/gobject-introspection/loader.rb:551:in `invoke' | |
from /path/to/home/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/gobject-introspection-3.0.7/lib/gobject-introspection/loader.rb:551:in `block in define_method' | |
from /path/to/home/GitHub/rabbit/lib/rabbit/command/rabbit.rb:54:in `block in run' | |
from /path/to/home/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/gobject-introspection-3.0.7/lib/gobject-introspection/loader.rb:551:in `call' | |
from /path/to/home/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/gobject-introspection-3.0.7/lib/gobject-introspection/loader.rb:551:in `invoke' | |
from /path/to/home/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/gobject-introspection-3.0.7/lib/gobject-introspection/loader.rb:551:in `block in define_method' | |
from /path/to/home/GitHub/rabbit/lib/rabbit/command/rabbit.rb:64:in `run' | |
from /path/to/home/GitHub/rabbit/lib/rabbit/command/rabbit.rb:29:in `run' | |
from ../rabbit/bin/rabbit:24:in `<main>' | |
/path/to/home/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/glib2-3.0.7/lib/glib2.rb:41:in `exit': exit | |
from /path/to/home/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/glib2-3.0.7/lib/glib2.rb:41:in `exit_application' | |
from /path/to/home/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/gobject-introspection-3.0.7/lib/gobject-introspection/loader.rb:551:in `invoke' | |
from /path/to/home/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/gobject-introspection-3.0.7/lib/gobject-introspection/loader.rb:551:in `block in define_method' | |
from /path/to/home/GitHub/rabbit/lib/rabbit/command/rabbit.rb:54:in `block in run' | |
from /path/to/home/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/gobject-introspection-3.0.7/lib/gobject-introspection/loader.rb:551:in `call' | |
from /path/to/home/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/gobject-introspection-3.0.7/lib/gobject-introspection/loader.rb:551:in `invoke' | |
from /path/to/home/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/gobject-introspection-3.0.7/lib/gobject-introspection/loader.rb:551:in `block in define_method' | |
from /path/to/home/GitHub/rabbit/lib/rabbit/command/rabbit.rb:64:in `run' | |
from /path/to/home/GitHub/rabbit/lib/rabbit/command/rabbit.rb:29:in `run' | |
from ../rabbit/bin/rabbit:24:in `<main>' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//======================================================================== | |
// | |
// CairoOutputDev.cc | |
// | |
// Copyright 2003 Glyph & Cog, LLC | |
// Copyright 2004 Red Hat, Inc | |
// | |
//======================================================================== | |
//======================================================================== | |
// | |
// Modified under the Poppler project - http://poppler.freedesktop.org | |
// | |
// All changes made under the Poppler project to this file are licensed | |
// under GPL version 2 or later | |
// | |
// Copyright (C) 2005-2008 Jeff Muizelaar <jeff@infidigm.net> | |
// Copyright (C) 2005, 2006 Kristian Høgsberg <krh@redhat.com> | |
// Copyright (C) 2005, 2009, 2012 Albert Astals Cid <aacid@kde.org> | |
// Copyright (C) 2005 Nickolay V. Shmyrev <nshmyrev@yandex.ru> | |
// Copyright (C) 2006-2011, 2013, 2014 Carlos Garcia Campos <carlosgc@gnome.org> | |
// Copyright (C) 2008 Carl Worth <cworth@cworth.org> | |
// Copyright (C) 2008-2014 Adrian Johnson <ajohnson@redneon.com> | |
// Copyright (C) 2008 Michael Vrable <mvrable@cs.ucsd.edu> | |
// Copyright (C) 2008, 2009 Chris Wilson <chris@chris-wilson.co.uk> | |
// Copyright (C) 2008, 2012 Hib Eris <hib@hiberis.nl> | |
// Copyright (C) 2009, 2010 David Benjamin <davidben@mit.edu> | |
// Copyright (C) 2011-2014 Thomas Freitag <Thomas.Freitag@alfa.de> | |
// Copyright (C) 2012 Patrick Pfeifer <p2000@mailinator.com> | |
// Copyright (C) 2012, 2015 Jason Crain <jason@aquaticape.us> | |
// Copyright (C) 2015 Suzuki Toshiya <mpsuzuki@hiroshima-u.ac.jp> | |
// | |
// To see a description of the changes please see the Changelog file that | |
// came with your tarball or type make ChangeLog if you are building from git | |
// | |
//======================================================================== | |
#include <config.h> | |
#ifdef USE_GCC_PRAGMAS | |
#pragma implementation | |
#endif | |
#include <string.h> | |
#include <math.h> | |
#include <assert.h> | |
#include <cairo.h> | |
#include "goo/gfile.h" | |
#include "goo/gtypes_p.h" | |
#include "GlobalParams.h" | |
#include "Error.h" | |
#include "Object.h" | |
#include "Gfx.h" | |
#include "GfxState.h" | |
#include "GfxFont.h" | |
#include "Page.h" | |
#include "Link.h" | |
#include "FontEncodingTables.h" | |
#include "PDFDocEncoding.h" | |
#include <fofi/FoFiTrueType.h> | |
#include <splash/SplashBitmap.h> | |
#include "CairoOutputDev.h" | |
#include "CairoFontEngine.h" | |
#include "CairoRescaleBox.h" | |
#include "UnicodeMap.h" | |
#include "JBIG2Stream.h" | |
//------------------------------------------------------------------------ | |
#define LOG_CAIRO | |
#ifdef LOG_CAIRO | |
#define LOG(x) (x) | |
#else | |
#define LOG(x) | |
#endif | |
static inline void printMatrix(cairo_matrix_t *matrix){ | |
printf("%f %f, %f %f (%f %f)\n", matrix->xx, matrix->yx, | |
matrix->xy, matrix->yy, | |
matrix->x0, matrix->y0); | |
} | |
#define MIN(a,b) (((a) < (b)) ? (a) : (b)) | |
#define MAX(a,b) (((a) > (b)) ? (a) : (b)) | |
//------------------------------------------------------------------------ | |
// CairoImage | |
//------------------------------------------------------------------------ | |
CairoImage::CairoImage (double x1, double y1, double x2, double y2) { | |
this->image = NULL; | |
this->x1 = x1; | |
this->y1 = y1; | |
this->x2 = x2; | |
this->y2 = y2; | |
} | |
CairoImage::~CairoImage () { | |
if (image) | |
cairo_surface_destroy (image); | |
} | |
void CairoImage::setImage (cairo_surface_t *image) { | |
if (this->image) | |
cairo_surface_destroy (this->image); | |
this->image = cairo_surface_reference (image); | |
} | |
//------------------------------------------------------------------------ | |
// CairoOutputDev | |
//------------------------------------------------------------------------ | |
// We cannot tie the lifetime of an FT_Library object to that of | |
// CairoOutputDev, since any FT_Faces created with it may end up with a | |
// reference by Cairo which can be held long after the CairoOutputDev is | |
// deleted. The simplest way to avoid problems is to never tear down the | |
// FT_Library instance; to avoid leaks, just use a single global instance | |
// initialized the first time it is needed. | |
FT_Library CairoOutputDev::ft_lib; | |
GBool CairoOutputDev::ft_lib_initialized = gFalse; | |
CairoOutputDev::CairoOutputDev() { | |
doc = NULL; | |
if (!ft_lib_initialized) { | |
FT_Init_FreeType(&ft_lib); | |
ft_lib_initialized = gTrue; | |
} | |
fontEngine = NULL; | |
fontEngine_owner = gFalse; | |
glyphs = NULL; | |
fill_pattern = NULL; | |
fill_color.r = fill_color.g = fill_color.b = 0; | |
stroke_pattern = NULL; | |
stroke_color.r = stroke_color.g = stroke_color.b = 0; | |
stroke_opacity = 1.0; | |
fill_opacity = 1.0; | |
textClipPath = NULL; | |
strokePathClip = NULL; | |
cairo = NULL; | |
currentFont = NULL; | |
prescaleImages = gTrue; | |
printing = gTrue; | |
use_show_text_glyphs = gFalse; | |
inUncoloredPattern = gFalse; | |
inType3Char = gFalse; | |
t3_glyph_has_bbox = gFalse; | |
text_matrix_valid = gTrue; | |
groupColorSpaceStack = NULL; | |
maskStack = NULL; | |
group = NULL; | |
mask = NULL; | |
shape = NULL; | |
cairo_shape = NULL; | |
knockoutCount = 0; | |
text = NULL; | |
actualText = NULL; | |
// the SA parameter supposedly defaults to false, but Acrobat | |
// apparently hardwires it to true | |
stroke_adjust = globalParams->getStrokeAdjust(); | |
align_stroke_coords = gFalse; | |
adjusted_stroke_width = gFalse; | |
xref = NULL; | |
} | |
CairoOutputDev::~CairoOutputDev() { | |
if (fontEngine_owner && fontEngine) { | |
delete fontEngine; | |
} | |
if (cairo) | |
cairo_destroy (cairo); | |
cairo_pattern_destroy (stroke_pattern); | |
cairo_pattern_destroy (fill_pattern); | |
if (group) | |
cairo_pattern_destroy (group); | |
if (mask) | |
cairo_pattern_destroy (mask); | |
if (shape) | |
cairo_pattern_destroy (shape); | |
if (text) | |
text->decRefCnt(); | |
if (actualText) | |
delete actualText; | |
} | |
void CairoOutputDev::setCairo(cairo_t *cairo) | |
{ | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
if (this->cairo != NULL) { | |
cairo_status_t status = cairo_status (this->cairo); | |
if (status) { | |
error(errInternal, -1, "cairo context error: {0:s}\n", cairo_status_to_string(status)); | |
} | |
cairo_destroy (this->cairo); | |
assert(!cairo_shape); | |
} | |
if (cairo != NULL) { | |
this->cairo = cairo_reference (cairo); | |
/* save the initial matrix so that we can use it for type3 fonts. */ | |
//XXX: is this sufficient? could we miss changes to the matrix somehow? | |
cairo_get_matrix(cairo, &orig_matrix); | |
} else { | |
this->cairo = NULL; | |
this->cairo_shape = NULL; | |
} | |
} | |
void CairoOutputDev::setTextPage(TextPage *text) | |
{ | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
if (this->text) | |
this->text->decRefCnt(); | |
if (actualText) | |
delete actualText; | |
if (text) { | |
this->text = text; | |
this->text->incRefCnt(); | |
actualText = new ActualText(text); | |
} else { | |
this->text = NULL; | |
actualText = NULL; | |
} | |
} | |
void CairoOutputDev::startDoc(PDFDoc *docA, | |
CairoFontEngine *parentFontEngine) { | |
doc = docA; | |
if (parentFontEngine) { | |
fontEngine = parentFontEngine; | |
} else { | |
if (fontEngine) { | |
delete fontEngine; | |
} | |
fontEngine = new CairoFontEngine(ft_lib); | |
fontEngine_owner = gTrue; | |
} | |
xref = doc->getXRef(); | |
} | |
void CairoOutputDev::startPage(int pageNum, GfxState *state, XRef *xrefA) { | |
/* set up some per page defaults */ | |
cairo_pattern_destroy(fill_pattern); | |
cairo_pattern_destroy(stroke_pattern); | |
fill_pattern = cairo_pattern_create_rgb(0., 0., 0.); | |
fill_color.r = fill_color.g = fill_color.b = 0; | |
stroke_pattern = cairo_pattern_reference(fill_pattern); | |
stroke_color.r = stroke_color.g = stroke_color.b = 0; | |
if (text) | |
text->startPage(state); | |
if (xrefA != NULL) { | |
xref = xrefA; | |
} | |
} | |
void CairoOutputDev::endPage() { | |
if (text) { | |
text->endPage(); | |
text->coalesce(gTrue, 0, gFalse); | |
} | |
} | |
void CairoOutputDev::saveState(GfxState *state) { | |
LOG(printf ("save\n")); | |
cairo_save (cairo); | |
if (cairo_shape) | |
cairo_save (cairo_shape); | |
MaskStack *ms = new MaskStack; | |
ms->mask = cairo_pattern_reference(mask); | |
ms->mask_matrix = mask_matrix; | |
ms->next = maskStack; | |
maskStack = ms; | |
} | |
void CairoOutputDev::restoreState(GfxState *state) { | |
LOG(printf ("restore\n")); | |
cairo_restore (cairo); | |
if (cairo_shape) | |
cairo_restore (cairo_shape); | |
text_matrix_valid = gTrue; | |
/* These aren't restored by cairo_restore() since we keep them in | |
* the output device. */ | |
updateFillColor(state); | |
updateStrokeColor(state); | |
updateFillOpacity(state); | |
updateStrokeOpacity(state); | |
printf("UPDATE BLEND DEBUG %s:%d\n",__FILE__,__LINE__); | |
updateBlendMode(state); | |
MaskStack* ms = maskStack; | |
if (ms) { | |
if (mask) | |
cairo_pattern_destroy(mask); | |
mask = ms->mask; | |
mask_matrix = ms->mask_matrix; | |
maskStack = ms->next; | |
delete ms; | |
} | |
printf("%s:%d\n",__FILE__,__LINE__); | |
} | |
void CairoOutputDev::updateAll(GfxState *state) { | |
updateLineDash(state); | |
updateLineJoin(state); | |
updateLineCap(state); | |
updateLineWidth(state); | |
updateFlatness(state); | |
updateMiterLimit(state); | |
updateFillColor(state); | |
updateStrokeColor(state); | |
updateFillOpacity(state); | |
updateStrokeOpacity(state); | |
printf("UPDATE DEBUG %s:%d\n",__FILE__,__LINE__); | |
updateBlendMode(state); | |
needFontUpdate = gTrue; | |
if (text) | |
text->updateFont(state); | |
} | |
void CairoOutputDev::setDefaultCTM(double *ctm) { | |
cairo_matrix_t matrix; | |
matrix.xx = ctm[0]; | |
matrix.yx = ctm[1]; | |
matrix.xy = ctm[2]; | |
matrix.yy = ctm[3]; | |
matrix.x0 = ctm[4]; | |
matrix.y0 = ctm[5]; | |
cairo_transform (cairo, &matrix); | |
if (cairo_shape) | |
cairo_transform (cairo_shape, &matrix); | |
OutputDev::setDefaultCTM(ctm); | |
} | |
void CairoOutputDev::updateCTM(GfxState *state, double m11, double m12, | |
double m21, double m22, | |
double m31, double m32) { | |
cairo_matrix_t matrix, invert_matrix; | |
matrix.xx = m11; | |
matrix.yx = m12; | |
matrix.xy = m21; | |
matrix.yy = m22; | |
matrix.x0 = m31; | |
matrix.y0 = m32; | |
/* Make sure the matrix is invertible before setting it. | |
* cairo will blow up if we give it a matrix that's not | |
* invertible, so we need to check before passing it | |
* to cairo_transform. Ignoring it is likely to give better | |
* results than not rendering anything at all. See #14398 | |
* | |
* Ideally, we could do the cairo_transform | |
* and then check if anything went wrong and fix it then | |
* instead of having to invert the matrix. */ | |
invert_matrix = matrix; | |
if (cairo_matrix_invert(&invert_matrix)) { | |
error(errSyntaxWarning, -1, "matrix not invertible\n"); | |
return; | |
} | |
cairo_transform (cairo, &matrix); | |
if (cairo_shape) | |
cairo_transform (cairo_shape, &matrix); | |
updateLineDash(state); | |
updateLineJoin(state); | |
updateLineCap(state); | |
updateLineWidth(state); | |
} | |
void CairoOutputDev::updateLineDash(GfxState *state) { | |
double *dashPattern; | |
int dashLength; | |
double dashStart; | |
state->getLineDash(&dashPattern, &dashLength, &dashStart); | |
cairo_set_dash (cairo, dashPattern, dashLength, dashStart); | |
if (cairo_shape) | |
cairo_set_dash (cairo_shape, dashPattern, dashLength, dashStart); | |
} | |
void CairoOutputDev::updateFlatness(GfxState *state) { | |
// cairo_set_tolerance (cairo, state->getFlatness()); | |
} | |
void CairoOutputDev::updateLineJoin(GfxState *state) { | |
switch (state->getLineJoin()) { | |
case 0: | |
cairo_set_line_join (cairo, CAIRO_LINE_JOIN_MITER); | |
break; | |
case 1: | |
cairo_set_line_join (cairo, CAIRO_LINE_JOIN_ROUND); | |
break; | |
case 2: | |
cairo_set_line_join (cairo, CAIRO_LINE_JOIN_BEVEL); | |
break; | |
} | |
if (cairo_shape) | |
cairo_set_line_join (cairo_shape, cairo_get_line_join(cairo)); | |
} | |
void CairoOutputDev::updateLineCap(GfxState *state) { | |
switch (state->getLineCap()) { | |
case 0: | |
cairo_set_line_cap (cairo, CAIRO_LINE_CAP_BUTT); | |
break; | |
case 1: | |
cairo_set_line_cap (cairo, CAIRO_LINE_CAP_ROUND); | |
break; | |
case 2: | |
cairo_set_line_cap (cairo, CAIRO_LINE_CAP_SQUARE); | |
break; | |
} | |
if (cairo_shape) | |
cairo_set_line_cap (cairo_shape, cairo_get_line_cap(cairo)); | |
} | |
void CairoOutputDev::updateMiterLimit(GfxState *state) { | |
cairo_set_miter_limit (cairo, state->getMiterLimit()); | |
if (cairo_shape) | |
cairo_set_miter_limit (cairo_shape, state->getMiterLimit()); | |
} | |
void CairoOutputDev::updateLineWidth(GfxState *state) { | |
LOG(printf ("line width: %f\n", state->getLineWidth())); | |
adjusted_stroke_width = gFalse; | |
double width = state->getLineWidth(); | |
if (stroke_adjust && !printing) { | |
double x, y; | |
x = y = width; | |
/* find out line width in device units */ | |
cairo_user_to_device_distance(cairo, &x, &y); | |
if (fabs(x) <= 1.0 && fabs(y) <= 1.0) { | |
/* adjust width to at least one device pixel */ | |
x = y = 1.0; | |
cairo_device_to_user_distance(cairo, &x, &y); | |
width = MIN(fabs(x),fabs(y)); | |
adjusted_stroke_width = gTrue; | |
} | |
} else if (width == 0.0) { | |
/* Cairo does not support 0 line width == 1 device pixel. Find out | |
* how big pixels (device unit) are in the x and y | |
* directions. Choose the smaller of the two as our line width. | |
*/ | |
double x = 1.0, y = 1.0; | |
if (printing) { | |
// assume printer pixel size is 1/600 inch | |
x = 72.0/600; | |
y = 72.0/600; | |
} | |
cairo_device_to_user_distance(cairo, &x, &y); | |
width = MIN(fabs(x),fabs(y)); | |
} | |
cairo_set_line_width (cairo, width); | |
if (cairo_shape) | |
cairo_set_line_width (cairo_shape, cairo_get_line_width (cairo)); | |
} | |
void CairoOutputDev::updateFillColor(GfxState *state) { | |
GfxRGB color = fill_color; | |
if (inUncoloredPattern) | |
return; | |
state->getFillRGB(&fill_color); | |
if (cairo_pattern_get_type (fill_pattern) != CAIRO_PATTERN_TYPE_SOLID || | |
color.r != fill_color.r || | |
color.g != fill_color.g || | |
color.b != fill_color.b) | |
{ | |
cairo_pattern_destroy(fill_pattern); | |
fill_pattern = cairo_pattern_create_rgba(colToDbl(fill_color.r), | |
colToDbl(fill_color.g), | |
colToDbl(fill_color.b), | |
fill_opacity); | |
LOG(printf ("fill color: %d %d %d\n", | |
fill_color.r, fill_color.g, fill_color.b)); | |
} | |
} | |
void CairoOutputDev::updateStrokeColor(GfxState *state) { | |
GfxRGB color = stroke_color; | |
if (inUncoloredPattern) | |
return; | |
state->getStrokeRGB(&stroke_color); | |
if (cairo_pattern_get_type (fill_pattern) != CAIRO_PATTERN_TYPE_SOLID || | |
color.r != stroke_color.r || | |
color.g != stroke_color.g || | |
color.b != stroke_color.b) | |
{ | |
cairo_pattern_destroy(stroke_pattern); | |
stroke_pattern = cairo_pattern_create_rgba(colToDbl(stroke_color.r), | |
colToDbl(stroke_color.g), | |
colToDbl(stroke_color.b), | |
stroke_opacity); | |
LOG(printf ("stroke color: %d %d %d\n", | |
stroke_color.r, stroke_color.g, stroke_color.b)); | |
} | |
} | |
void CairoOutputDev::updateFillOpacity(GfxState *state) { | |
double opacity = fill_opacity; | |
if (inUncoloredPattern) | |
return; | |
fill_opacity = state->getFillOpacity(); | |
if (opacity != fill_opacity) { | |
cairo_pattern_destroy(fill_pattern); | |
fill_pattern = cairo_pattern_create_rgba(colToDbl(fill_color.r), | |
colToDbl(fill_color.g), | |
colToDbl(fill_color.b), | |
fill_opacity); | |
LOG(printf ("fill opacity: %f\n", fill_opacity)); | |
} | |
} | |
void CairoOutputDev::updateStrokeOpacity(GfxState *state) { | |
double opacity = stroke_opacity; | |
if (inUncoloredPattern) | |
return; | |
stroke_opacity = state->getStrokeOpacity(); | |
if (opacity != stroke_opacity) { | |
cairo_pattern_destroy(stroke_pattern); | |
stroke_pattern = cairo_pattern_create_rgba(colToDbl(stroke_color.r), | |
colToDbl(stroke_color.g), | |
colToDbl(stroke_color.b), | |
stroke_opacity); | |
LOG(printf ("stroke opacity: %f\n", stroke_opacity)); | |
} | |
} | |
void CairoOutputDev::updateFillColorStop(GfxState *state, double offset) { | |
if (inUncoloredPattern) | |
return; | |
state->getFillRGB(&fill_color); | |
cairo_pattern_add_color_stop_rgba(fill_pattern, offset, | |
colToDbl(fill_color.r), | |
colToDbl(fill_color.g), | |
colToDbl(fill_color.b), | |
fill_opacity); | |
LOG(printf ("fill color stop: %f (%d, %d, %d)\n", | |
offset, fill_color.r, fill_color.g, fill_color.b)); | |
} | |
void CairoOutputDev::updateBlendMode(GfxState *state) { | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
switch (state->getBlendMode()) { | |
default: | |
case gfxBlendNormal: | |
cairo_set_operator (cairo, CAIRO_OPERATOR_OVER); | |
break; | |
case gfxBlendMultiply: | |
cairo_set_operator (cairo, CAIRO_OPERATOR_MULTIPLY); | |
break; | |
case gfxBlendScreen: | |
cairo_set_operator (cairo, CAIRO_OPERATOR_SCREEN); | |
break; | |
case gfxBlendOverlay: | |
cairo_set_operator (cairo, CAIRO_OPERATOR_OVERLAY); | |
break; | |
case gfxBlendDarken: | |
cairo_set_operator (cairo, CAIRO_OPERATOR_DARKEN); | |
break; | |
case gfxBlendLighten: | |
cairo_set_operator (cairo, CAIRO_OPERATOR_LIGHTEN); | |
break; | |
case gfxBlendColorDodge: | |
cairo_set_operator (cairo, CAIRO_OPERATOR_COLOR_DODGE); | |
break; | |
case gfxBlendColorBurn: | |
cairo_set_operator (cairo, CAIRO_OPERATOR_COLOR_BURN); | |
break; | |
case gfxBlendHardLight: | |
cairo_set_operator (cairo, CAIRO_OPERATOR_HARD_LIGHT); | |
break; | |
case gfxBlendSoftLight: | |
cairo_set_operator (cairo, CAIRO_OPERATOR_SOFT_LIGHT); | |
break; | |
case gfxBlendDifference: | |
cairo_set_operator (cairo, CAIRO_OPERATOR_DIFFERENCE); | |
break; | |
case gfxBlendExclusion: | |
cairo_set_operator (cairo, CAIRO_OPERATOR_EXCLUSION); | |
break; | |
case gfxBlendHue: | |
cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_HUE); | |
break; | |
case gfxBlendSaturation: | |
cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_SATURATION); | |
break; | |
case gfxBlendColor: | |
cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_COLOR); | |
break; | |
case gfxBlendLuminosity: | |
cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_LUMINOSITY); | |
break; | |
} | |
LOG(printf ("blend mode: %d\n", (int)state->getBlendMode())); | |
} | |
void CairoOutputDev::updateFont(GfxState *state) { | |
cairo_font_face_t *font_face; | |
cairo_matrix_t matrix, invert_matrix; | |
LOG(printf ("updateFont() font=%s\n", state->getFont()->getName()->getCString())); | |
needFontUpdate = gFalse; | |
//FIXME: use cairo font engine? | |
if (text) | |
text->updateFont(state); | |
currentFont = fontEngine->getFont (state->getFont(), doc, printing, xref); | |
if (!currentFont) | |
return; | |
font_face = currentFont->getFontFace(); | |
cairo_set_font_face (cairo, font_face); | |
use_show_text_glyphs = state->getFont()->hasToUnicodeCMap() && | |
cairo_surface_has_show_text_glyphs (cairo_get_target (cairo)); | |
double fontSize = state->getFontSize(); | |
double *m = state->getTextMat(); | |
/* NOTE: adjusting by a constant is hack. The correct solution | |
* is probably to use user-fonts and compute the scale on a per | |
* glyph basis instead of for the entire font */ | |
double w = currentFont->getSubstitutionCorrection(state->getFont()); | |
matrix.xx = m[0] * fontSize * state->getHorizScaling() * w; | |
matrix.yx = m[1] * fontSize * state->getHorizScaling() * w; | |
matrix.xy = -m[2] * fontSize; | |
matrix.yy = -m[3] * fontSize; | |
matrix.x0 = 0; | |
matrix.y0 = 0; | |
LOG(printf ("font matrix: %f %f %f %f\n", matrix.xx, matrix.yx, matrix.xy, matrix.yy)); | |
/* Make sure the font matrix is invertible before setting it. cairo | |
* will blow up if we give it a matrix that's not invertible, so we | |
* need to check before passing it to cairo_set_font_matrix. Ignoring it | |
* is likely to give better results than not rendering anything at | |
* all. See #18254. | |
*/ | |
invert_matrix = matrix; | |
if (cairo_matrix_invert(&invert_matrix)) { | |
error(errSyntaxWarning, -1, "font matrix not invertible"); | |
text_matrix_valid = gFalse; | |
return; | |
} | |
cairo_set_font_matrix (cairo, &matrix); | |
text_matrix_valid = gTrue; | |
} | |
/* Tolerance in pixels for checking if strokes are horizontal or vertical | |
* lines in device space */ | |
#define STROKE_COORD_TOLERANCE 0.5 | |
/* Align stroke coordinate i if the point is the start or end of a | |
* horizontal or vertical line */ | |
void CairoOutputDev::alignStrokeCoords(GfxSubpath *subpath, int i, double *x, double *y) | |
{ | |
double x1, y1, x2, y2; | |
GBool align = gFalse; | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
x1 = subpath->getX(i); | |
y1 = subpath->getY(i); | |
cairo_user_to_device (cairo, &x1, &y1); | |
// Does the current coord and prev coord form a horiz or vert line? | |
if (i > 0 && !subpath->getCurve(i - 1)) { | |
x2 = subpath->getX(i - 1); | |
y2 = subpath->getY(i - 1); | |
cairo_user_to_device (cairo, &x2, &y2); | |
if (fabs(x2 - x1) < STROKE_COORD_TOLERANCE || fabs(y2 - y1) < STROKE_COORD_TOLERANCE) | |
align = gTrue; | |
} | |
// Does the current coord and next coord form a horiz or vert line? | |
if (i < subpath->getNumPoints() - 1 && !subpath->getCurve(i + 1)) { | |
x2 = subpath->getX(i + 1); | |
y2 = subpath->getY(i + 1); | |
cairo_user_to_device (cairo, &x2, &y2); | |
if (fabs(x2 - x1) < STROKE_COORD_TOLERANCE || fabs(y2 - y1) < STROKE_COORD_TOLERANCE) | |
align = gTrue; | |
} | |
*x = subpath->getX(i); | |
*y = subpath->getY(i); | |
if (align) { | |
/* see http://www.cairographics.org/FAQ/#sharp_lines */ | |
cairo_user_to_device (cairo, x, y); | |
*x = floor(*x) + 0.5; | |
*y = floor(*y) + 0.5; | |
cairo_device_to_user (cairo, x, y); | |
} | |
} | |
#undef STROKE_COORD_TOLERANCE | |
void CairoOutputDev::doPath(cairo_t *cairo, GfxState *state, GfxPath *path) { | |
GfxSubpath *subpath; | |
int i, j; | |
double x, y; | |
cairo_new_path (cairo); | |
for (i = 0; i < path->getNumSubpaths(); ++i) { | |
subpath = path->getSubpath(i); | |
if (subpath->getNumPoints() > 0) { | |
if (align_stroke_coords) { | |
alignStrokeCoords(subpath, 0, &x, &y); | |
} else { | |
x = subpath->getX(0); | |
y = subpath->getY(0); | |
} | |
cairo_move_to (cairo, x, y); | |
j = 1; | |
while (j < subpath->getNumPoints()) { | |
if (subpath->getCurve(j)) { | |
if (align_stroke_coords) { | |
alignStrokeCoords(subpath, j + 2, &x, &y); | |
} else { | |
x = subpath->getX(j+2); | |
y = subpath->getY(j+2); | |
} | |
cairo_curve_to( cairo, | |
subpath->getX(j), subpath->getY(j), | |
subpath->getX(j+1), subpath->getY(j+1), | |
x, y); | |
j += 3; | |
} else { | |
if (align_stroke_coords) { | |
alignStrokeCoords(subpath, j, &x, &y); | |
} else { | |
x = subpath->getX(j); | |
y = subpath->getY(j); | |
} | |
cairo_line_to (cairo, x, y); | |
++j; | |
} | |
} | |
if (subpath->isClosed()) { | |
LOG (printf ("close\n")); | |
cairo_close_path (cairo); | |
} | |
} | |
} | |
} | |
void CairoOutputDev::stroke(GfxState *state) { | |
if (inType3Char) { | |
GfxGray gray; | |
state->getFillGray(&gray); | |
if (colToDbl(gray) > 0.5) | |
return; | |
} | |
if (adjusted_stroke_width) | |
align_stroke_coords = gTrue; | |
doPath (cairo, state, state->getPath()); | |
align_stroke_coords = gFalse; | |
cairo_set_source (cairo, stroke_pattern); | |
LOG(printf ("stroke\n")); | |
cairo_stroke (cairo); | |
if (cairo_shape) { | |
doPath (cairo_shape, state, state->getPath()); | |
cairo_stroke (cairo_shape); | |
} | |
} | |
void CairoOutputDev::fill(GfxState *state) { | |
if (inType3Char) { | |
GfxGray gray; | |
state->getFillGray(&gray); | |
if (colToDbl(gray) > 0.5) | |
return; | |
} | |
doPath (cairo, state, state->getPath()); | |
cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_WINDING); | |
cairo_set_source (cairo, fill_pattern); | |
LOG(printf ("fill\n")); | |
//XXX: how do we get the path | |
if (mask) { | |
cairo_save (cairo); | |
cairo_clip (cairo); | |
cairo_set_matrix (cairo, &mask_matrix); | |
cairo_mask (cairo, mask); | |
cairo_restore (cairo); | |
} else if (strokePathClip) { | |
fillToStrokePathClip(state); | |
} else { | |
cairo_fill (cairo); | |
} | |
if (cairo_shape) { | |
cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_WINDING); | |
doPath (cairo_shape, state, state->getPath()); | |
cairo_fill (cairo_shape); | |
} | |
} | |
void CairoOutputDev::eoFill(GfxState *state) { | |
doPath (cairo, state, state->getPath()); | |
cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_EVEN_ODD); | |
cairo_set_source (cairo, fill_pattern); | |
LOG(printf ("fill-eo\n")); | |
if (mask) { | |
cairo_save (cairo); | |
cairo_clip (cairo); | |
cairo_set_matrix (cairo, &mask_matrix); | |
cairo_mask (cairo, mask); | |
cairo_restore (cairo); | |
} else { | |
cairo_fill (cairo); | |
} | |
if (cairo_shape) { | |
cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_EVEN_ODD); | |
doPath (cairo_shape, state, state->getPath()); | |
cairo_fill (cairo_shape); | |
} | |
} | |
GBool CairoOutputDev::tilingPatternFill(GfxState *state, Gfx *gfxA, Catalog *cat, Object *str, | |
double *pmat, int paintType, int /*tilingType*/, Dict *resDict, | |
double *mat, double *bbox, | |
int x0, int y0, int x1, int y1, | |
double xStep, double yStep) | |
{ | |
PDFRectangle box; | |
Gfx *gfx; | |
cairo_pattern_t *pattern; | |
cairo_surface_t *surface; | |
cairo_matrix_t matrix; | |
cairo_matrix_t pattern_matrix; | |
cairo_t *old_cairo; | |
double xMin, yMin, xMax, yMax; | |
double width, height; | |
int surface_width, surface_height; | |
StrokePathClip *strokePathTmp; | |
GBool adjusted_stroke_width_tmp; | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
width = bbox[2] - bbox[0]; | |
height = bbox[3] - bbox[1]; | |
if (xStep != width || yStep != height) | |
return gFalse; | |
/* TODO: implement the other cases here too */ | |
// Find the width and height of the transformed pattern | |
cairo_get_matrix (cairo, &matrix); | |
cairo_matrix_init (&pattern_matrix, mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); | |
cairo_matrix_multiply (&matrix, &matrix, &pattern_matrix); | |
double widthX = width, widthY = 0; | |
cairo_matrix_transform_distance (&matrix, &widthX, &widthY); | |
surface_width = ceil (sqrt (widthX * widthX + widthY * widthY)); | |
double heightX = 0, heightY = height; | |
cairo_matrix_transform_distance (&matrix, &heightX, &heightY); | |
surface_height = ceil (sqrt (heightX * heightX + heightY * heightY)); | |
surface = cairo_surface_create_similar (cairo_get_target (cairo), | |
CAIRO_CONTENT_COLOR_ALPHA, | |
surface_width, surface_height); | |
if (cairo_surface_status (surface)) | |
return gFalse; | |
old_cairo = cairo; | |
cairo = cairo_create (surface); | |
cairo_surface_destroy (surface); | |
cairo_scale (cairo, surface_width / width, surface_height / height); | |
box.x1 = bbox[0]; box.y1 = bbox[1]; | |
box.x2 = bbox[2]; box.y2 = bbox[3]; | |
strokePathTmp = strokePathClip; | |
strokePathClip = NULL; | |
adjusted_stroke_width_tmp = adjusted_stroke_width; | |
gfx = new Gfx(doc, this, resDict, &box, NULL, NULL, NULL, gfxA->getXRef()); | |
if (paintType == 2) | |
inUncoloredPattern = gTrue; | |
gfx->display(str); | |
if (paintType == 2) | |
inUncoloredPattern = gFalse; | |
delete gfx; | |
strokePathClip = strokePathTmp; | |
adjusted_stroke_width = adjusted_stroke_width_tmp; | |
pattern = cairo_pattern_create_for_surface (cairo_get_target (cairo)); | |
cairo_destroy (cairo); | |
cairo = old_cairo; | |
if (cairo_pattern_status (pattern)) | |
return gFalse; | |
state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); | |
cairo_rectangle (cairo, xMin, yMin, xMax - xMin, yMax - yMin); | |
cairo_matrix_init_scale (&matrix, surface_width / width, surface_height / height); | |
cairo_pattern_set_matrix (pattern, &matrix); | |
cairo_transform (cairo, &pattern_matrix); | |
cairo_set_source (cairo, pattern); | |
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); | |
if (strokePathClip) { | |
fillToStrokePathClip(state); | |
} else { | |
cairo_fill (cairo); | |
} | |
cairo_pattern_destroy (pattern); | |
return gTrue; | |
} | |
GBool CairoOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) { | |
double x0, y0, x1, y1; | |
double dx, dy; | |
shading->getCoords(&x0, &y0, &x1, &y1); | |
dx = x1 - x0; | |
dy = y1 - y0; | |
cairo_pattern_destroy(fill_pattern); | |
fill_pattern = cairo_pattern_create_linear (x0 + tMin * dx, y0 + tMin * dy, | |
x0 + tMax * dx, y0 + tMax * dy); | |
if (!shading->getExtend0() && !shading->getExtend1()) | |
cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_NONE); | |
else | |
cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_PAD); | |
LOG (printf ("axial-sh\n")); | |
// TODO: use the actual stops in the shading in the case | |
// of linear interpolation (Type 2 Exponential functions with N=1) | |
return gFalse; | |
} | |
GBool CairoOutputDev::axialShadedSupportExtend(GfxState *state, GfxAxialShading *shading) | |
{ | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
return (shading->getExtend0() == shading->getExtend1()); | |
} | |
GBool CairoOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double sMin, double sMax) { | |
double x0, y0, r0, x1, y1, r1; | |
double dx, dy, dr; | |
shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1); | |
dx = x1 - x0; | |
dy = y1 - y0; | |
dr = r1 - r0; | |
cairo_pattern_destroy(fill_pattern); | |
fill_pattern = cairo_pattern_create_radial (x0 + sMin * dx, | |
y0 + sMin * dy, | |
r0 + sMin * dr, | |
x0 + sMax * dx, | |
y0 + sMax * dy, | |
r0 + sMax * dr); | |
if (shading->getExtend0() && shading->getExtend1()) | |
cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_PAD); | |
else | |
cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_NONE); | |
LOG (printf ("radial-sh\n")); | |
return gFalse; | |
} | |
GBool CairoOutputDev::radialShadedSupportExtend(GfxState *state, GfxRadialShading *shading) | |
{ | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
return (shading->getExtend0() == shading->getExtend1()); | |
} | |
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) | |
GBool CairoOutputDev::gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading) | |
{ | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
double x0, y0, x1, y1, x2, y2; | |
GfxColor color[3]; | |
int i, j; | |
GfxRGB rgb; | |
cairo_pattern_destroy(fill_pattern); | |
fill_pattern = cairo_pattern_create_mesh (); | |
for (i = 0; i < shading->getNTriangles(); i++) { | |
if (shading->isParameterized()) { | |
double color0, color1, color2; | |
shading->getTriangle(i, &x0, &y0, &color0, | |
&x1, &y1, &color1, | |
&x2, &y2, &color2); | |
shading->getParameterizedColor(color0, &color[0]); | |
shading->getParameterizedColor(color1, &color[1]); | |
shading->getParameterizedColor(color2, &color[2]); | |
} else { | |
shading->getTriangle(i, | |
&x0, &y0, &color[0], | |
&x1, &y1, &color[1], | |
&x2, &y2, &color[2]); | |
} | |
cairo_mesh_pattern_begin_patch (fill_pattern); | |
cairo_mesh_pattern_move_to (fill_pattern, x0, y0); | |
cairo_mesh_pattern_line_to (fill_pattern, x1, y1); | |
cairo_mesh_pattern_line_to (fill_pattern, x2, y2); | |
for (j = 0; j < 3; j++) { | |
shading->getColorSpace()->getRGB(&color[j], &rgb); | |
cairo_mesh_pattern_set_corner_color_rgb (fill_pattern, j, | |
colToDbl(rgb.r), | |
colToDbl(rgb.g), | |
colToDbl(rgb.b)); | |
} | |
cairo_mesh_pattern_end_patch (fill_pattern); | |
} | |
double xMin, yMin, xMax, yMax; | |
// get the clip region bbox | |
state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); | |
state->moveTo(xMin, yMin); | |
state->lineTo(xMin, yMax); | |
state->lineTo(xMax, yMax); | |
state->lineTo(xMax, yMin); | |
state->closePath(); | |
fill(state); | |
state->clearPath(); | |
return gTrue; | |
} | |
GBool CairoOutputDev::patchMeshShadedFill(GfxState *state, GfxPatchMeshShading *shading) | |
{ | |
int i, j, k; | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
cairo_pattern_destroy(fill_pattern); | |
fill_pattern = cairo_pattern_create_mesh (); | |
for (i = 0; i < shading->getNPatches(); i++) { | |
GfxPatch *patch = shading->getPatch(i); | |
GfxColor color; | |
GfxRGB rgb; | |
cairo_mesh_pattern_begin_patch (fill_pattern); | |
cairo_mesh_pattern_move_to (fill_pattern, patch->x[0][0], patch->y[0][0]); | |
cairo_mesh_pattern_curve_to (fill_pattern, | |
patch->x[0][1], patch->y[0][1], | |
patch->x[0][2], patch->y[0][2], | |
patch->x[0][3], patch->y[0][3]); | |
cairo_mesh_pattern_curve_to (fill_pattern, | |
patch->x[1][3], patch->y[1][3], | |
patch->x[2][3], patch->y[2][3], | |
patch->x[3][3], patch->y[3][3]); | |
cairo_mesh_pattern_curve_to (fill_pattern, | |
patch->x[3][2], patch->y[3][2], | |
patch->x[3][1], patch->y[3][1], | |
patch->x[3][0], patch->y[3][0]); | |
cairo_mesh_pattern_curve_to (fill_pattern, | |
patch->x[2][0], patch->y[2][0], | |
patch->x[1][0], patch->y[1][0], | |
patch->x[0][0], patch->y[0][0]); | |
cairo_mesh_pattern_set_control_point (fill_pattern, 0, patch->x[1][1], patch->y[1][1]); | |
cairo_mesh_pattern_set_control_point (fill_pattern, 1, patch->x[1][2], patch->y[1][2]); | |
cairo_mesh_pattern_set_control_point (fill_pattern, 2, patch->x[2][2], patch->y[2][2]); | |
cairo_mesh_pattern_set_control_point (fill_pattern, 3, patch->x[2][1], patch->y[2][1]); | |
for (j = 0; j < 4; j++) { | |
int u, v; | |
switch (j) { | |
case 0: | |
u = 0; v = 0; | |
break; | |
case 1: | |
u = 0; v = 1; | |
break; | |
case 2: | |
u = 1; v = 1; | |
break; | |
case 3: | |
u = 1; v = 0; | |
break; | |
} | |
if (shading->isParameterized()) { | |
shading->getParameterizedColor (patch->color[u][v].c[0], &color); | |
} else { | |
for (k = 0; k < shading->getColorSpace()->getNComps(); k++) { | |
// simply cast to the desired type; that's all what is needed. | |
color.c[k] = GfxColorComp (patch->color[u][v].c[k]); | |
} | |
} | |
shading->getColorSpace()->getRGB(&color, &rgb); | |
cairo_mesh_pattern_set_corner_color_rgb (fill_pattern, j, | |
colToDbl(rgb.r), | |
colToDbl(rgb.g), | |
colToDbl(rgb.b)); | |
} | |
cairo_mesh_pattern_end_patch (fill_pattern); | |
} | |
double xMin, yMin, xMax, yMax; | |
// get the clip region bbox | |
state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); | |
state->moveTo(xMin, yMin); | |
state->lineTo(xMin, yMax); | |
state->lineTo(xMax, yMax); | |
state->lineTo(xMax, yMin); | |
state->closePath(); | |
fill(state); | |
state->clearPath(); | |
return gTrue; | |
} | |
#endif /* CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) */ | |
void CairoOutputDev::clip(GfxState *state) { | |
doPath (cairo, state, state->getPath()); | |
cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_WINDING); | |
cairo_clip (cairo); | |
LOG (printf ("clip\n")); | |
if (cairo_shape) { | |
doPath (cairo_shape, state, state->getPath()); | |
cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_WINDING); | |
cairo_clip (cairo_shape); | |
} | |
} | |
void CairoOutputDev::eoClip(GfxState *state) { | |
doPath (cairo, state, state->getPath()); | |
cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_EVEN_ODD); | |
cairo_clip (cairo); | |
LOG (printf ("clip-eo\n")); | |
if (cairo_shape) { | |
doPath (cairo_shape, state, state->getPath()); | |
cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_EVEN_ODD); | |
cairo_clip (cairo_shape); | |
} | |
} | |
void CairoOutputDev::clipToStrokePath(GfxState *state) { | |
LOG(printf("clip-to-stroke-path\n")); | |
strokePathClip = (StrokePathClip*)gmalloc (sizeof(*strokePathClip)); | |
strokePathClip->path = state->getPath()->copy(); | |
cairo_get_matrix (cairo, &strokePathClip->ctm); | |
strokePathClip->line_width = cairo_get_line_width (cairo); | |
strokePathClip->dash_count = cairo_get_dash_count (cairo); | |
if (strokePathClip->dash_count) { | |
strokePathClip->dashes = (double*) gmallocn (sizeof(double), strokePathClip->dash_count); | |
cairo_get_dash (cairo, strokePathClip->dashes, &strokePathClip->dash_offset); | |
} else { | |
strokePathClip->dashes = NULL; | |
} | |
strokePathClip->cap = cairo_get_line_cap (cairo); | |
strokePathClip->join = cairo_get_line_join (cairo); | |
strokePathClip->miter = cairo_get_miter_limit (cairo); | |
} | |
void CairoOutputDev::fillToStrokePathClip(GfxState *state) { | |
cairo_save (cairo); | |
cairo_set_matrix (cairo, &strokePathClip->ctm); | |
cairo_set_line_width (cairo, strokePathClip->line_width); | |
strokePathClip->dash_count = cairo_get_dash_count (cairo); | |
cairo_set_dash (cairo, strokePathClip->dashes, strokePathClip->dash_count, strokePathClip->dash_offset); | |
cairo_set_line_cap (cairo, strokePathClip->cap); | |
cairo_set_line_join (cairo, strokePathClip->join); | |
cairo_set_miter_limit (cairo, strokePathClip->miter); | |
doPath (cairo, state, strokePathClip->path); | |
cairo_stroke (cairo); | |
cairo_restore (cairo); | |
delete strokePathClip->path; | |
if (strokePathClip->dashes) | |
gfree (strokePathClip->dashes); | |
gfree (strokePathClip); | |
strokePathClip = NULL; | |
} | |
void CairoOutputDev::beginString(GfxState *state, GooString *s) | |
{ | |
int len = s->getLength(); | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
if (needFontUpdate) | |
updateFont(state); | |
if (!currentFont) | |
return; | |
glyphs = (cairo_glyph_t *) gmallocn (len, sizeof (cairo_glyph_t)); | |
glyphCount = 0; | |
if (use_show_text_glyphs) { | |
clusters = (cairo_text_cluster_t *) gmallocn (len, sizeof (cairo_text_cluster_t)); | |
clusterCount = 0; | |
utf8Max = len*2; // start with twice the number of glyphs. we will realloc if we need more. | |
utf8 = (char *) gmalloc (utf8Max); | |
utf8Count = 0; | |
} | |
} | |
void CairoOutputDev::drawChar(GfxState *state, double x, double y, | |
double dx, double dy, | |
double originX, double originY, | |
CharCode code, int nBytes, Unicode *u, int uLen) | |
{ | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
if (currentFont) { | |
glyphs[glyphCount].index = currentFont->getGlyph (code, u, uLen); | |
glyphs[glyphCount].x = x - originX; | |
glyphs[glyphCount].y = y - originY; | |
glyphCount++; | |
if (use_show_text_glyphs) { | |
GooString enc("UTF-8"); | |
UnicodeMap *utf8Map = globalParams->getUnicodeMap(&enc); | |
if (utf8Max - utf8Count < uLen*6) { | |
// utf8 encoded characters can be up to 6 bytes | |
if (utf8Max > uLen*6) | |
utf8Max *= 2; | |
else | |
utf8Max += 2*uLen*6; | |
utf8 = (char *) grealloc (utf8, utf8Max); | |
} | |
clusters[clusterCount].num_bytes = 0; | |
for (int i = 0; i < uLen; i++) { | |
int size = utf8Map->mapUnicode(u[i], utf8 + utf8Count, utf8Max - utf8Count); | |
utf8Count += size; | |
clusters[clusterCount].num_bytes += size; | |
} | |
clusters[clusterCount].num_glyphs = 1; | |
clusterCount++; | |
} | |
} | |
if (!text) | |
return; | |
actualText->addChar (state, x, y, dx, dy, code, nBytes, u, uLen); | |
} | |
void CairoOutputDev::endString(GfxState *state) | |
{ | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
int render; | |
if (!currentFont) | |
return; | |
// endString can be called without a corresponding beginString. If this | |
// happens glyphs will be null so don't draw anything, just return. | |
// XXX: OutputDevs should probably not have to deal with this... | |
if (!glyphs) | |
return; | |
// ignore empty strings and invisible text -- this is used by | |
// Acrobat Capture | |
render = state->getRender(); | |
if (render == 3 || glyphCount == 0 || !text_matrix_valid) { | |
goto finish; | |
} | |
if (!(render & 1)) { | |
LOG (printf ("fill string\n")); | |
cairo_set_source (cairo, fill_pattern); | |
if (use_show_text_glyphs) | |
cairo_show_text_glyphs (cairo, utf8, utf8Count, glyphs, glyphCount, clusters, clusterCount, (cairo_text_cluster_flags_t)0); | |
else | |
cairo_show_glyphs (cairo, glyphs, glyphCount); | |
if (cairo_shape) | |
cairo_show_glyphs (cairo_shape, glyphs, glyphCount); | |
} | |
// stroke | |
if ((render & 3) == 1 || (render & 3) == 2) { | |
LOG (printf ("stroke string\n")); | |
cairo_set_source (cairo, stroke_pattern); | |
cairo_glyph_path (cairo, glyphs, glyphCount); | |
cairo_stroke (cairo); | |
if (cairo_shape) { | |
cairo_glyph_path (cairo_shape, glyphs, glyphCount); | |
cairo_stroke (cairo_shape); | |
} | |
} | |
// clip | |
if ((render & 4)) { | |
LOG (printf ("clip string\n")); | |
// append the glyph path to textClipPath. | |
// set textClipPath as the currentPath | |
if (textClipPath) { | |
cairo_append_path (cairo, textClipPath); | |
if (cairo_shape) { | |
cairo_append_path (cairo_shape, textClipPath); | |
} | |
cairo_path_destroy (textClipPath); | |
} | |
// append the glyph path | |
cairo_glyph_path (cairo, glyphs, glyphCount); | |
// move the path back into textClipPath | |
// and clear the current path | |
textClipPath = cairo_copy_path (cairo); | |
cairo_new_path (cairo); | |
if (cairo_shape) { | |
cairo_new_path (cairo_shape); | |
} | |
} | |
finish: | |
gfree (glyphs); | |
glyphs = NULL; | |
if (use_show_text_glyphs) { | |
gfree (clusters); | |
clusters = NULL; | |
gfree (utf8); | |
utf8 = NULL; | |
} | |
} | |
GBool CairoOutputDev::beginType3Char(GfxState *state, double x, double y, | |
double dx, double dy, | |
CharCode code, Unicode *u, int uLen) { | |
cairo_save (cairo); | |
double *ctm; | |
cairo_matrix_t matrix; | |
ctm = state->getCTM(); | |
matrix.xx = ctm[0]; | |
matrix.yx = ctm[1]; | |
matrix.xy = ctm[2]; | |
matrix.yy = ctm[3]; | |
matrix.x0 = ctm[4]; | |
matrix.y0 = ctm[5]; | |
/* Restore the original matrix and then transform to matrix needed for the | |
* type3 font. This is ugly but seems to work. Perhaps there is a better way to do it?*/ | |
cairo_set_matrix(cairo, &orig_matrix); | |
cairo_transform(cairo, &matrix); | |
if (cairo_shape) { | |
cairo_save (cairo_shape); | |
cairo_set_matrix(cairo_shape, &orig_matrix); | |
cairo_transform(cairo_shape, &matrix); | |
} | |
cairo_pattern_destroy(stroke_pattern); | |
cairo_pattern_reference(fill_pattern); | |
stroke_pattern = fill_pattern; | |
return gFalse; | |
} | |
void CairoOutputDev::endType3Char(GfxState *state) { | |
cairo_restore (cairo); | |
if (cairo_shape) { | |
cairo_restore (cairo_shape); | |
} | |
} | |
void CairoOutputDev::type3D0(GfxState *state, double wx, double wy) { | |
t3_glyph_wx = wx; | |
t3_glyph_wy = wy; | |
} | |
void CairoOutputDev::type3D1(GfxState *state, double wx, double wy, | |
double llx, double lly, double urx, double ury) { | |
t3_glyph_wx = wx; | |
t3_glyph_wy = wy; | |
t3_glyph_bbox[0] = llx; | |
t3_glyph_bbox[1] = lly; | |
t3_glyph_bbox[2] = urx; | |
t3_glyph_bbox[3] = ury; | |
t3_glyph_has_bbox = gTrue; | |
} | |
void CairoOutputDev::beginTextObject(GfxState *state) { | |
} | |
void CairoOutputDev::endTextObject(GfxState *state) { | |
if (textClipPath) { | |
// clip the accumulated text path | |
cairo_append_path (cairo, textClipPath); | |
cairo_clip (cairo); | |
if (cairo_shape) { | |
cairo_append_path (cairo_shape, textClipPath); | |
cairo_clip (cairo_shape); | |
} | |
cairo_path_destroy (textClipPath); | |
textClipPath = NULL; | |
} | |
} | |
void CairoOutputDev::beginActualText(GfxState *state, GooString *text) | |
{ | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
if (this->text) | |
actualText->begin(state, text); | |
} | |
void CairoOutputDev::endActualText(GfxState *state) | |
{ | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
if (text) | |
actualText->end(state); | |
} | |
static inline int splashRound(SplashCoord x) { | |
return (int)floor(x + 0.5); | |
} | |
static inline int splashCeil(SplashCoord x) { | |
return (int)ceil(x); | |
} | |
static inline int splashFloor(SplashCoord x) { | |
return (int)floor(x); | |
} | |
static | |
cairo_surface_t *cairo_surface_create_similar_clip (cairo_t *cairo, cairo_content_t content) | |
{ | |
cairo_pattern_t *pattern; | |
cairo_surface_t *surface = NULL; | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
cairo_push_group_with_content (cairo, content); | |
pattern = cairo_pop_group (cairo); | |
cairo_pattern_get_surface (pattern, &surface); | |
cairo_surface_reference (surface); | |
cairo_pattern_destroy (pattern); | |
return surface; | |
} | |
void CairoOutputDev::beginTransparencyGroup(GfxState * /*state*/, double * /*bbox*/, | |
GfxColorSpace * blendingColorSpace, | |
GBool /*isolated*/, GBool knockout, | |
GBool forSoftMask) { | |
/* push color space */ | |
ColorSpaceStack* css = new ColorSpaceStack; | |
css->cs = blendingColorSpace; | |
css->knockout = knockout; | |
cairo_get_matrix(cairo, &css->group_matrix); | |
css->next = groupColorSpaceStack; | |
groupColorSpaceStack = css; | |
LOG(printf ("begin transparency group. knockout: %s\n", knockout ? "yes":"no")); | |
if (knockout) { | |
knockoutCount++; | |
if (!cairo_shape) { | |
/* create a surface for tracking the shape */ | |
cairo_surface_t *cairo_shape_surface = cairo_surface_create_similar_clip (cairo, CAIRO_CONTENT_ALPHA); | |
cairo_shape = cairo_create (cairo_shape_surface); | |
cairo_surface_destroy (cairo_shape_surface); | |
/* the color doesn't matter as long as it is opaque */ | |
cairo_set_source_rgb (cairo_shape, 0, 0, 0); | |
cairo_matrix_t matrix; | |
cairo_get_matrix (cairo, &matrix); | |
//printMatrix(&matrix); | |
cairo_set_matrix (cairo_shape, &matrix); | |
} else { | |
cairo_reference (cairo_shape); | |
} | |
} | |
if (groupColorSpaceStack->next && groupColorSpaceStack->next->knockout) { | |
/* we need to track the shape */ | |
cairo_push_group (cairo_shape); | |
} | |
if (0 && forSoftMask) | |
cairo_push_group_with_content (cairo, CAIRO_CONTENT_ALPHA); | |
else | |
cairo_push_group (cairo); | |
/* push_group has an implicit cairo_save() */ | |
if (knockout) { | |
/*XXX: let's hope this matches the semantics needed */ | |
cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); | |
} else { | |
cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); | |
} | |
} | |
void CairoOutputDev::endTransparencyGroup(GfxState * /*state*/) { | |
if (group) | |
cairo_pattern_destroy(group); | |
group = cairo_pop_group (cairo); | |
LOG(printf ("end transparency group\n")); | |
if (groupColorSpaceStack->next && groupColorSpaceStack->next->knockout) { | |
if (shape) | |
cairo_pattern_destroy(shape); | |
shape = cairo_pop_group (cairo_shape); | |
} | |
} | |
void CairoOutputDev::paintTransparencyGroup(GfxState * /*state*/, double * /*bbox*/) { | |
LOG(printf ("paint transparency group\n")); | |
cairo_save (cairo); | |
cairo_set_matrix (cairo, &groupColorSpaceStack->group_matrix); | |
cairo_set_source (cairo, group); | |
if (!mask) { | |
//XXX: deal with mask && shape case | |
if (shape) { | |
cairo_save (cairo); | |
/* OPERATOR_SOURCE w/ a mask is defined as (src IN mask) ADD (dest OUT mask) | |
* however our source has already been clipped to mask so we only need to | |
* do ADD and OUT */ | |
/* clear the shape mask */ | |
cairo_set_source (cairo, shape); | |
cairo_set_operator (cairo, CAIRO_OPERATOR_DEST_OUT); | |
cairo_paint (cairo); | |
cairo_set_operator (cairo, CAIRO_OPERATOR_ADD); | |
cairo_set_source (cairo, group); | |
cairo_paint (cairo); | |
cairo_restore (cairo); | |
cairo_pattern_destroy (shape); | |
shape = NULL; | |
} else { | |
cairo_paint_with_alpha (cairo, fill_opacity); | |
} | |
cairo_status_t status = cairo_status(cairo); | |
if (status) | |
printf("BAD status: %s\n", cairo_status_to_string(status)); | |
} else { | |
if (fill_opacity < 1.0) { | |
cairo_push_group(cairo); | |
} | |
cairo_save(cairo); | |
cairo_set_matrix(cairo, &mask_matrix); | |
cairo_mask(cairo, mask); | |
cairo_restore(cairo); | |
if (fill_opacity < 1.0) { | |
cairo_pop_group_to_source(cairo); | |
cairo_paint_with_alpha (cairo, fill_opacity); | |
} | |
cairo_pattern_destroy(mask); | |
mask = NULL; | |
} | |
popTransparencyGroup(); | |
cairo_restore(cairo); | |
} | |
static int luminocity(uint32_t x) | |
{ | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
int r = (x >> 16) & 0xff; | |
int g = (x >> 8) & 0xff; | |
int b = (x >> 0) & 0xff; | |
// an arbitrary integer approximation of .3*r + .59*g + .11*b | |
int y = (r*19661+g*38666+b*7209 + 32829)>>16; | |
return y; | |
} | |
/* XXX: do we need to deal with shape here? */ | |
void CairoOutputDev::setSoftMask(GfxState * state, double * bbox, GBool alpha, | |
Function * transferFunc, GfxColor * backdropColor) { | |
cairo_pattern_destroy(mask); | |
LOG(printf ("set softMask\n")); | |
if (!alpha || transferFunc) { | |
/* We need to mask according to the luminocity of the group. | |
* So we paint the group to an image surface convert it to a luminocity map | |
* and then use that as the mask. */ | |
/* Get clip extents in device space */ | |
double x1, y1, x2, y2, x_min, y_min, x_max, y_max; | |
cairo_clip_extents(cairo, &x1, &y1, &x2, &y2); | |
cairo_user_to_device(cairo, &x1, &y1); | |
cairo_user_to_device(cairo, &x2, &y2); | |
x_min = MIN(x1, x2); | |
y_min = MIN(y1, y2); | |
x_max = MAX(x1, x2); | |
y_max = MAX(y1, y2); | |
cairo_clip_extents(cairo, &x1, &y1, &x2, &y2); | |
cairo_user_to_device(cairo, &x1, &y2); | |
cairo_user_to_device(cairo, &x2, &y1); | |
x_min = MIN(x_min,MIN(x1, x2)); | |
y_min = MIN(y_min,MIN(y1, y2)); | |
x_max = MAX(x_max,MAX(x1, x2)); | |
y_max = MAX(y_max,MAX(y1, y2)); | |
int width = (int)(ceil(x_max) - floor(x_min)); | |
int height = (int)(ceil(y_max) - floor(y_min)); | |
/* Get group device offset */ | |
double x_offset, y_offset; | |
if (cairo_get_group_target(cairo) == cairo_get_target(cairo)) { | |
cairo_surface_get_device_offset(cairo_get_group_target(cairo), &x_offset, &y_offset); | |
} else { | |
cairo_surface_t *pats; | |
cairo_pattern_get_surface(group, &pats); | |
cairo_surface_get_device_offset(pats, &x_offset, &y_offset); | |
} | |
/* Adjust extents by group offset */ | |
x_min += x_offset; | |
y_min += y_offset; | |
cairo_surface_t *source = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); | |
cairo_t *maskCtx = cairo_create(source); | |
//XXX: hopefully this uses the correct color space */ | |
if (!alpha && groupColorSpaceStack->cs) { | |
GfxRGB backdropColorRGB; | |
groupColorSpaceStack->cs->getRGB(backdropColor, &backdropColorRGB); | |
/* paint the backdrop */ | |
cairo_set_source_rgb(maskCtx, | |
colToDbl(backdropColorRGB.r), | |
colToDbl(backdropColorRGB.g), | |
colToDbl(backdropColorRGB.b)); | |
} | |
cairo_paint(maskCtx); | |
/* Copy source ctm to mask ctm and translate origin so that the | |
* mask appears it the same location on the source surface. */ | |
cairo_matrix_t mat, tmat; | |
cairo_matrix_init_translate(&tmat, -x_min, -y_min); | |
cairo_get_matrix(cairo, &mat); | |
cairo_matrix_multiply(&mat, &mat, &tmat); | |
cairo_set_matrix(maskCtx, &mat); | |
/* make the device offset of the new mask match that of the group */ | |
cairo_surface_set_device_offset(source, x_offset, y_offset); | |
/* paint the group */ | |
cairo_set_source(maskCtx, group); | |
cairo_paint(maskCtx); | |
/* XXX status = cairo_status(maskCtx); */ | |
cairo_destroy(maskCtx); | |
/* convert to a luminocity map */ | |
uint32_t *source_data = (uint32_t*)cairo_image_surface_get_data(source); | |
/* get stride in units of 32 bits */ | |
int stride = cairo_image_surface_get_stride(source)/4; | |
for (int y=0; y<height; y++) { | |
for (int x=0; x<width; x++) { | |
int lum = alpha ? fill_opacity : luminocity(source_data[y*stride + x]); | |
if (transferFunc) { | |
double lum_in, lum_out; | |
lum_in = lum/256.0; | |
transferFunc->transform(&lum_in, &lum_out); | |
lum = (int)(lum_out * 255.0 + 0.5); | |
} | |
source_data[y*stride + x] = lum << 24; | |
} | |
} | |
cairo_surface_mark_dirty (source); | |
/* setup the new mask pattern */ | |
mask = cairo_pattern_create_for_surface(source); | |
cairo_get_matrix(cairo, &mask_matrix); | |
if (cairo_get_group_target(cairo) == cairo_get_target(cairo)) { | |
cairo_pattern_set_matrix(mask, &mat); | |
} else { | |
cairo_matrix_t patMatrix; | |
cairo_pattern_get_matrix(group, &patMatrix); | |
/* Apply x_min, y_min offset to it appears in the same location as source. */ | |
cairo_matrix_multiply(&patMatrix, &patMatrix, &tmat); | |
cairo_pattern_set_matrix(mask, &patMatrix); | |
} | |
cairo_surface_destroy(source); | |
} else if (alpha) { | |
mask = cairo_pattern_reference(group); | |
cairo_get_matrix(cairo, &mask_matrix); | |
} | |
popTransparencyGroup(); | |
} | |
void CairoOutputDev::popTransparencyGroup() { | |
/* pop color space */ | |
ColorSpaceStack *css = groupColorSpaceStack; | |
if (css->knockout) { | |
knockoutCount--; | |
if (!knockoutCount) { | |
/* we don't need to track the shape anymore because | |
* we are not above any knockout groups */ | |
cairo_destroy(cairo_shape); | |
cairo_shape = NULL; | |
} | |
} | |
groupColorSpaceStack = css->next; | |
delete css; | |
} | |
void CairoOutputDev::clearSoftMask(GfxState * /*state*/) { | |
if (mask) | |
cairo_pattern_destroy(mask); | |
mask = NULL; | |
} | |
/* Taken from cairo/doc/tutorial/src/singular.c */ | |
static void | |
get_singular_values (const cairo_matrix_t *matrix, | |
double *major, | |
double *minor) | |
{ | |
double xx = matrix->xx, xy = matrix->xy; | |
double yx = matrix->yx, yy = matrix->yy; | |
double a = xx*xx+yx*yx; | |
double b = xy*xy+yy*yy; | |
double k = xx*xy+yx*yy; | |
double f = (a+b) * .5; | |
double g = (a-b) * .5; | |
double delta = sqrt (g*g + k*k); | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
if (major) | |
*major = sqrt (f + delta); | |
if (minor) | |
*minor = sqrt (f - delta); | |
} | |
void CairoOutputDev::getScaledSize(const cairo_matrix_t *matrix, | |
int orig_width, | |
int orig_height, | |
int *scaledWidth, | |
int *scaledHeight) | |
{ | |
double xScale; | |
double yScale; | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
if (orig_width > orig_height) | |
get_singular_values (matrix, &xScale, &yScale); | |
else | |
get_singular_values (matrix, &yScale, &xScale); | |
int tx, tx2, ty, ty2; /* the integer co-oridinates of the resulting image */ | |
if (xScale >= 0) { | |
tx = splashRound(matrix->x0 - 0.01); | |
tx2 = splashRound(matrix->x0 + xScale + 0.01) - 1; | |
} else { | |
tx = splashRound(matrix->x0 + 0.01) - 1; | |
tx2 = splashRound(matrix->x0 + xScale - 0.01); | |
} | |
*scaledWidth = abs(tx2 - tx) + 1; | |
//scaledWidth = splashRound(fabs(xScale)); | |
if (*scaledWidth == 0) { | |
// technically, this should draw nothing, but it generally seems | |
// better to draw a one-pixel-wide stripe rather than throwing it | |
// away | |
*scaledWidth = 1; | |
} | |
if (yScale >= 0) { | |
ty = splashFloor(matrix->y0 + 0.01); | |
ty2 = splashCeil(matrix->y0 + yScale - 0.01); | |
} else { | |
ty = splashCeil(matrix->y0 - 0.01); | |
ty2 = splashFloor(matrix->y0 + yScale + 0.01); | |
} | |
*scaledHeight = abs(ty2 - ty); | |
if (*scaledHeight == 0) { | |
*scaledHeight = 1; | |
} | |
} | |
cairo_filter_t | |
CairoOutputDev::getFilterForSurface(cairo_surface_t *image, | |
GBool interpolate) | |
{ | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
if (interpolate) | |
return CAIRO_FILTER_BILINEAR; | |
int orig_width = cairo_image_surface_get_width (image); | |
int orig_height = cairo_image_surface_get_height (image); | |
if (orig_width == 0 || orig_height == 0) | |
return CAIRO_FILTER_NEAREST; | |
/* When printing, don't change the interpolation. */ | |
if (printing) | |
return CAIRO_FILTER_NEAREST; | |
cairo_matrix_t matrix; | |
cairo_get_matrix(cairo, &matrix); | |
int scaled_width, scaled_height; | |
getScaledSize (&matrix, orig_width, orig_height, &scaled_width, &scaled_height); | |
/* When scale factor is >= 400% we don't interpolate. See bugs #25268, #9860 */ | |
if (scaled_width / orig_width >= 4 || scaled_height / orig_height >= 4) | |
return CAIRO_FILTER_NEAREST; | |
return CAIRO_FILTER_BILINEAR; | |
} | |
void CairoOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, | |
int width, int height, GBool invert, | |
GBool interpolate, GBool inlineImg) { | |
/* FIXME: Doesn't the image mask support any colorspace? */ | |
cairo_set_source (cairo, fill_pattern); | |
/* work around a cairo bug when scaling 1x1 surfaces */ | |
if (width == 1 && height == 1) { | |
ImageStream *imgStr; | |
Guchar pix; | |
int invert_bit; | |
imgStr = new ImageStream(str, width, 1, 1); | |
imgStr->reset(); | |
imgStr->getPixel(&pix); | |
imgStr->close(); | |
delete imgStr; | |
invert_bit = invert ? 1 : 0; | |
if (pix ^ invert_bit) | |
return; | |
cairo_save (cairo); | |
cairo_rectangle (cairo, 0., 0., width, height); | |
cairo_fill (cairo); | |
cairo_restore (cairo); | |
if (cairo_shape) { | |
cairo_save (cairo_shape); | |
cairo_rectangle (cairo_shape, 0., 0., width, height); | |
cairo_fill (cairo_shape); | |
cairo_restore (cairo_shape); | |
} | |
return; | |
} | |
/* shape is 1.0 for painted areas, 0.0 for unpainted ones */ | |
cairo_matrix_t matrix; | |
cairo_get_matrix (cairo, &matrix); | |
//XXX: it is possible that we should only do sub pixel positioning if | |
// we are rendering fonts */ | |
if (!printing && prescaleImages | |
/* not rotated */ | |
&& matrix.xy == 0 && matrix.yx == 0 | |
/* axes not flipped / not 180 deg rotated */ | |
&& matrix.xx > 0 && (upsideDown() ? -1 : 1) * matrix.yy > 0) { | |
drawImageMaskPrescaled(state, ref, str, width, height, invert, interpolate, inlineImg); | |
} else { | |
drawImageMaskRegular(state, ref, str, width, height, invert, interpolate, inlineImg); | |
} | |
} | |
void CairoOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, | |
int width, int height, GBool invert, | |
GBool inlineImg, double *baseMatrix) { | |
/* FIXME: Doesn't the image mask support any colorspace? */ | |
cairo_set_source (cairo, fill_pattern); | |
/* work around a cairo bug when scaling 1x1 surfaces */ | |
if (width == 1 && height == 1) { | |
ImageStream *imgStr; | |
Guchar pix; | |
int invert_bit; | |
imgStr = new ImageStream(str, width, 1, 1); | |
imgStr->reset(); | |
imgStr->getPixel(&pix); | |
imgStr->close(); | |
delete imgStr; | |
invert_bit = invert ? 1 : 0; | |
if (!(pix ^ invert_bit)) { | |
cairo_save (cairo); | |
cairo_rectangle (cairo, 0., 0., width, height); | |
cairo_fill (cairo); | |
cairo_restore (cairo); | |
if (cairo_shape) { | |
cairo_save (cairo_shape); | |
cairo_rectangle (cairo_shape, 0., 0., width, height); | |
cairo_fill (cairo_shape); | |
cairo_restore (cairo_shape); | |
} | |
} | |
} else { | |
cairo_push_group_with_content (cairo, CAIRO_CONTENT_ALPHA); | |
/* shape is 1.0 for painted areas, 0.0 for unpainted ones */ | |
cairo_matrix_t matrix; | |
cairo_get_matrix (cairo, &matrix); | |
//XXX: it is possible that we should only do sub pixel positioning if | |
// we are rendering fonts */ | |
if (!printing && prescaleImages && matrix.xy == 0.0 && matrix.yx == 0.0) { | |
drawImageMaskPrescaled(state, ref, str, width, height, invert, gFalse, inlineImg); | |
} else { | |
drawImageMaskRegular(state, ref, str, width, height, invert, gFalse, inlineImg); | |
} | |
if (state->getFillColorSpace()->getMode() == csPattern) { | |
cairo_set_source_rgb (cairo, 1, 1, 1); | |
cairo_set_matrix (cairo, &mask_matrix); | |
cairo_mask (cairo, mask); | |
} | |
if (mask) | |
cairo_pattern_destroy (mask); | |
mask = cairo_pop_group (cairo); | |
} | |
saveState(state); | |
double bbox[4] = {0,0,1,1}; // dummy | |
beginTransparencyGroup(state, bbox, state->getFillColorSpace(), | |
gTrue, gFalse, gFalse); | |
} | |
void CairoOutputDev::unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix) { | |
double bbox[4] = {0,0,1,1}; // dummy | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
endTransparencyGroup(state); | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
restoreState(state); | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
paintTransparencyGroup(state, bbox); | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
clearSoftMask(state); | |
} | |
void CairoOutputDev::drawImageMaskRegular(GfxState *state, Object *ref, Stream *str, | |
int width, int height, GBool invert, | |
GBool interpolate, GBool inlineImg) { | |
unsigned char *buffer; | |
unsigned char *dest; | |
cairo_surface_t *image; | |
cairo_pattern_t *pattern; | |
int x, y, i, bit; | |
ImageStream *imgStr; | |
Guchar *pix; | |
cairo_matrix_t matrix; | |
int invert_bit; | |
int row_stride; | |
cairo_filter_t filter; | |
/* TODO: Do we want to cache these? */ | |
imgStr = new ImageStream(str, width, 1, 1); | |
imgStr->reset(); | |
image = cairo_image_surface_create (CAIRO_FORMAT_A1, width, height); | |
if (cairo_surface_status (image)) | |
goto cleanup; | |
buffer = cairo_image_surface_get_data (image); | |
row_stride = cairo_image_surface_get_stride (image); | |
invert_bit = invert ? 1 : 0; | |
for (y = 0; y < height; y++) { | |
pix = imgStr->getLine(); | |
dest = buffer + y * row_stride; | |
i = 0; | |
bit = 0; | |
for (x = 0; x < width; x++) { | |
if (bit == 0) | |
dest[i] = 0; | |
if (!(pix[x] ^ invert_bit)) { | |
#ifdef WORDS_BIGENDIAN | |
dest[i] |= (1 << (7 - bit)); | |
#else | |
dest[i] |= (1 << bit); | |
#endif | |
} | |
bit++; | |
if (bit > 7) { | |
bit = 0; | |
i++; | |
} | |
} | |
} | |
filter = getFilterForSurface (image, interpolate); | |
cairo_surface_mark_dirty (image); | |
pattern = cairo_pattern_create_for_surface (image); | |
cairo_surface_destroy (image); | |
if (cairo_pattern_status (pattern)) | |
goto cleanup; | |
LOG (printf ("drawImageMask %dx%d\n", width, height)); | |
cairo_pattern_set_filter (pattern, filter); | |
if (!printing) | |
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); | |
cairo_matrix_init_translate (&matrix, 0, height); | |
cairo_matrix_scale (&matrix, width, -height); | |
cairo_pattern_set_matrix (pattern, &matrix); | |
if (cairo_pattern_status (pattern)) { | |
cairo_pattern_destroy (pattern); | |
goto cleanup; | |
} | |
if (state->getFillColorSpace()->getMode() == csPattern) { | |
mask = cairo_pattern_reference (pattern); | |
cairo_get_matrix (cairo, &mask_matrix); | |
} else if (!printing) { | |
cairo_save (cairo); | |
cairo_rectangle (cairo, 0., 0., 1., 1.); | |
cairo_clip (cairo); | |
cairo_mask (cairo, pattern); | |
cairo_restore (cairo); | |
} else { | |
cairo_mask (cairo, pattern); | |
} | |
if (cairo_shape) { | |
cairo_save (cairo_shape); | |
cairo_set_source (cairo_shape, pattern); | |
if (!printing) { | |
cairo_rectangle (cairo_shape, 0., 0., 1., 1.); | |
cairo_fill (cairo_shape); | |
} else { | |
cairo_mask (cairo_shape, pattern); | |
} | |
cairo_restore (cairo_shape); | |
} | |
cairo_pattern_destroy (pattern); | |
cleanup: | |
imgStr->close(); | |
delete imgStr; | |
} | |
void CairoOutputDev::drawImageMaskPrescaled(GfxState *state, Object *ref, Stream *str, | |
int width, int height, GBool invert, | |
GBool interpolate, GBool inlineImg) { | |
unsigned char *buffer; | |
cairo_surface_t *image; | |
cairo_pattern_t *pattern; | |
ImageStream *imgStr; | |
Guchar *pix; | |
cairo_matrix_t matrix; | |
int invert_bit; | |
int row_stride; | |
/* cairo does a very poor job of scaling down images so we scale them ourselves */ | |
LOG (printf ("drawImageMaskPrescaled %dx%d\n", width, height)); | |
/* this scaling code is adopted from the splash image scaling code */ | |
cairo_get_matrix(cairo, &matrix); | |
#if 0 | |
printf("[%f %f], [%f %f], %f %f\n", matrix.xx, matrix.xy, matrix.yx, matrix.yy, matrix.x0, matrix.y0); | |
#endif | |
/* this whole computation should be factored out */ | |
double xScale = matrix.xx; | |
double yScale = matrix.yy; | |
int tx, tx2, ty, ty2; /* the integer co-oridinates of the resulting image */ | |
int scaledHeight; | |
int scaledWidth; | |
if (xScale >= 0) { | |
tx = splashRound(matrix.x0 - 0.01); | |
tx2 = splashRound(matrix.x0 + xScale + 0.01) - 1; | |
} else { | |
tx = splashRound(matrix.x0 + 0.01) - 1; | |
tx2 = splashRound(matrix.x0 + xScale - 0.01); | |
} | |
scaledWidth = abs(tx2 - tx) + 1; | |
//scaledWidth = splashRound(fabs(xScale)); | |
if (scaledWidth == 0) { | |
// technically, this should draw nothing, but it generally seems | |
// better to draw a one-pixel-wide stripe rather than throwing it | |
// away | |
scaledWidth = 1; | |
} | |
if (yScale >= 0) { | |
ty = splashFloor(matrix.y0 + 0.01); | |
ty2 = splashCeil(matrix.y0 + yScale - 0.01); | |
} else { | |
ty = splashCeil(matrix.y0 - 0.01); | |
ty2 = splashFloor(matrix.y0 + yScale + 0.01); | |
} | |
scaledHeight = abs(ty2 - ty); | |
if (scaledHeight == 0) { | |
scaledHeight = 1; | |
} | |
#if 0 | |
printf("xscale: %g, yscale: %g\n", xScale, yScale); | |
printf("width: %d, height: %d\n", width, height); | |
printf("scaledWidth: %d, scaledHeight: %d\n", scaledWidth, scaledHeight); | |
#endif | |
/* compute the required padding */ | |
/* Padding is used to preserve the aspect ratio. | |
We compute total_pad to make (height+total_pad)/scaledHeight as close to height/yScale as possible */ | |
int head_pad = 0; | |
int tail_pad = 0; | |
int total_pad = splashRound(height*(scaledHeight/fabs(yScale)) - height); | |
/* compute the two pieces of padding */ | |
if (total_pad > 0) { | |
//XXX: i'm not positive fabs() is correct | |
float tail_error = fabs(matrix.y0 - ty); | |
float head_error = fabs(ty2 - (matrix.y0 + yScale)); | |
float tail_fraction = tail_error/(tail_error + head_error); | |
tail_pad = splashRound(total_pad*tail_fraction); | |
head_pad = total_pad - tail_pad; | |
} else { | |
tail_pad = 0; | |
head_pad = 0; | |
} | |
int origHeight = height; | |
height += tail_pad; | |
height += head_pad; | |
#if 0 | |
printf("head_pad: %d tail_pad: %d\n", head_pad, tail_pad); | |
printf("origHeight: %d height: %d\n", origHeight, height); | |
printf("ty: %d, ty2: %d\n", ty, ty2); | |
#endif | |
/* TODO: Do we want to cache these? */ | |
imgStr = new ImageStream(str, width, 1, 1); | |
imgStr->reset(); | |
invert_bit = invert ? 1 : 0; | |
image = cairo_image_surface_create (CAIRO_FORMAT_A8, scaledWidth, scaledHeight); | |
if (cairo_surface_status (image)) { | |
imgStr->close(); | |
delete imgStr; | |
return; | |
} | |
buffer = cairo_image_surface_get_data (image); | |
row_stride = cairo_image_surface_get_stride (image); | |
int yp = height / scaledHeight; | |
int yq = height % scaledHeight; | |
int xp = width / scaledWidth; | |
int xq = width % scaledWidth; | |
int yt = 0; | |
int origHeight_c = origHeight; | |
/* use MIN() because yp might be > origHeight because of padding */ | |
unsigned char *pixBuf = (unsigned char *)malloc(MIN(yp+1, origHeight)*width); | |
int lastYStep = 1; | |
int total = 0; | |
for (int y = 0; y < scaledHeight; y++) { | |
// y scale Bresenham | |
int yStep = yp; | |
yt += yq; | |
if (yt >= scaledHeight) { | |
yt -= scaledHeight; | |
++yStep; | |
} | |
// read row (s) from image ignoring the padding as appropriate | |
{ | |
int n = (yp > 0) ? yStep : lastYStep; | |
total += n; | |
if (n > 0) { | |
unsigned char *p = pixBuf; | |
int head_pad_count = head_pad; | |
int origHeight_count = origHeight; | |
int tail_pad_count = tail_pad; | |
for (int i=0; i<n; i++) { | |
// get row | |
if (head_pad_count) { | |
head_pad_count--; | |
} else if (origHeight_count) { | |
pix = imgStr->getLine(); | |
for (int j=0; j<width; j++) { | |
if (pix[j] ^ invert_bit) | |
p[j] = 0; | |
else | |
p[j] = 255; | |
} | |
origHeight_count--; | |
p += width; | |
} else if (tail_pad_count) { | |
tail_pad_count--; | |
} else { | |
printf("%d %d\n", n, total); | |
assert(0 && "over run\n"); | |
} | |
} | |
} | |
} | |
lastYStep = yStep; | |
int k1 = y; | |
int xt = 0; | |
int xSrc = 0; | |
int x1 = k1; | |
int n = yStep > 0 ? yStep : 1; | |
int origN = n; | |
/* compute the size of padding and pixels that will be used for this row */ | |
int head_pad_size = MIN(n, head_pad); | |
n -= head_pad_size; | |
head_pad -= MIN(head_pad_size, yStep); | |
int pix_size = MIN(n, origHeight); | |
n -= pix_size; | |
origHeight -= MIN(pix_size, yStep); | |
int tail_pad_size = MIN(n, tail_pad); | |
n -= tail_pad_size; | |
tail_pad -= MIN(tail_pad_size, yStep); | |
if (n != 0) { | |
printf("n = %d (%d %d %d)\n", n, head_pad_size, pix_size, tail_pad_size); | |
assert(n == 0); | |
} | |
for (int x = 0; x < scaledWidth; ++x) { | |
int xStep = xp; | |
xt += xq; | |
if (xt >= scaledWidth) { | |
xt -= scaledWidth; | |
++xStep; | |
} | |
int m = xStep > 0 ? xStep : 1; | |
float pixAcc0 = 0; | |
/* could m * head_pad_size * tail_pad_size overflow? */ | |
if (invert_bit) { | |
pixAcc0 += m * head_pad_size * tail_pad_size * 255; | |
} else { | |
pixAcc0 += m * head_pad_size * tail_pad_size * 0; | |
} | |
/* Accumulate all of the source pixels for the destination pixel */ | |
for (int i = 0; i < pix_size; ++i) { | |
for (int j = 0; j< m; ++j) { | |
if (xSrc + i*width + j > MIN(yp + 1, origHeight_c)*width) { | |
printf("%d > %d (%d %d %d %d) (%d %d %d)\n", xSrc + i*width + j, MIN(yp + 1, origHeight_c)*width, xSrc, i , width, j, yp, origHeight_c, width); | |
printf("%d %d %d\n", head_pad_size, pix_size, tail_pad_size); | |
assert(0 && "bad access\n"); | |
} | |
pixAcc0 += pixBuf[xSrc + i*width + j]; | |
} | |
} | |
buffer[y * row_stride + x] = splashFloor(pixAcc0 / (origN*m)); | |
xSrc += xStep; | |
x1 += 1; | |
} | |
} | |
free(pixBuf); | |
cairo_surface_mark_dirty (image); | |
pattern = cairo_pattern_create_for_surface (image); | |
cairo_surface_destroy (image); | |
if (cairo_pattern_status (pattern)) { | |
imgStr->close(); | |
delete imgStr; | |
return; | |
} | |
/* we should actually be using CAIRO_FILTER_NEAREST here. However, | |
* cairo doesn't yet do minifaction filtering causing scaled down | |
* images with CAIRO_FILTER_NEAREST to look really bad */ | |
cairo_pattern_set_filter (pattern, | |
interpolate ? CAIRO_FILTER_BEST : CAIRO_FILTER_FAST); | |
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); | |
if (state->getFillColorSpace()->getMode() == csPattern) { | |
cairo_matrix_init_translate (&matrix, 0, scaledHeight); | |
cairo_matrix_scale (&matrix, scaledWidth, -scaledHeight); | |
cairo_pattern_set_matrix (pattern, &matrix); | |
if (cairo_pattern_status (pattern)) { | |
cairo_pattern_destroy (pattern); | |
imgStr->close(); | |
delete imgStr; | |
return; | |
} | |
mask = cairo_pattern_reference (pattern); | |
cairo_get_matrix (cairo, &mask_matrix); | |
} else { | |
cairo_save (cairo); | |
/* modify our current transformation so that the prescaled image | |
* goes where it is supposed to */ | |
cairo_get_matrix(cairo, &matrix); | |
cairo_scale(cairo, 1.0/matrix.xx, 1.0/matrix.yy); | |
// get integer co-ords | |
cairo_translate (cairo, tx - matrix.x0, ty2 - matrix.y0); | |
if (yScale > 0) | |
cairo_scale(cairo, 1, -1); | |
cairo_rectangle (cairo, 0., 0., scaledWidth, scaledHeight); | |
cairo_clip (cairo); | |
cairo_mask (cairo, pattern); | |
//cairo_get_matrix(cairo, &matrix); | |
//printf("mask at: [%f %f], [%f %f], %f %f\n\n", matrix.xx, matrix.xy, matrix.yx, matrix.yy, matrix.x0, matrix.y0); | |
cairo_restore(cairo); | |
} | |
if (cairo_shape) { | |
cairo_save (cairo_shape); | |
/* modify our current transformation so that the prescaled image | |
* goes where it is supposed to */ | |
cairo_get_matrix(cairo_shape, &matrix); | |
cairo_scale(cairo_shape, 1.0/matrix.xx, 1.0/matrix.yy); | |
// get integer co-ords | |
cairo_translate (cairo_shape, tx - matrix.x0, ty2 - matrix.y0); | |
if (yScale > 0) | |
cairo_scale(cairo_shape, 1, -1); | |
cairo_rectangle (cairo_shape, 0., 0., scaledWidth, scaledHeight); | |
cairo_fill (cairo_shape); | |
cairo_restore(cairo_shape); | |
} | |
cairo_pattern_destroy (pattern); | |
imgStr->close(); | |
delete imgStr; | |
} | |
void CairoOutputDev::drawMaskedImage(GfxState *state, Object *ref, | |
Stream *str, int width, int height, | |
GfxImageColorMap *colorMap, | |
GBool interpolate, | |
Stream *maskStr, int maskWidth, | |
int maskHeight, GBool maskInvert, | |
GBool maskInterpolate) | |
{ | |
ImageStream *maskImgStr, *imgStr; | |
int row_stride; | |
unsigned char *maskBuffer, *buffer; | |
unsigned char *maskDest; | |
unsigned int *dest; | |
cairo_surface_t *maskImage, *image; | |
cairo_pattern_t *maskPattern, *pattern; | |
cairo_matrix_t matrix; | |
cairo_matrix_t maskMatrix; | |
Guchar *pix; | |
int x, y; | |
int invert_bit; | |
cairo_filter_t filter; | |
cairo_filter_t maskFilter; | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
maskImgStr = new ImageStream(maskStr, maskWidth, 1, 1); | |
maskImgStr->reset(); | |
maskImage = cairo_image_surface_create (CAIRO_FORMAT_A8, maskWidth, maskHeight); | |
if (cairo_surface_status (maskImage)) { | |
maskImgStr->close(); | |
delete maskImgStr; | |
return; | |
} | |
maskBuffer = cairo_image_surface_get_data (maskImage); | |
row_stride = cairo_image_surface_get_stride (maskImage); | |
invert_bit = maskInvert ? 1 : 0; | |
for (y = 0; y < maskHeight; y++) { | |
pix = maskImgStr->getLine(); | |
maskDest = maskBuffer + y * row_stride; | |
for (x = 0; x < maskWidth; x++) { | |
if (pix[x] ^ invert_bit) | |
*maskDest++ = 0; | |
else | |
*maskDest++ = 255; | |
} | |
} | |
maskImgStr->close(); | |
delete maskImgStr; | |
maskFilter = getFilterForSurface (maskImage, maskInterpolate); | |
cairo_surface_mark_dirty (maskImage); | |
maskPattern = cairo_pattern_create_for_surface (maskImage); | |
cairo_surface_destroy (maskImage); | |
if (cairo_pattern_status (maskPattern)) | |
return; | |
#if 0 | |
/* ICCBased color space doesn't do any color correction | |
* so check its underlying color space as well */ | |
int is_identity_transform; | |
is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB || | |
(colorMap->getColorSpace()->getMode() == csICCBased && | |
((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB); | |
#endif | |
/* TODO: Do we want to cache these? */ | |
imgStr = new ImageStream(str, width, | |
colorMap->getNumPixelComps(), | |
colorMap->getBits()); | |
imgStr->reset(); | |
image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); | |
if (cairo_surface_status (image)) | |
goto cleanup; | |
buffer = cairo_image_surface_get_data (image); | |
row_stride = cairo_image_surface_get_stride (image); | |
for (y = 0; y < height; y++) { | |
dest = (unsigned int *) (buffer + y * row_stride); | |
pix = imgStr->getLine(); | |
colorMap->getRGBLine (pix, dest, width); | |
} | |
filter = getFilterForSurface (image, interpolate); | |
cairo_surface_mark_dirty (image); | |
pattern = cairo_pattern_create_for_surface (image); | |
cairo_surface_destroy (image); | |
if (cairo_pattern_status (pattern)) | |
goto cleanup; | |
LOG (printf ("drawMaskedImage %dx%d\n", width, height)); | |
cairo_pattern_set_filter (pattern, filter); | |
cairo_pattern_set_filter (maskPattern, maskFilter); | |
if (!printing) { | |
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); | |
cairo_pattern_set_extend (maskPattern, CAIRO_EXTEND_PAD); | |
} | |
cairo_matrix_init_translate (&matrix, 0, height); | |
cairo_matrix_scale (&matrix, width, -height); | |
cairo_pattern_set_matrix (pattern, &matrix); | |
if (cairo_pattern_status (pattern)) { | |
cairo_pattern_destroy (pattern); | |
cairo_pattern_destroy (maskPattern); | |
goto cleanup; | |
} | |
cairo_matrix_init_translate (&maskMatrix, 0, maskHeight); | |
cairo_matrix_scale (&maskMatrix, maskWidth, -maskHeight); | |
cairo_pattern_set_matrix (maskPattern, &maskMatrix); | |
if (cairo_pattern_status (maskPattern)) { | |
cairo_pattern_destroy (maskPattern); | |
cairo_pattern_destroy (pattern); | |
goto cleanup; | |
} | |
if (!printing) { | |
cairo_save (cairo); | |
cairo_set_source (cairo, pattern); | |
cairo_rectangle (cairo, 0., 0., 1., 1.); | |
cairo_clip (cairo); | |
cairo_mask (cairo, maskPattern); | |
cairo_restore (cairo); | |
} else { | |
cairo_set_source (cairo, pattern); | |
cairo_mask (cairo, maskPattern); | |
} | |
if (cairo_shape) { | |
cairo_save (cairo_shape); | |
cairo_set_source (cairo_shape, pattern); | |
if (!printing) { | |
cairo_rectangle (cairo_shape, 0., 0., 1., 1.); | |
cairo_fill (cairo_shape); | |
} else { | |
cairo_mask (cairo_shape, pattern); | |
} | |
cairo_restore (cairo_shape); | |
} | |
cairo_pattern_destroy (maskPattern); | |
cairo_pattern_destroy (pattern); | |
cleanup: | |
imgStr->close(); | |
delete imgStr; | |
} | |
//XXX: is this affect by AIS(alpha is shape)? | |
void CairoOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, | |
int width, int height, | |
GfxImageColorMap *colorMap, | |
GBool interpolate, | |
Stream *maskStr, | |
int maskWidth, int maskHeight, | |
GfxImageColorMap *maskColorMap, | |
GBool maskInterpolate) | |
{ | |
ImageStream *maskImgStr, *imgStr; | |
int row_stride; | |
unsigned char *maskBuffer, *buffer; | |
unsigned char *maskDest; | |
unsigned int *dest; | |
cairo_surface_t *maskImage, *image; | |
cairo_pattern_t *maskPattern, *pattern; | |
cairo_matrix_t maskMatrix, matrix; | |
Guchar *pix; | |
int y; | |
cairo_filter_t filter; | |
cairo_filter_t maskFilter; | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
maskImgStr = new ImageStream(maskStr, maskWidth, | |
maskColorMap->getNumPixelComps(), | |
maskColorMap->getBits()); | |
maskImgStr->reset(); | |
maskImage = cairo_image_surface_create (CAIRO_FORMAT_A8, maskWidth, maskHeight); | |
if (cairo_surface_status (maskImage)) { | |
maskImgStr->close(); | |
delete maskImgStr; | |
return; | |
} | |
maskBuffer = cairo_image_surface_get_data (maskImage); | |
row_stride = cairo_image_surface_get_stride (maskImage); | |
for (y = 0; y < maskHeight; y++) { | |
maskDest = (unsigned char *) (maskBuffer + y * row_stride); | |
pix = maskImgStr->getLine(); | |
maskColorMap->getGrayLine (pix, maskDest, maskWidth); | |
} | |
maskImgStr->close(); | |
delete maskImgStr; | |
maskFilter = getFilterForSurface (maskImage, maskInterpolate); | |
cairo_surface_mark_dirty (maskImage); | |
maskPattern = cairo_pattern_create_for_surface (maskImage); | |
cairo_surface_destroy (maskImage); | |
if (cairo_pattern_status (maskPattern)) | |
return; | |
#if 0 | |
/* ICCBased color space doesn't do any color correction | |
* so check its underlying color space as well */ | |
int is_identity_transform; | |
is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB || | |
(colorMap->getColorSpace()->getMode() == csICCBased && | |
((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB); | |
#endif | |
/* TODO: Do we want to cache these? */ | |
imgStr = new ImageStream(str, width, | |
colorMap->getNumPixelComps(), | |
colorMap->getBits()); | |
imgStr->reset(); | |
image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); | |
if (cairo_surface_status (image)) | |
goto cleanup; | |
buffer = cairo_image_surface_get_data (image); | |
row_stride = cairo_image_surface_get_stride (image); | |
for (y = 0; y < height; y++) { | |
dest = (unsigned int *) (buffer + y * row_stride); | |
pix = imgStr->getLine(); | |
colorMap->getRGBLine (pix, dest, width); | |
} | |
filter = getFilterForSurface (image, interpolate); | |
cairo_surface_mark_dirty (image); | |
setMimeData(state, str, ref, colorMap, image); | |
pattern = cairo_pattern_create_for_surface (image); | |
cairo_surface_destroy (image); | |
if (cairo_pattern_status (pattern)) | |
goto cleanup; | |
LOG (printf ("drawSoftMaskedImage %dx%d\n", width, height)); | |
cairo_pattern_set_filter (pattern, filter); | |
cairo_pattern_set_filter (maskPattern, maskFilter); | |
if (!printing) { | |
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); | |
cairo_pattern_set_extend (maskPattern, CAIRO_EXTEND_PAD); | |
} | |
cairo_matrix_init_translate (&matrix, 0, height); | |
cairo_matrix_scale (&matrix, width, -height); | |
cairo_pattern_set_matrix (pattern, &matrix); | |
if (cairo_pattern_status (pattern)) { | |
cairo_pattern_destroy (pattern); | |
cairo_pattern_destroy (maskPattern); | |
goto cleanup; | |
} | |
cairo_matrix_init_translate (&maskMatrix, 0, maskHeight); | |
cairo_matrix_scale (&maskMatrix, maskWidth, -maskHeight); | |
cairo_pattern_set_matrix (maskPattern, &maskMatrix); | |
if (cairo_pattern_status (maskPattern)) { | |
cairo_pattern_destroy (maskPattern); | |
cairo_pattern_destroy (pattern); | |
goto cleanup; | |
} | |
if (fill_opacity != 1.0) | |
cairo_push_group (cairo); | |
else | |
cairo_save (cairo); | |
cairo_set_source (cairo, pattern); | |
if (!printing) { | |
cairo_rectangle (cairo, 0., 0., 1., 1.); | |
cairo_clip (cairo); | |
} | |
cairo_mask (cairo, maskPattern); | |
if (fill_opacity != 1.0) { | |
cairo_pop_group_to_source (cairo); | |
cairo_save (cairo); | |
if (!printing) { | |
cairo_rectangle (cairo, 0., 0., 1., 1.); | |
cairo_clip (cairo); | |
} | |
cairo_paint_with_alpha (cairo, fill_opacity); | |
} | |
cairo_restore (cairo); | |
if (cairo_shape) { | |
cairo_save (cairo_shape); | |
cairo_set_source (cairo_shape, pattern); | |
if (!printing) { | |
cairo_rectangle (cairo_shape, 0., 0., 1., 1.); | |
cairo_fill (cairo_shape); | |
} else { | |
cairo_mask (cairo_shape, pattern); | |
} | |
cairo_restore (cairo_shape); | |
} | |
cairo_pattern_destroy (maskPattern); | |
cairo_pattern_destroy (pattern); | |
cleanup: | |
imgStr->close(); | |
delete imgStr; | |
} | |
GBool CairoOutputDev::getStreamData (Stream *str, char **buffer, int *length) | |
{ | |
int len, i; | |
char *strBuffer; | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
len = 0; | |
str->close(); | |
str->reset(); | |
while (str->getChar() != EOF) len++; | |
if (len == 0) | |
return gFalse; | |
strBuffer = (char *)gmalloc (len); | |
str->close(); | |
str->reset(); | |
for (i = 0; i < len; ++i) | |
strBuffer[i] = str->getChar(); | |
*buffer = strBuffer; | |
*length = len; | |
return gTrue; | |
} | |
static GBool colorMapHasIdentityDecodeMap(GfxImageColorMap *colorMap) | |
{ | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
for (int i = 0; i < colorMap->getNumPixelComps(); i++) { | |
if (colorMap->getDecodeLow(i) != 0.0 || colorMap->getDecodeHigh(i) != 1.0) | |
return gFalse; | |
} | |
return gTrue; | |
} | |
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 11, 2) | |
static cairo_status_t setMimeIdFromRef(cairo_surface_t *surface, | |
const char *mime_type, | |
const char *mime_id_prefix, | |
Ref ref) | |
{ | |
GooString *mime_id; | |
char *idBuffer; | |
cairo_status_t status; | |
mime_id = new GooString; | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
if (mime_id_prefix) | |
mime_id->append(mime_id_prefix); | |
mime_id->appendf("{0:d}-{1:d}", ref.gen, ref.num); | |
idBuffer = copyString(mime_id->getCString()); | |
status = cairo_surface_set_mime_data (surface, mime_type, | |
(const unsigned char *)idBuffer, | |
mime_id->getLength(), | |
gfree, idBuffer); | |
delete mime_id; | |
if (status) | |
gfree (idBuffer); | |
return status; | |
} | |
#endif | |
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0) | |
GBool CairoOutputDev::setMimeDataForJBIG2Globals(Stream *str, | |
cairo_surface_t *image) | |
{ | |
JBIG2Stream *jb2Str = static_cast<JBIG2Stream *>(str); | |
Object* globalsStr = jb2Str->getGlobalsStream(); | |
char *globalsBuffer; | |
int globalsLength; | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
// nothing to do for JBIG2 stream without Globals | |
if (!globalsStr->isStream()) | |
return gTrue; | |
if (setMimeIdFromRef(image, CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID, NULL, | |
jb2Str->getGlobalsStreamRef())) | |
return gFalse; | |
if (!getStreamData(globalsStr->getStream(), &globalsBuffer, &globalsLength)) | |
return gFalse; | |
if (cairo_surface_set_mime_data (image, CAIRO_MIME_TYPE_JBIG2_GLOBAL, | |
(const unsigned char*)globalsBuffer, | |
globalsLength, | |
gfree, (void*)globalsBuffer)) | |
{ | |
gfree (globalsBuffer); | |
return gFalse; | |
} | |
return gTrue; | |
} | |
#endif | |
void CairoOutputDev::setMimeData(GfxState *state, Stream *str, Object *ref, | |
GfxImageColorMap *colorMap, cairo_surface_t *image) | |
{ | |
char *strBuffer; | |
int len; | |
Object obj; | |
GfxColorSpace *colorSpace; | |
StreamKind strKind = str->getKind(); | |
const char *mime_type; | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
if (!printing) | |
return; | |
switch (strKind) { | |
case strDCT: | |
mime_type = CAIRO_MIME_TYPE_JPEG; | |
break; | |
case strJPX: | |
mime_type = CAIRO_MIME_TYPE_JP2; | |
break; | |
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0) | |
case strJBIG2: | |
mime_type = CAIRO_MIME_TYPE_JBIG2; | |
break; | |
#endif | |
default: | |
return; | |
} | |
str->getDict()->lookup("ColorSpace", &obj); | |
colorSpace = GfxColorSpace::parse(NULL, &obj, this, state); | |
obj.free(); | |
// colorspace in stream dict may be different from colorspace in jpx | |
// data | |
if (strKind == strJPX && colorSpace) | |
return; | |
// only embed mime data for gray, rgb, and cmyk colorspaces. | |
if (colorSpace) { | |
GfxColorSpaceMode mode = colorSpace->getMode(); | |
delete colorSpace; | |
switch (mode) { | |
case csDeviceGray: | |
case csCalGray: | |
case csDeviceRGB: | |
case csCalRGB: | |
case csDeviceCMYK: | |
case csICCBased: | |
break; | |
case csLab: | |
case csIndexed: | |
case csSeparation: | |
case csDeviceN: | |
case csPattern: | |
return; | |
} | |
} | |
if (!colorMapHasIdentityDecodeMap(colorMap)) | |
return; | |
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0) | |
if (strKind == strJBIG2 && !setMimeDataForJBIG2Globals(str, image)) | |
return; | |
#endif | |
if (getStreamData (str->getNextStream(), &strBuffer, &len)) { | |
cairo_status_t status = CAIRO_STATUS_SUCCESS; | |
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 11, 2) | |
if (ref && ref->isRef()) { | |
status = setMimeIdFromRef(image, CAIRO_MIME_TYPE_UNIQUE_ID, | |
"poppler-surface-", ref->getRef()); | |
} | |
#endif | |
if (!status) { | |
status = cairo_surface_set_mime_data (image, mime_type, | |
(const unsigned char *)strBuffer, len, | |
gfree, strBuffer); | |
} | |
if (status) | |
gfree (strBuffer); | |
} | |
} | |
class RescaleDrawImage : public CairoRescaleBox { | |
private: | |
ImageStream *imgStr; | |
GfxRGB *lookup; | |
int width; | |
GfxImageColorMap *colorMap; | |
int *maskColors; | |
int current_row; | |
GBool imageError; | |
public: | |
cairo_surface_t *getSourceImage(Stream *str, | |
int widthA, int height, | |
int scaledWidth, int scaledHeight, | |
GBool printing, | |
GfxImageColorMap *colorMapA, | |
int *maskColorsA) { | |
cairo_surface_t *image = NULL; | |
int i; | |
lookup = NULL; | |
colorMap = colorMapA; | |
maskColors = maskColorsA; | |
width = widthA; | |
current_row = -1; | |
imageError = gFalse; | |
/* TODO: Do we want to cache these? */ | |
imgStr = new ImageStream(str, width, | |
colorMap->getNumPixelComps(), | |
colorMap->getBits()); | |
imgStr->reset(); | |
#if 0 | |
/* ICCBased color space doesn't do any color correction | |
* so check its underlying color space as well */ | |
int is_identity_transform; | |
is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB || | |
(colorMap->getColorSpace()->getMode() == csICCBased && | |
((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB); | |
#endif | |
// special case for one-channel (monochrome/gray/separation) images: | |
// build a lookup table here | |
if (colorMap->getNumPixelComps() == 1) { | |
int n; | |
Guchar pix; | |
n = 1 << colorMap->getBits(); | |
lookup = (GfxRGB *)gmallocn(n, sizeof(GfxRGB)); | |
for (i = 0; i < n; ++i) { | |
pix = (Guchar)i; | |
colorMap->getRGB(&pix, &lookup[i]); | |
} | |
} | |
if (printing || scaledWidth >= width || scaledHeight >= height) { | |
// No downscaling. Create cairo image containing the source image data. | |
unsigned char *buffer; | |
int stride; | |
image = cairo_image_surface_create (maskColors ? | |
CAIRO_FORMAT_ARGB32 : | |
CAIRO_FORMAT_RGB24, | |
width, height); | |
if (cairo_surface_status (image)) | |
goto cleanup; | |
buffer = cairo_image_surface_get_data (image); | |
stride = cairo_image_surface_get_stride (image); | |
for (int y = 0; y < height; y++) { | |
uint32_t *dest = (uint32_t *) (buffer + y * stride); | |
getRow(y, dest); | |
} | |
} else { | |
// // Downscaling required. Create cairo image the size of the | |
// rescaled image and // downscale the source image data into | |
// the cairo image. downScaleImage() will call getRow() to read | |
// source image data from the image stream. This avoids having | |
// to create an image the size of the source image which may | |
// exceed cairo's 32676x32767 image size limit (and also saves a | |
// lot of memory). | |
image = cairo_image_surface_create (maskColors ? | |
CAIRO_FORMAT_ARGB32 : | |
CAIRO_FORMAT_RGB24, | |
scaledWidth, scaledHeight); | |
if (cairo_surface_status (image)) | |
goto cleanup; | |
downScaleImage(width, height, | |
scaledWidth, scaledHeight, | |
0, 0, scaledWidth, scaledHeight, | |
image); | |
} | |
cairo_surface_mark_dirty (image); | |
cleanup: | |
gfree(lookup); | |
imgStr->close(); | |
delete imgStr; | |
return image; | |
} | |
void getRow(int row_num, uint32_t *row_data) { | |
int i; | |
Guchar *pix; | |
if (row_num <= current_row) | |
return; | |
while (current_row < row_num) { | |
pix = imgStr->getLine(); | |
current_row++; | |
} | |
if (unlikely(pix == NULL)) { | |
memset(row_data, 0, width*4); | |
if (!imageError) { | |
error(errInternal, -1, "Bad image stream"); | |
imageError = gTrue; | |
} | |
} else if (lookup) { | |
Guchar *p = pix; | |
GfxRGB rgb; | |
for (i = 0; i < width; i++) { | |
rgb = lookup[*p]; | |
row_data[i] = | |
((int) colToByte(rgb.r) << 16) | | |
((int) colToByte(rgb.g) << 8) | | |
((int) colToByte(rgb.b) << 0); | |
p++; | |
} | |
} else { | |
colorMap->getRGBLine (pix, row_data, width); | |
} | |
if (maskColors) { | |
for (int x = 0; x < width; x++) { | |
bool is_opaque = false; | |
for (int i = 0; i < colorMap->getNumPixelComps(); ++i) { | |
if (pix[i] < maskColors[2*i] || | |
pix[i] > maskColors[2*i+1]) { | |
is_opaque = true; | |
break; | |
} | |
} | |
if (is_opaque) | |
*row_data |= 0xff000000; | |
else | |
*row_data = 0; | |
row_data++; | |
pix += colorMap->getNumPixelComps(); | |
} | |
} | |
} | |
}; | |
void CairoOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, | |
int widthA, int heightA, | |
GfxImageColorMap *colorMap, | |
GBool interpolate, | |
int *maskColors, GBool inlineImg) | |
{ | |
cairo_surface_t *image; | |
cairo_pattern_t *pattern, *maskPattern; | |
cairo_matrix_t matrix; | |
int width, height; | |
int scaledWidth, scaledHeight; | |
cairo_filter_t filter = CAIRO_FILTER_BILINEAR; | |
RescaleDrawImage rescale; | |
LOG (printf ("drawImage %dx%d\n", widthA, heightA)); | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
cairo_get_matrix(cairo, &matrix); | |
getScaledSize (&matrix, widthA, heightA, &scaledWidth, &scaledHeight); | |
image = rescale.getSourceImage(str, widthA, heightA, scaledWidth, scaledHeight, printing, colorMap, maskColors); | |
if (!image) | |
return; | |
width = cairo_image_surface_get_width (image); | |
height = cairo_image_surface_get_height (image); | |
if (width == widthA && height == heightA) | |
filter = getFilterForSurface (image, interpolate); | |
if (!inlineImg) /* don't read stream twice if it is an inline image */ | |
setMimeData(state, str, ref, colorMap, image); | |
pattern = cairo_pattern_create_for_surface (image); | |
cairo_surface_destroy (image); | |
if (cairo_pattern_status (pattern)) | |
return; | |
cairo_pattern_set_filter (pattern, filter); | |
if (!printing) | |
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); | |
cairo_matrix_init_translate (&matrix, 0, height); | |
cairo_matrix_scale (&matrix, width, -height); | |
cairo_pattern_set_matrix (pattern, &matrix); | |
if (cairo_pattern_status (pattern)) { | |
cairo_pattern_destroy (pattern); | |
return; | |
} | |
if (!mask && fill_opacity != 1.0) { | |
maskPattern = cairo_pattern_create_rgba (1., 1., 1., fill_opacity); | |
} else if (mask) { | |
maskPattern = cairo_pattern_reference (mask); | |
} else { | |
maskPattern = NULL; | |
} | |
cairo_save (cairo); | |
cairo_set_source (cairo, pattern); | |
if (!printing) | |
cairo_rectangle (cairo, 0., 0., 1., 1.); | |
if (maskPattern) { | |
if (!printing) | |
cairo_clip (cairo); | |
if (mask) | |
cairo_set_matrix (cairo, &mask_matrix); | |
cairo_mask (cairo, maskPattern); | |
} else { | |
if (printing) | |
cairo_paint (cairo); | |
else | |
cairo_fill (cairo); | |
} | |
cairo_restore (cairo); | |
cairo_pattern_destroy (maskPattern); | |
if (cairo_shape) { | |
cairo_save (cairo_shape); | |
cairo_set_source (cairo_shape, pattern); | |
if (printing) { | |
cairo_paint (cairo_shape); | |
} else { | |
cairo_rectangle (cairo_shape, 0., 0., 1., 1.); | |
cairo_fill (cairo_shape); | |
} | |
cairo_restore (cairo_shape); | |
} | |
cairo_pattern_destroy (pattern); | |
} | |
//------------------------------------------------------------------------ | |
// ImageOutputDev | |
//------------------------------------------------------------------------ | |
CairoImageOutputDev::CairoImageOutputDev() | |
{ | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
images = NULL; | |
numImages = 0; | |
size = 0; | |
imgDrawCbk = NULL; | |
imgDrawCbkData = NULL; | |
} | |
CairoImageOutputDev::~CairoImageOutputDev() | |
{ | |
int i; | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
for (i = 0; i < numImages; i++) | |
delete images[i]; | |
gfree (images); | |
} | |
void CairoImageOutputDev::saveImage(CairoImage *image) | |
{ | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
if (numImages >= size) { | |
size += 16; | |
images = (CairoImage **) greallocn (images, size, sizeof (CairoImage *)); | |
} | |
images[numImages++] = image; | |
} | |
void CairoImageOutputDev::getBBox(GfxState *state, int width, int height, | |
double *x1, double *y1, double *x2, double *y2) | |
{ | |
double *ctm = state->getCTM(); | |
cairo_matrix_t matrix; | |
cairo_matrix_init(&matrix, | |
ctm[0], ctm[1], | |
-ctm[2], -ctm[3], | |
ctm[2] + ctm[4], ctm[3] + ctm[5]); | |
int scaledWidth, scaledHeight; | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
getScaledSize (&matrix, width, height, &scaledWidth, &scaledHeight); | |
if (matrix.xx >= 0) { | |
*x1 = matrix.x0; | |
} else { | |
*x1 = matrix.x0 - scaledWidth; | |
} | |
*x2 = *x1 + scaledWidth; | |
if (matrix.yy >= 0) { | |
*y1 = matrix.y0; | |
} else { | |
*y1 = matrix.y0 - scaledHeight; | |
} | |
*y2 = *y1 + scaledHeight; | |
} | |
void CairoImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, | |
int width, int height, GBool invert, | |
GBool interpolate, GBool inlineImg) | |
{ | |
cairo_t *cr; | |
cairo_surface_t *surface; | |
double x1, y1, x2, y2; | |
CairoImage *image; | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
getBBox(state, width, height, &x1, &y1, &x2, &y2); | |
image = new CairoImage (x1, y1, x2, y2); | |
saveImage (image); | |
if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) { | |
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); | |
cr = cairo_create (surface); | |
LOG(printf("DEBUG %s:%d\n",__FILE__,__LINE__)); | |
printf("DEBUG SET CAIRO %s:%d\n",__FILE__,__LINE__); | |
setCairo (cr); | |
cairo_translate (cr, 0, height); | |
cairo_scale (cr, width, -height); | |
CairoOutputDev::drawImageMask(state, ref, str, width, height, invert, interpolate, inlineImg); | |
image->setImage (surface); | |
LOG(printf("DEBUG %s:%d\n",__FILE__,__LINE__)); | |
printf("DEBUG SET CAIRO %s:%d\n",__FILE__,__LINE__); | |
setCairo (NULL); | |
cairo_surface_destroy (surface); | |
cairo_destroy (cr); | |
} | |
} | |
void CairoImageOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, | |
int width, int height, GBool invert, | |
GBool inlineImg, double *baseMatrix) | |
{ | |
cairo_t *cr; | |
cairo_surface_t *surface; | |
double x1, y1, x2, y2; | |
CairoImage *image; | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
getBBox(state, width, height, &x1, &y1, &x2, &y2); | |
image = new CairoImage (x1, y1, x2, y2); | |
saveImage (image); | |
if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) { | |
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); | |
cr = cairo_create (surface); | |
LOG(printf("DEBUG %s:%d\n",__FILE__,__LINE__)); | |
printf("DEBUG SET CAIRO %s:%d\n",__FILE__,__LINE__); | |
setCairo (cr); | |
cairo_translate (cr, 0, height); | |
cairo_scale (cr, width, -height); | |
CairoOutputDev::drawImageMask(state, ref, str, width, height, invert, inlineImg, gFalse); | |
if (state->getFillColorSpace()->getMode() == csPattern) { | |
cairo_mask (cairo, mask); | |
} | |
image->setImage (surface); | |
LOG(printf("DEBUG %s:%d\n",__FILE__,__LINE__)); | |
setCairo (NULL); | |
cairo_surface_destroy (surface); | |
cairo_destroy (cr); | |
} | |
} | |
void CairoImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, | |
int width, int height, GfxImageColorMap *colorMap, | |
GBool interpolate, int *maskColors, GBool inlineImg) | |
{ | |
cairo_t *cr; | |
cairo_surface_t *surface; | |
double x1, y1, x2, y2; | |
CairoImage *image; | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
getBBox(state, width, height, &x1, &y1, &x2, &y2); | |
image = new CairoImage (x1, y1, x2, y2); | |
saveImage (image); | |
if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) { | |
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); | |
cr = cairo_create (surface); | |
LOG(printf("DEBUG %s:%d\n",__FILE__,__LINE__)); | |
printf("DEBUG SET CAIRO %s:%d\n",__FILE__,__LINE__); | |
setCairo (cr); | |
cairo_translate (cr, 0, height); | |
cairo_scale (cr, width, -height); | |
CairoOutputDev::drawImage(state, ref, str, width, height, colorMap, interpolate, maskColors, inlineImg); | |
image->setImage (surface); | |
LOG(printf("DEBUG %s:%d\n",__FILE__,__LINE__)); | |
setCairo (NULL); | |
cairo_surface_destroy (surface); | |
cairo_destroy (cr); | |
} | |
} | |
void CairoImageOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, | |
int width, int height, | |
GfxImageColorMap *colorMap, | |
GBool interpolate, | |
Stream *maskStr, | |
int maskWidth, int maskHeight, | |
GfxImageColorMap *maskColorMap, | |
GBool maskInterpolate) | |
{ | |
cairo_t *cr; | |
cairo_surface_t *surface; | |
double x1, y1, x2, y2; | |
CairoImage *image; | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
getBBox(state, width, height, &x1, &y1, &x2, &y2); | |
image = new CairoImage (x1, y1, x2, y2); | |
saveImage (image); | |
if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) { | |
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); | |
cr = cairo_create (surface); | |
LOG(printf("DEBUG %s:%d\n",__FILE__,__LINE__)); | |
printf("DEBUG SET CAIRO %s:%d\n",__FILE__,__LINE__); | |
setCairo (cr); | |
cairo_translate (cr, 0, height); | |
cairo_scale (cr, width, -height); | |
CairoOutputDev::drawSoftMaskedImage(state, ref, str, width, height, colorMap, interpolate, | |
maskStr, maskWidth, maskHeight, maskColorMap, maskInterpolate); | |
image->setImage (surface); | |
LOG(printf("DEBUG %s:%d\n",__FILE__,__LINE__)); | |
printf("DEBUG SET CAIRO %s:%d\n",__FILE__,__LINE__); | |
setCairo (NULL); | |
cairo_surface_destroy (surface); | |
cairo_destroy (cr); | |
} | |
} | |
void CairoImageOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, | |
int width, int height, | |
GfxImageColorMap *colorMap, | |
GBool interpolate, | |
Stream *maskStr, | |
int maskWidth, int maskHeight, | |
GBool maskInvert, GBool maskInterpolate) | |
{ | |
cairo_t *cr; | |
cairo_surface_t *surface; | |
double x1, y1, x2, y2; | |
CairoImage *image; | |
printf("DEBUG %s:%d\n",__FILE__,__LINE__); | |
getBBox(state, width, height, &x1, &y1, &x2, &y2); | |
image = new CairoImage (x1, y1, x2, y2); | |
saveImage (image); | |
if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) { | |
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); | |
cr = cairo_create (surface); | |
LOG(printf("DEBUG %s:%d\n",__FILE__,__LINE__)); | |
printf("DEBUG SET CAIRO %s:%d\n",__FILE__,__LINE__); | |
setCairo (cr); | |
cairo_translate (cr, 0, height); | |
cairo_scale (cr, width, -height); | |
CairoOutputDev::drawMaskedImage(state, ref, str, width, height, colorMap, interpolate, | |
maskStr, maskWidth, maskHeight, maskInvert, maskInterpolate); | |
image->setImage (surface); | |
LOG(printf("DEBUG %s:%d\n",__FILE__,__LINE__)); | |
setCairo (NULL); | |
cairo_surface_destroy (surface); | |
cairo_destroy (cr); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment