/*****************************************************************************\
     Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
                This file is licensed under the Snes9x License.
   For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/

/***********************************************************************************
  SNES9X for Mac OS (c) Copyright John Stiles

  Snes9x for Mac OS X

  (c) Copyright 2001 - 2011  zones
  (c) Copyright 2002 - 2005  107
  (c) Copyright 2002         PB1400c
  (c) Copyright 2004         Alexander and Sander
  (c) Copyright 2004 - 2005  Steven Seeger
  (c) Copyright 2005         Ryan Vogt
 ***********************************************************************************/


#import "port.h"

#import <Foundation/Foundation.h>
#import <QuartzCore/QuartzCore.h>
#import <OpenGL/OpenGL.h>

#import "mac-prefix.h"
#import "mac-dialog.h"
#import "mac-os.h"
#import "mac-coreimage.h"

enum
{
	kCITypeNone    = 0,
	kCITypeBoolean = 1000,
	kCITypeScalar,
	kCITypeColor
};

#define	mCoreImageFilter		501
#define	FIXEDRANGE				0x10000
#define	kCommandFilterMenuBase	0x41000000
#define	kCommandCheckBoxBase	0x49000000
#define	kCommandSliderBase		0x51000000
#define	kCommandColorButtonBase	0x59000000
#define	kCIFilterNamePrefKey	CFSTR("CoreImageFilterName")

#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
#define	truncEnd				0
#endif

typedef struct {
	char	name[256];
	char	displayName[256];
	int		type;
	union {
		struct {
			bool8	cur;
		}	b;
		
		struct {
			float	max, min, cur;
		}	s;
		
		struct {
			float	r, g, b, a;
		}	c;
	}	u;	
}	FilterParam;

static NSMutableArray	*ciFilterNameList          = NULL;
static NSMutableArray	*ciFilterLocalizedNameList = NULL;
static NSArray			*ciFilterInputKeys         = NULL;
static CIFilter			*ciFilter                  = NULL;
static CIContext		*ciContext                 = NULL;
static FilterParam		*ciFilterParam             = NULL;
static CFStringRef		ciFilterName               = NULL;
static HIViewRef		ciFilterUIPane             = NULL;
static MenuRef			ciFilterMenu               = NULL;
static CGColorSpaceRef	cgColor                    = NULL;
static MPSemaphoreID	cisem                      = NULL;
static bool8			ciFilterHasInputCenter     = false;
static bool8			ciFilterHasInputImage      = false;
static int				ciFilterInputKeysCount     = 0;

static void LoadFilterPrefs (void);
static void SaveFilterPrefs (void);
static void FilterParamToFilter (void);
static void FilterToFilterParam (void);
static void BuildCoreImageFilterListAndMenu (void);
static void ReleaseCoreImageFilterListAndMenu (void);
static void ReplaceFilterUI (WindowRef);
static void FilterUIAddSubviews (WindowRef, HIViewRef);
static void FilterUISetValues (HIViewRef);
static bool8 IsCoreImageFilterSupported (CIFilter *);
static pascal OSStatus CoreImageFilterEventHandler (EventHandlerCallRef, EventRef, void *);


void InitCoreImage (void)
{
	OSStatus			err;
	NSAutoreleasePool	*pool;

	pool = [[NSAutoreleasePool alloc] init];
	
	ciFilterName = (CFStringRef) CFPreferencesCopyAppValue(kCIFilterNamePrefKey, kCFPreferencesCurrentApplication);
	if (!ciFilterName)
		ciFilterName = CFStringCreateCopy(kCFAllocatorDefault, CFSTR("CIGammaAdjust"));
	
	BuildCoreImageFilterListAndMenu();
	
	err = MPCreateBinarySemaphore(&cisem);
	
	[pool release];
}

void DeinitCoreImage (void)
{
	OSStatus			err;
	NSAutoreleasePool	*pool;

	pool = [[NSAutoreleasePool alloc] init];
	
	err = MPDeleteSemaphore(cisem);
	
	ReleaseCoreImageFilterListAndMenu();
	
	CFPreferencesSetAppValue(kCIFilterNamePrefKey, ciFilterName, kCFPreferencesCurrentApplication);
	
	CFRelease(ciFilterName);
	
	[pool release];
}	

