1/* GSGState - Generic graphic state
2
3   Copyright (C) 1998-2010 Free Software Foundation, Inc.
4
5   Written by:  Adam Fedor <fedor@gnu.org>
6   Date: Mar 2002
7
8   This file is part of the GNU Objective C User Interface Library.
9
10   This library is free software; you can redistribute it and/or
11   modify it under the terms of the GNU Lesser General Public
12   License as published by the Free Software Foundation; either
13   version 2 of the License, or (at your option) any later version.
14
15   This library is distributed in the hope that it will be useful,
16   but WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
18   Lesser General Public License for more details.
19
20   You should have received a copy of the GNU Lesser General Public
21   License along with this library; see the file COPYING.LIB.
22   If not, see <http://www.gnu.org/licenses/> or write to the
23   Free Software Foundation, 51 Franklin Street, Fifth Floor,
24   Boston, MA 02110-1301, USA.
25*/
26
27#include "config.h"
28#import <Foundation/NSObjCRuntime.h>
29#import <Foundation/NSValue.h>
30#import <Foundation/NSDictionary.h>
31#import <AppKit/NSAffineTransform.h>
32#import <AppKit/NSBezierPath.h>
33#import <AppKit/NSColor.h>
34#import <AppKit/NSColorSpace.h>
35#import <AppKit/NSImage.h>
36#import <GNUstepGUI/GSFontInfo.h>
37#import <AppKit/NSGraphics.h>
38#import "gsc/GSContext.h"
39#import "gsc/GSGState.h"
40#import "gsc/GSFunction.h"
41#include "math.h"
42#import <GNUstepBase/Unicode.h>
43
44#define CHECK_PATH \
45  if (!path) \
46    { \
47      path = [NSBezierPath new]; \
48    }
49
50@implementation GSGState
51
52/* Designated initializer. */
53- initWithDrawContext: (GSContext *)drawContext
54{
55  self = [super init];
56  if (!self)
57    return nil;
58
59  drawcontext = drawContext;
60  offset = NSMakePoint(0, 0);
61  path   = nil;
62  font   = nil;
63  fillColorS   = nil;
64  strokeColorS = nil;
65  [self DPSinitgraphics];
66  return self;
67}
68
69- (void) dealloc
70{
71  TEST_RELEASE(font);
72  TEST_RELEASE(path);
73  RELEASE(ctm);
74  RELEASE(textCtm);
75  RELEASE(fillColorS);
76  RELEASE(strokeColorS);
77  TEST_RELEASE(pattern);
78  [super dealloc];
79}
80
81- (id) deepen
82{
83  NSZone *zone = [self zone];
84
85  if (path)
86    self->path = [path copyWithZone: zone];
87
88  self->ctm     = [ctm copyWithZone: zone];
89  self->textCtm = [textCtm copyWithZone: zone];
90
91  // Just retain the other objects
92  if (font != nil)
93    RETAIN(font);
94  if (fillColorS != nil)
95    RETAIN(fillColorS);
96  if (strokeColorS != nil)
97    RETAIN(strokeColorS);
98  if (pattern != nil)
99    RETAIN(pattern);
100
101  return self;
102}
103
104- (NSString*) description
105{
106  NSMutableString *description = [[super description] mutableCopy];
107  [description appendFormat: @" drawcontext: %@",drawcontext];
108  [description appendFormat: @" ctm: %@",ctm];
109  return [description copy];
110}
111
112- (id)copyWithZone: (NSZone *)zone
113{
114  GSGState *new = (GSGState *)NSCopyObject(self, 0, zone);
115  /* Do a deep copy since gstates are isolated from each other */
116  return [new deepen];
117}
118
119- (void) setOffset: (NSPoint)theOffset
120{
121  offset = theOffset;
122}
123
124- (NSPoint) offset
125{
126  return offset;
127}
128
129/** Subclasses should override this method to be notified of changes
130    in the current color */
131- (void) setColor: (device_color_t *)color state: (color_state_t)cState
132{
133  if ((cState & COLOR_FILL) && (&fillColor != color))
134    {
135      fillColor = *color;
136    }
137  if ((cState & COLOR_STROKE) && (&strokeColor != color))
138    {
139      strokeColor = *color;
140    }
141  cstate = cState;
142  DESTROY(pattern);
143}
144
145- (void) GSSetPatterColor: (NSImage*)image
146{
147  ASSIGN(pattern, image);
148}
149
150- (void) setShouldAntialias: (BOOL)antialias
151{
152  _antialias = antialias;
153}
154
155- (BOOL) shouldAntialias
156{
157  return _antialias;
158}
159
160- (NSPoint) patternPhase
161{
162  return _patternPhase;
163}
164
165- (void) setPatternPhase: (NSPoint)phase
166{
167  _patternPhase = phase;
168}
169
170- (NSCompositingOperation) compositingOperation
171{
172  return _compositingOperation;
173}
174
175- (void) setCompositingOperation: (NSCompositingOperation)operation
176{
177  _compositingOperation = operation;
178}
179
180// This is only a fall back, the method should not be called any more.
181- (void) compositeGState: (GSGState *)source
182                fromRect: (NSRect)aRect
183                 toPoint: (NSPoint)aPoint
184                      op: (NSCompositingOperation)op
185{
186  [self compositeGState: source
187        fromRect: aRect
188        toPoint: aPoint
189        op: op
190        fraction: 1.0];
191
192}
193
194// This is only a fall back, the method should not be called any more.
195- (void) dissolveGState: (GSGState *)source
196               fromRect: (NSRect)aRect
197                toPoint: (NSPoint)aPoint
198                  delta: (CGFloat)delta
199{
200  [self compositeGState: source
201        fromRect: aRect
202        toPoint: aPoint
203        op: NSCompositeSourceOver
204        fraction: delta];
205}
206
207- (void) compositeGState: (GSGState *)source
208                fromRect: (NSRect)aRect
209                 toPoint: (NSPoint)aPoint
210                      op: (NSCompositingOperation)op
211                fraction: (CGFloat)delta
212{
213  [self subclassResponsibility: _cmd];
214}
215
216- (void) compositerect: (NSRect)aRect
217                    op: (NSCompositingOperation)op
218{
219  [self subclassResponsibility: _cmd];
220}
221
222- (NSPoint) pointInMatrixSpace: (NSPoint)aPoint
223{
224  return [ctm transformPoint: aPoint];
225}
226
227- (NSPoint) deltaPointInMatrixSpace: (NSPoint)aPoint
228{
229  return [ctm deltaPointInMatrixSpace: aPoint];
230}
231
232- (NSRect) rectInMatrixSpace: (NSRect)rect
233{
234  return [ctm rectInMatrixSpace: rect];
235}
236
237@end
238
239@implementation GSGState (Ops)
240
241/* ----------------------------------------------------------------------- */
242/* Color operations */
243/* ----------------------------------------------------------------------- */
244- (void) DPScurrentalpha: (CGFloat*)a
245{
246  *a = fillColor.field[AINDEX];
247}
248
249- (void) DPScurrentcmykcolor: (CGFloat*)c : (CGFloat*)m : (CGFloat*)y : (CGFloat*)k
250{
251  device_color_t new = fillColor;
252  gsColorToCMYK(&new);
253  *c = new.field[0];
254  *m = new.field[1];
255  *y = new.field[2];
256  *k = new.field[3];
257}
258
259- (void) DPScurrentgray: (CGFloat*)gray
260{
261  device_color_t gcolor = fillColor;
262  gsColorToGray(&gcolor);
263  *gray = gcolor.field[0];
264}
265
266- (void) DPScurrenthsbcolor: (CGFloat*)h : (CGFloat*)s : (CGFloat*)b
267{
268  device_color_t gcolor = fillColor;
269  gsColorToHSB(&gcolor);
270  *h = gcolor.field[0]; *s = gcolor.field[1]; *b = gcolor.field[2];
271}
272
273- (void) DPScurrentrgbcolor: (CGFloat*)r : (CGFloat*)g : (CGFloat*)b
274{
275  device_color_t gcolor = fillColor;
276  gsColorToRGB(&gcolor);
277  *r = gcolor.field[0]; *g = gcolor.field[1]; *b = gcolor.field[2];
278}
279
280#define CLAMP(x) \
281  if (x < 0.0) x = 0.0; \
282  if (x > 1.0) x = 1.0;
283
284- (void) DPSsetalpha: (CGFloat)a
285{
286  CLAMP(a)
287  fillColor.field[AINDEX] = strokeColor.field[AINDEX] = a;
288  [self setColor: &fillColor state: COLOR_FILL];
289  [self setColor: &strokeColor state: COLOR_STROKE];
290}
291
292- (void) DPSsetcmykcolor: (CGFloat)c : (CGFloat)m : (CGFloat)y : (CGFloat)k
293{
294  device_color_t col;
295  CLAMP(c)
296  CLAMP(m)
297  CLAMP(y)
298  CLAMP(k)
299  gsMakeColor(&col, cmyk_colorspace, c, m, y, k);
300  // Keep the old alpha value
301  col.field[AINDEX] = fillColor.field[AINDEX];
302  [self setColor: &col state: COLOR_BOTH];
303}
304
305- (void) DPSsetgray: (CGFloat)gray
306{
307  device_color_t col;
308  CLAMP(gray)
309  gsMakeColor(&col, gray_colorspace, gray, 0, 0, 0);
310  // Keep the old alpha value
311  col.field[AINDEX] = fillColor.field[AINDEX];
312  [self setColor: &col  state: COLOR_BOTH];
313}
314
315- (void) DPSsethsbcolor: (CGFloat)h : (CGFloat)s : (CGFloat)b
316{
317  device_color_t col;
318  CLAMP(h)
319  CLAMP(s)
320  CLAMP(b)
321  gsMakeColor(&col, hsb_colorspace, h, s, b, 0);
322  // Keep the old alpha value
323  col.field[AINDEX] = fillColor.field[AINDEX];
324  [self setColor: &col state: COLOR_BOTH];
325}
326
327- (void) DPSsetrgbcolor: (CGFloat)r : (CGFloat)g : (CGFloat)b
328{
329  device_color_t col;
330  CLAMP(r)
331  CLAMP(g)
332  CLAMP(b)
333  gsMakeColor(&col, rgb_colorspace, r, g, b, 0);
334  // Keep the old alpha value
335  col.field[AINDEX] = fillColor.field[AINDEX];
336  [self setColor: &col state: COLOR_BOTH];
337}
338
339
340- (void) GSSetFillColorspace: (void *)spaceref
341{
342  device_color_t col;
343
344  ASSIGN(fillColorS, (NSColorSpace*)spaceref);
345  gsMakeColor(&col, rgb_colorspace, 0, 0, 0, 0);
346  // Keep the old alpha value
347  col.field[AINDEX] = fillColor.field[AINDEX];
348  [self setColor: &col state: COLOR_FILL];
349}
350
351- (void) GSSetStrokeColorspace: (void *)spaceref
352{
353  device_color_t col;
354
355  ASSIGN(strokeColorS, (NSColorSpace*)spaceref);
356  gsMakeColor(&col, rgb_colorspace, 0, 0, 0, 0);
357  // Keep the old alpha value
358  col.field[AINDEX] = fillColor.field[AINDEX];
359  [self setColor: &col state: COLOR_STROKE];
360}
361
362- (void) GSSetFillColor: (const CGFloat *)values
363{
364  device_color_t dcolor;
365  NSColor *color;
366
367  if ((fillColorS == nil)
368      || ((color = [NSColor colorWithColorSpace: fillColorS
369                            components: values
370                            count: [fillColorS numberOfColorComponents] + 1]) == nil)
371      || ((color = [color colorUsingColorSpaceName: NSDeviceRGBColorSpace]) == nil))
372    {
373      DPS_ERROR(DPSundefined, @"No fill colorspace defined, assume DeviceRGB");
374      gsMakeColor(&dcolor, rgb_colorspace, values[0], values[1],
375                  values[2], values[3]);
376      dcolor.field[AINDEX] = values[4];
377    }
378  else
379    {
380      CGFloat r, g, b, a;
381      [color getRed: &r
382             green: &g
383             blue: &b
384             alpha: &a];
385      dcolor.space = rgb_colorspace;
386      dcolor.field[0] = r;
387      dcolor.field[1] = g;
388      dcolor.field[2] = b;
389      dcolor.field[AINDEX] = a;
390    }
391
392  [self setColor: &dcolor state: COLOR_FILL];
393}
394
395- (void) GSSetStrokeColor: (const CGFloat *)values
396{
397  device_color_t dcolor;
398  NSColor *color;
399
400  if ((strokeColorS == nil)
401      || ((color = [NSColor colorWithColorSpace: strokeColorS
402                            components: values
403                            count: [strokeColorS numberOfColorComponents] + 1]) == nil)
404      || ((color = [color colorUsingColorSpaceName: NSDeviceRGBColorSpace]) == nil))
405    {
406      DPS_ERROR(DPSundefined, @"No stroke colorspace defined, assume DeviceRGB");
407      gsMakeColor(&dcolor, rgb_colorspace, values[0], values[1],
408                  values[2], values[3]);
409      dcolor.field[AINDEX] = values[4];
410    }
411  else
412    {
413      CGFloat r, g, b, a;
414      [color getRed: &r
415             green: &g
416             blue: &b
417             alpha: &a];
418      dcolor.space = rgb_colorspace;
419      dcolor.field[0] = r;
420      dcolor.field[1] = g;
421      dcolor.field[2] = b;
422      dcolor.field[AINDEX] = a;
423    }
424
425  [self setColor: &dcolor state: COLOR_STROKE];
426}
427
428/* ----------------------------------------------------------------------- */
429/* Text operations */
430/* ----------------------------------------------------------------------- */
431
432typedef enum {
433  show_delta, show_array_x, show_array_y, show_array_xy
434} show_array_t;
435
436/* Omnibus show string routine that combines that characteristics of
437   ashow, awidthshow, widthshow, xshow, xyshow, and yshow */
438- (void) _showString: (const char *)s
439	    xCharAdj: (CGFloat)cx
440	    yCharAdj: (CGFloat)cy
441		char: (char)c
442	    adjArray: (const CGFloat *)arr
443	     arrType: (show_array_t)type
444	  isRelative: (BOOL)relative;
445{
446  NSPoint point = [path currentPoint];
447  unichar *uch;
448  unsigned int ulen;
449  int i;
450
451  /*
452     FIXME: We should use proper glyph generation here.
453  */
454  uch = NULL;
455  ulen = 0;
456  GSToUnicode(&uch, &ulen, (const unsigned char*)s, strlen(s),
457    [font mostCompatibleStringEncoding], NSDefaultMallocZone(), 0);
458
459  for (i = 0; i < ulen; i++)
460    {
461      NSPoint delta;
462      NSGlyph glyph;
463
464      glyph = (NSGlyph)uch[i];
465      [self GSShowGlyphs: &glyph : 1];
466      /* Note we update the current point according to the current
467	 transformation scaling, although the text isn't currently
468	 scaled (FIXME). */
469      if (type == show_array_xy)
470	{
471	  delta.x = arr[2*i]; delta.y = arr[2*i+1];
472	}
473      else if (type == show_array_x)
474	{
475	  delta.x = arr[i]; delta.y = 0;
476	}
477      else if (type == show_array_y)
478	{
479	  delta.x = 0; delta.y = arr[i];
480	}
481      else
482	{
483	  delta.x = arr[0]; delta.y = arr[1];
484	}
485      delta = [ctm deltaPointInMatrixSpace: delta];
486      if (relative == YES)
487	{
488	  NSSize advancement;
489
490	  advancement = [font advancementForGlyph: glyph];
491	  /* Use only delta transformations (no offset). Is this conversion needed?*/
492	  advancement = [ctm transformSize: NSMakeSize(advancement.width,
493						       [font ascender])];
494	  delta.x += advancement.width;
495	  delta.y += advancement.height;
496	}
497      if (c && *(s+i) == c)
498	{
499	  NSPoint cdelta;
500
501	  cdelta.x = cx; cdelta.y = cy;
502	  cdelta = [ctm deltaPointInMatrixSpace: cdelta];
503	  delta.x += cdelta.x; delta.y += cdelta.y;
504	}
505      point.x += delta.x;
506      if (type != show_delta)
507        {
508	  point.y += delta.y;
509	}
510      [path moveToPoint: point];
511    }
512  free(uch);
513}
514
515- (void) DPSashow: (CGFloat)x : (CGFloat)y : (const char*)s
516{
517  CGFloat arr[2];
518
519  arr[0] = x; arr[1] = y;
520  [self _showString: s
521    xCharAdj: 0 yCharAdj: 0 char: 0 adjArray: arr arrType: show_delta
522    isRelative: YES];
523}
524
525- (void) DPSawidthshow: (CGFloat)cx : (CGFloat)cy : (int)c : (CGFloat)ax : (CGFloat)ay
526		      : (const char*)s
527{
528  CGFloat arr[2];
529
530  arr[0] = ax; arr[1] = ay;
531  [self _showString: s
532    xCharAdj: cx yCharAdj: cy char: c adjArray: arr arrType: show_delta
533    isRelative: YES];
534}
535
536- (void) DPScharpath: (const char*)s : (int)count
537{
538  NSGlyph glBuf[count];
539  int i;
540
541  if (!font)
542    return;
543
544  // FIXME
545  for (i = 0; i < count; i++)
546    {
547      glBuf[i] = [font glyphForCharacter: s[i]];
548    }
549
550  CHECK_PATH;
551  [font appendBezierPathWithGlyphs: glBuf
552        count: count
553        toBezierPath: path];
554}
555
556- (void) appendBezierPathWithPackedGlyphs: (const char *)packedGlyphs
557                                     path: (NSBezierPath*)aPath
558{
559  unsigned int count = packedGlyphs[0];
560  NSMultibyteGlyphPacking packing;
561  NSGlyph glBuf[count];
562  int i;
563  int j;
564  unsigned char a, b, c, d;
565
566  if (!font)
567    return;
568
569  packing = [font glyphPacking];
570  j = 1;
571  for (i = 0; i < count; i++)
572    {
573      switch (packing)
574        {
575          case NSOneByteGlyphPacking:
576            glBuf[i] = (NSGlyph)packedGlyphs[j++];
577            break;
578          case NSTwoByteGlyphPacking:
579            a= packedGlyphs[j++];
580            glBuf[i] = (NSGlyph)((a << 8) | packedGlyphs[j++]);
581            break;
582          case NSFourByteGlyphPacking:
583            a = packedGlyphs[j++];
584            b = packedGlyphs[j++];
585            c = packedGlyphs[j++];
586            d = packedGlyphs[j++];
587            glBuf[i] = (NSGlyph)((a << 24) | (b << 16)
588                                 | (c << 8) | d);
589            break;
590          case NSJapaneseEUCGlyphPacking:
591          case NSAsciiWithDoubleByteEUCGlyphPacking:
592          default:
593            // FIXME
594            break;
595        }
596    }
597
598  [font appendBezierPathWithGlyphs: glBuf
599        count: count
600        toBezierPath: aPath];
601}
602
603- (void) DPSshow: (const char*)s
604{
605  [self subclassResponsibility: _cmd];
606}
607
608- (void) DPSwidthshow: (CGFloat)x : (CGFloat)y : (int)c : (const char*)s
609{
610  CGFloat arr[2];
611
612  arr[0] = 0; arr[1] = 0;
613  [self _showString: s
614    xCharAdj: x yCharAdj: y char: c adjArray: arr arrType: show_delta
615    isRelative: YES];
616}
617
618- (void) DPSxshow: (const char*)s : (const CGFloat*)numarray : (int)size
619{
620  [self _showString: s
621    xCharAdj: 0 yCharAdj: 0 char: 0 adjArray: numarray arrType: show_array_x
622    isRelative: NO];
623}
624
625- (void) DPSxyshow: (const char*)s : (const CGFloat*)numarray : (int)size
626{
627  [self _showString: s
628    xCharAdj: 0 yCharAdj: 0 char: 0 adjArray: numarray arrType: show_array_xy
629    isRelative: NO];
630}
631
632- (void) DPSyshow: (const char*)s : (const CGFloat*)numarray : (int)size
633{
634  [self _showString: s
635    xCharAdj: 0 yCharAdj: 0 char: 0 adjArray: numarray arrType: show_array_y
636    isRelative: NO];
637}
638
639- (void) GSSetCharacterSpacing: (CGFloat)extra
640{
641  charSpacing = extra;
642}
643
644- (void) GSSetFont: (GSFontInfo *)fontref
645{
646  if (font == fontref)
647    return;
648  ASSIGN(font, fontref);
649}
650
651- (void) GSSetFontSize: (CGFloat)size
652{
653}
654
655- (NSAffineTransform *) GSGetTextCTM
656{
657  return textCtm;
658}
659
660- (NSPoint) GSGetTextPosition
661{
662  return [textCtm transformPoint: NSMakePoint(0,0)];
663}
664
665- (void) GSSetTextCTM: (NSAffineTransform *)newCtm
666{
667  ASSIGN(textCtm, newCtm);
668}
669
670- (void) GSSetTextDrawingMode: (GSTextDrawingMode)mode
671{
672  textMode = mode;
673}
674
675- (void) GSSetTextPosition: (NSPoint)loc
676{
677  [textCtm translateToPoint: loc];
678}
679
680- (void) GSShowText: (const char *)string : (size_t) length
681{
682  [self subclassResponsibility: _cmd];
683}
684
685- (void) GSShowGlyphs: (const NSGlyph *)glyphs : (size_t) length
686{
687  int i;
688  NSSize advances[length];
689
690  for (i=0; i<length; i++)
691    {
692      advances[i] = [font advancementForGlyph: glyphs[i]];
693    }
694
695  [self GSShowGlyphsWithAdvances: glyphs : advances : length];
696}
697
698- (void) GSShowGlyphsWithAdvances: (const NSGlyph *)glyphs : (const NSSize *)advances : (size_t) length
699{
700  [self subclassResponsibility: _cmd];
701}
702
703/* ----------------------------------------------------------------------- */
704/* Gstate operations */
705/* ----------------------------------------------------------------------- */
706- (void) DPSinitgraphics
707{
708  DESTROY(path);
709  DESTROY(font);
710  DESTROY(fillColorS);
711  DESTROY(strokeColorS);
712  if (ctm)
713    [ctm makeIdentityMatrix];
714  else
715    ctm = [[NSAffineTransform allocWithZone: [self zone]] init];
716
717   /* Initialize colors. By default the same color is used for filling and
718     stroking unless fill and/or stroke color is set explicitly */
719  gsMakeColor(&fillColor, gray_colorspace, 0, 0, 0, 0);
720  fillColor.field[AINDEX] = 1.0;
721  [self setColor: &fillColor state: COLOR_BOTH];
722
723  charSpacing = 0;
724  textMode    = GSTextFill;
725  if (textCtm)
726    [textCtm makeIdentityMatrix];
727  else
728    textCtm = [[NSAffineTransform allocWithZone: [self zone]] init];
729}
730
731- (void)DPScurrentflat: (CGFloat *)flatness
732{
733  if (path)
734    *flatness = [path flatness];
735  else
736    *flatness = 1.0;
737}
738
739- (void) DPScurrentlinecap: (int*)linecap
740{
741  [self subclassResponsibility: _cmd];
742}
743
744- (void) DPScurrentlinejoin: (int*)linejoin
745{
746  [self subclassResponsibility: _cmd];
747}
748
749- (void) DPScurrentlinewidth: (CGFloat*)width
750{
751  [self subclassResponsibility: _cmd];
752}
753
754- (void) DPScurrentmiterlimit: (CGFloat*)limit
755{
756  [self subclassResponsibility: _cmd];
757}
758
759- (NSPoint) currentPoint
760{
761  NSAffineTransform *ictm;
762  NSPoint user;
763
764  if (path == nil)
765    {
766      return NSMakePoint(0, 0);
767    }
768
769  // This is rather slow, but it is not used very often
770  ictm = [ctm copyWithZone: [self zone]];
771  [ictm invert];
772  user = [ictm transformPoint: [path currentPoint]];
773  RELEASE(ictm);
774  return user;
775}
776
777- (void)DPScurrentpoint: (CGFloat *)x : (CGFloat *)y
778{
779  NSPoint user;
780
781  user = [self currentPoint];
782  *x = user.x;
783  *y = user.y;
784}
785
786- (void) DPScurrentstrokeadjust: (int*)b
787{
788  [self subclassResponsibility: _cmd];
789}
790
791- (void) DPSsetdash: (const CGFloat*)pat : (NSInteger)size : (CGFloat)offset
792{
793  [self subclassResponsibility: _cmd];
794}
795
796- (void)DPSsetflat: (CGFloat)flatness
797{
798  if (path)
799    [path setFlatness: flatness];
800}
801
802- (void) DPSsetlinecap: (int)linecap
803{
804  [self subclassResponsibility: _cmd];
805}
806
807- (void) DPSsetlinejoin: (int)linejoin
808{
809  [self subclassResponsibility: _cmd];
810}
811
812- (void) DPSsetlinewidth: (CGFloat)width
813{
814  [self subclassResponsibility: _cmd];
815}
816
817- (void) DPSsetmiterlimit: (CGFloat)limit
818{
819  [self subclassResponsibility: _cmd];
820}
821
822- (void) DPSsetstrokeadjust: (int)b
823{
824  [self subclassResponsibility: _cmd];
825}
826
827/* ----------------------------------------------------------------------- */
828/* Matrix operations */
829/* ----------------------------------------------------------------------- */
830- (void)DPSconcat: (const CGFloat *)m
831{
832  NSAffineTransformStruct matrix;
833  NSAffineTransform *new_ctm = [NSAffineTransform new];
834
835  matrix.m11 = m[0];
836  matrix.m12 = m[1];
837  matrix.m21 = m[2];
838  matrix.m22 = m[3];
839  matrix.tX  = m[4];
840  matrix.tY  = m[5];
841  [new_ctm setTransformStruct: matrix];
842
843  [ctm prependTransform: new_ctm];
844  RELEASE(new_ctm);
845}
846
847- (void)DPSinitmatrix
848{
849  [ctm makeIdentityMatrix];
850}
851
852- (void)DPSrotate: (CGFloat)angle
853{
854  [ctm rotateByDegrees: angle];
855}
856
857- (void)DPSscale: (CGFloat)x : (CGFloat)y
858{
859  [ctm scaleXBy: x  yBy: y];
860}
861
862- (void)DPStranslate: (CGFloat)x : (CGFloat)y
863{
864  [ctm translateToPoint: NSMakePoint(x, y)];
865}
866
867- (NSAffineTransform *) GSCurrentCTM
868{
869  return AUTORELEASE([ctm copy]);
870}
871
872- (void) GSSetCTM: (NSAffineTransform *)newctm
873{
874  ASSIGN(ctm, newctm);
875}
876
877- (void) GSConcatCTM: (NSAffineTransform *)newctm
878{
879  [ctm prependTransform: newctm];
880}
881
882/* ----------------------------------------------------------------------- */
883/* Paint operations */
884/* ----------------------------------------------------------------------- */
885- (void) DPSarc: (CGFloat)x : (CGFloat)y : (CGFloat)r : (CGFloat)angle1 : (CGFloat)angle2
886{
887  NSBezierPath *newPath;
888
889  newPath = [[NSBezierPath alloc] init];
890  if ((path != nil) && ([path elementCount] != 0))
891    {
892      [newPath lineToPoint: [self currentPoint]];
893    }
894  [newPath appendBezierPathWithArcWithCenter: NSMakePoint(x, y)
895	   radius: r
896	   startAngle: angle1
897	   endAngle: angle2
898	   clockwise: NO];
899  [newPath transformUsingAffineTransform: ctm];
900  CHECK_PATH;
901  [path appendBezierPath: newPath];
902  RELEASE(newPath);
903}
904
905- (void) DPSarcn: (CGFloat)x : (CGFloat)y : (CGFloat)r : (CGFloat)angle1 : (CGFloat)angle2
906{
907  NSBezierPath *newPath;
908
909  newPath = [[NSBezierPath alloc] init];
910  if ((path != nil) && ([path elementCount] != 0))
911    {
912      [newPath lineToPoint: [self currentPoint]];
913    }
914  [newPath appendBezierPathWithArcWithCenter: NSMakePoint(x, y)
915	   radius: r
916	   startAngle: angle1
917	   endAngle: angle2
918	   clockwise: YES];
919  [newPath transformUsingAffineTransform: ctm];
920  CHECK_PATH;
921  [path appendBezierPath: newPath];
922  RELEASE(newPath);
923}
924
925- (void)DPSarct: (CGFloat)x1 : (CGFloat)y1 : (CGFloat)x2 : (CGFloat)y2 : (CGFloat)r
926{
927  NSBezierPath *newPath;
928
929  newPath = [[NSBezierPath alloc] init];
930  if ((path != nil) && ([path elementCount] != 0))
931    {
932	[newPath lineToPoint: [self currentPoint]];
933    }
934  [newPath appendBezierPathWithArcFromPoint: NSMakePoint(x1, y1)
935	   toPoint: NSMakePoint(x2, y2)
936	   radius: r];
937  [newPath transformUsingAffineTransform: ctm];
938  CHECK_PATH;
939  [path appendBezierPath: newPath];
940  RELEASE(newPath);
941}
942
943- (void) DPSclip
944{
945  [self subclassResponsibility: _cmd];
946}
947
948- (void)DPSclosepath
949{
950  CHECK_PATH;
951  [path closePath];
952}
953
954- (void)DPScurveto: (CGFloat)x1 : (CGFloat)y1 : (CGFloat)x2 : (CGFloat)y2 : (CGFloat)x3
955		  : (CGFloat)y3
956{
957  NSPoint p1 = [ctm transformPoint: NSMakePoint(x1, y1)];
958  NSPoint p2 = [ctm transformPoint: NSMakePoint(x2, y2)];
959  NSPoint p3 = [ctm transformPoint: NSMakePoint(x3, y3)];
960
961  CHECK_PATH;
962  [path curveToPoint: p3 controlPoint1: p1 controlPoint2: p2];
963}
964
965- (void) DPSeoclip
966{
967  [self subclassResponsibility: _cmd];
968}
969
970- (void) DPSeofill
971{
972  [self subclassResponsibility: _cmd];
973}
974
975- (void) DPSfill
976{
977  [self subclassResponsibility: _cmd];
978}
979
980- (void)DPSflattenpath
981{
982  if (path)
983    ASSIGN(path, [path bezierPathByFlatteningPath]);
984}
985
986- (void) DPSinitclip
987{
988  [self subclassResponsibility: _cmd];
989}
990
991- (void)DPSlineto: (CGFloat)x : (CGFloat)y
992{
993  NSPoint p = [ctm transformPoint: NSMakePoint(x, y)];
994
995  CHECK_PATH;
996  [path lineToPoint: p];
997}
998
999- (void)DPSmoveto: (CGFloat)x : (CGFloat)y
1000{
1001  NSPoint p = [ctm transformPoint: NSMakePoint(x, y)];
1002
1003  CHECK_PATH;
1004  [path moveToPoint: p];
1005}
1006
1007- (void)DPSnewpath
1008{
1009  if (path)
1010    [path removeAllPoints];
1011}
1012
1013- (NSBezierPath *) bezierPath
1014{
1015  // This is rather slow, but it is not used very often
1016  NSBezierPath *newPath = [path copy];
1017  NSAffineTransform *ictm = [ctm copyWithZone: [self zone]];
1018
1019  [ictm invert];
1020  [newPath transformUsingAffineTransform: ictm];
1021  RELEASE(ictm);
1022  return AUTORELEASE(newPath);
1023}
1024
1025- (void)DPSpathbbox: (CGFloat *)llx : (CGFloat *)lly : (CGFloat *)urx : (CGFloat *)ury
1026{
1027  NSBezierPath *bpath = [self bezierPath];
1028  NSRect rect = [bpath controlPointBounds];
1029
1030  if (llx)
1031    *llx = NSMinX(rect);
1032  if (lly)
1033    *lly = NSMinY(rect);
1034  if (urx)
1035    *urx = NSMaxX(rect);
1036  if (ury)
1037    *ury = NSMaxY(rect);
1038}
1039
1040- (void)DPSrcurveto: (CGFloat)x1 : (CGFloat)y1 : (CGFloat)x2 : (CGFloat)y2 : (CGFloat)x3
1041		   : (CGFloat)y3
1042{
1043  NSPoint p1 = [ctm deltaPointInMatrixSpace: NSMakePoint(x1, y1)];
1044  NSPoint p2 = [ctm deltaPointInMatrixSpace: NSMakePoint(x2, y2)];
1045  NSPoint p3 = [ctm deltaPointInMatrixSpace: NSMakePoint(x3, y3)];
1046
1047  CHECK_PATH;
1048  [path relativeCurveToPoint: p3
1049	controlPoint1: p1
1050	controlPoint2: p2];
1051}
1052
1053- (void) DPSrectclip: (CGFloat)x : (CGFloat)y : (CGFloat)w : (CGFloat)h
1054{
1055  NSBezierPath *oldPath = path;
1056
1057  path = [[NSBezierPath alloc] init];
1058  [path appendBezierPathWithRect: NSMakeRect(x, y, w, h)];
1059  [path transformUsingAffineTransform: ctm];
1060  [self DPSclip];
1061  RELEASE(path);
1062  path = oldPath;
1063  if (path)
1064    [path removeAllPoints];
1065}
1066
1067- (void) DPSrectfill: (CGFloat)x : (CGFloat)y : (CGFloat)w : (CGFloat)h
1068{
1069  NSBezierPath *oldPath = path;
1070
1071  path = [[NSBezierPath alloc] init];
1072  [path appendBezierPathWithRect: NSMakeRect(x, y, w, h)];
1073  [path transformUsingAffineTransform: ctm];
1074  [self DPSfill];
1075  RELEASE(path);
1076  path = oldPath;
1077}
1078
1079- (void) DPSrectstroke: (CGFloat)x : (CGFloat)y : (CGFloat)w : (CGFloat)h
1080{
1081  NSBezierPath *oldPath = path;
1082
1083  path = [[NSBezierPath alloc] init];
1084  [path appendBezierPathWithRect: NSMakeRect(x, y, w, h)];
1085  [path transformUsingAffineTransform: ctm];
1086  [self DPSstroke];
1087  RELEASE(path);
1088  path = oldPath;
1089}
1090
1091- (void)DPSreversepath
1092{
1093  if (path)
1094    ASSIGN(path, [path bezierPathByReversingPath]);
1095}
1096
1097- (void)DPSrlineto: (CGFloat)x : (CGFloat)y
1098{
1099  NSPoint p = [ctm deltaPointInMatrixSpace: NSMakePoint(x, y)];
1100
1101  CHECK_PATH;
1102  [path relativeLineToPoint: p];
1103}
1104
1105- (void)DPSrmoveto: (CGFloat)x : (CGFloat)y
1106{
1107  NSPoint p = [ctm deltaPointInMatrixSpace: NSMakePoint(x, y)];
1108
1109  CHECK_PATH;
1110  [path relativeMoveToPoint: p];
1111}
1112
1113- (void) DPSstroke;
1114{
1115  [self subclassResponsibility: _cmd];
1116}
1117
1118- (void) GSSendBezierPath: (NSBezierPath *)newpath
1119{
1120  NSInteger count = 10;
1121  CGFloat dash_pattern[10];
1122  CGFloat phase;
1123
1124  // Appending to the current path is a lot faster than copying!
1125  //ASSIGNCOPY(path, newpath);
1126  CHECK_PATH;
1127  [path removeAllPoints];
1128  [path appendBezierPath: newpath];
1129  [path transformUsingAffineTransform: ctm];
1130
1131  // The following should be moved down into the specific subclasses
1132  [self DPSsetlinewidth: [newpath lineWidth]];
1133  [self DPSsetlinejoin: [newpath lineJoinStyle]];
1134  [self DPSsetlinecap: [newpath lineCapStyle]];
1135  [self DPSsetmiterlimit: [newpath miterLimit]];
1136  [self DPSsetflat: [newpath flatness]];
1137
1138  [newpath getLineDash: dash_pattern count: &count phase: &phase];
1139  [self DPSsetdash: dash_pattern : count : phase];
1140}
1141
1142- (void) GSRectClipList: (const NSRect *)rects : (int) count
1143{
1144  int i;
1145  NSRect union_rect;
1146
1147  if (count == 0)
1148    return;
1149
1150  /*
1151     The specification is not clear if the union of the rects
1152     should produce the new clip rect or if the outline of all rects
1153     should be used as clip path.
1154  */
1155  union_rect = rects[0];
1156  for (i = 1; i < count; i++)
1157    union_rect = NSUnionRect(union_rect, rects[i]);
1158
1159  [self DPSrectclip: NSMinX(union_rect) : NSMinY(union_rect)
1160	  : NSWidth(union_rect) : NSHeight(union_rect)];
1161}
1162
1163
1164- (void) GSRectFillList: (const NSRect *)rects : (int) count
1165{
1166  int i;
1167  for (i=0; i < count; i++)
1168    [self DPSrectfill: NSMinX(rects[i]) : NSMinY(rects[i])
1169	  : NSWidth(rects[i]) : NSHeight(rects[i])];
1170}
1171
1172- (NSDictionary *) GSReadRect: (NSRect)r
1173{
1174  return nil;
1175}
1176
1177- (void)DPSimage: (NSAffineTransform*) matrix
1178		: (NSInteger) pixelsWide : (NSInteger) pixelsHigh
1179		: (NSInteger) bitsPerSample : (NSInteger) samplesPerPixel
1180		: (NSInteger) bitsPerPixel : (NSInteger) bytesPerRow : (BOOL) isPlanar
1181		: (BOOL) hasAlpha : (NSString *) colorSpaceName
1182		: (const unsigned char *const [5]) data
1183{
1184  [self subclassResponsibility: _cmd];
1185}
1186
1187- (void) DPSshfill: (NSDictionary *)shader
1188{
1189  NSNumber *v;
1190  NSDictionary *function_dict;
1191  GSFunction2in3out *function;
1192  NSAffineTransform *matrix, *inverse;
1193  NSAffineTransformStruct	ts;
1194  NSRect rect;
1195  int iwidth, iheight;
1196  double x, y;
1197  int i;
1198  unsigned char *data;
1199
1200  v = [shader objectForKey: @"ShadingType"];
1201
1202  /* only type 1 shaders */
1203  if ([v intValue] != 1)
1204    {
1205      NSLog(@"ShadingType != 1 not supported.");
1206      return;
1207    }
1208
1209  /* in device rgb space */
1210  if ([shader objectForKey: @"ColorSpace"])
1211    if (![[shader objectForKey: @"ColorSpace"] isEqual: NSDeviceRGBColorSpace])
1212      {
1213        NSLog(@"Only device RGB ColorSpace supported for shading.");
1214        return;
1215      }
1216
1217  function_dict = [shader objectForKey: @"Function"];
1218  if (!function_dict)
1219    {
1220      NSLog(@"Shading function not set.");
1221      return;
1222    }
1223
1224  function = [[GSFunction2in3out alloc] initWith: function_dict];
1225  if (!function)
1226    return;
1227
1228  matrix = [ctm copy];
1229  if ([shader objectForKey: @"Matrix"])
1230    {
1231      [matrix prependTransform: [shader objectForKey: @"Matrix"]];
1232    }
1233
1234  inverse = [matrix copy];
1235  [inverse invert];
1236  ts = [inverse transformStruct];
1237
1238  rect = [function affectedRect];
1239  iwidth = rect.size.width;
1240  iheight = rect.size.height;
1241  data = malloc(sizeof(char) * iwidth * iheight * 4);
1242  i = 0;
1243
1244  for (y = NSMinY(rect); y < NSMaxY(rect); y++)
1245    {
1246      double in[2], out[3];
1247      NSPoint p;
1248
1249      p = [inverse transformPoint: NSMakePoint(NSMinX(rect), y)];
1250      in[0] = p.x;
1251      in[1] = p.y;
1252
1253      out[0] = out[1] = out[2] = 0.0;
1254      for (x = NSMinX(rect); x < NSMaxX(rect); x++)
1255        {
1256          unsigned char r, g, b, a;
1257
1258          [function eval: in : out];
1259
1260          // Set data at x - NSMinX(rect), y - NSMinY(rect) to out
1261          r = out[0] * 255;
1262          g = out[1] * 255;
1263          b = out[2] * 255;
1264          a = 255;
1265          data[i++] = r;
1266          data[i++] = g;
1267          data[i++] = b;
1268          data[i++] = a;
1269
1270          // This gives the same result as:
1271          // p = [inverse transformPoint: NSMakePoint(x, y)];
1272          in[0] += ts.m11;
1273          in[1] += ts.m12;
1274        }
1275    }
1276
1277  // Copy data to device
1278  DESTROY(matrix);
1279  matrix = [NSAffineTransform new];
1280  [matrix translateXBy: NSMinX(rect) yBy: NSMinY(rect)];
1281  [self DPSimage: matrix
1282        : iwidth : iheight
1283        : 8 : 4
1284        : 32 : 4 * iwidth  : NO
1285        : YES : NSDeviceRGBColorSpace
1286        : (const unsigned char **)&data];
1287  free(data);
1288
1289  DESTROY(matrix);
1290  DESTROY(inverse);
1291  DESTROY(function);
1292}
1293
1294@end
1295
1296
1297@implementation GSGState (PatternColor)
1298
1299- (void *) saveClip
1300{
1301  [self subclassResponsibility: _cmd];
1302  return NULL;
1303}
1304
1305- (void) restoreClip: (void *)savedClip
1306{
1307  [self subclassResponsibility: _cmd];
1308}
1309
1310- (void) _fillRect: (NSRect)rect withPattern: (NSImage*)color_pattern
1311{
1312  NSSize size;
1313  NSAffineTransform *ictm;
1314  NSPoint patternPhase, startPoint, endPoint, point;
1315
1316  // The coordinates we get here are already in device space,
1317  // but compositeToPoint needs user space coordinates
1318  ictm = [ctm copyWithZone: [self zone]];
1319  [ictm invert];
1320
1321  size = [color_pattern size];
1322  patternPhase = [self patternPhase];
1323
1324  if (!NSEqualPoints(patternPhase, NSZeroPoint))
1325    {
1326      // patternPhase % pattern size
1327      patternPhase.x -= floor(patternPhase.x / size.width) * size.width;
1328      patternPhase.y -= floor(patternPhase.y / size.height) * size.height;
1329    }
1330
1331  startPoint = NSMakePoint(floor((NSMinX(rect) - patternPhase.x) / size.width) * size.width
1332                           + patternPhase.x,
1333                           floor((NSMinY(rect) - patternPhase.y) / size.height) * size.height
1334                           + patternPhase.y);
1335
1336  endPoint = NSMakePoint(NSMaxX(rect), NSMaxY(rect));
1337
1338  for (point.y = startPoint.y; point.y < endPoint.y; point.y += size.height)
1339    {
1340      for (point.x = startPoint.x; point.x < endPoint.x; point.x += size.width)
1341        {
1342	  [color_pattern compositeToPoint: [ictm transformPoint: point]
1343                                operation: NSCompositeSourceOver];
1344        }
1345    }
1346  RELEASE(ictm);
1347}
1348
1349- (void) fillRect: (NSRect)rect withPattern: (NSImage*)color_pattern
1350{
1351  NSBezierPath *oldPath = path;
1352  void *oldClip;
1353
1354  oldClip = [self saveClip];
1355  path = [[NSBezierPath alloc] init];
1356  [path appendBezierPathWithRect: rect];
1357  [self DPSclip];
1358
1359  [self _fillRect: rect withPattern: color_pattern];
1360
1361  [self restoreClip: oldClip];
1362  RELEASE(path);
1363  path = oldPath;
1364}
1365
1366- (void) fillPath: (NSBezierPath*)fillPath withPattern: (NSImage*)color_pattern
1367{
1368  NSBezierPath *oldPath = path;
1369  NSRect rect;
1370  void *oldClip;
1371
1372  oldClip = [self saveClip];
1373  rect = [fillPath bounds];
1374  path = fillPath;
1375  [self DPSclip];
1376
1377  [self _fillRect: rect withPattern: color_pattern];
1378
1379  [self restoreClip: oldClip];
1380  path = oldPath;
1381  [self DPSnewpath];
1382}
1383
1384- (void) eofillPath: (NSBezierPath*)fillPath withPattern: (NSImage*)color_pattern
1385{
1386  NSBezierPath *oldPath = path;
1387  NSRect rect;
1388  void *oldClip;
1389
1390  oldClip = [self saveClip];
1391  rect = [fillPath bounds];
1392  path = fillPath;
1393  [self DPSeoclip];
1394
1395  [self _fillRect: rect withPattern: color_pattern];
1396
1397  [self restoreClip: oldClip];
1398  path = oldPath;
1399  [self DPSnewpath];
1400}
1401
1402@end
1403
1404@implementation GSGState (NSGradient)
1405
1406- (void) drawGradient: (NSGradient*)gradient
1407           fromCenter: (NSPoint)startCenter
1408               radius: (CGFloat)startRadius
1409             toCenter: (NSPoint)endCenter
1410               radius: (CGFloat)endRadius
1411              options: (NSUInteger)options
1412{
1413  [self subclassResponsibility: _cmd];
1414}
1415
1416- (void) drawGradient: (NSGradient*)gradient
1417            fromPoint: (NSPoint)startPoint
1418              toPoint: (NSPoint)endPoint
1419              options: (NSUInteger)options
1420{
1421  [self subclassResponsibility: _cmd];
1422}
1423
1424@end
1425