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