void InitCoreImageFilter (void)
{
	NSAutoreleasePool	*pool;
	
	pool = [[NSAutoreleasePool alloc] init];
	
	ciFilter = [[CIFilter filterWithName: (NSString *) ciFilterName] retain];
	[ciFilter setDefaults];
	
	ciFilterInputKeys = [[ciFilter inputKeys] retain];
	ciFilterInputKeysCount = [ciFilterInputKeys count];
	
	ciFilterParam = new FilterParam [ciFilterInputKeysCount];
	memset(ciFilterParam, 0, sizeof(FilterParam) * ciFilterInputKeysCount);
	
	ciFilterHasInputCenter = false;
	ciFilterHasInputImage  = false;
	
	LoadFilterPrefs();
	
	[pool release];
}

void DeinitCoreImageFilter (void)
{
	NSAutoreleasePool	*pool;

	pool = [[NSAutoreleasePool alloc] init];
	
	SaveFilterPrefs();
	
	ciFilterHasInputCenter = false;
	ciFilterHasInputImage  = false;
	
	delete [] ciFilterParam;
	
	[ciFilterInputKeys release];
	ciFilterInputKeysCount = 0;
	
	[ciFilter release];
	
	[pool release];
}

static void LoadFilterPrefs (void)
{
	CFDataRef	data;
	int			n = sizeof(FilterParam) * ciFilterInputKeysCount;
	
	data = (CFDataRef) CFPreferencesCopyAppValue(ciFilterName, kCFPreferencesCurrentApplication);
	if (data)
	{
		if (CFDataGetLength(data) == n)
		{
			CFDataGetBytes(data, CFRangeMake(0, n), (UInt8 *) ciFilterParam);
			FilterParamToFilter();
		}
		
		CFRelease(data);
	}
	
	FilterToFilterParam();
}

static void SaveFilterPrefs (void)
{
	CFDataRef	data;
	int			n = sizeof(FilterParam) * ciFilterInputKeysCount;

	data = CFDataCreate(kCFAllocatorDefault, (UInt8 *) ciFilterParam, n);
	if (data)
	{
		CFPreferencesSetAppValue(ciFilterName, data, kCFPreferencesCurrentApplication);
		CFRelease(data);
	}
}

static void FilterParamToFilter (void)
{
	NSString	*key;
	NSNumber	*num;
	CIColor		*color;
	
	for (int i = 0; i < ciFilterInputKeysCount; i++)
	{
		key = [NSString stringWithUTF8String: ciFilterParam[i].name];
		if (key)
		{		
			switch (ciFilterParam[i].type)
			{
				case kCITypeBoolean:
					num = [NSNumber numberWithBool: ciFilterParam[i].u.b.cur];
					[ciFilter setValue: num forKey: key];
					break;
					
				case kCITypeScalar:
					num = [NSNumber numberWithFloat: ciFilterParam[i].u.s.cur];
					[ciFilter setValue: num forKey: key];
					break;
					
				case kCITypeColor:
					color = [CIColor colorWithRed: ciFilterParam[i].u.c.r green: ciFilterParam[i].u.c.g
											 blue: ciFilterParam[i].u.c.b alpha: ciFilterParam[i].u.c.a];
					[ciFilter setValue: color forKey: key];
					break;
					
				default:
					break;
			}
		}
	}
}

