libs-back/Source/cairo/CairoGState.m
Fred Kiefer 1ce0bd5175 Add PDF output for cairo backend.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/back/trunk@25479 72102866-910b-0410-8b05-ffd578937521
2007-09-12 15:46:55 +00:00

1238 lines
29 KiB
Objective-C

/*
* CairoGState.m
* Copyright (C) 2003 Free Software Foundation, Inc.
* August 31, 2003
* Written by Banlu Kemiyatorn <object at gmail dot com>
* Rewrite: Fred Kiefer <fredkiefer@gmx.de>
* Date: Jan 2006
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA.
*/
#include <AppKit/NSAffineTransform.h>
#include <AppKit/NSBezierPath.h>
#include <AppKit/NSColor.h>
#include <AppKit/NSGraphics.h>
#include "cairo/CairoGState.h"
#include "cairo/CairoFontInfo.h"
#include "cairo/CairoSurface.h"
#include "cairo/CairoContext.h"
#include <math.h>
// Macro stolen from base/Header/Additions/GNUstepBase/GSObjRuntime.h
#ifndef GS_MAX_OBJECTS_FROM_STACK
/**
* The number of objects to try to get from varargs into an array on
* the stack ... if there are more than this, use the heap.
* NB. This MUST be a multiple of 2
*/
#define GS_MAX_OBJECTS_FROM_STACK 128
#endif
// Macros stolen from base/Source/GSPrivate.h
/**
* Macro to manage memory for chunks of code that need to work with
* arrays of items. Use this to start the block of code using
* the array and GS_ENDITEMBUF() to end it. The idea is to ensure that small
* arrays are allocated on the stack (for speed), but large arrays are
* allocated from the heap (to avoid stack overflow).
*/
#define GS_BEGINITEMBUF(P, S, T) { \
T _ibuf[(S) <= GS_MAX_OBJECTS_FROM_STACK ? (S) : 0]; \
T *_base = ((S) <= GS_MAX_OBJECTS_FROM_STACK) ? _ibuf \
: (T*)NSZoneMalloc(NSDefaultMallocZone(), (S) * sizeof(T)); \
T *(P) = _base;
/**
* Macro to manage memory for chunks of code that need to work with
* arrays of items. Use GS_BEGINITEMBUF() to start the block of code using
* the array and this macro to end it.
*/
#define GS_ENDITEMBUF() \
if (_base != _ibuf) \
NSZoneFree(NSDefaultMallocZone(), _base); \
}
@implementation CairoGState
+ (void) initialize
{
if (self == [CairoGState class])
{
}
}
- (void) dealloc
{
if (_ct)
{
cairo_destroy(_ct);
}
RELEASE(_surface);
[super dealloc];
}
- (id) copyWithZone: (NSZone *)zone
{
CairoGState *copy = (CairoGState *)[super copyWithZone: zone];
RETAIN(_surface);
if (_ct)
{
cairo_status_t status;
// FIXME: Need some way to do a copy
// but there isnt anything like copy->_ct = cairo_copy(_ct);
copy->_ct = cairo_create(cairo_get_target(_ct));
status = cairo_status(copy->_ct);
if (status != CAIRO_STATUS_SUCCESS)
{
NSLog(@"Cairo status %s in copy", cairo_status_to_string(status));
_ct = NULL;
}
else
{
cairo_path_t *cpath;
cairo_matrix_t local_matrix;
cairo_rectangle_list_t *clip_rects;
int num_dashes;
cairo_get_matrix(_ct, &local_matrix);
cairo_set_matrix(copy->_ct, &local_matrix);
cpath = cairo_copy_path(_ct);
status = cpath->status;
if (status != CAIRO_STATUS_SUCCESS)
{
NSLog(@"Cairo status %s in copy path", cairo_status_to_string(status));
}
else
{
cairo_append_path(copy->_ct, cpath);
}
cairo_path_destroy(cpath);
cairo_set_operator(copy->_ct, cairo_get_operator(_ct));
cairo_set_source(copy->_ct, cairo_get_source(_ct));
cairo_set_tolerance(copy->_ct, cairo_get_tolerance(_ct));
cairo_set_antialias(copy->_ct, cairo_get_antialias(_ct));
cairo_set_line_width(copy->_ct, cairo_get_line_width(_ct));
cairo_set_line_cap(copy->_ct, cairo_get_line_cap(_ct));
cairo_set_line_join(copy->_ct, cairo_get_line_join(_ct));
cairo_set_miter_limit(copy->_ct, cairo_get_miter_limit(_ct));
// In cairo 1.4 there is a way get the dash.
num_dashes = cairo_get_dash_count(_ct);
if (num_dashes != 0)
{
double dash_offset;
GS_BEGINITEMBUF(dashes, num_dashes, double);
dashes = (double *)malloc(sizeof(double) * num_dashes);
cairo_get_dash(_ct, dashes, &dash_offset);
cairo_set_dash (copy->_ct, dashes, num_dashes, dash_offset);
GS_ENDITEMBUF();
}
// In cairo 1.4 there also is a way to get the current clipping path
clip_rects = cairo_copy_clip_rectangle_list(_ct);
status = clip_rects->status;
if (status != CAIRO_STATUS_SUCCESS)
{
NSLog(@"Cairo status %s in copy clip", cairo_status_to_string(status));
}
else
{
int i;
for (i = 0; i < clip_rects->num_rectangles; i++)
{
cairo_rectangle_t rect = clip_rects->rectangles[i];
NSSize size = [_surface size];
// This strange computation is due to the device offset.
cairo_rectangle(copy->_ct, rect.x,
rect.y + 2*(offset.y - size.height),
rect.width, rect.height);
cairo_clip(copy->_ct);
}
}
cairo_rectangle_list_destroy(clip_rects);
}
}
return copy;
}
- (void) GSCurrentSurface: (CairoSurface **)surface: (int *)x : (int *)y
{
if (x)
*x = offset.x;
if (y)
*y = offset.y;
if (surface)
{
*surface = _surface;
}
}
- (void) GSSetSurface: (CairoSurface *)surface : (int)x : (int)y
{
ASSIGN(_surface, surface);
[self setOffset: NSMakePoint(x, y)];
[self DPSinitgraphics];
}
- (void) setOffset: (NSPoint)theOffset
{
if (_surface != nil)
{
NSSize size = [_surface size];
cairo_surface_set_device_offset([_surface surface], -theOffset.x,
theOffset.y - size.height);
}
[super setOffset: theOffset];
}
- (void) showPage
{
if (_ct)
{
cairo_show_page(_ct);
}
}
/*
* Color operations
*/
- (void) setColor: (device_color_t *)color state: (color_state_t)cState
{
device_color_t c;
[super setColor: color state: cState];
if (_ct == NULL)
{
// Window device isn't set yet
return;
}
c = fillColor;
gsColorToRGB(&c);
// The underlying concept does not allow to determine if alpha is set or not.
cairo_set_source_rgba(_ct, c.field[0], c.field[1], c.field[2], c.field[AINDEX]);
}
- (void) GSSetPatterColor: (NSImage*)image
{
// FIXME: Create a cairo surface from the image and set it as source.
[super GSSetPatterColor: image];
}
/*
* Text operations
*/
- (void) _setPoint
{
NSPoint p;
p = [path currentPoint];
cairo_move_to(_ct, floorf(p.x), floorf(p.y));
}
- (void) DPScharpath: (const char *)s : (int)b
{
if (_ct)
{
GS_BEGINITEMBUF(c, b + 1, char);
[self _setPoint];
memcpy(c, s, b);
c[b] = 0;
cairo_text_path(_ct, c);
GS_ENDITEMBUF();
}
}
- (void) DPSshow: (const char *)s
{
if (_ct)
{
cairo_matrix_t saved_matrix;
cairo_matrix_t local_matrix;
NSPoint p = [path currentPoint];
cairo_get_matrix(_ct, &saved_matrix);
cairo_matrix_init_scale(&local_matrix, 1, 1);
cairo_matrix_translate(&local_matrix, 0, [_surface size].height-(p.y*2));
cairo_set_matrix(_ct, &local_matrix);
cairo_move_to(_ct, p.x, p.y);
cairo_show_text(_ct, s);
cairo_set_matrix(_ct, &saved_matrix);
}
}
- (void) GSSetFont: (GSFontInfo *)fontref
{
cairo_matrix_t font_matrix;
const float *matrix;
[super GSSetFont: fontref];
if (_ct)
{
matrix = [font matrix];
cairo_set_font_face(_ct, [((CairoFontInfo *)font)->_faceInfo fontFace]);
cairo_matrix_init(&font_matrix, matrix[0], matrix[1], matrix[2],
matrix[3], matrix[4], matrix[5]);
cairo_set_font_matrix(_ct, &font_matrix);
}
}
- (void) GSSetFontSize: (float)size
{
if (_ct)
{
cairo_set_font_size(_ct, size);
}
}
- (void) GSShowText: (const char *)string : (size_t)length
{
if (_ct)
{
GS_BEGINITEMBUF(c, length + 1, char);
[self _setPoint];
memcpy(c, string, length);
c[length] = 0;
cairo_show_text(_ct, c);
GS_ENDITEMBUF();
}
}
- (void) GSShowGlyphs: (const NSGlyph *)glyphs : (size_t)length
{
if (_ct)
{
cairo_matrix_t local_matrix;
NSAffineTransformStruct matrix = [ctm transformStruct];
[self _setPoint];
// FIXME: Hack to get font in rotated view working
cairo_save(_ct);
cairo_matrix_init(&local_matrix, matrix.m11, matrix.m12, matrix.m21,
matrix.m22, 0, 0);
cairo_transform(_ct, &local_matrix);
// Undo the
cairo_matrix_init_scale(&local_matrix, 1, -1);
cairo_matrix_translate(&local_matrix, 0, -[_surface size].height);
cairo_transform(_ct, &local_matrix);
[(CairoFontInfo *)font drawGlyphs: glyphs
length: length
on: _ct];
cairo_restore(_ct);
}
}
/*
* GState operations
*/
- (void) DPSinitgraphics
{
cairo_status_t status;
cairo_matrix_t local_matrix;
[super DPSinitgraphics];
if (_ct)
{
cairo_destroy(_ct);
}
if (!_surface)
{
return;
}
_ct = cairo_create([_surface surface]);
status = cairo_status(_ct);
if (status != CAIRO_STATUS_SUCCESS)
{
NSLog(@"Cairo status %s in DPSinitgraphics", cairo_status_to_string(status));
_ct = NULL;
return;
}
// cairo draws the other way around.
// At this point in time viewIsFlipped has not been set, but it is
// OK to ignore this here, as in that case the matrix will later
// get flipped by GUI,
cairo_matrix_init_scale(&local_matrix, 1, -1);
cairo_matrix_translate(&local_matrix, 0, -[_surface size].height);
cairo_set_matrix(_ct, &local_matrix);
// super call did go to the old _ct, so redo it
[self setColor: &fillColor state: COLOR_BOTH];
// Cairo's default line width is 2.0
cairo_set_line_width(_ct, 1.0);
cairo_set_operator(_ct, CAIRO_OPERATOR_OVER);
cairo_new_path(_ct);
_strokeadjust = 1;
}
- (void) DPScurrentflat: (float *)flatness
{
if (_ct)
{
*flatness = cairo_get_tolerance(_ct);
}
}
- (void) DPScurrentlinecap: (int *)linecap
{
cairo_line_cap_t lc;
if (_ct)
{
lc = cairo_get_line_cap(_ct);
*linecap = lc;
}
/*
switch (lc)
{
case CAIRO_LINE_CAP_BUTT:
*linecap = 0;
break;
case CAIRO_LINE_CAP_ROUND:
*linecap = 1;
break;
case CAIRO_LINE_CAP_SQUARE:
*linecap = 2;
break;
default:
NSLog(@"ERROR Line cap unknown");
exit(-1);
}
*/
}
- (void) DPScurrentlinejoin: (int *)linejoin
{
cairo_line_join_t lj;
if (_ct)
{
lj = cairo_get_line_join(_ct);
*linejoin = lj;
}
/*
switch (lj)
{
case CAIRO_LINE_JOIN_MITER:
*linejoin = 0;
break;
case CAIRO_LINE_JOIN_ROUND:
*linejoin = 1;
break;
case CAIRO_LINE_JOIN_BEVEL:
*linejoin = 2;
break;
default:
NSLog(@"ERROR Line join unknown");
exit(-1);
}
*/
}
- (void) DPScurrentlinewidth: (float *)width
{
if (_ct)
{
*width = cairo_get_line_width(_ct);
}
}
- (void) DPScurrentmiterlimit: (float *)limit
{
if (_ct)
{
*limit = cairo_get_miter_limit(_ct);
}
}
- (void) DPScurrentstrokeadjust: (int *)b
{
// FIXME
}
- (void) DPSsetdash: (const float *)pat : (int)size : (float)foffset
{
if (_ct)
{
GS_BEGINITEMBUF(dpat, size, double);
double doffset = foffset;
int i;
i = size;
while (i)
{
i--;
dpat[i] = pat[i];
}
// FIXME: There may be a difference in concept as some dashes look wrong
cairo_set_dash(_ct, dpat, size, doffset);
GS_ENDITEMBUF();
}
}
- (void) DPSsetflat: (float)flatness
{
[super DPSsetflat: flatness];
if (_ct)
{
cairo_set_tolerance(_ct, flatness);
}
}
- (void) DPSsetlinecap: (int)linecap
{
if (_ct)
{
cairo_set_line_cap(_ct, (cairo_line_cap_t)linecap);
}
}
- (void) DPSsetlinejoin: (int)linejoin
{
if (_ct)
{
cairo_set_line_join(_ct, (cairo_line_join_t)linejoin);
}
}
- (void) DPSsetlinewidth: (float)width
{
if (_ct)
{
cairo_set_line_width(_ct, width);
}
}
- (void) DPSsetmiterlimit: (float)limit
{
if (_ct)
{
cairo_set_miter_limit(_ct, limit);
}
}
- (void) DPSsetstrokeadjust: (int)b
{
_strokeadjust = b;
}
/*
* Path operations
*/
- (void) _adjustPath: (float)offs
{
unsigned count = [path elementCount];
NSBezierPathElement type;
NSPoint points[3] = {{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}};
NSPoint last_points[3] = {{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}};
unsigned i;
int index, last_index;
for (i = 0; i < count; i++)
{
type = [path elementAtIndex:i associatedPoints:points];
if (type == NSCurveToBezierPathElement) break;
points[0].x = floorf(points[0].x) + offs;
points[0].y = floorf(points[0].y) + offs;
index = i;
last_index = i - 1;
if (type == NSClosePathBezierPathElement)
{
index = 0;
[path elementAtIndex:0 associatedPoints:points];
if (fabs(last_points[0].x - points[0].x) < 1.0)
{
last_points[0].x = floorf(last_points[0].x) + offs;
points[0].x = last_points[0].x;
}
else if (fabs(last_points[0].y - points[0].y) < 1.0)
{
last_points[0].y = floorf(last_points[0].y) + offs;
points[0].y = last_points[0].y;
}
else
{
index = -1;
}
}
else if (fabs(last_points[0].x - points[0].x) < 1.0)
{ // Vertical path
points[0].x = floorf(points[0].x) + offs;
points[0].y = floorf(points[0].y);
if (type == NSLineToBezierPathElement)
{
last_points[0].x = points[0].x;
}
}
else if (fabs(last_points[0].y - points[0].y) < 1.0)
{ // Horizontal path
points[0].x = floorf(points[0].x);
points[0].y = floorf(points[0].y) + offs;
if (type == NSLineToBezierPathElement)
{
last_points[0].y = points[0].y;
}
}
// Save adjusted values into NSBezierPath
if (index >= 0)
[path setAssociatedPoints:points atIndex:index];
if (last_index >= 0)
[path setAssociatedPoints:last_points atIndex:last_index];
last_points[0].x = points[0].x;
last_points[0].y = points[0].y;
}
}
- (void) _setPath: (BOOL)fillOrClip
{
unsigned count = [path elementCount];
unsigned i;
SEL elmsel = @selector(elementAtIndex:associatedPoints:);
IMP elmidx = [path methodForSelector: elmsel];
if (_strokeadjust)
{
float offs;
if ((remainderf(cairo_get_line_width(_ct), 2.0) == 0.0)
|| fillOrClip == YES)
offs = 0.0;
else
offs = 0.5;
[self _adjustPath:offs];
}
// reset current cairo path
cairo_new_path(_ct);
for (i = 0; i < count; i++)
{
NSBezierPathElement type;
NSPoint points[3];
type = (NSBezierPathElement)(*elmidx)(path, elmsel, i, points);
switch(type)
{
case NSMoveToBezierPathElement:
cairo_move_to(_ct, points[0].x, points[0].y);
break;
case NSLineToBezierPathElement:
cairo_line_to(_ct, points[0].x, points[0].y);
break;
case NSCurveToBezierPathElement:
cairo_curve_to(_ct, points[0].x, points[0].y,
points[1].x, points[1].y,
points[2].x, points[2].y);
break;
case NSClosePathBezierPathElement:
cairo_close_path(_ct);
break;
default:
break;
}
}
}
- (void) DPSclip
{
if (_ct)
{
[self _setPath:YES];
cairo_clip(_ct);
}
}
- (void) DPSeoclip
{
if (_ct)
{
[self _setPath:YES];
cairo_set_fill_rule(_ct, CAIRO_FILL_RULE_EVEN_ODD);
cairo_clip(_ct);
cairo_set_fill_rule(_ct, CAIRO_FILL_RULE_WINDING);
}
}
- (void) DPSeofill
{
if (_ct)
{
[self _setPath:YES];
cairo_set_fill_rule(_ct, CAIRO_FILL_RULE_EVEN_ODD);
cairo_fill(_ct);
cairo_set_fill_rule(_ct, CAIRO_FILL_RULE_WINDING);
}
[self DPSnewpath];
}
- (void) DPSfill
{
if (_ct)
{
[self _setPath:YES];
cairo_fill(_ct);
}
[self DPSnewpath];
}
- (void) DPSinitclip
{
if (_ct)
{
cairo_reset_clip(_ct);
}
}
- (void) DPSstroke
{
if (_ct)
{
[self _setPath:NO];
cairo_stroke(_ct);
}
[self DPSnewpath];
}
- (NSDictionary *) GSReadRect: (NSRect)r
{
NSMutableDictionary *dict;
NSSize ssize;
NSAffineTransform *matrix;
double x, y;
int ix, iy;
cairo_format_t format = CAIRO_FORMAT_ARGB32;
cairo_surface_t *surface;
cairo_surface_t *isurface;
cairo_t *ct;
cairo_status_t status;
int size;
int i;
NSMutableData *data;
unsigned char *cdata;
if (!_ct)
{
return nil;
}
r = [ctm rectInMatrixSpace: r];
x = NSWidth(r);
y = NSHeight(r);
ix = abs(floor(x));
iy = abs(floor(y));
ssize = NSMakeSize(ix, iy);
dict = [NSMutableDictionary dictionary];
[dict setObject: [NSValue valueWithSize: ssize] forKey: @"Size"];
[dict setObject: NSDeviceRGBColorSpace forKey: @"ColorSpace"];
[dict setObject: [NSNumber numberWithUnsignedInt: 8] forKey: @"BitsPerSample"];
[dict setObject: [NSNumber numberWithUnsignedInt: 32]
forKey: @"Depth"];
[dict setObject: [NSNumber numberWithUnsignedInt: 4]
forKey: @"SamplesPerPixel"];
[dict setObject: [NSNumber numberWithUnsignedInt: 1]
forKey: @"HasAlpha"];
matrix = [self GSCurrentCTM];
[matrix translateXBy: -r.origin.x - offset.x
yBy: r.origin.y + NSHeight(r) - offset.y];
[dict setObject: matrix forKey: @"Matrix"];
size = ix*iy*4;
data = [NSMutableData dataWithLength: size];
if (data == nil)
return nil;
cdata = [data mutableBytes];
surface = cairo_get_target(_ct);
isurface = cairo_image_surface_create_for_data(cdata, format, ix, iy, 4*ix);
status = cairo_surface_status(isurface);
if (status != CAIRO_STATUS_SUCCESS)
{
NSLog(@"Cairo status %s in GSReadRect", cairo_status_to_string(status));
return nil;
}
ct = cairo_create(isurface);
status = cairo_status(ct);
if (status != CAIRO_STATUS_SUCCESS)
{
NSLog(@"Cairo status %s in GSReadRect", cairo_status_to_string(status));
cairo_surface_destroy(isurface);
return nil;
}
if (_surface != nil)
{
ssize = [_surface size];
}
else
{
ssize = NSMakeSize(0, 0);
}
cairo_set_source_surface(ct, surface, -r.origin.x,
-ssize.height + r.size.height + r.origin.y);
cairo_rectangle(ct, 0, 0, ix, iy);
cairo_paint(ct);
cairo_destroy(ct);
cairo_surface_destroy(isurface);
for (i = 0; i < 4 * ix * iy; i += 4)
{
unsigned char d = cdata[i];
#if GS_WORDS_BIGENDIAN
cdata[i] = cdata[i + 1];
cdata[i + 1] = cdata[i + 2];
cdata[i + 2] = cdata[i + 3];
cdata[i + 3] = d;
#else
cdata[i] = cdata[i + 2];
//cdata[i + 1] = cdata[i + 1];
cdata[i + 2] = d;
//cdata[i + 3] = cdata[i + 3];
#endif
}
[dict setObject: data forKey: @"Data"];
return dict;
}
static void
_set_op(cairo_t *ct, NSCompositingOperation op)
{
switch (op)
{
case NSCompositeClear:
cairo_set_operator(ct, CAIRO_OPERATOR_CLEAR);
break;
case NSCompositeCopy:
cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE);
break;
case NSCompositeSourceOver:
cairo_set_operator(ct, CAIRO_OPERATOR_OVER);
break;
case NSCompositeSourceIn:
cairo_set_operator(ct, CAIRO_OPERATOR_IN);
break;
case NSCompositeSourceOut:
cairo_set_operator(ct, CAIRO_OPERATOR_OUT);
break;
case NSCompositeSourceAtop:
cairo_set_operator(ct, CAIRO_OPERATOR_ATOP);
break;
case NSCompositeDestinationOver:
cairo_set_operator(ct, CAIRO_OPERATOR_DEST_OVER);
break;
case NSCompositeDestinationIn:
cairo_set_operator(ct, CAIRO_OPERATOR_DEST_IN);
break;
case NSCompositeDestinationOut:
cairo_set_operator(ct, CAIRO_OPERATOR_DEST_OUT);
break;
case NSCompositeDestinationAtop:
cairo_set_operator(ct, CAIRO_OPERATOR_DEST_ATOP);
break;
case NSCompositeXOR:
cairo_set_operator(ct, CAIRO_OPERATOR_XOR);
break;
case NSCompositePlusDarker:
// FIXME: There is no match for this operation in cairo!!!
cairo_set_operator(ct, CAIRO_OPERATOR_SATURATE);
break;
case NSCompositeHighlight:
// MacOSX 10.4 documentation maps this value onto NSCompositeSourceOver
cairo_set_operator(ct, CAIRO_OPERATOR_OVER);
break;
case NSCompositePlusLighter:
cairo_set_operator(ct, CAIRO_OPERATOR_ADD);
break;
default:
cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE);
}
}
- (void) DPSimage: (NSAffineTransform *)matrix : (int)pixelsWide
: (int)pixelsHigh : (int)bitsPerSample
: (int)samplesPerPixel : (int)bitsPerPixel
: (int)bytesPerRow : (BOOL)isPlanar
: (BOOL)hasAlpha : (NSString *)colorSpaceName
: (const unsigned char *const[5])data
{
cairo_format_t format;
NSAffineTransformStruct tstruct;
cairo_surface_t *surface;
unsigned char *tmp = NULL;
int i = 0;
int j;
int index;
unsigned int pixels = pixelsHigh * pixelsWide;
unsigned char *rowData;
cairo_matrix_t local_matrix;
cairo_status_t status;
if (!_ct)
{
return;
}
if (isPlanar || !([colorSpaceName isEqualToString: NSDeviceRGBColorSpace] ||
[colorSpaceName isEqualToString: NSCalibratedRGBColorSpace]))
{
// FIXME: Need to conmvert to something that is supported
NSLog(@"Image format not support in cairo backend.\n colour space: %@ planar %d", colorSpaceName, isPlanar);
return;
}
// default is 8 bit grayscale
if (!bitsPerSample)
bitsPerSample = 8;
if (!samplesPerPixel)
samplesPerPixel = 1;
// FIXME - does this work if we are passed a planar image but no hints ?
if (!bitsPerPixel)
bitsPerPixel = bitsPerSample * samplesPerPixel;
if (!bytesPerRow)
bytesPerRow = (bitsPerPixel * pixelsWide) / 8;
/* make sure its sane - also handles row padding if hint missing */
while ((bytesPerRow * 8) < (bitsPerPixel * pixelsWide))
bytesPerRow++;
switch (bitsPerPixel)
{
case 32:
rowData = (unsigned char *)data[0];
tmp = objc_malloc(pixels * 4);
index = 0;
for (i = 0; i < pixelsHigh; i++)
{
unsigned char *d = rowData;
for (j = 0; j < pixelsWide; j++)
{
#if GS_WORDS_BIGENDIAN
tmp[index++] = d[3];
tmp[index++] = d[0];
tmp[index++] = d[1];
tmp[index++] = d[2];
#else
tmp[index++] = d[2];
tmp[index++] = d[1];
tmp[index++] = d[0];
tmp[index++] = d[3];
#endif
d += 4;
}
rowData += bytesPerRow;
}
format = CAIRO_FORMAT_ARGB32;
break;
case 24:
rowData = (unsigned char *)data[0];
tmp = objc_malloc(pixels * 4);
index = 0;
for (i = 0; i < pixelsHigh; i++)
{
unsigned char *d = rowData;
for (j = 0; j < pixelsWide; j++)
{
#if GS_WORDS_BIGENDIAN
tmp[index++] = 0;
tmp[index++] = d[0];
tmp[index++] = d[1];
tmp[index++] = d[2];
#else
tmp[index++] = d[2];
tmp[index++] = d[1];
tmp[index++] = d[0];
tmp[index++] = 0;
#endif
d += 3;
}
rowData += bytesPerRow;
}
format = CAIRO_FORMAT_RGB24;
break;
default:
NSLog(@"Image format not support");
return;
}
surface = cairo_image_surface_create_for_data((void*)tmp,
format,
pixelsWide,
pixelsHigh,
pixelsWide * 4);
status = cairo_surface_status(surface);
if (status != CAIRO_STATUS_SUCCESS)
{
NSLog(@"Cairo status %s in DPSimage", cairo_status_to_string(status));
if (tmp)
{
objc_free(tmp);
}
return;
}
cairo_save(_ct);
cairo_set_operator(_ct, CAIRO_OPERATOR_SOURCE);
// Set the basic transformation
tstruct = [ctm transformStruct];
cairo_matrix_init(&local_matrix,
tstruct.m11, tstruct.m12,
tstruct.m21, tstruct.m22,
tstruct.tX, tstruct.tY);
cairo_transform(_ct, &local_matrix);
// add the local tranformation
tstruct = [matrix transformStruct];
cairo_matrix_init(&local_matrix,
tstruct.m11, tstruct.m12,
tstruct.m21, tstruct.m22,
tstruct.tX, tstruct.tY);
cairo_transform(_ct, &local_matrix);
// Make up for flip done in GUI
if (viewIsFlipped)
{
cairo_pattern_t *cpattern;
cairo_matrix_t local_matrix;
cpattern = cairo_pattern_create_for_surface(surface);
cairo_matrix_init_scale(&local_matrix, 1, -1);
cairo_matrix_translate(&local_matrix, 0, -2*pixelsHigh);
cairo_pattern_set_matrix(cpattern, &local_matrix);
cairo_set_source(_ct, cpattern);
cairo_pattern_destroy(cpattern);
cairo_rectangle(_ct, 0, pixelsHigh, pixelsWide, pixelsHigh);
}
else
{
cairo_pattern_t *cpattern;
cairo_matrix_t local_matrix;
cpattern = cairo_pattern_create_for_surface(surface);
cairo_matrix_init_scale(&local_matrix, 1, -1);
cairo_matrix_translate(&local_matrix, 0, -pixelsHigh);
cairo_pattern_set_matrix(cpattern, &local_matrix);
cairo_set_source(_ct, cpattern);
cairo_pattern_destroy(cpattern);
cairo_rectangle(_ct, 0, 0, pixelsWide, pixelsHigh);
}
cairo_clip(_ct);
cairo_paint(_ct);
cairo_surface_destroy(surface);
cairo_restore(_ct);
if (tmp)
{
objc_free(tmp);
}
}
- (void) compositerect: (NSRect)aRect op: (NSCompositingOperation)op
{
if (_ct)
{
NSBezierPath *oldPath = path;
cairo_save(_ct);
_set_op(_ct, op);
// This is almost a rectclip::::, but the path stays unchanged.
path = [NSBezierPath bezierPathWithRect: aRect];
[path transformUsingAffineTransform: ctm];
[self _setPath:YES];
cairo_clip(_ct);
cairo_paint(_ct);
cairo_restore(_ct);
path = oldPath;
}
}
- (void) compositeGState: (CairoGState *)source
fromRect: (NSRect)aRect
toPoint: (NSPoint)aPoint
op: (NSCompositingOperation)op
fraction: (float)delta
{
cairo_surface_t *src;
double minx, miny;
double width, height;
double x, y;
NSSize ssize;
cairo_pattern_t *cpattern;
cairo_matrix_t local_matrix;
BOOL copyOnSelf = NO;
if (!_ct || !source->_ct)
{
return;
}
/*
* we check if we copy on ourself, if that's the case
* we'll use the groups trick...
*/
src = cairo_get_target(source->_ct);
if (src == cairo_get_target(_ct))
{
NSRect targetRect;
targetRect.origin = aPoint;
targetRect.size = aRect.size;
if (!NSIsEmptyRect(NSIntersectionRect(aRect, targetRect)))
{
copyOnSelf = YES;
}
}
cairo_save(_ct);
if (copyOnSelf) cairo_push_group (_ct);
cairo_new_path(_ct);
_set_op(_ct, op);
if (viewIsFlipped && !copyOnSelf)
{
aPoint.y -= aRect.size.height;
}
{
NSRect newRect;
newRect.origin = aPoint;
newRect.size = aRect.size;
[ctm boundingRectFor: newRect result: &newRect];
aPoint = newRect.origin;
}
[source->ctm boundingRectFor: aRect result: &aRect];
x = floorf(aPoint.x);
y = floorf(aPoint.y);
minx = NSMinX(aRect);
miny = NSMinY(aRect);
width = NSWidth(aRect);
height = NSHeight(aRect);
if (source->_surface != nil)
{
ssize = [source->_surface size];
}
else
{
ssize = NSMakeSize(0, 0);
}
cpattern = cairo_pattern_create_for_surface(src);
cairo_matrix_init_scale(&local_matrix, 1, -1);
cairo_matrix_translate(&local_matrix, -x + minx, - ssize.height - y + miny);
cairo_pattern_set_matrix(cpattern, &local_matrix);
cairo_set_source(_ct, cpattern);
cairo_pattern_destroy(cpattern);
cairo_rectangle(_ct, x, y, width, height);
cairo_clip(_ct);
if (delta < 1.0)
{
cairo_paint_with_alpha(_ct, delta);
}
else
{
cairo_paint(_ct);
}
if (copyOnSelf)
{
cairo_pop_group_to_source (_ct);
cairo_paint(_ct);
}
cairo_restore(_ct);
}
- (void) compositeGState: (CairoGState *)source
fromRect: (NSRect)aRect
toPoint: (NSPoint)aPoint
op: (NSCompositingOperation)op
{
[self compositeGState: source
fromRect: aRect
toPoint: aPoint
op: op
fraction: 1.0];
}
- (void) dissolveGState: (CairoGState *)source
fromRect: (NSRect)aRect
toPoint: (NSPoint)aPoint
delta: (float)delta
{
[self compositeGState: source
fromRect: aRect
toPoint: aPoint
op: NSCompositeSourceOver
fraction: delta];
}
@end