1/***********************************************************************************
2  Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3
4  (c) Copyright 1996 - 2002  Gary Henderson (gary.henderson@ntlworld.com),
5                             Jerremy Koot (jkoot@snes9x.com)
6
7  (c) Copyright 2002 - 2004  Matthew Kendora
8
9  (c) Copyright 2002 - 2005  Peter Bortas (peter@bortas.org)
10
11  (c) Copyright 2004 - 2005  Joel Yliluoma (http://iki.fi/bisqwit/)
12
13  (c) Copyright 2001 - 2006  John Weidman (jweidman@slip.net)
14
15  (c) Copyright 2002 - 2006  funkyass (funkyass@spam.shaw.ca),
16                             Kris Bleakley (codeviolation@hotmail.com)
17
18  (c) Copyright 2002 - 2010  Brad Jorsch (anomie@users.sourceforge.net),
19                             Nach (n-a-c-h@users.sourceforge.net),
20
21  (c) Copyright 2002 - 2011  zones (kasumitokoduck@yahoo.com)
22
23  (c) Copyright 2006 - 2007  nitsuja
24
25  (c) Copyright 2009 - 2016  BearOso,
26                             OV2
27
28
29  BS-X C emulator code
30  (c) Copyright 2005 - 2006  Dreamer Nom,
31                             zones
32
33  C4 x86 assembler and some C emulation code
34  (c) Copyright 2000 - 2003  _Demo_ (_demo_@zsnes.com),
35                             Nach,
36                             zsKnight (zsknight@zsnes.com)
37
38  C4 C++ code
39  (c) Copyright 2003 - 2006  Brad Jorsch,
40                             Nach
41
42  DSP-1 emulator code
43  (c) Copyright 1998 - 2006  _Demo_,
44                             Andreas Naive (andreasnaive@gmail.com),
45                             Gary Henderson,
46                             Ivar (ivar@snes9x.com),
47                             John Weidman,
48                             Kris Bleakley,
49                             Matthew Kendora,
50                             Nach,
51                             neviksti (neviksti@hotmail.com)
52
53  DSP-2 emulator code
54  (c) Copyright 2003         John Weidman,
55                             Kris Bleakley,
56                             Lord Nightmare (lord_nightmare@users.sourceforge.net),
57                             Matthew Kendora,
58                             neviksti
59
60  DSP-3 emulator code
61  (c) Copyright 2003 - 2006  John Weidman,
62                             Kris Bleakley,
63                             Lancer,
64                             z80 gaiden
65
66  DSP-4 emulator code
67  (c) Copyright 2004 - 2006  Dreamer Nom,
68                             John Weidman,
69                             Kris Bleakley,
70                             Nach,
71                             z80 gaiden
72
73  OBC1 emulator code
74  (c) Copyright 2001 - 2004  zsKnight,
75                             pagefault (pagefault@zsnes.com),
76                             Kris Bleakley
77                             Ported from x86 assembler to C by sanmaiwashi
78
79  SPC7110 and RTC C++ emulator code used in 1.39-1.51
80  (c) Copyright 2002         Matthew Kendora with research by
81                             zsKnight,
82                             John Weidman,
83                             Dark Force
84
85  SPC7110 and RTC C++ emulator code used in 1.52+
86  (c) Copyright 2009         byuu,
87                             neviksti
88
89  S-DD1 C emulator code
90  (c) Copyright 2003         Brad Jorsch with research by
91                             Andreas Naive,
92                             John Weidman
93
94  S-RTC C emulator code
95  (c) Copyright 2001 - 2006  byuu,
96                             John Weidman
97
98  ST010 C++ emulator code
99  (c) Copyright 2003         Feather,
100                             John Weidman,
101                             Kris Bleakley,
102                             Matthew Kendora
103
104  Super FX x86 assembler emulator code
105  (c) Copyright 1998 - 2003  _Demo_,
106                             pagefault,
107                             zsKnight
108
109  Super FX C emulator code
110  (c) Copyright 1997 - 1999  Ivar,
111                             Gary Henderson,
112                             John Weidman
113
114  Sound emulator code used in 1.5-1.51
115  (c) Copyright 1998 - 2003  Brad Martin
116  (c) Copyright 1998 - 2006  Charles Bilyue'
117
118  Sound emulator code used in 1.52+
119  (c) Copyright 2004 - 2007  Shay Green (gblargg@gmail.com)
120
121  S-SMP emulator code used in 1.54+
122  (c) Copyright 2016         byuu
123
124  SH assembler code partly based on x86 assembler code
125  (c) Copyright 2002 - 2004  Marcus Comstedt (marcus@mc.pp.se)
126
127  2xSaI filter
128  (c) Copyright 1999 - 2001  Derek Liauw Kie Fa
129
130  HQ2x, HQ3x, HQ4x filters
131  (c) Copyright 2003         Maxim Stepin (maxim@hiend3d.com)
132
133  NTSC filter
134  (c) Copyright 2006 - 2007  Shay Green
135
136  GTK+ GUI code
137  (c) Copyright 2004 - 2016  BearOso
138
139  Win32 GUI code
140  (c) Copyright 2003 - 2006  blip,
141                             funkyass,
142                             Matthew Kendora,
143                             Nach,
144                             nitsuja
145  (c) Copyright 2009 - 2016  OV2
146
147  Mac OS GUI code
148  (c) Copyright 1998 - 2001  John Stiles
149  (c) Copyright 2001 - 2011  zones
150
151
152  Specific ports contains the works of other authors. See headers in
153  individual files.
154
155
156  Snes9x homepage: http://www.snes9x.com/
157
158  Permission to use, copy, modify and/or distribute Snes9x in both binary
159  and source form, for non-commercial purposes, is hereby granted without
160  fee, providing that this license information and copyright notice appear
161  with all copies and any derived work.
162
163  This software is provided 'as-is', without any express or implied
164  warranty. In no event shall the authors be held liable for any damages
165  arising from the use of this software or it's derivatives.
166
167  Snes9x is freeware for PERSONAL USE only. Commercial users should
168  seek permission of the copyright holders first. Commercial use includes,
169  but is not limited to, charging money for Snes9x or software derived from
170  Snes9x, including Snes9x or derivatives in commercial game bundles, and/or
171  using Snes9x as a promotion for your commercial product.
172
173  The copyright holders request that bug fixes and improvements to the code
174  should be forwarded to them so everyone can benefit from the modifications
175  in future versions.
176
177  Super NES and Super Nintendo Entertainment System are trademarks of
178  Nintendo Co., Limited and its subsidiary companies.
179 ***********************************************************************************/
180
181/***********************************************************************************
182  SNES9X for Mac OS (c) Copyright John Stiles
183
184  Snes9x for Mac OS X
185
186  (c) Copyright 2001 - 2011  zones
187  (c) Copyright 2002 - 2005  107
188  (c) Copyright 2002         PB1400c
189  (c) Copyright 2004         Alexander and Sander
190  (c) Copyright 2004 - 2005  Steven Seeger
191  (c) Copyright 2005         Ryan Vogt
192 ***********************************************************************************/
193
194
195#import "port.h"
196
197#import <Foundation/Foundation.h>
198#import <QuartzCore/QuartzCore.h>
199#import <OpenGL/OpenGL.h>
200
201#import "mac-prefix.h"
202#import "mac-dialog.h"
203#import "mac-os.h"
204#import "mac-coreimage.h"
205
206enum
207{
208	kCITypeNone    = 0,
209	kCITypeBoolean = 1000,
210	kCITypeScalar,
211	kCITypeColor
212};
213
214#define	mCoreImageFilter		501
215#define	FIXEDRANGE				0x10000
216#define	kCommandFilterMenuBase	0x41000000
217#define	kCommandCheckBoxBase	0x49000000
218#define	kCommandSliderBase		0x51000000
219#define	kCommandColorButtonBase	0x59000000
220#define	kCIFilterNamePrefKey	CFSTR("CoreImageFilterName")
221
222typedef struct {
223	char	name[256];
224	char	displayName[256];
225	int		type;
226	union {
227		struct {
228			bool8	cur;
229		}	b;
230
231		struct {
232			float	max, min, cur;
233		}	s;
234
235		struct {
236			float	r, g, b, a;
237		}	c;
238	}	u;
239}	FilterParam;
240
241static NSMutableArray	*ciFilterNameList          = NULL;
242static NSMutableArray	*ciFilterLocalizedNameList = NULL;
243static NSArray			*ciFilterInputKeys         = NULL;
244static CIFilter			*ciFilter                  = NULL;
245static CIContext		*ciContext                 = NULL;
246static FilterParam		*ciFilterParam             = NULL;
247static CFStringRef		ciFilterName               = NULL;
248static HIViewRef		ciFilterUIPane             = NULL;
249static MenuRef			ciFilterMenu               = NULL;
250static CGColorSpaceRef	cgColor                    = NULL;
251static MPSemaphoreID	cisem                      = NULL;
252static bool8			ciFilterHasInputCenter     = false;
253static bool8			ciFilterHasInputImage      = false;
254static int				ciFilterInputKeysCount     = 0;
255
256static void LoadFilterPrefs (void);
257static void SaveFilterPrefs (void);
258static void FilterParamToFilter (void);
259static void FilterToFilterParam (void);
260static void BuildCoreImageFilterListAndMenu (void);
261static void ReleaseCoreImageFilterListAndMenu (void);
262static void ReplaceFilterUI (WindowRef);
263static void FilterUIAddSubviews (WindowRef, HIViewRef);
264static void FilterUISetValues (HIViewRef);
265static bool8 IsCoreImageFilterSupported (CIFilter *);
266static pascal OSStatus CoreImageFilterEventHandler (EventHandlerCallRef, EventRef, void *);
267
268
269void InitCoreImage (void)
270{
271	OSStatus			err;
272	NSAutoreleasePool	*pool;
273
274	pool = [[NSAutoreleasePool alloc] init];
275
276	ciFilterName = (CFStringRef) CFPreferencesCopyAppValue(kCIFilterNamePrefKey, kCFPreferencesCurrentApplication);
277	if (!ciFilterName)
278		ciFilterName = CFStringCreateCopy(kCFAllocatorDefault, CFSTR("CIGammaAdjust"));
279
280	BuildCoreImageFilterListAndMenu();
281
282	err = MPCreateBinarySemaphore(&cisem);
283
284	[pool release];
285}
286
287void DeinitCoreImage (void)
288{
289	OSStatus			err;
290	NSAutoreleasePool	*pool;
291
292	pool = [[NSAutoreleasePool alloc] init];
293
294	err = MPDeleteSemaphore(cisem);
295
296	ReleaseCoreImageFilterListAndMenu();
297
298	CFPreferencesSetAppValue(kCIFilterNamePrefKey, ciFilterName, kCFPreferencesCurrentApplication);
299
300	CFRelease(ciFilterName);
301
302	[pool release];
303}
304
305void InitCoreImageFilter (void)
306{
307	NSAutoreleasePool	*pool;
308
309	pool = [[NSAutoreleasePool alloc] init];
310
311	ciFilter = [[CIFilter filterWithName: (NSString *) ciFilterName] retain];
312	[ciFilter setDefaults];
313
314	ciFilterInputKeys = [[ciFilter inputKeys] retain];
315	ciFilterInputKeysCount = [ciFilterInputKeys count];
316
317	ciFilterParam = new FilterParam [ciFilterInputKeysCount];
318	memset(ciFilterParam, 0, sizeof(FilterParam) * ciFilterInputKeysCount);
319
320	ciFilterHasInputCenter = false;
321	ciFilterHasInputImage  = false;
322
323	LoadFilterPrefs();
324
325	[pool release];
326}
327
328void DeinitCoreImageFilter (void)
329{
330	NSAutoreleasePool	*pool;
331
332	pool = [[NSAutoreleasePool alloc] init];
333
334	SaveFilterPrefs();
335
336	ciFilterHasInputCenter = false;
337	ciFilterHasInputImage  = false;
338
339	delete [] ciFilterParam;
340
341	[ciFilterInputKeys release];
342	ciFilterInputKeysCount = 0;
343
344	[ciFilter release];
345
346	[pool release];
347}
348
349static void LoadFilterPrefs (void)
350{
351	CFDataRef	data;
352	int			n = sizeof(FilterParam) * ciFilterInputKeysCount;
353
354	data = (CFDataRef) CFPreferencesCopyAppValue(ciFilterName, kCFPreferencesCurrentApplication);
355	if (data)
356	{
357		if (CFDataGetLength(data) == n)
358		{
359			CFDataGetBytes(data, CFRangeMake(0, n), (UInt8 *) ciFilterParam);
360			FilterParamToFilter();
361		}
362
363		CFRelease(data);
364	}
365
366	FilterToFilterParam();
367}
368
369static void SaveFilterPrefs (void)
370{
371	CFDataRef	data;
372	int			n = sizeof(FilterParam) * ciFilterInputKeysCount;
373
374	data = CFDataCreate(kCFAllocatorDefault, (UInt8 *) ciFilterParam, n);
375	if (data)
376	{
377		CFPreferencesSetAppValue(ciFilterName, data, kCFPreferencesCurrentApplication);
378		CFRelease(data);
379	}
380}
381
382static void FilterParamToFilter (void)
383{
384	NSString	*key;
385	NSNumber	*num;
386	CIColor		*color;
387
388	for (int i = 0; i < ciFilterInputKeysCount; i++)
389	{
390		key = [NSString stringWithUTF8String: ciFilterParam[i].name];
391		if (key)
392		{
393			switch (ciFilterParam[i].type)
394			{
395				case kCITypeBoolean:
396					num = [NSNumber numberWithBool: ciFilterParam[i].u.b.cur];
397					[ciFilter setValue: num forKey: key];
398					break;
399
400				case kCITypeScalar:
401					num = [NSNumber numberWithFloat: ciFilterParam[i].u.s.cur];
402					[ciFilter setValue: num forKey: key];
403					break;
404
405				case kCITypeColor:
406					color = [CIColor colorWithRed: ciFilterParam[i].u.c.r green: ciFilterParam[i].u.c.g
407											 blue: ciFilterParam[i].u.c.b alpha: ciFilterParam[i].u.c.a];
408					[ciFilter setValue: color forKey: key];
409					break;
410
411				default:
412					break;
413			}
414		}
415	}
416}
417
418static void FilterToFilterParam (void)
419{
420	NSDictionary	*attr;
421	NSString		*key, *label, *className, *typeName;
422	NSNumber		*num;
423	CIColor			*color;
424	id				param;
425
426	attr = [ciFilter attributes];
427	ciFilterHasInputCenter = false;
428	ciFilterHasInputImage  = false;
429
430    for (int i = 0; i < ciFilterInputKeysCount; i++)
431    {
432		key = [ciFilterInputKeys objectAtIndex: i];
433		param = [attr objectForKey: key];
434
435		strncpy(ciFilterParam[i].name, [key UTF8String], sizeof(ciFilterParam[i].name));
436		ciFilterParam[i].displayName[0] = 0;
437
438        if ([param isKindOfClass: [NSDictionary class]])
439        {
440			label = [(NSDictionary *) param objectForKey: kCIAttributeDisplayName];
441			if (!label)
442				label = [NSString stringWithString: key];
443			strncpy(ciFilterParam[i].displayName, [label UTF8String], sizeof(ciFilterParam[i].displayName));
444
445			className = [(NSDictionary *) param objectForKey: kCIAttributeClass];
446
447            if ([className isEqualToString: @"NSNumber"])
448            {
449                typeName = [(NSDictionary *) param objectForKey: kCIAttributeType];
450
451                if ([typeName isEqualToString: kCIAttributeTypeBoolean])
452				{
453					ciFilterParam[i].type = kCITypeBoolean;
454
455					num = [ciFilter valueForKey: key];
456    				ciFilterParam[i].u.b.cur = [num boolValue];
457				}
458                else
459				{
460                    ciFilterParam[i].type = kCITypeScalar;
461
462					num = [ciFilter valueForKey: key];
463    				ciFilterParam[i].u.s.cur = [num floatValue];
464
465					num = [(NSDictionary *) param objectForKey: kCIAttributeSliderMax];
466				    if (!num)
467				        num = [(NSDictionary *) param objectForKey: kCIAttributeMax];
468				    ciFilterParam[i].u.s.max = [num floatValue];
469
470					num = [(NSDictionary *) param objectForKey: kCIAttributeSliderMin];
471				    if (!num)
472				        num = [(NSDictionary *) param objectForKey: kCIAttributeMin];
473				    ciFilterParam[i].u.s.min = [num floatValue];
474				}
475            }
476            else
477			if ([className isEqualToString: @"CIColor"])
478			{
479				ciFilterParam[i].type = kCITypeColor;
480
481				color = [ciFilter valueForKey: key];
482				ciFilterParam[i].u.c.r = [color red];
483				ciFilterParam[i].u.c.g = [color green];
484				ciFilterParam[i].u.c.b = [color blue];
485				ciFilterParam[i].u.c.a = [color alpha];
486			}
487            else
488			{
489				ciFilterParam[i].type = kCITypeNone;
490
491				if ([className isEqualToString: @"CIVector"] && [key isEqualToString: @"inputCenter"])
492					ciFilterHasInputCenter = true;
493
494				if ([className isEqualToString: @"CIImage" ] && [key isEqualToString: @"inputImage" ])
495					ciFilterHasInputImage  = true;
496			}
497		}
498    }
499}
500
501static void BuildCoreImageFilterListAndMenu (void)
502{
503	NSArray		*categories, *filterNames;
504	OSStatus	err;
505
506	categories = [NSArray arrayWithObject: kCICategoryStillImage];
507	filterNames = [CIFilter filterNamesInCategories: categories];
508
509	ciFilterNameList = [[NSMutableArray alloc] initWithCapacity: 1];
510	ciFilterLocalizedNameList = [[NSMutableArray alloc] initWithCapacity: 1];
511	err = CreateNewMenu(mCoreImageFilter, 0, &ciFilterMenu);
512
513	int	n = [filterNames count], m = 0;
514	for (int i = 0; i < n; i++)
515	{
516		CIFilter	*filter;
517		NSString	*name, *localName;
518
519		name = [filterNames objectAtIndex: i];
520		filter = [CIFilter filterWithName: name];
521
522		if (IsCoreImageFilterSupported(filter))
523		{
524			[ciFilterNameList addObject: name];
525
526			localName = [CIFilter localizedNameForFilterName: name];
527			if (!localName)
528				localName = [NSString stringWithString: name];
529
530			[ciFilterLocalizedNameList addObject: localName];
531
532			err = AppendMenuItemTextWithCFString(ciFilterMenu, (CFStringRef) localName, 0, kCommandFilterMenuBase + m, NULL);
533			m++;
534		}
535	}
536}
537
538static void ReleaseCoreImageFilterListAndMenu (void)
539{
540	CFRelease(ciFilterMenu);
541	[ciFilterLocalizedNameList release];
542	[ciFilterNameList release];
543}
544
545static bool8 IsCoreImageFilterSupported (CIFilter *filter)
546{
547	NSDictionary	*attr;
548	NSArray			*inputKeys;
549	NSString		*key, *className;
550	id				param;
551	bool8			result = true, hasInputImage = false;
552
553	attr = [filter attributes];
554	inputKeys = [filter inputKeys];
555
556	int	n = [inputKeys count];
557	for (int i = 0; i < n; i++)
558	{
559	    key = [inputKeys objectAtIndex: i];
560		param = [attr objectForKey: key];
561
562		if ([param isKindOfClass: [NSDictionary class]])
563	    {
564	        className = [(NSDictionary *) param objectForKey: kCIAttributeClass];
565
566			if ([className isEqualToString: @"CIImage"])
567			{
568				if (![key isEqualToString: @"inputImage"])
569					result = false;
570				else
571					hasInputImage = true;
572			}
573			else
574			if ([className isEqualToString: @"CIVector"])
575			{
576				if (![key isEqualToString: @"inputCenter"])
577					result = false;
578			}
579			else
580			if (![className isEqualToString: @"NSNumber"] && ![className isEqualToString: @"CIColor"])
581				result = false;
582		}
583	}
584
585	if (hasInputImage == false)
586		result = false;
587
588	return (result);
589}
590
591void ConfigureCoreImageFilter (void)
592{
593	NSAutoreleasePool	*pool;
594	OSStatus			err;
595	IBNibRef			nibRef;
596
597	pool = [[NSAutoreleasePool alloc] init];
598
599	err = CreateNibReference(kMacS9XCFString, &nibRef);
600	if (err == noErr)
601	{
602		WindowRef	window;
603
604		err = CreateWindowFromNib(nibRef, CFSTR("CIFilter"), &window);
605		if (err == noErr)
606		{
607			EventHandlerRef	eref;
608			EventHandlerUPP	eUPP;
609			EventTypeSpec	event[] = { { kEventClassWindow,  kEventWindowClose         },
610										{ kEventClassCommand, kEventCommandProcess      },
611										{ kEventClassCommand, kEventCommandUpdateStatus } };
612			HIViewRef		ctl, root;
613			HIViewID		cid;
614			Rect			rct;
615			int				value;
616
617			ciFilterUIPane = NULL;
618
619			FilterToFilterParam();
620
621			root = HIViewGetRoot(window);
622
623			SetHIViewID(&cid, 'FILT', 0);
624			rct.left   = 74;
625			rct.top    = 20;
626			rct.right  = 74 + 279;
627			rct.bottom = 20 +  20;
628			err = CreatePopupButtonControl(window, &rct, NULL, -12345, false, 0, 0, 0, &ctl);
629			HIViewSetID(ctl, cid);
630			int	n = CountMenuItems(ciFilterMenu);
631			SetControlPopupMenuHandle(ctl, ciFilterMenu);
632			HIViewSetMaximum(ctl, n);
633			value = [ciFilterNameList indexOfObject: (NSString *) ciFilterName];
634			HIViewSetValue(ctl, value + 1);
635
636			ReplaceFilterUI(window);
637
638			eUPP = NewEventHandlerUPP(CoreImageFilterEventHandler);
639			err = InstallWindowEventHandler(window, eUPP, GetEventTypeCount(event), event, (void *) window, &eref);
640
641			MoveWindowPosition(window, kWindowCoreImageFilter, false);
642			ShowWindow(window);
643			err = RunAppModalLoopForWindow(window);
644			HideWindow(window);
645			SaveWindowPosition(window, kWindowCoreImageFilter);
646
647			err = RemoveEventHandler(eref);
648			DisposeEventHandlerUPP(eUPP);
649
650			FilterParamToFilter();
651
652			CFRelease(window);
653		}
654
655		DisposeNibReference(nibRef);
656	}
657
658	[pool release];
659}
660
661static void ReplaceFilterUI (WindowRef window)
662{
663	OSStatus	err;
664	HIRect		frame;
665	Rect		bounds, rct;
666
667	if (ciFilterUIPane)
668	{
669		HIViewSetVisible(ciFilterUIPane, false);
670		DisposeControl(ciFilterUIPane);
671		ciFilterUIPane = NULL;
672	}
673
674	GetWindowBounds(window, kWindowStructureRgn, &bounds);
675
676	rct.left   = 15;
677	rct.right  = bounds.right - bounds.left - 15;
678	rct.top    = 81;
679	rct.bottom = rct.top + 40;
680	err = CreateUserPaneControl(window, &rct, kControlSupportsEmbedding, &ciFilterUIPane);
681	HIViewSetVisible(ciFilterUIPane, false);
682	FilterUIAddSubviews(window, ciFilterUIPane);
683
684	HIViewGetFrame(ciFilterUIPane, &frame);
685	bounds.bottom = bounds.top + (short) (frame.origin.y + frame.size.height + 30);
686
687	err = TransitionWindow(window, kWindowSlideTransitionEffect, kWindowResizeTransitionAction, &bounds);
688	HIViewSetVisible(ciFilterUIPane, true);
689}
690
691static void FilterUIAddSubviews (WindowRef window, HIViewRef parent)
692{
693	OSStatus			err;
694	CFMutableStringRef	label;
695	CFStringRef			str;
696	HIViewRef			ctl;
697	HIViewID			cid;
698	HIRect				bounds, frame;
699	Rect				rct;
700	SInt32				value;
701
702	HIViewGetFrame(parent, &bounds);
703	rct.left   = 0;
704	rct.top    = 0;
705	rct.right  = 200;
706	rct.bottom = 20;
707
708	int	m = 0;
709	for (int i = 0; i < ciFilterInputKeysCount; i++)
710    {
711		str = CFStringCreateWithCString(kCFAllocatorDefault, ciFilterParam[i].displayName, kCFStringEncodingUTF8);
712		if (!str)
713			str = CFStringCreateCopy(kCFAllocatorDefault, CFSTR("Parameter"));
714		label = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, str);
715		CFRelease(str);
716
717		switch (ciFilterParam[i].type)
718		{
719			case kCITypeBoolean:
720			{
721				err = CreateCheckBoxControl(window, &rct, label, ciFilterParam[i].u.b.cur, true, &ctl);
722				SetHIViewID(&cid, kCommandCheckBoxBase + i, i);
723				HIViewSetID(ctl, cid);
724				HIViewSetCommandID(ctl, cid.signature);
725				err = HIViewAddSubview(parent, ctl);
726				frame.origin.x = 5.0f;
727				frame.origin.y = (float) (m * 28);
728				frame.size.width  = bounds.size.width - 10.0f;
729				frame.size.height = 20.0f;
730				err = HIViewSetFrame(ctl, &frame);
731				m++;
732
733				break;
734			}
735
736			case kCITypeScalar:
737			{
738				CFStringAppend(label, CFSTR(" :"));
739				err = CreateStaticTextControl(window, &rct, label, NULL, &ctl);
740				SetStaticTextTrunc(ctl, truncEnd, true);
741				err = HIViewAddSubview(parent, ctl);
742				frame.origin.x = 5.0f;
743				frame.origin.y = (float) (m * 28);
744				frame.size.width  = 120.0f;
745				frame.size.height = 20.0f;
746				err = HIViewSetFrame(ctl, &frame);
747
748				value = (SInt32) ((ciFilterParam[i].u.s.cur - ciFilterParam[i].u.s.min) / (ciFilterParam[i].u.s.max - ciFilterParam[i].u.s.min) * (float) FIXEDRANGE);
749				err = CreateSliderControl(window, &rct, value, 0, FIXEDRANGE, kControlSliderDoesNotPoint, 0, false, NULL, &ctl);
750				SetHIViewID(&cid, kCommandSliderBase + i, i);
751				HIViewSetID(ctl, cid);
752				HIViewSetCommandID(ctl, cid.signature);
753				err = HIViewAddSubview(parent, ctl);
754				frame.origin.x = 135.0f;
755				frame.origin.y = (float) (m * 28) - 1.0f;
756				frame.size.width  = bounds.size.width - 140.0f;
757				frame.size.height = 20.0f;
758				err = HIViewSetFrame(ctl, &frame);
759				m++;
760
761				break;
762			}
763
764			case kCITypeColor:
765			{
766				CFStringAppend(label, CFSTR("..."));
767				err = CreatePushButtonControl(window, &rct, label, &ctl);
768				SetHIViewID(&cid, kCommandColorButtonBase + i, i);
769				HIViewSetID(ctl, cid);
770				HIViewSetCommandID(ctl, cid.signature);
771				err = HIViewAddSubview(parent, ctl);
772				frame.origin.x = bounds.size.width - 180.0f;
773				frame.origin.y = (float) (m * 28);
774				frame.size.width  = 175.0f;
775				frame.size.height = 20.0f;
776				err = HIViewSetFrame(ctl, &frame);
777				m++;
778
779				break;
780			}
781
782			default:
783				break;
784		}
785
786		CFRelease(label);
787	}
788
789	if (m)
790	{
791		str = CFCopyLocalizedString(CFSTR("ResetCIFilter"), "Reset");
792		err = CreatePushButtonControl(window, &rct, str, &ctl);
793		SetHIViewID(&cid, 'rSET', 0);
794		HIViewSetID(ctl, cid);
795		HIViewSetCommandID(ctl, cid.signature);
796		err = HIViewAddSubview(parent, ctl);
797		frame.origin.x = bounds.size.width - 180.0f;
798		frame.origin.y = (float) (m * 28 + 12);
799		frame.size.width  = 175.0f;
800		frame.size.height = 20.0f;
801		err = HIViewSetFrame(ctl, &frame);
802		CFRelease(str);
803		bounds.size.height = frame.origin.y + 32.0f;
804	}
805	else
806		bounds.size.height = 4.0f;
807
808	err = HIViewSetFrame(parent, &bounds);
809}
810
811static void FilterUISetValues (HIViewRef parent)
812{
813	HIViewRef	ctl;
814	HIViewID	cid;
815	SInt32		value;
816
817	for (int i = 0; i < ciFilterInputKeysCount; i++)
818    {
819		switch (ciFilterParam[i].type)
820		{
821			case kCITypeBoolean:
822				SetHIViewID(&cid, kCommandCheckBoxBase + i, i);
823				HIViewFindByID(parent, cid, &ctl);
824				HIViewSetValue(ctl, ciFilterParam[i].u.b.cur);
825				break;
826
827			case kCITypeScalar:
828				value = (SInt32) ((ciFilterParam[i].u.s.cur - ciFilterParam[i].u.s.min) / (ciFilterParam[i].u.s.max - ciFilterParam[i].u.s.min) * (float) FIXEDRANGE);
829				SetHIViewID(&cid, kCommandSliderBase + i, i);
830				HIViewFindByID(parent, cid, &ctl);
831				HIViewSetValue(ctl, value);
832				break;
833
834			default:
835				break;
836		}
837	}
838}
839
840static pascal OSStatus CoreImageFilterEventHandler (EventHandlerCallRef inHandlerRef, EventRef inEvent, void *inUserData)
841{
842	OSStatus	err, result = eventNotHandledErr;
843	WindowRef	window = (WindowRef) inUserData;
844
845	switch (GetEventClass(inEvent))
846	{
847		case kEventClassWindow:
848			switch (GetEventKind(inEvent))
849			{
850				case kEventWindowClose:
851					QuitAppModalLoopForWindow(window);
852					result = noErr;
853			}
854
855			break;
856
857		case kEventClassCommand:
858			switch (GetEventKind(inEvent))
859			{
860				HICommandExtended	tHICommand;
861
862				case kEventCommandUpdateStatus:
863					err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommandExtended), NULL, &tHICommand);
864					if (err == noErr && tHICommand.commandID == 'clos')
865					{
866						UpdateMenuCommandStatus(true);
867						result = noErr;
868					}
869
870					break;
871
872				case kEventCommandProcess:
873					err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommandExtended), NULL, &tHICommand);
874					if (err == noErr)
875					{
876						err = MPWaitOnSemaphore(cisem, kDurationForever);
877
878						if (tHICommand.commandID == 'rSET')
879						{
880							[ciFilter setDefaults];
881							FilterToFilterParam();
882							FilterUISetValues(ciFilterUIPane);
883
884							result = noErr;
885						}
886						else
887						{
888							unsigned long	i = tHICommand.commandID & 0x00FFFFFF;
889
890							switch (tHICommand.commandID & 0xFF000000)
891							{
892								case kCommandFilterMenuBase:
893									DeinitCoreImageFilter();
894
895									CFRelease(ciFilterName);
896									ciFilterName = CFStringCreateCopy(kCFAllocatorDefault, (CFStringRef) [ciFilterNameList objectAtIndex: i]);
897
898									InitCoreImageFilter();
899
900									ReplaceFilterUI(window);
901
902									break;
903
904								case kCommandCheckBoxBase:
905									ciFilterParam[i].u.b.cur = !(ciFilterParam[i].u.b.cur);
906									FilterParamToFilter();
907									result = noErr;
908
909									break;
910
911								case kCommandSliderBase:
912									SInt32	value;
913
914									value = HIViewGetValue(tHICommand.source.control);
915									ciFilterParam[i].u.s.cur = ciFilterParam[i].u.s.min + (ciFilterParam[i].u.s.max - ciFilterParam[i].u.s.min) * (float) value / (float) FIXEDRANGE;
916									FilterParamToFilter();
917									result = noErr;
918
919									break;
920
921								case kCommandColorButtonBase:
922									NColorPickerInfo	info;
923
924									memset(&info, 0, sizeof(NColorPickerInfo));
925									info.placeWhere = kCenterOnMainScreen;
926									info.flags      = kColorPickerDialogIsMoveable | kColorPickerDialogIsModal;
927									info.theColor.color.rgb.red   = (int) (65535.0 * ciFilterParam[i].u.c.r);
928									info.theColor.color.rgb.green = (int) (65535.0 * ciFilterParam[i].u.c.g);
929									info.theColor.color.rgb.blue  = (int) (65535.0 * ciFilterParam[i].u.c.b);
930
931									err = NPickColor(&info);
932
933									if ((err == noErr) && info.newColorChosen)
934									{
935										ciFilterParam[i].u.c.r = (float) info.theColor.color.rgb.red   / 65535.0f;
936										ciFilterParam[i].u.c.g = (float) info.theColor.color.rgb.green / 65535.0f;
937										ciFilterParam[i].u.c.b = (float) info.theColor.color.rgb.blue  / 65535.0f;
938									}
939
940									FilterParamToFilter();
941									result = noErr;
942
943									break;
944							}
945						}
946
947						err = MPSignalSemaphore(cisem);
948					}
949			}
950	}
951
952	return (result);
953}
954
955void InitCoreImageContext (CGLContextObj cglctx, CGLPixelFormatObj cglpix)
956{
957	NSAutoreleasePool	*pool;
958
959	pool = [[NSAutoreleasePool alloc] init];
960
961	FilterToFilterParam();
962
963	cgColor = CGColorSpaceCreateDeviceRGB();
964
965#ifdef MAC_LEOPARD_TIGER_PANTHER_SUPPORT
966	ciContext = [[CIContext contextWithCGLContext: cglctx pixelFormat: cglpix options: NULL] retain];
967#else
968	ciContext = [[CIContext contextWithCGLContext: cglctx pixelFormat: cglpix colorSpace: cgColor options: NULL] retain];
969#endif
970
971	[pool release];
972}
973
974void DeinitCoreImageContext (void)
975{
976	NSAutoreleasePool	*pool;
977
978	pool = [[NSAutoreleasePool alloc] init];
979
980	[ciContext release];
981	CGColorSpaceRelease(cgColor);
982
983	[pool release];
984}
985
986void DrawWithCoreImageFilter (CGRect src, CGImageRef img)
987{
988	OSStatus			err;
989	NSAutoreleasePool	*pool;
990
991	pool = [[NSAutoreleasePool alloc] init];
992
993	err = MPWaitOnSemaphore(cisem, kDurationForever);
994
995	if (ciFilterHasInputImage)
996	{
997		CIImage		*image;
998
999		image = [CIImage imageWithCGImage: img];
1000		[ciFilter setValue: image  forKey: @"inputImage" ];
1001	}
1002
1003	if (ciFilterHasInputCenter)
1004	{
1005		CIVector	*vector;
1006
1007		vector = [CIVector vectorWithX: (src.origin.x + src.size.width / 2) Y: (src.origin.y + src.size.height / 2)];
1008		[ciFilter setValue: vector forKey: @"inputCenter"];
1009	}
1010
1011	[ciContext drawImage: [ciFilter valueForKey: @"outputImage"] atPoint: CGPointZero fromRect: src];
1012
1013	err = MPSignalSemaphore(cisem);
1014
1015	[pool release];
1016}
1017