static void FilterToFilterParam (void)
{
	NSDictionary	*attr;
	NSString		*key, *label, *className, *typeName;
	NSNumber		*num;
	CIColor			*color;
	id				param;
	
	attr = [ciFilter attributes];
	ciFilterHasInputCenter = false;
	ciFilterHasInputImage  = false;

    for (int i = 0; i < ciFilterInputKeysCount; i++)
    {
		key = [ciFilterInputKeys objectAtIndex: i];
		param = [attr objectForKey: key];
		
		strncpy(ciFilterParam[i].name, [key UTF8String], sizeof(ciFilterParam[i].name));
		ciFilterParam[i].displayName[0] = 0;
		
        if ([param isKindOfClass: [NSDictionary class]])
        {
			label = [(NSDictionary *) param objectForKey: kCIAttributeDisplayName];
			if (!label)
				label = [NSString stringWithString: key];
			strncpy(ciFilterParam[i].displayName, [label UTF8String], sizeof(ciFilterParam[i].displayName));
			
			className = [(NSDictionary *) param objectForKey: kCIAttributeClass];
			
            if ([className isEqualToString: @"NSNumber"])
            {
                typeName = [(NSDictionary *) param objectForKey: kCIAttributeType];
				
                if ([typeName isEqualToString: kCIAttributeTypeBoolean])
				{
					ciFilterParam[i].type = kCITypeBoolean;
					
					num = [ciFilter valueForKey: key];
    				ciFilterParam[i].u.b.cur = [num boolValue];
				}
                else
				{
                    ciFilterParam[i].type = kCITypeScalar;
					
					num = [ciFilter valueForKey: key];
    				ciFilterParam[i].u.s.cur = [num floatValue];
					
					num = [(NSDictionary *) param objectForKey: kCIAttributeSliderMax];
				    if (!num)
				        num = [(NSDictionary *) param objectForKey: kCIAttributeMax];
				    ciFilterParam[i].u.s.max = [num floatValue];
					
					num = [(NSDictionary *) param objectForKey: kCIAttributeSliderMin];
				    if (!num)
				        num = [(NSDictionary *) param objectForKey: kCIAttributeMin];
				    ciFilterParam[i].u.s.min = [num floatValue];
				}
            }
            else
			if ([className isEqualToString: @"CIColor"])
			{
				ciFilterParam[i].type = kCITypeColor;
				
				color = [ciFilter valueForKey: key];
				ciFilterParam[i].u.c.r = [color red];
				ciFilterParam[i].u.c.g = [color green];
				ciFilterParam[i].u.c.b = [color blue];
				ciFilterParam[i].u.c.a = [color alpha];
			}
            else
			{
				ciFilterParam[i].type = kCITypeNone;
				
				if ([className isEqualToString: @"CIVector"] && [key isEqualToString: @"inputCenter"])
					ciFilterHasInputCenter = true;
					
				if ([className isEqualToString: @"CIImage" ] && [key isEqualToString: @"inputImage" ])
					ciFilterHasInputImage  = true;
			}
		}
    }
}

static void BuildCoreImageFilterListAndMenu (void)
{
	NSArray		*categories, *filterNames;
	OSStatus	err;
	
	categories = [NSArray arrayWithObject: kCICategoryStillImage];
	filterNames = [CIFilter filterNamesInCategories: categories];
	
	ciFilterNameList = [[NSMutableArray alloc] initWithCapacity: 1];
	ciFilterLocalizedNameList = [[NSMutableArray alloc] initWithCapacity: 1];
	err = CreateNewMenu(mCoreImageFilter, 0, &ciFilterMenu);
	
	int	n = [filterNames count], m = 0;
	for (int i = 0; i < n; i++)
	{
		CIFilter	*filter;
		NSString	*name, *localName;
		
		name = [filterNames objectAtIndex: i];
		filter = [CIFilter filterWithName: name];
		
		if (IsCoreImageFilterSupported(filter))
		{
			[ciFilterNameList addObject: name];
			
			localName = [CIFilter localizedNameForFilterName: name];
			if (!localName)
				localName = [NSString stringWithString: name];
			
			[ciFilterLocalizedNameList addObject: localName];
			
			err = AppendMenuItemTextWithCFString(ciFilterMenu, (CFStringRef) localName, 0, kCommandFilterMenuBase + m, NULL);
			m++;
		}
	}
}

static void ReleaseCoreImageFilterListAndMenu (void)
{
	CFRelease(ciFilterMenu);
	[ciFilterLocalizedNameList release];
	[ciFilterNameList release];
}

