/* GSGState - Generic graphic state Copyright (C) 1998-2010 Free Software Foundation, Inc. Written by: Adam Fedor Date: Mar 2002 This file is part of the GNU Objective C User Interface Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; see the file COPYING.LIB. If not, see or write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" #import #import #import #import #import #import #import #import #import #import #import "gsc/GSContext.h" #import "gsc/GSGState.h" #import "gsc/GSFunction.h" #include "math.h" #import #define CHECK_PATH \ if (!path) \ { \ path = [NSBezierPath new]; \ } @implementation GSGState /* Designated initializer. */ - initWithDrawContext: (GSContext *)drawContext { self = [super init]; if (!self) return nil; drawcontext = drawContext; offset = NSMakePoint(0, 0); path = nil; font = nil; fillColorS = nil; strokeColorS = nil; [self DPSinitgraphics]; return self; } - (void) dealloc { TEST_RELEASE(font); TEST_RELEASE(path); RELEASE(ctm); RELEASE(textCtm); RELEASE(fillColorS); RELEASE(strokeColorS); TEST_RELEASE(pattern); [super dealloc]; } - (id) deepen { NSZone *zone = [self zone]; if (path) self->path = [path copyWithZone: zone]; self->ctm = [ctm copyWithZone: zone]; self->textCtm = [textCtm copyWithZone: zone]; // Just retain the other objects if (font != nil) RETAIN(font); if (fillColorS != nil) RETAIN(fillColorS); if (strokeColorS != nil) RETAIN(strokeColorS); if (pattern != nil) RETAIN(pattern); return self; } - (NSString*) description { NSMutableString *description = [[super description] mutableCopy]; [description appendFormat: @" drawcontext: %@",drawcontext]; [description appendFormat: @" ctm: %@",ctm]; return [description copy]; } - (id)copyWithZone: (NSZone *)zone { GSGState *new = (GSGState *)NSCopyObject(self, 0, zone); /* Do a deep copy since gstates are isolated from each other */ return [new deepen]; } - (void) setOffset: (NSPoint)theOffset { offset = theOffset; } - (NSPoint) offset { return offset; } /** Subclasses should override this method to be notified of changes in the current color */ - (void) setColor: (device_color_t *)color state: (color_state_t)cState { if ((cState & COLOR_FILL) && (&fillColor != color)) { fillColor = *color; } if ((cState & COLOR_STROKE) && (&strokeColor != color)) { strokeColor = *color; } cstate = cState; DESTROY(pattern); } - (void) GSSetPatterColor: (NSImage*)image { ASSIGN(pattern, image); } - (void) setShouldAntialias: (BOOL)antialias { _antialias = antialias; } - (BOOL) shouldAntialias { return _antialias; } - (NSPoint) patternPhase { return _patternPhase; } - (void) setPatternPhase: (NSPoint)phase { _patternPhase = phase; } - (NSCompositingOperation) compositingOperation { return _compositingOperation; } - (void) setCompositingOperation: (NSCompositingOperation)operation { _compositingOperation = operation; } // This is only a fall back, the method should not be called any more. - (void) compositeGState: (GSGState *)source fromRect: (NSRect)aRect toPoint: (NSPoint)aPoint op: (NSCompositingOperation)op { [self compositeGState: source fromRect: aRect toPoint: aPoint op: op fraction: 1.0]; } // This is only a fall back, the method should not be called any more. - (void) dissolveGState: (GSGState *)source fromRect: (NSRect)aRect toPoint: (NSPoint)aPoint delta: (CGFloat)delta { [self compositeGState: source fromRect: aRect toPoint: aPoint op: NSCompositeSourceOver fraction: delta]; } - (void) compositeGState: (GSGState *)source fromRect: (NSRect)aRect toPoint: (NSPoint)aPoint op: (NSCompositingOperation)op fraction: (CGFloat)delta { [self subclassResponsibility: _cmd]; } - (void) compositerect: (NSRect)aRect op: (NSCompositingOperation)op { [self subclassResponsibility: _cmd]; } - (NSPoint) pointInMatrixSpace: (NSPoint)aPoint { return [ctm transformPoint: aPoint]; } - (NSPoint) deltaPointInMatrixSpace: (NSPoint)aPoint { return [ctm deltaPointInMatrixSpace: aPoint]; } - (NSRect) rectInMatrixSpace: (NSRect)rect { return [ctm rectInMatrixSpace: rect]; } @end @implementation GSGState (Ops) /* ----------------------------------------------------------------------- */ /* Color operations */ /* ----------------------------------------------------------------------- */ - (void) DPScurrentalpha: (CGFloat*)a { *a = fillColor.field[AINDEX]; } - (void) DPScurrentcmykcolor: (CGFloat*)c : (CGFloat*)m : (CGFloat*)y : (CGFloat*)k { device_color_t new = fillColor; gsColorToCMYK(&new); *c = new.field[0]; *m = new.field[1]; *y = new.field[2]; *k = new.field[3]; } - (void) DPScurrentgray: (CGFloat*)gray { device_color_t gcolor = fillColor; gsColorToGray(&gcolor); *gray = gcolor.field[0]; } - (void) DPScurrenthsbcolor: (CGFloat*)h : (CGFloat*)s : (CGFloat*)b { device_color_t gcolor = fillColor; gsColorToHSB(&gcolor); *h = gcolor.field[0]; *s = gcolor.field[1]; *b = gcolor.field[2]; } - (void) DPScurrentrgbcolor: (CGFloat*)r : (CGFloat*)g : (CGFloat*)b { device_color_t gcolor = fillColor; gsColorToRGB(&gcolor); *r = gcolor.field[0]; *g = gcolor.field[1]; *b = gcolor.field[2]; } #define CLAMP(x) \ if (x < 0.0) x = 0.0; \ if (x > 1.0) x = 1.0; - (void) DPSsetalpha: (CGFloat)a { CLAMP(a) fillColor.field[AINDEX] = strokeColor.field[AINDEX] = a; [self setColor: &fillColor state: COLOR_FILL]; [self setColor: &strokeColor state: COLOR_STROKE]; } - (void) DPSsetcmykcolor: (CGFloat)c : (CGFloat)m : (CGFloat)y : (CGFloat)k { device_color_t col; CLAMP(c) CLAMP(m) CLAMP(y) CLAMP(k) gsMakeColor(&col, cmyk_colorspace, c, m, y, k); // Keep the old alpha value col.field[AINDEX] = fillColor.field[AINDEX]; [self setColor: &col state: COLOR_BOTH]; } - (void) DPSsetgray: (CGFloat)gray { device_color_t col; CLAMP(gray) gsMakeColor(&col, gray_colorspace, gray, 0, 0, 0); // Keep the old alpha value col.field[AINDEX] = fillColor.field[AINDEX]; [self setColor: &col state: COLOR_BOTH]; } - (void) DPSsethsbcolor: (CGFloat)h : (CGFloat)s : (CGFloat)b { device_color_t col; CLAMP(h) CLAMP(s) CLAMP(b) gsMakeColor(&col, hsb_colorspace, h, s, b, 0); // Keep the old alpha value col.field[AINDEX] = fillColor.field[AINDEX]; [self setColor: &col state: COLOR_BOTH]; } - (void) DPSsetrgbcolor: (CGFloat)r : (CGFloat)g : (CGFloat)b { device_color_t col; CLAMP(r) CLAMP(g) CLAMP(b) gsMakeColor(&col, rgb_colorspace, r, g, b, 0); // Keep the old alpha value col.field[AINDEX] = fillColor.field[AINDEX]; [self setColor: &col state: COLOR_BOTH]; } - (void) GSSetFillColorspace: (void *)spaceref { device_color_t col; ASSIGN(fillColorS, (NSColorSpace*)spaceref); gsMakeColor(&col, rgb_colorspace, 0, 0, 0, 0); // Keep the old alpha value col.field[AINDEX] = fillColor.field[AINDEX]; [self setColor: &col state: COLOR_FILL]; } - (void) GSSetStrokeColorspace: (void *)spaceref { device_color_t col; ASSIGN(strokeColorS, (NSColorSpace*)spaceref); gsMakeColor(&col, rgb_colorspace, 0, 0, 0, 0); // Keep the old alpha value col.field[AINDEX] = fillColor.field[AINDEX]; [self setColor: &col state: COLOR_STROKE]; } - (void) GSSetFillColor: (const CGFloat *)values { device_color_t dcolor; NSColor *color; if ((fillColorS == nil) || ((color = [NSColor colorWithColorSpace: fillColorS components: values count: [fillColorS numberOfColorComponents] + 1]) == nil) || ((color = [color colorUsingColorSpaceName: NSDeviceRGBColorSpace]) == nil)) { DPS_ERROR(DPSundefined, @"No fill colorspace defined, assume DeviceRGB"); gsMakeColor(&dcolor, rgb_colorspace, values[0], values[1], values[2], values[3]); dcolor.field[AINDEX] = values[4]; } else { CGFloat r, g, b, a; [color getRed: &r green: &g blue: &b alpha: &a]; dcolor.space = rgb_colorspace; dcolor.field[0] = r; dcolor.field[1] = g; dcolor.field[2] = b; dcolor.field[AINDEX] = a; } [self setColor: &dcolor state: COLOR_FILL]; } - (void) GSSetStrokeColor: (const CGFloat *)values { device_color_t dcolor; NSColor *color; if ((strokeColorS == nil) || ((color = [NSColor colorWithColorSpace: strokeColorS components: values count: [strokeColorS numberOfColorComponents] + 1]) == nil) || ((color = [color colorUsingColorSpaceName: NSDeviceRGBColorSpace]) == nil)) { DPS_ERROR(DPSundefined, @"No stroke colorspace defined, assume DeviceRGB"); gsMakeColor(&dcolor, rgb_colorspace, values[0], values[1], values[2], values[3]); dcolor.field[AINDEX] = values[4]; } else { CGFloat r, g, b, a; [color getRed: &r green: &g blue: &b alpha: &a]; dcolor.space = rgb_colorspace; dcolor.field[0] = r; dcolor.field[1] = g; dcolor.field[2] = b; dcolor.field[AINDEX] = a; } [self setColor: &dcolor state: COLOR_STROKE]; } /* ----------------------------------------------------------------------- */ /* Text operations */ /* ----------------------------------------------------------------------- */ typedef enum { show_delta, show_array_x, show_array_y, show_array_xy } show_array_t; /* Omnibus show string routine that combines that characteristics of ashow, awidthshow, widthshow, xshow, xyshow, and yshow */ - (void) _showString: (const char *)s xCharAdj: (CGFloat)cx yCharAdj: (CGFloat)cy char: (char)c adjArray: (const CGFloat *)arr arrType: (show_array_t)type isRelative: (BOOL)relative; { NSPoint point = [path currentPoint]; unichar *uch; unsigned int ulen; int i; /* FIXME: We should use proper glyph generation here. */ uch = NULL; ulen = 0; GSToUnicode(&uch, &ulen, (const unsigned char*)s, strlen(s), [font mostCompatibleStringEncoding], NSDefaultMallocZone(), 0); for (i = 0; i < ulen; i++) { NSPoint delta; NSGlyph glyph; glyph = (NSGlyph)uch[i]; [self GSShowGlyphs: &glyph : 1]; /* Note we update the current point according to the current transformation scaling, although the text isn't currently scaled (FIXME). */ if (type == show_array_xy) { delta.x = arr[2*i]; delta.y = arr[2*i+1]; } else if (type == show_array_x) { delta.x = arr[i]; delta.y = 0; } else if (type == show_array_y) { delta.x = 0; delta.y = arr[i]; } else { delta.x = arr[0]; delta.y = arr[1]; } delta = [ctm deltaPointInMatrixSpace: delta]; if (relative == YES) { NSSize advancement; advancement = [font advancementForGlyph: glyph]; /* Use only delta transformations (no offset). Is this conversion needed?*/ advancement = [ctm transformSize: NSMakeSize(advancement.width, [font ascender])]; delta.x += advancement.width; delta.y += advancement.height; } if (c && *(s+i) == c) { NSPoint cdelta; cdelta.x = cx; cdelta.y = cy; cdelta = [ctm deltaPointInMatrixSpace: cdelta]; delta.x += cdelta.x; delta.y += cdelta.y; } point.x += delta.x; if (type != show_delta) { point.y += delta.y; } [path moveToPoint: point]; } free(uch); } - (void) DPSashow: (CGFloat)x : (CGFloat)y : (const char*)s { CGFloat arr[2]; arr[0] = x; arr[1] = y; [self _showString: s xCharAdj: 0 yCharAdj: 0 char: 0 adjArray: arr arrType: show_delta isRelative: YES]; } - (void) DPSawidthshow: (CGFloat)cx : (CGFloat)cy : (int)c : (CGFloat)ax : (CGFloat)ay : (const char*)s { CGFloat arr[2]; arr[0] = ax; arr[1] = ay; [self _showString: s xCharAdj: cx yCharAdj: cy char: c adjArray: arr arrType: show_delta isRelative: YES]; } - (void) DPScharpath: (const char*)s : (int)count { NSGlyph glBuf[count]; int i; if (!font) return; // FIXME for (i = 0; i < count; i++) { glBuf[i] = [font glyphForCharacter: s[i]]; } CHECK_PATH; [font appendBezierPathWithGlyphs: glBuf count: count toBezierPath: path]; } - (void) appendBezierPathWithPackedGlyphs: (const char *)packedGlyphs path: (NSBezierPath*)aPath { unsigned int count = packedGlyphs[0]; NSMultibyteGlyphPacking packing; NSGlyph glBuf[count]; int i; int j; unsigned char a, b, c, d; if (!font) return; packing = [font glyphPacking]; j = 1; for (i = 0; i < count; i++) { switch (packing) { case NSOneByteGlyphPacking: glBuf[i] = (NSGlyph)packedGlyphs[j++]; break; case NSTwoByteGlyphPacking: a= packedGlyphs[j++]; glBuf[i] = (NSGlyph)((a << 8) | packedGlyphs[j++]); break; case NSFourByteGlyphPacking: a = packedGlyphs[j++]; b = packedGlyphs[j++]; c = packedGlyphs[j++]; d = packedGlyphs[j++]; glBuf[i] = (NSGlyph)((a << 24) | (b << 16) | (c << 8) | d); break; case NSJapaneseEUCGlyphPacking: case NSAsciiWithDoubleByteEUCGlyphPacking: default: // FIXME break; } } [font appendBezierPathWithGlyphs: glBuf count: count toBezierPath: aPath]; } - (void) DPSshow: (const char*)s { [self subclassResponsibility: _cmd]; } - (void) DPSwidthshow: (CGFloat)x : (CGFloat)y : (int)c : (const char*)s { CGFloat arr[2]; arr[0] = 0; arr[1] = 0; [self _showString: s xCharAdj: x yCharAdj: y char: c adjArray: arr arrType: show_delta isRelative: YES]; } - (void) DPSxshow: (const char*)s : (const CGFloat*)numarray : (int)size { [self _showString: s xCharAdj: 0 yCharAdj: 0 char: 0 adjArray: numarray arrType: show_array_x isRelative: NO]; } - (void) DPSxyshow: (const char*)s : (const CGFloat*)numarray : (int)size { [self _showString: s xCharAdj: 0 yCharAdj: 0 char: 0 adjArray: numarray arrType: show_array_xy isRelative: NO]; } - (void) DPSyshow: (const char*)s : (const CGFloat*)numarray : (int)size { [self _showString: s xCharAdj: 0 yCharAdj: 0 char: 0 adjArray: numarray arrType: show_array_y isRelative: NO]; } - (void) GSSetCharacterSpacing: (CGFloat)extra { charSpacing = extra; } - (void) GSSetFont: (GSFontInfo *)fontref { if (font == fontref) return; ASSIGN(font, fontref); } - (void) GSSetFontSize: (CGFloat)size { } - (NSAffineTransform *) GSGetTextCTM { return textCtm; } - (NSPoint) GSGetTextPosition { return [textCtm transformPoint: NSMakePoint(0,0)]; } - (void) GSSetTextCTM: (NSAffineTransform *)newCtm { ASSIGN(textCtm, newCtm); } - (void) GSSetTextDrawingMode: (GSTextDrawingMode)mode { textMode = mode; } - (void) GSSetTextPosition: (NSPoint)loc { [textCtm translateToPoint: loc]; } - (void) GSShowText: (const char *)string : (size_t) length { [self subclassResponsibility: _cmd]; } - (void) GSShowGlyphs: (const NSGlyph *)glyphs : (size_t) length { int i; NSSize advances[length]; for (i=0; i