/**
NSGraphicsContext
GNUstep drawing context class.
Copyright (C) 1998 Free Software Foundation, Inc.
Written by: Adam Fedor
Date: Nov 1998
Updated by: Richard Frith-Macdonald
Date: Feb 1999
Reworked heavily by Dr. H. Nikolaus Schaller
Date: Feb 2006
This file is part of the mySTEP AppKit Library.
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., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/
#import
#import
#import
#import
#import
#import
#import
#import
#import
#import
#import
#import
#import
#import "AppKit/NSGraphicsContext.h"
#import "AppKit/NSAffineTransform.h"
#import "AppKit/NSBezierPath.h"
#import "AppKit/NSWindow.h"
#import "AppKit/NSView.h"
#import "NSAppKitPrivate.h"
#import "NSBackendPrivate.h"
#include
#include
@implementation NSObject (Backend)
// we should use the MacOS X version...
#ifndef object_is_instance
#define object_is_instance(A) YES
#endif
- (id) _backendResponsibility:(SEL)aSel
{
[NSException raise:@"NSAppKit" format:@"*** %@[%@ %@]: should be implemented in Backend", object_is_instance(self)?@"-":@"+", NSStringFromClass([self class]), NSStringFromSelector(aSel)];
return nil;
}
@end
static NSRecursiveLock *contextLock = nil; /* Lock for use when creating contexts */
static NSString *NSGraphicsContextThreadKey = @"NSGraphicsContextThreadKey";
static NSString *NSGraphicsContextStackKey = @"NSGraphicsContextStackKey";
NSString *NSGraphicsContextDestinationAttributeName=@"NSGraphicsContextDestinationAttributeName";
NSString *NSGraphicsContextRepresentationFormatAttributeName=@"NSGraphicsContextRepresentationFormatAttributeName";
NSString *NSGraphicsContextPDFFormat=@"pdf";
NSString *NSGraphicsContextPSFormat=@"ps";
static NSMapTable *_gState2struct; // map unique ID to gStates
NSGraphicsContext *GSCurrentContext(void)
{ // Function for rapid access to current graphics context
#ifdef GNUSTEP_BASE_LIBRARY
/*
* gstep-base has a faster mechanism to get the current thread.
*/
NSThread *th = GSCurrentThread();
return (NSGraphicsContext*) th->_gcontext;
#else
NSMutableDictionary *dict = [[NSThread currentThread] threadDictionary];
#if 0
NSLog(@"GSCurrentContext = %@", [dict objectForKey:NSGraphicsContextThreadKey]);
#endif
return (NSGraphicsContext *) [dict objectForKey:NSGraphicsContextThreadKey];
#endif
}
@implementation NSGraphicsContext
+ (void) initialize
{
contextLock = [NSRecursiveLock new]; // where should we use that?
_gState2struct = NSCreateMapTable(NSIntMapKeyCallBacks,
NSNonOwnedPointerMapValueCallBacks, 20);
}
+ (void) setCurrentContext:(NSGraphicsContext *) context
{
#ifdef GNUSTEP_BASE_LIBRARY
/*
* gstep-base has a faster mechanism to get the current thread.
*/
NSThread *th = GSCurrentThread();
ASSIGN(th->_gcontext, context);
#else
NSMutableDictionary *dict = [[NSThread currentThread] threadDictionary];
if(!context)
[dict removeObjectForKey:NSGraphicsContextThreadKey];
else
[dict setObject:context forKey:NSGraphicsContextThreadKey];
#endif
}
+ (NSGraphicsContext *) currentContext
{
return GSCurrentContext();
}
+ (BOOL) currentContextDrawingToScreen
{
return [GSCurrentContext() isDrawingToScreen];
}
+ (NSGraphicsContext *) graphicsContextWithAttributes:(NSDictionary *) attr;
{
return [[[self alloc] _initWithAttributes:attr] autorelease];
}
+ (NSGraphicsContext *) graphicsContextWithBitmapImageRep:(NSBitmapImageRep *) bitmap;
{
return [self graphicsContextWithAttributes:
[NSDictionary dictionaryWithObject:bitmap forKey:NSGraphicsContextDestinationAttributeName]];
}
+ (NSGraphicsContext *) graphicsContextWithWindow:(NSWindow *) window
{
return BACKEND;
}
+ (NSGraphicsContext *) graphicsContextWithGraphicsPort:(void *) port flipped:(BOOL) flipped;
{
return BACKEND;
}
+ (void) restoreGraphicsState
{
NSGraphicsContext *ctxt;
NSMutableDictionary *dict = [[NSThread currentThread] threadDictionary];
NSMutableArray *stack = [dict objectForKey:NSGraphicsContextStackKey];
if(stack == nil)
[NSException raise: NSGenericException format: @"+restoreGraphicsState without previous save"];
ctxt = [stack lastObject]; // might be nil, i.e. no current context
[NSGraphicsContext setCurrentContext:ctxt];
if(ctxt)
{
[stack removeLastObject];
[ctxt restoreGraphicsState];
}
}
+ (void) saveGraphicsState
{
// FIXME: this is not consistent with the Apple doc which says that this method simply saves the state of the current context
NSGraphicsContext *ctxt;
NSMutableDictionary *dict = [[NSThread currentThread] threadDictionary];
NSMutableArray *stack = [dict objectForKey:NSGraphicsContextStackKey];
if(stack == nil)
{ // create stack
stack=[NSMutableArray arrayWithCapacity:10];
[dict setObject:stack forKey:NSGraphicsContextStackKey];
}
ctxt = GSCurrentContext();
if(ctxt)
{ // if we have a current context, save it
[ctxt saveGraphicsState];
[stack addObject:ctxt];
}
}
+ (void) setGraphicsState:(int) graphicsState
{ // make context the current and reset graphics state
_NSGraphicsState *state=NSMapGet(_gState2struct, (void *) graphicsState);
if(!state)
[NSException raise:NSGenericException format: @"setGraphicsState: invalid graphics state %d", graphicsState];
#if 0
NSLog(@"setGraphicsState:%d", graphicsState);
#endif
[self setCurrentContext:state->_context]; // select the associated context as current
}
- (void) dealloc
{
while(_graphicsState)
[self restoreGraphicsState]; // release graphics state stack
[(NSMutableArray *) _focusStack release];
[super dealloc];
}
- (id) init;
{
if((self=[super init]))
{
_compositingOperation = NSCompositeCopy;
}
return self;
}
- (id) _initWithAttributes:(NSDictionary *) attributes;
{
if((self=[super init]))
{
_compositingOperation = NSCompositeCopy;
// ignore attributes
}
return self;
}
- (void) flushGraphics { BACKEND; }
- (BOOL) isDrawingToScreen { BACKEND; return YES; } // default
- (void) restoreGraphicsState
{ // Backend may override - must call [super restoreGraphicsState] to include default operation
_NSGraphicsState *tos=(_NSGraphicsState *) _graphicsState; // current
#if 0
NSLog(@"restoreGraphicsState: %d", [self _currentGState]);
#endif
if(!tos)
[NSException raise: NSGenericException format: @"-restoreGraphicsState without previous -saveGraphicsState"];
NSMapRemove(_gState2struct, (void *)(tos->_gState)); // remove from mapping
_graphicsState=tos->_nextOnStack; // pop from stack
objc_free(tos); // and release
}
- (void) saveGraphicsState
{ // Backend may override - but must call [super saveGraphicsState] for default operation
static int gState;
// might need to be locked
_NSGraphicsState *new;
#if 0
NSLog(@"saveGraphicsState: %d", gState+1);
#endif
new=[self _copyGraphicsState:(_NSGraphicsState *) _graphicsState];
new->_gState=++gState; // assign unique gState number
NSMapInsert(_gState2struct, (void *)(new->_gState), new); // allow for associative mapping
new->_context=self;
new->_nextOnStack=_graphicsState; // push on stack
_graphicsState=new; // and make current
// unlock
}
- (_NSGraphicsState *) _copyGraphicsState:(_NSGraphicsState *) state;
{ // Backend can override to provide extended data object
return (_NSGraphicsState *) objc_calloc(1, sizeof(*state)); // we have no private data to copy
}
- (int) _currentGState;
{ // current top of stack gState
if(_graphicsState)
return ((_NSGraphicsState *) _graphicsState)->_gState;
return -1; // none
}
- (NSDictionary *) attributes { return nil; }
- (void *) focusStack { return _focusStack; }
- (void *) graphicsPort { return _graphicsPort; }
- (BOOL) isFlipped
{
NSView *focusView=[(NSArray *) _focusStack lastObject];
if(focusView)
return [focusView isFlipped]; // ask view
return _isFlipped; // return default value
}
- (void) setFocusStack: (void *)stack { _focusStack=stack; }
// CHECKME: shouldn't that be fetched from current gState?
- (NSCompositingOperation) compositingOperation; { return _compositingOperation; }
- (NSImageInterpolation) imageInterpolation; { return _imageInterpolation; }
- (NSPoint) patternPhase; { return _patternPhase; } // SUBCLASS may override
- (BOOL) shouldAntialias; { return _shouldAntialias; }
// should be changed by the backend
- (void) setCompositingOperation:(NSCompositingOperation) operation; { _compositingOperation=operation; }
- (void) setImageInterpolation:(NSImageInterpolation) inter; { _imageInterpolation=inter; } // SUBCLASS may override
- (void) setPatternPhase:(NSPoint) phase; { _patternPhase=phase; } // SUBCLASS may override
- (void) setShouldAntialias:(BOOL) flag; { _shouldAntialias=flag; } // SUBCLASS may override
@end
#if 0 // already defined in Externs.m
NSString *NSCalibratedWhiteColorSpace; // Colorspace Names
NSString *NSCalibratedBlackColorSpace;
NSString *NSCalibratedRGBColorSpace;
NSString *NSDeviceWhiteColorSpace;
NSString *NSDeviceBlackColorSpace;
NSString *NSDeviceRGBColorSpace;
NSString *NSDeviceCMYKColorSpace;
NSString *NSNamedColorSpace;
NSString *NSPatternImageColorSpace;
NSString *NSCustomColorSpace;
NSString *NSDeviceResolution; // Device Dict Keys
NSString *NSDeviceColorSpaceName;
NSString *NSDeviceBitsPerSample;
NSString *NSDeviceIsScreen;
NSString *NSDeviceIsPrinter;
NSString *NSDeviceSize;
#endif
// Functions (alphabetically sorted)
void NSShowAnimationEffect(NSAnimationEffect animationEffect,
NSPoint centerLocation,
NSSize size,
id animationDelegate,
SEL didEndSelector,
void *contextInfo)
{
// FIXME: run animation
if(animationDelegate)
[animationDelegate performSelector:didEndSelector withObject:(id)contextInfo]; // notify completion
}
const NSWindowDepth *NSAvailableWindowDepths(void)
{
return [[NSScreen deepestScreen] supportedWindowDepths];
}
void NSBeep(void)
{ // Play System Beep
NSLog(@"beeeeep.....");
// NIMP
// could call [NSScreen _beep] -> XBell(_display, 100);
// [GSCurrentServer() beep];
}
NSWindowDepth NSBestDepth(NSString *colorSpace,
int bitsPerSample,
int bitsPerPixel,
BOOL planar,
BOOL *exactMatch)
{
// NIMP
return 0;
}
int NSBitsPerPixelFromDepth(NSWindowDepth depth)
{
return (depth&63); // 0..63
}
int NSBitsPerSampleFromDepth(NSWindowDepth depth)
{
return ((depth>>6)&15); // 0..15
}
NSString *NSColorSpaceFromDepth(NSWindowDepth depth)
{
NSColorSpace *csp=[[[NSColorSpace alloc] _initWithColorSpaceModel:(depth>>11)&15] autorelease];
return [csp localizedName];
}
void NSCopyBits(int srcGstate, NSRect srcRect, NSPoint destPoint)
{
_NSGraphicsState *state;
NSGraphicsContext *ctxt=[NSGraphicsContext currentContext];
if(!srcGstate)
state=(_NSGraphicsState *) (ctxt->_graphicsState); // current
else
{
state=NSMapGet(_gState2struct, (void *) srcGstate);
if(!state)
[NSException raise:NSGenericException format:@"NSCopyBits: invalid source graphics state %d", srcGstate];
}
// FIXME: should we save/restore the compositing operation?
[ctxt setCompositingOperation:NSCompositeCopy];
[ctxt _copyBits:state fromRect:srcRect toPoint:destPoint];
}
void NSCountWindows(int *count)
{
return NSCountWindowsForContext(0, count);
}
void NSCountWindowsForContext(int context, int *count)
{ // for a specific application
*count=[NSScreen _systemWindowListForContext:context size:999999 list:NULL];
}
void NSDisableScreenUpdates(void)
{
// NIMP
return;
}
void NSDrawBitmap(NSRect rect, // Bitmap Images
int pixelsWide,
int pixelsHigh,
int bitsPerSample,
int samplesPerPixel,
int bitsPerPixel,
int bytesPerRow,
BOOL isPlanar,
BOOL hasAlpha,
NSString *colorSpaceName,
const unsigned char *const data[5])
{
NSBitmapImageRep *bitmap=[[NSBitmapImageRep alloc]
initWithBitmapDataPlanes:(unsigned char **)data
pixelsWide:pixelsWide
pixelsHigh:pixelsHigh
bitsPerSample:bitsPerSample
samplesPerPixel:samplesPerPixel
hasAlpha:hasAlpha
isPlanar:isPlanar
colorSpaceName:colorSpaceName
bytesPerRow:bytesPerRow
bitsPerPixel:bitsPerPixel]; // create a temporary bitmap with given data
[bitmap drawInRect:rect];
[bitmap release];
}
void NSDrawButton(NSRect aRect, NSRect clipRect)
{
float grays[] = { NSBlack, NSBlack, NSWhite, NSWhite, NSDarkGray, NSDarkGray };
NSRect rect = NSDrawTiledRects(aRect, clipRect, BUTTON_EDGES_NORMAL, grays, 6);
[[NSColor lightGrayColor] set];
NSRectFill(rect);
}
NSRect NSDrawColorTiledRects( NSRect boundsRect,
NSRect clipRect,
const NSRectEdge *sides,
NSColor **colors,
int count)
{
NSRect slice, remainder = boundsRect;
NSRect rects[count];
int i;
if(!NSIntersectsRect(boundsRect, clipRect))
return NSZeroRect;
for(i = 0; i < count; i++)
{
NSDivideRect(remainder, &slice, &remainder, 1.0, sides[i]);
rects[i] = NSIntersectionRect(slice, clipRect);
}
NSRectFillListWithColors(rects, colors, count);
return remainder;
}
void NSDrawGrayBezel(NSRect aRect, NSRect clipRect)
{
float grays[] = { NSWhite, NSWhite, NSDarkGray, NSDarkGray,
NSLightGray, NSLightGray, NSBlack, NSBlack };
NSRect rect;
rect = NSDrawTiledRects(aRect, clipRect, BEZEL_EDGES_NORMAL, grays, 8);
[[NSColor darkGrayColor] set];
NSRectFill(NSMakeRect(NSMinX(aRect) + 1., NSMinY(aRect) + 1., 1., 1.));
[[NSColor lightGrayColor] set];
NSRectFill(rect);
}
void NSDrawGroove(NSRect aRect, NSRect clipRect)
{
NSRectEdge edges[] = { NSMinXEdge, NSMaxYEdge, NSMinXEdge, NSMaxYEdge,
NSMaxXEdge, NSMinYEdge, NSMaxXEdge, NSMinYEdge };
float grays[] = { NSDarkGray, NSDarkGray, NSWhite, NSWhite,
NSWhite, NSWhite, NSDarkGray, NSDarkGray };
NSRect rect = NSDrawTiledRects(aRect, clipRect, edges, grays, 8);
[[NSColor lightGrayColor] set];
NSRectFill(rect);
}
NSRect
NSDrawTiledRects( NSRect boundsRect,
NSRect clipRect,
const NSRectEdge *sides,
const float *grays,
int count)
{
NSRect slice, remainder = boundsRect;
NSRect rects[count]; // FIXME - stack overflow!!!
int i;
if (!NSIntersectsRect(boundsRect, clipRect))
return NSZeroRect;
for (i = 0; i < count; i++)
{
NSDivideRect(remainder, &slice, &remainder, 1.0, sides[i]);
rects[i] = NSIntersectionRect(slice, clipRect);
}
NSRectFillListWithGrays(rects, grays, count);
return remainder;
}
void NSDrawWhiteBezel(NSRect aRect, NSRect clipRect)
{
float grays[] = { NSWhite, NSWhite, NSDarkGray, NSDarkGray,
NSLightGray, NSLightGray, NSDarkGray, NSDarkGray };
NSRect rect = NSDrawTiledRects(aRect, clipRect, BEZEL_EDGES_NORMAL, grays, 8);
[[NSColor whiteColor] set];
NSRectFill(rect);
}
void NSDrawWindowBackground(NSRect rect)
{
[[NSColor windowBackgroundColor] set]; // can be a pattern color...
NSRectFill(rect);
}
void NSEnableScreenUpdates(void)
{
// FIXME: NIMP
}
void NSEraseRect(NSRect aRect)
{
NSGraphicsContext *ctx=[NSGraphicsContext currentContext];
[ctx saveGraphicsState];
[[NSColor whiteColor] set];
NSRectFill(aRect);
[ctx restoreGraphicsState];
}
void NSFrameRect(NSRect aRect)
{
NSFrameRectWithWidth(aRect, 1.0);
}
void NSFrameRectWithWidth(NSRect aRect, float frameWidth)
{
NSRectEdge sides[] = { NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge };
NSRect remainder = aRect;
NSRect rects[4];
int i;
for(i = 0; i < 4; i++)
NSDivideRect(remainder, &rects[i], &remainder, frameWidth, sides[i]); // chop off rects from all sides
NSRectFillList(rects, 4);
}
void NSFrameRectWithWidthUsingOperation(NSRect r,
float w,
NSCompositingOperation op)
{
NSGraphicsContext *ctx=[NSGraphicsContext currentContext];
[ctx saveGraphicsState];
[ctx setCompositingOperation:op];
NSFrameRectWithWidth(r, w);
[ctx restoreGraphicsState];
}
void NSHighlightRect(NSRect aRect)
{
NSRectFillUsingOperation(aRect, NSCompositeHighlight);
}
int NSNumberOfColorComponents(NSString *colorSpaceName)
{
return [[NSColorSpace _colorSpaceWithName:colorSpaceName] numberOfColorComponents];
}
BOOL NSPlanarFromDepth(NSWindowDepth depth)
{
return ((depth>>10)&1) != 0;
}
NSColor *NSReadPixel(NSPoint location)
{ // Read pixel color from current drawable
return [[NSGraphicsContext currentContext] _readPixel:location];
}
void NSRectClip(NSRect aRect)
{ // intersect with rect
[NSBezierPath clipRect:aRect];
}
void NSRectClipList(const NSRect *rects, int count)
{ // intersect with all rects
int i;
for (i = 0; i < count; i++)
NSRectClip(rects[i]);
}
void NSRectFill(NSRect aRect)
{ // fill rect
if(NSIsEmptyRect(aRect))
return;
[NSBezierPath fillRect:aRect];
}
void NSRectFillList(const NSRect *rects, int count)
{ // Fill an array of rects with the current color.
int i;
for(i = 0; i < count; i++)
NSRectFill(rects[i]);
}
void NSRectFillListUsingOperation(const NSRect *rects,
int count,
NSCompositingOperation op)
{
int i;
for (i = 0; i < count; i++)
NSRectFillUsingOperation(rects[i], op);
}
void NSRectFillListWithColorsUsingOperation(const NSRect *rects,
NSColor **colors,
int num,
NSCompositingOperation op)
{
int i;
for (i = 0; i < num; i++)
{
[colors[i] set];
NSRectFillUsingOperation(rects[i], op);
}
}
void NSRectFillListWithGrays(const NSRect *rects, const float *grays, int count)
{
int i;
for (i = 0; i < count; i++) // Fills each rectangle in the
{ // array rects[] with the gray
[[NSColor colorWithCalibratedWhite:grays[i] alpha:1.0] set];
NSRectFill(rects[i]); // array grays[].
}
}
void
NSRectFillListWithColors(const NSRect *rects, NSColor **colors, int count)
{
int i;
for (i = 0; i < count; i++)
{
[colors[i] set];
NSRectFill(rects[i]);
}
}
void NSRectFillUsingOperation(NSRect aRect, NSCompositingOperation op)
{
NSGraphicsContext *ctx=[NSGraphicsContext currentContext];
NSCompositingOperation co=[ctx compositingOperation]; // save
[ctx setCompositingOperation:op];
[NSBezierPath fillRect:aRect];
[ctx setCompositingOperation:co]; // restore
}
void NSSetFocusRingStyle(NSFocusRingPlacement placement)
{ // callee must save the current context if it sould not be changed permanently
// FIXME: NIMP
NSLog(@"*** NSSetFocusRingStyle not implemented ***");
// what should it do?
// according to http://lists.apple.com/archives/cocoa-de...t/msg01602.html
// it should draw the focus ring around the current clipping rect
// or does it only set the colors and patterns
// and change the clippingRect so that one can draw around?
// yes, we can remove clipping if we use [NSBezierPath clipRect:NSMakeRect(0.0, 0.0, 16000.0, 16000.0)];
// but does it really draw or just set the style?
// how can it modify 'below/above' ==> [nsaffinetransform concat];
// can also set fill&stroke colors
switch(placement)
{
case NSFocusRingOnly:
case NSFocusRingBelow:
case NSFocusRingAbove:
break;
}
}
void NSWindowList(int size, int list[])
{
NSWindowListForContext(0, size, list);
}
void NSWindowListForContext(int context, int size, int list[])
{ // for a specific context (application id) - ask BACKEND
[NSScreen _systemWindowListForContext:context size:size list:list];
}
int NSGetWindowServerMemory(int context,
int *virtualMemory,
int *windowBackingMemory,
NSString **windowDumpStream)
{
if(!context)
context=getpid(); // why do we pass integers for contexts?
// NIMP
return -1;
}