static bool8 IsCoreImageFilterSupported (CIFilter *filter)
{
	NSDictionary	*attr;
	NSArray			*inputKeys;
	NSString		*key, *className;
	id				param;
	bool8			result = true, hasInputImage = false;
	
	attr = [filter attributes];
	inputKeys = [filter inputKeys];
	
	int	n = [inputKeys count];
	for (int i = 0; i < n; i++)
	{
	    key = [inputKeys objectAtIndex: i];
		param = [attr objectForKey: key];
	    
		if ([param isKindOfClass: [NSDictionary class]])
	    {
	        className = [(NSDictionary *) param objectForKey: kCIAttributeClass];
			
			if ([className isEqualToString: @"CIImage"])
			{
				if (![key isEqualToString: @"inputImage"])
					result = false;
				else
					hasInputImage = true;
			}
			else
			if ([className isEqualToString: @"CIVector"])
			{
				if (![key isEqualToString: @"inputCenter"])
					result = false;
			}
			else
			if (![className isEqualToString: @"NSNumber"] && ![className isEqualToString: @"CIColor"])
				result = false;
		}
	}
	
	if (hasInputImage == false)
		result = false;
	
	return (result);
}

void ConfigureCoreImageFilter (void)
{
	NSAutoreleasePool	*pool;
	OSStatus			err;
	IBNibRef			nibRef;
	
	pool = [[NSAutoreleasePool alloc] init];
	
	err = CreateNibReference(kMacS9XCFString, &nibRef);
	if (err == noErr)
	{
		WindowRef	window;
		
		err = CreateWindowFromNib(nibRef, CFSTR("CIFilter"), &window);
		if (err == noErr)
		{
			EventHandlerRef	eref;
			EventHandlerUPP	eUPP;
			EventTypeSpec	event[] = { { kEventClassWindow,  kEventWindowClose         },
										{ kEventClassCommand, kEventCommandProcess      },
										{ kEventClassCommand, kEventCommandUpdateStatus } };
			HIViewRef		ctl, root;
			HIViewID		cid;
			Rect			rct;
			int				value;
			
			ciFilterUIPane = NULL;
			
			FilterToFilterParam();
			
			root = HIViewGetRoot(window);
						
			SetHIViewID(&cid, 'FILT', 0);
			rct.left   = 74;
			rct.top    = 20;
			rct.right  = 74 + 279;
			rct.bottom = 20 +  20;
			err = CreatePopupButtonControl(window, &rct, NULL, -12345, false, 0, 0, 0, &ctl);
			HIViewSetID(ctl, cid);
			int	n = CountMenuItems(ciFilterMenu);
			SetControlPopupMenuHandle(ctl, ciFilterMenu);
			HIViewSetMaximum(ctl, n);
			value = [ciFilterNameList indexOfObject: (NSString *) ciFilterName];
			HIViewSetValue(ctl, value + 1);
			
			ReplaceFilterUI(window);
			
			eUPP = NewEventHandlerUPP(CoreImageFilterEventHandler);
			err = InstallWindowEventHandler(window, eUPP, GetEventTypeCount(event), event, (void *) window, &eref);
						
			MoveWindowPosition(window, kWindowCoreImageFilter, false);
			ShowWindow(window);
			err = RunAppModalLoopForWindow(window);
			HideWindow(window);
			SaveWindowPosition(window, kWindowCoreImageFilter);
			
			err = RemoveEventHandler(eref);
			DisposeEventHandlerUPP(eUPP);
			
			FilterParamToFilter();
			
			CFRelease(window);
		}
		
		DisposeNibReference(nibRef);
	}
	
	[pool release];
}

