EffectStack.m
/* |
File: EffectStack.m |
Abstract: n/a |
Version: 2.1 |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple |
Inc. ("Apple") in consideration of your agreement to the following |
terms, and your use, installation, modification or redistribution of |
this Apple software constitutes acceptance of these terms. If you do |
not agree with these terms, please do not use, install, modify or |
redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and |
subject to these terms, Apple grants you a personal, non-exclusive |
license, under Apple's copyrights in this original Apple software (the |
"Apple Software"), to use, reproduce, modify and redistribute the Apple |
Software, with or without modifications, in source and/or binary forms; |
provided that if you redistribute the Apple Software in its entirety and |
without modifications, you must retain this notice and the following |
text and disclaimers in all such redistributions of the Apple Software. |
Neither the name, trademarks, service marks or logos of Apple Inc. may |
be used to endorse or promote products derived from the Apple Software |
without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or |
implied, are granted by Apple herein, including but not limited to any |
patent rights that may be infringed by your derivative works or by other |
works in which the Apple Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
POSSIBILITY OF SUCH DAMAGE. |
Copyright (C) 2014 Apple Inc. All Rights Reserved. |
*/ |
#import "EffectStack.h" |
#import "CoreImageView.h" |
@implementation EffectStack |
- (id)init |
{ |
self = [super init]; |
if (self) |
{ |
layers = [[NSMutableArray alloc] init]; |
} |
return self; |
} |
- (void)dealloc |
{ |
[baseImage release]; |
[layers release]; |
[super dealloc]; |
} |
// insert a filter layer into the layers array |
- (void)insertFilterLayer:(CIFilter *)filter atIndex:(NSInteger)index |
{ |
NSMutableDictionary *d; |
d = [NSMutableDictionary dictionaryWithCapacity:2]; |
[d setValue:@"filter" forKey:@"type"]; |
[d setValue:filter forKey:@"filter"]; |
[d setValue:[NSNumber numberWithBool:YES] forKey:@"enabled"]; |
[layers insertObject:d atIndex:index]; |
} |
// insert an image layer into the layers array |
- (void)insertImageLayer:(CIImage *)image withFilename:(NSString *)filename atIndex:(NSInteger)index |
{ |
NSMutableDictionary *d; |
d = [NSMutableDictionary dictionaryWithCapacity:2]; |
[d setValue:@"image" forKey:@"type"]; |
[d setValue:image forKey:@"image"]; |
[d setValue:[NSNumber numberWithDouble:0.0] forKey:@"offsetX"]; |
[d setValue:[NSNumber numberWithDouble:0.0] forKey:@"offsetY"]; |
[d setValue:[NSNumber numberWithBool:YES] forKey:@"enabled"]; |
[d setValue:filename forKey:@"filename"]; |
[layers insertObject:d atIndex:index]; |
} |
// insert a text layer into the layers array |
- (void)insertTextLayer:(NSString *)string withImage:(CIImage *)image atIndex:(NSInteger)index |
{ |
NSMutableDictionary *d; |
d = [NSMutableDictionary dictionaryWithCapacity:2]; |
[d setValue:@"text" forKey:@"type"]; |
[d setValue:image forKey:@"image"]; |
[d setValue:string forKey:@"string"]; |
[d setValue:[NSNumber numberWithDouble:1.0] forKey:@"scale"]; |
[d setValue:[NSNumber numberWithDouble:0.0] forKey:@"offsetX"]; |
[d setValue:[NSNumber numberWithDouble:0.0] forKey:@"offsetY"]; |
[d setValue:[NSNumber numberWithBool:YES] forKey:@"enabled"]; |
[layers insertObject:d atIndex:index]; |
} |
// remove an element from the layers array |
- (void)removeLayerAtIndex:(NSInteger)index |
{ |
[layers removeObjectAtIndex:index]; |
} |
// remove all elements from the layers array |
- (void)removeAllLayers |
{ |
[layers removeAllObjects]; |
} |
// return the number of elements in the layers array |
- (NSInteger)layerCount |
{ |
return [layers count]; |
} |
// getter: layers[index].enabled |
- (BOOL)layerEnabled:(NSInteger)index |
{ |
NSMutableDictionary *d; |
if (index < 0 || index >= [self layerCount]) |
{ |
printf("index %d is out of range (%d layers in model)\n", (int)index, (int)[self layerCount]); |
return NO; |
} |
d = [layers objectAtIndex:index]; |
return [[d valueForKey:@"enabled"] boolValue]; |
} |
// getter: layers[index].type |
- (NSString *)typeAtIndex:(NSInteger)index |
{ |
NSMutableDictionary *d; |
if (index < 0 || index >= [self layerCount]) |
{ |
printf("index %d is out of range (%d layers in model)\n", (int)index, (int)[self layerCount]); |
return nil; |
} |
d = [layers objectAtIndex:index]; |
return [d valueForKey:@"type"]; |
} |
// getter: layers[index].filter |
- (CIFilter *)filterAtIndex:(NSInteger)index |
{ |
NSMutableDictionary *d; |
if (index < 0 || index >= [self layerCount]) |
{ |
printf("index %d is out of range (%d layers in model)\n", (int)index, (int)[self layerCount]); |
return nil; |
} |
d = [layers objectAtIndex:index]; |
if (![[d valueForKey:@"type"] isEqualToString:@"filter"]) |
return nil; |
return [d valueForKey:@"filter"]; |
} |
// getter: layers[index].image |
- (CIImage *)imageAtIndex:(NSInteger)index |
{ |
NSMutableDictionary *d; |
if (index < 0 || index >= [self layerCount]) |
{ |
printf("index %d is out of range (%d layers in model)\n", (int)index, (int)[self layerCount]); |
return nil; |
} |
d = [layers objectAtIndex:index]; |
if (![[d valueForKey:@"type"] isEqualToString:@"image"] && ![[d valueForKey:@"type"] isEqualToString:@"text"]) |
return nil; |
return [d valueForKey:@"image"]; |
} |
// getter: layers[index].offset |
- (NSPoint)offsetAtIndex:(NSInteger)index |
{ |
NSMutableDictionary *d; |
if (index < 0 || index >= [self layerCount]) |
{ |
printf("index %d is out of range (%d layers in model)\n", (int)index, (int)[self layerCount]); |
return NSMakePoint(0.0, 0.0); |
} |
d = [layers objectAtIndex:index]; |
if (![[d valueForKey:@"type"] isEqualToString:@"image"] && ![[d valueForKey:@"type"] isEqualToString:@"text"]) |
return NSMakePoint(0.0, 0.0); |
return NSMakePoint([[d valueForKey:@"offsetX"] doubleValue], [[d valueForKey:@"offsetY"] doubleValue]); |
} |
// getter: layers[index].filename |
- (NSString *)filenameAtIndex:(NSInteger)index |
{ |
NSMutableDictionary *d; |
if (index < 0 || index >= [self layerCount]) |
{ |
printf("index %d is out of range (%d layers in model)\n", (int)index, (int)[self layerCount]); |
return nil; |
} |
d = [layers objectAtIndex:index]; |
if (![[d valueForKey:@"type"] isEqualToString:@"image"]) |
return nil; |
return [d valueForKey:@"filename"]; |
} |
// getter: layers[index].imageFilePath |
- (NSString *)imageFilePathAtIndex:(NSInteger)index |
{ |
NSMutableDictionary *d; |
if (index < 0 || index >= [self layerCount]) |
{ |
printf("index %d is out of range (%d layers in model)\n", (int)index, (int)[self layerCount]); |
return nil; |
} |
d = [layers objectAtIndex:index]; |
if (![[d valueForKey:@"type"] isEqualToString:@"image"]) |
return nil; |
return [d valueForKey:@"imageFilePath"]; |
} |
// getter: layers[index].imageFileData |
- (NSData *)imageFileDataAtIndex:(NSInteger)index |
{ |
NSMutableDictionary *d; |
if (index < 0 || index >= [self layerCount]) |
{ |
printf("index %d is out of range (%d layers in model)\n", (int)index, (int)[self layerCount]); |
return nil; |
} |
d = [layers objectAtIndex:index]; |
if (![[d valueForKey:@"type"] isEqualToString:@"image"]) |
return nil; |
return [d valueForKey:@"imageFileData"]; |
} |
// getter: layers[index].string |
- (NSString *)stringAtIndex:(NSInteger)index |
{ |
NSMutableDictionary *d; |
if (index < 0 || index >= [self layerCount]) |
{ |
printf("index %d is out of range (%d layers in model)\n", (int)index, (int)[self layerCount]); |
return nil; |
} |
d = [layers objectAtIndex:index]; |
if (![[d valueForKey:@"type"] isEqualToString:@"text"]) |
return nil; |
return [d valueForKey:@"string"]; |
} |
// getter: layers[index] - used for text case where there are way too many to access otherwise |
- (NSMutableDictionary *)mutableDictionaryAtIndex:(NSInteger)index |
{ |
if (index < 0 || index >= [self layerCount]) |
{ |
printf("index %d is out of range (%d layers in model)\n", (int)index, (int)[self layerCount]); |
return nil; |
} |
return [layers objectAtIndex:index]; |
} |
// getter: layers[0].image |
- (CIImage *)baseImage |
{ |
if ([[self typeAtIndex:0] isEqualToString:@"image"]) |
return [self imageAtIndex:0]; |
return nil; |
} |
// getter: [layers[0].imageFilePaths valueForKey:key].path |
- (NSString *)filterLayer:(NSInteger)index imageFilePathValueForKey:(NSString *)key |
{ |
NSMutableDictionary *d; |
if (index < 0 || index >= [self layerCount]) |
{ |
printf("index %d is out of range (%d layers in model)\n", (int)index, (int)[self layerCount]); |
return nil; |
} |
d = [layers objectAtIndex:index]; |
if (![[d valueForKey:@"type"] isEqualToString:@"filter"]) |
return nil; |
d = [d valueForKey:@"imageFilePaths"]; |
if (d == nil) |
return nil; |
return [[d valueForKey:key] valueForKey:@"path"]; |
} |
// getter: [layers[0].imageFilePaths valueForKey:key].data |
- (NSData *)filterLayer:(NSInteger)index imageFileDataValueForKey:(NSString *)key |
{ |
NSMutableDictionary *d; |
if (index < 0 || index >= [self layerCount]) |
{ |
printf("index %d is out of range (%d layers in model)\n", (int)index, (int)[self layerCount]); |
return nil; |
} |
d = [layers objectAtIndex:index]; |
if (![[d valueForKey:@"type"] isEqualToString:@"filter"]) |
return nil; |
d = [d valueForKey:@"imageFilePaths"]; |
if (d == nil) |
return nil; |
return [[d valueForKey:key] valueForKey:@"data"]; |
} |
// setter: [layers[index].imageFilePaths setValue:path forKey:key] |
- (void)setFilterLayer:(NSInteger)index imageFilePathValue:(NSString *)path forKey:(NSString *)key |
{ |
NSMutableDictionary *d, *d2, *d3; |
if (index < 0 || index >= [self layerCount]) |
{ |
printf("index %d is out of range (%d layers in model)\n", (int)index, (int)[self layerCount]); |
return; |
} |
d = [layers objectAtIndex:index]; |
if ([[d valueForKey:@"type"] isEqualToString:@"filter"]) |
{ |
d2 = [d valueForKey:@"imageFilePaths"]; |
if (d2 == nil) |
{ |
d2 = [NSMutableDictionary dictionary]; |
[d setValue:d2 forKey:@"imageFilePaths"]; |
} |
d3 = [NSMutableDictionary dictionary]; |
[d3 setValue:path forKey:@"path"]; |
// keep image file data around too! |
[d3 setValue:[NSData dataWithContentsOfMappedFile:path] forKey:@"data"]; |
[d2 setValue:d3 forKey:key]; |
} |
} |
// setter: layers[index].enabled |
- (void)setLayer:(NSInteger)index enabled:(BOOL)enabled |
{ |
NSMutableDictionary *d; |
if (index < 0 || index >= [self layerCount]) |
{ |
printf("index %d is out of range (%d layers in model)\n", (int)index, (int)[self layerCount]); |
return; |
} |
d = [layers objectAtIndex:index]; |
[d setValue:[NSNumber numberWithBool:enabled] forKey:@"enabled"]; |
} |
// setter: layers[index].offset |
- (void)setImageLayer:(NSInteger)index offset:(NSPoint)offset |
{ |
NSMutableDictionary *d; |
if (index < 0 || index >= [self layerCount]) |
{ |
printf("index %d is out of range (%d layers in model)\n", (int)index, (int)[self layerCount]); |
return; |
} |
d = [layers objectAtIndex:index]; |
if ([[d valueForKey:@"type"] isEqualToString:@"image"]) |
{ |
[d setValue:[NSNumber numberWithDouble:offset.x] forKey:@"offsetX"]; |
[d setValue:[NSNumber numberWithDouble:offset.y] forKey:@"offsetY"]; |
} |
} |
// setter: layers[index].imageFilePath |
- (void)setImageLayer:(NSInteger)index imageFilePath:(NSString *)path |
{ |
NSMutableDictionary *d; |
if (index < 0 || index >= [self layerCount]) |
{ |
printf("index %d is out of range (%d layers in model)\n", (int)index, (int)[self layerCount]); |
return; |
} |
d = [layers objectAtIndex:index]; |
if ([[d valueForKey:@"type"] isEqualToString:@"image"]) |
{ |
[d setValue:path forKey:@"imageFilePath"]; |
// keep image file data around too! |
[d setValue:[NSData dataWithContentsOfMappedFile:path] forKey:@"imageFileData"]; |
} |
} |
// setter: layers[index].offset |
- (void)setTextLayer:(NSInteger)index offset:(NSPoint)offset |
{ |
NSMutableDictionary *d; |
if (index < 0 || index >= [self layerCount]) |
{ |
printf("index %d is out of range (%d layers in model)\n", (int)index, (int)[self layerCount]); |
return; |
} |
d = [layers objectAtIndex:index]; |
if ([[d valueForKey:@"type"] isEqualToString:@"text"]) |
{ |
[d setValue:[NSNumber numberWithDouble:offset.x] forKey:@"offsetX"]; |
[d setValue:[NSNumber numberWithDouble:offset.y] forKey:@"offsetY"]; |
} |
} |
// setter: layers[index].image/filename |
- (void)setImageLayer:(NSInteger)index image:(CIImage *)image andFilename:(NSString *)filename |
{ |
NSMutableDictionary *d; |
if (index < 0 || index >= [self layerCount]) |
{ |
printf("index %d is out of range (%d layers in model)\n", (int)index, (int)[self layerCount]); |
return; |
} |
d = [layers objectAtIndex:index]; |
if ([[d valueForKey:@"type"] isEqualToString:@"image"]) |
{ |
[d setValue:image forKey:@"image"]; |
[d setValue:filename forKey:@"filename"]; |
} |
} |
// setter: layers[index].string/image |
- (void)setTextLayer:(NSInteger)index string:(NSString *)string andImage:(CIImage *)image |
{ |
NSMutableDictionary *d; |
if (index < 0 || index >= [self layerCount]) |
{ |
printf("index %d is out of range (%d layers in model)\n", (int)index, (int)[self layerCount]); |
return; |
} |
d = [layers objectAtIndex:index]; |
if ([[d valueForKey:@"type"] isEqualToString:@"text"]) |
{ |
[d setValue:image forKey:@"image"]; |
[d setValue:string forKey:@"string"]; |
} |
} |
// setter: layers[0].image/filename and image/path |
- (void)setBaseImage:(CIImage *)image withFilename:(NSString *)filename andImageFilePath:(NSString *)path |
{ |
NSMutableDictionary *d; |
if ([self layerCount] > 0 && [[self typeAtIndex:0] isEqualToString:@"image"]) |
{ |
d = [layers objectAtIndex:0]; |
[d setValue:image forKey:@"image"]; |
[d setValue:filename forKey:@"filename"]; |
[d setValue:path forKey:@"imageFilePath"]; |
} |
else if ([self layerCount] == 0) |
{ |
[self insertImageLayer:image withFilename:filename atIndex:0]; |
d = [layers objectAtIndex:0]; |
[d setValue:path forKey:@"imageFilePath"]; |
// keep image file data around too! |
[d setValue:[NSData dataWithContentsOfMappedFile:path] forKey:@"imageFileData"]; |
} |
else |
printf("attempted setBaseImage with non-empty effect stack\n"); |
} |
// return the core image graph for the effect stack (constrained to the rectangle) |
- (CIImage *)coreImageResultForRect:(NSRect)bounds |
{ |
BOOL usesExtent, usesImage, hasBackground; |
NSInteger i, count; |
CIFilter *f; |
CIImage *result; |
NSDictionary *attr; |
NSArray *inputKeys; |
NSString *key, *classstring, *type; |
NSEnumerator *enumerator; |
NSMutableArray *resultstack; |
resultstack = [NSMutableArray arrayWithCapacity:10]; |
// get result of filter running over image |
count = [self layerCount]; |
result = nil; |
for (i = 0; i < count; i++) |
{ |
if (![self layerEnabled:i]) |
continue; |
type = [self typeAtIndex:i]; |
if ([type isEqualToString:@"filter"]) |
{ |
// filter layer |
f = [self filterAtIndex:i]; |
if (f == nil) |
continue; |
usesExtent = NO; |
usesImage = NO; |
hasBackground = NO; |
attr = [f attributes]; |
inputKeys = [f inputKeys]; |
// scan the input parameters for various cases we need to handle |
enumerator = [inputKeys objectEnumerator]; |
while ((key = [enumerator nextObject]) != nil) |
{ |
id parameter = [attr objectForKey:key]; |
if ([parameter isKindOfClass:[NSDictionary class]]) |
{ |
classstring = [(NSDictionary *)parameter objectForKey: kCIAttributeClass]; |
if ([classstring isEqualToString:@"CIVector"] && [key isEqualToString:@"inputExtent"]) |
usesExtent = YES; |
if ([key isEqualToString:@"inputImage"]) |
usesImage = YES; |
if ([key isEqualToString:@"inputBackgroundImage"]) |
hasBackground = YES; |
} |
} |
// stack generators here |
if (!usesImage && result != nil) |
// check for automatic SOver of layered results |
[resultstack addObject:result]; // keep result around for SOver at end |
// supply chained image parameters here |
if (usesImage) |
{ |
if (result != nil) |
{ |
if (hasBackground) |
[f setValue:result forKey:@"inputBackgroundImage"]; // chain layers (blend modes by background) |
else |
[f setValue:result forKey:@"inputImage"]; // chain layers |
} |
} |
// supply the obvious extent for any extent parameters |
if (usesExtent) |
[f setValue: [CIVector vectorWithX: 0 Y: 0 Z: NSWidth(bounds) W: NSHeight(bounds)] |
forKey: @"inputExtent"]; |
// get the filter result |
result = [f valueForKey: @"outputImage"]; |
} |
else if ([type isEqualToString:@"image"] || [type isEqualToString:@"text"]) |
{ |
// image or text layer |
if (result != nil) |
// check for automatic SOver of layered results |
[resultstack addObject:result]; // keep result around for SOver at end |
// get the image and offset |
CIImage *im = [self imageAtIndex:i]; |
NSPoint offset = [self offsetAtIndex:i]; |
// apply an affine transform to the iamge to account for the offset |
f = [CIFilter filterWithName:@"CIAffineTransform"]; |
NSAffineTransform *t = [NSAffineTransform transform]; |
[t translateXBy:offset.x yBy:offset.y]; |
[f setValue:t forKey:@"inputTransform"]; |
[f setValue:im forKey:@"inputImage"]; |
// and get the result |
result = [f valueForKey:@"outputImage"]; |
} |
} |
// at the end, if there are results stacked (base image, other image layers, text layers, generators), overlay them using SOver |
if ([resultstack count] > 0) |
{ |
CIFilter *sover; |
CIImage *background = nil; |
count = [resultstack count]; |
for (i = 0; i < count; i++) |
{ |
if (i == 0) |
background = [resultstack objectAtIndex:i]; |
else |
{ |
sover = [CIFilter filterWithName:@"CISourceOverCompositing" keysAndValues: |
@"inputBackgroundImage", background, @"inputImage", [resultstack objectAtIndex:i], nil]; |
background = [sover valueForKey:@"outputImage"]; |
} |
} |
if (result == nil) |
result = background; |
else |
{ |
// finally composite result over stacked items |
sover = [CIFilter filterWithName:@"CISourceOverCompositing" keysAndValues: |
@"inputBackgroundImage", background, @"inputImage", result, nil]; |
result = [sover valueForKey:@"outputImage"]; |
} |
} |
return result; |
} |
// decide if a filter in the effect stack has a missing image parameter |
// ignore the chained image parameter (either inputImage or inputBackgroundImage for blend modes/Porter-Duff modes) |
- (BOOL)filterHasMissingImage:(CIFilter *)f |
{ |
BOOL hasBackground, missingImage; |
NSString *key, *classstring; |
NSDictionary *parameter, *attr; |
NSArray *inputKeys; |
NSEnumerator *enumerator; |
// first check the filter for an uninitialized image |
attr = [f attributes]; |
inputKeys = [f inputKeys]; |
hasBackground = NO; |
enumerator = [inputKeys objectEnumerator]; |
// decide first if the filter has a background image - it is a blend mode or compositing method |
while ((key = [enumerator nextObject]) != nil) |
{ |
parameter = [attr objectForKey:key]; |
classstring = [parameter objectForKey: kCIAttributeClass]; |
if ([classstring isEqualToString:@"CIImage"] && [key isEqualToString:@"inputBackgroundImage"]) |
hasBackground = YES; |
} |
missingImage = NO; |
enumerator = [inputKeys objectEnumerator]; |
while ((key = [enumerator nextObject]) != nil) |
{ |
parameter = [attr objectForKey:key]; |
classstring = [parameter objectForKey: kCIAttributeClass]; |
if ([classstring isEqualToString:@"CIImage"]) |
{ |
if (hasBackground) |
{ |
if (![key isEqualToString:@"inputBackgroundImage"] && [f valueForKey:key] == nil) |
{ |
missingImage = YES; |
break; |
} |
} |
else |
{ |
if (![key isEqualToString:@"inputImage"] && [f valueForKey:key] == nil) |
{ |
missingImage = YES; |
break; |
} |
} |
} |
} |
return missingImage; |
} |
// determine if the entire effect stack has a missing image - if yes, then the effect stack can't be evaluated |
- (BOOL)hasMissingImage |
{ |
NSInteger i, count; |
CIFilter *f; |
count = [self layerCount]; |
for (i = 0; i < count; i++) |
{ |
f = [self filterAtIndex:i]; |
if (f != nil && [self filterHasMissingImage:f]) |
return YES; |
} |
return NO; |
} |
// encode arbitrary parameter objects for filters into XML-compatible NSNumber's |
- (void)encodeValue:(id)obj forKey:(NSString *)key intoDictionary:(NSMutableDictionary *)v |
{ |
NSAffineTransformStruct S; |
// decide what it is and store values in the values dictionary |
if ([obj isKindOfClass:[NSNumber class]]) |
[v setValue:obj forKey:key]; |
else if ([obj isKindOfClass:[CIVector class]]) |
{ |
[v setValue:[obj stringRepresentation] forKey:[key stringByAppendingString:@"_CIVectorValue"]]; |
} |
else if ([obj isKindOfClass:[CIColor class]]) |
{ |
[v setValue:[obj stringRepresentation] forKey:[key stringByAppendingString:@"_CIColorValue"]]; |
} |
else if ([obj isKindOfClass:[NSAffineTransform class]]) |
{ |
S = [obj transformStruct]; |
[v setValue:[NSNumber numberWithDouble:S.m11] forKey:[key stringByAppendingString:@"_m11"]]; |
[v setValue:[NSNumber numberWithDouble:S.m12] forKey:[key stringByAppendingString:@"_m12"]]; |
[v setValue:[NSNumber numberWithDouble:S.m21] forKey:[key stringByAppendingString:@"_m21"]]; |
[v setValue:[NSNumber numberWithDouble:S.m22] forKey:[key stringByAppendingString:@"_m22"]]; |
[v setValue:[NSNumber numberWithDouble:S.tX] forKey:[key stringByAppendingString:@"_tX"]]; |
[v setValue:[NSNumber numberWithDouble:S.tY] forKey:[key stringByAppendingString:@"_tY"]]; |
} |
} |
// decode XML-compatible NSNumber's into arbitrary parameter objects for filters |
- (id)decodedValueForKey:(NSString *)key ofClass:(NSString *)classname fromDictionary:(NSDictionary *)v |
{ |
if ([classname isEqualToString:@"NSNumber"]) |
return [v valueForKey:key]; |
else if ([classname isEqualToString:@"CIVector"]) |
{ |
NSString *objValue = (NSString*)[v valueForKey:[key stringByAppendingString:@"_CIVectorValue"]]; |
if (objValue == nil) |
return nil; |
return [CIVector vectorWithString:objValue]; |
} |
else if ([classname isEqualToString:@"CIColor"]) |
{ |
NSString *objValue = (NSString*)[v valueForKey:[key stringByAppendingString:@"_CIColorValue"]]; |
if (objValue == nil) |
return nil; |
return [CIColor colorWithString:objValue]; |
} |
else if ([classname isEqualToString:@"NSAffineTransform"]) |
{ |
NSAffineTransformStruct S; |
NSAffineTransform *t; |
if ([v valueForKey:[key stringByAppendingString:@"_m11"]] == nil) |
return nil; |
S.m11 = [[v valueForKey:[key stringByAppendingString:@"_m11"]] doubleValue]; |
S.m12 = [[v valueForKey:[key stringByAppendingString:@"_m12"]] doubleValue]; |
S.m21 = [[v valueForKey:[key stringByAppendingString:@"_m21"]] doubleValue]; |
S.m22 = [[v valueForKey:[key stringByAppendingString:@"_m22"]] doubleValue]; |
S.tX = [[v valueForKey:[key stringByAppendingString:@"_tX"]] doubleValue]; |
S.tY = [[v valueForKey:[key stringByAppendingString:@"_tY"]] doubleValue]; |
t = [[[NSAffineTransform alloc] init] autorelease]; |
[t setTransformStruct:S]; |
return t; |
} |
else if ([classname isEqualToString:@"CIImage"]) |
{ |
if ([v valueForKey:key] == nil) |
return nil; |
} |
return nil; |
} |
@end |
Copyright © 2014 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2014-05-07