snes9x/macosx/mac-coreimage.mm

847 lines
22 KiB
Plaintext

/*****************************************************************************\
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];
}