static void ReplaceFilterUI (WindowRef window)
{
	OSStatus	err;
	HIRect		frame;
	Rect		bounds, rct;
		
	if (ciFilterUIPane)
	{
		HIViewSetVisible(ciFilterUIPane, false);
		DisposeControl(ciFilterUIPane);
		ciFilterUIPane = NULL;
	}
	
	GetWindowBounds(window, kWindowStructureRgn, &bounds);

	rct.left   = 15;
	rct.right  = bounds.right - bounds.left - 15;
	rct.top    = 81;
	rct.bottom = rct.top + 40;
	err = CreateUserPaneControl(window, &rct, kControlSupportsEmbedding, &ciFilterUIPane);
	HIViewSetVisible(ciFilterUIPane, false);
	FilterUIAddSubviews(window, ciFilterUIPane);
	
	HIViewGetFrame(ciFilterUIPane, &frame);
	bounds.bottom = bounds.top + (short) (frame.origin.y + frame.size.height + 30);
	
	err = TransitionWindow(window, kWindowSlideTransitionEffect, kWindowResizeTransitionAction, &bounds);
	HIViewSetVisible(ciFilterUIPane, true);
}

static void FilterUIAddSubviews (WindowRef window, HIViewRef parent)
{
	OSStatus			err;
	CFMutableStringRef	label;
	CFStringRef			str;
	HIViewRef			ctl;
	HIViewID			cid;
	HIRect				bounds, frame;
	Rect				rct;
	SInt32				value;
	
	HIViewGetFrame(parent, &bounds);
	rct.left   = 0;
	rct.top    = 0;
	rct.right  = 200;
	rct.bottom = 20;
	
	int	m = 0;
	for (int i = 0; i < ciFilterInputKeysCount; i++)
    {
		str = CFStringCreateWithCString(kCFAllocatorDefault, ciFilterParam[i].displayName, kCFStringEncodingUTF8);
		if (!str)
			str = CFStringCreateCopy(kCFAllocatorDefault, CFSTR("Parameter"));
		label = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, str);
		CFRelease(str);
		
		switch (ciFilterParam[i].type)
		{
			case kCITypeBoolean:
			{
				err = CreateCheckBoxControl(window, &rct, label, ciFilterParam[i].u.b.cur, true, &ctl);
				SetHIViewID(&cid, kCommandCheckBoxBase + i, i);
				HIViewSetID(ctl, cid);
				HIViewSetCommandID(ctl, cid.signature);
				err = HIViewAddSubview(parent, ctl);
				frame.origin.x = 5.0f;
				frame.origin.y = (float) (m * 28);
				frame.size.width  = bounds.size.width - 10.0f;
				frame.size.height = 20.0f;
				err = HIViewSetFrame(ctl, &frame);
				m++;
				
				break;
			}
			
			case kCITypeScalar:
			{
				CFStringAppend(label, CFSTR(" :"));
				err = CreateStaticTextControl(window, &rct, label, NULL, &ctl);
				SetStaticTextTrunc(ctl, truncEnd, true);
				err = HIViewAddSubview(parent, ctl);
				frame.origin.x = 5.0f;
				frame.origin.y = (float) (m * 28);
				frame.size.width  = 120.0f;
				frame.size.height = 20.0f;
				err = HIViewSetFrame(ctl, &frame);
				
				value = (SInt32) ((ciFilterParam[i].u.s.cur - ciFilterParam[i].u.s.min) / (ciFilterParam[i].u.s.max - ciFilterParam[i].u.s.min) * (float) FIXEDRANGE);
				err = CreateSliderControl(window, &rct, value, 0, FIXEDRANGE, kControlSliderDoesNotPoint, 0, false, NULL, &ctl);
				SetHIViewID(&cid, kCommandSliderBase + i, i);
				HIViewSetID(ctl, cid);
				HIViewSetCommandID(ctl, cid.signature);
				err = HIViewAddSubview(parent, ctl);
				frame.origin.x = 135.0f;
				frame.origin.y = (float) (m * 28) - 1.0f;
				frame.size.width  = bounds.size.width - 140.0f;
				frame.size.height = 20.0f;
				err = HIViewSetFrame(ctl, &frame);
				m++;
				
				break;
			}
			
			case kCITypeColor:
			{
				CFStringAppend(label, CFSTR("..."));
				err = CreatePushButtonControl(window, &rct, label, &ctl);
				SetHIViewID(&cid, kCommandColorButtonBase + i, i);
				HIViewSetID(ctl, cid);
				HIViewSetCommandID(ctl, cid.signature);
				err = HIViewAddSubview(parent, ctl);
				frame.origin.x = bounds.size.width - 180.0f;
				frame.origin.y = (float) (m * 28);
				frame.size.width  = 175.0f;
				frame.size.height = 20.0f;
				err = HIViewSetFrame(ctl, &frame);
				m++;
				
				break;
			}
			
			default:
				break;
		}
		
		CFRelease(label);
	}
	
	if (m)
	{
		str = CFCopyLocalizedString(CFSTR("ResetCIFilter"), "Reset");
		err = CreatePushButtonControl(window, &rct, str, &ctl);
		SetHIViewID(&cid, 'rSET', 0);
		HIViewSetID(ctl, cid);
		HIViewSetCommandID(ctl, cid.signature);
		err = HIViewAddSubview(parent, ctl);
		frame.origin.x = bounds.size.width - 180.0f;
		frame.origin.y = (float) (m * 28 + 12);
		frame.size.width  = 175.0f;
		frame.size.height = 20.0f;
		err = HIViewSetFrame(ctl, &frame);
		CFRelease(str);
		bounds.size.height = frame.origin.y + 32.0f;
	}
	else
		bounds.size.height = 4.0f;

	err = HIViewSetFrame(parent, &bounds);
}

static void FilterUISetValues (HIViewRef parent)
{
	HIViewRef	ctl;
	HIViewID	cid;
	SInt32		value;
	
	for (int i = 0; i < ciFilterInputKeysCount; i++)
    {
		switch (ciFilterParam[i].type)
		{
			case kCITypeBoolean:
				SetHIViewID(&cid, kCommandCheckBoxBase + i, i);
				HIViewFindByID(parent, cid, &ctl);
				HIViewSetValue(ctl, ciFilterParam[i].u.b.cur);
				break;
				
			case kCITypeScalar:
				value = (SInt32) ((ciFilterParam[i].u.s.cur - ciFilterParam[i].u.s.min) / (ciFilterParam[i].u.s.max - ciFilterParam[i].u.s.min) * (float) FIXEDRANGE);
				SetHIViewID(&cid, kCommandSliderBase + i, i);
				HIViewFindByID(parent, cid, &ctl);
				HIViewSetValue(ctl, value);
				break;
				
			default:
				break;
		}
	}
}

static pascal OSStatus CoreImageFilterEventHandler (EventHandlerCallRef inHandlerRef, EventRef inEvent, void *inUserData)
{
	OSStatus	err, result = eventNotHandledErr;
	WindowRef	window = (WindowRef) inUserData;
	
	switch (GetEventClass(inEvent))
	{
		case kEventClassWindow:
			switch (GetEventKind(inEvent))
			{
				case kEventWindowClose:
					QuitAppModalLoopForWindow(window);
					result = noErr;
			}
			
			break;
			
		case kEventClassCommand:
			switch (GetEventKind(inEvent))
			{
				HICommandExtended	tHICommand;

				case kEventCommandUpdateStatus:
					err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommandExtended), NULL, &tHICommand);
					if (err == noErr && tHICommand.commandID == 'clos')
					{
						UpdateMenuCommandStatus(true);
						result = noErr;
					}

					break;

				case kEventCommandProcess:
					err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommandExtended), NULL, &tHICommand);
					if (err == noErr)
					{
						err = MPWaitOnSemaphore(cisem, kDurationForever);
						
						if (tHICommand.commandID == 'rSET')
						{
							[ciFilter setDefaults];
							FilterToFilterParam();
							FilterUISetValues(ciFilterUIPane);
							
							result = noErr;
						}
						else
						{
							unsigned long	i = tHICommand.commandID & 0x00FFFFFF;
							
							switch (tHICommand.commandID & 0xFF000000)
							{
								case kCommandFilterMenuBase:
									DeinitCoreImageFilter();
									
									CFRelease(ciFilterName);
									ciFilterName = CFStringCreateCopy(kCFAllocatorDefault, (CFStringRef) [ciFilterNameList objectAtIndex: i]);
									
									InitCoreImageFilter();
									
									ReplaceFilterUI(window);
									
									break;
									
								case kCommandCheckBoxBase:
									ciFilterParam[i].u.b.cur = !(ciFilterParam[i].u.b.cur);
									FilterParamToFilter();
									result = noErr;
									
									break;
									
								case kCommandSliderBase:
									SInt32	value;
									
									value = HIViewGetValue(tHICommand.source.control);
									ciFilterParam[i].u.s.cur = ciFilterParam[i].u.s.min + (ciFilterParam[i].u.s.max - ciFilterParam[i].u.s.min) * (float) value / (float) FIXEDRANGE;
									FilterParamToFilter();
									result = noErr;
									
									break;
									
								case kCommandColorButtonBase:
									NColorPickerInfo	info;
									
									memset(&info, 0, sizeof(NColorPickerInfo));
									info.placeWhere = kCenterOnMainScreen;
									info.flags      = kColorPickerDialogIsMoveable | kColorPickerDialogIsModal;
									info.theColor.color.rgb.red   = (int) (65535.0 * ciFilterParam[i].u.c.r);
									info.theColor.color.rgb.green = (int) (65535.0 * ciFilterParam[i].u.c.g);
									info.theColor.color.rgb.blue  = (int) (65535.0 * ciFilterParam[i].u.c.b);
									
									err = NPickColor(&info);
									
									if ((err == noErr) && info.newColorChosen)
									{
										ciFilterParam[i].u.c.r = (float) info.theColor.color.rgb.red   / 65535.0f;
										ciFilterParam[i].u.c.g = (float) info.theColor.color.rgb.green / 65535.0f;
										ciFilterParam[i].u.c.b = (float) info.theColor.color.rgb.blue  / 65535.0f;
									}
									
									FilterParamToFilter();
									result = noErr;
									
									break;
							}
						}
						
						err = MPSignalSemaphore(cisem);
					}
			}
	}
	
	return (result);
}

void InitCoreImageContext (CGLContextObj cglctx, CGLPixelFormatObj cglpix)
{
	NSAutoreleasePool	*pool;

	pool = [[NSAutoreleasePool alloc] init];

	FilterToFilterParam();

	cgColor = CGColorSpaceCreateDeviceRGB();

#ifdef MAC_LEOPARD_TIGER_PANTHER_SUPPORT
	ciContext = [[CIContext contextWithCGLContext: cglctx pixelFormat: cglpix options: NULL] retain];
#else
	ciContext = [[CIContext contextWithCGLContext: cglctx pixelFormat: cglpix colorSpace: cgColor options: NULL] retain];
#endif

	[pool release];
}

void DeinitCoreImageContext (void)
{
	NSAutoreleasePool	*pool;

	pool = [[NSAutoreleasePool alloc] init];

	[ciContext release];
	CGColorSpaceRelease(cgColor);

	[pool release];
}

void DrawWithCoreImageFilter (CGRect src, CGImageRef img)
{
	OSStatus			err;
	NSAutoreleasePool	*pool;

	pool = [[NSAutoreleasePool alloc] init];

	err = MPWaitOnSemaphore(cisem, kDurationForever);

	if (ciFilterHasInputImage)
	{
		CIImage		*image;

		image = [CIImage imageWithCGImage: img];
		[ciFilter setValue: image  forKey: @"inputImage" ];
	}

	if (ciFilterHasInputCenter)
	{
		CIVector	*vector;

		vector = [CIVector vectorWithX: (src.origin.x + src.size.width / 2) Y: (src.origin.y + src.size.height / 2)];
		[ciFilter setValue: vector forKey: @"inputCenter"];
	}

	[ciContext drawImage: [ciFilter valueForKey: @"outputImage"] atPoint: CGPointZero fromRect: src];

	err = MPSignalSemaphore(cisem);

	[pool release];
}