This repository was archived by the owner on Aug 8, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathYUCIFilterPreviewGenerator.m
executable file
·205 lines (177 loc) · 9.97 KB
/
YUCIFilterPreviewGenerator.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
//
// YUCIFilterPreviewGenerator.m
// Pods
//
// Created by YuAo on 2/14/16.
//
//
#import "YUCIFilterPreviewGenerator.h"
#import <ImageIO/ImageIO.h>
#if __has_include(<MobileCoreServices/MobileCoreServices.h>)
#import <MobileCoreServices/MobileCoreServices.h>
#endif
CGSize const YUCIFilterPreviewImageSize = (CGSize){280,210};
CGFloat const YUCIFilterPreviewImageSpacing = 60;
CGFloat const YUCIFilterPreviewAreaEdgeInset = 0;
CGRect YUCIMakeRectWithAspectRatioFillRect(CGSize aspectRatio, CGRect boundingRect) {
if (CGRectIsInfinite(boundingRect)) {
boundingRect = CGRectMake(0, 0, 1600, 1200);
}
CGFloat horizontalRatio = boundingRect.size.width / aspectRatio.width;
CGFloat verticalRatio = boundingRect.size.height / aspectRatio.height;
CGFloat ratio;
ratio = MAX(horizontalRatio, verticalRatio);
//ratio = MIN(horizontalRatio, verticalRatio);
CGSize newSize = CGSizeMake(floor(aspectRatio.width * ratio), floor(aspectRatio.height * ratio));
CGRect rect = CGRectMake(boundingRect.origin.x + (boundingRect.size.width - newSize.width)/2, boundingRect.origin.y + (boundingRect.size.height - newSize.height)/2, newSize.width, newSize.height);
return rect;
}
@implementation YUCIFilterPreviewGenerator
+ (void)generatePreviewForFilter:(CIFilter *)filter context:(CIContext *)inputContext completion:(void (^)(NSData *, NSString *))completion {
CIContext *context = inputContext;
if (!context) {
context = [CIContext contextWithOptions:@{kCIContextWorkingColorSpace: CFBridgingRelease(CGColorSpaceCreateWithName(kCGColorSpaceSRGB))}];
}
NSArray *categories = filter.attributes[kCIAttributeFilterCategories];
if([categories containsObject:kCICategoryTransition]) {
//transition
NSData *data = [self generateGIFPreviewForFilter:filter context:context duration:2.0 frameCallback:^(NSTimeInterval frameTime) {
[filter setValue:@(frameTime/2.0) forKey:@"inputTime"];
}];
completion(data,[filter.name stringByAppendingPathExtension:@"gif"]);
} else if ([categories containsObject:kCICategoryGenerator] && [filter.inputKeys containsObject:@"inputTime"]) {
//animatable generator
NSData *data = [self generateGIFPreviewForFilter:filter context:context duration:5.0 frameCallback:^(NSTimeInterval frameTime) {
[filter setValue:@(frameTime) forKey:@"inputTime"];
}];
completion(data,[filter.name stringByAppendingPathExtension:@"gif"]);
} else {
NSMutableArray *inputImageKeys = [NSMutableArray array];
for (NSString *inputKey in filter.inputKeys) {
id value = [filter valueForKey:inputKey];
if ([value isKindOfClass:[CIImage class]]) {
if ([inputKey isEqualToString:kCIInputImageKey]) {
[inputImageKeys insertObject:inputKey atIndex:0];
} else {
[inputImageKeys addObject:inputKey];
}
}
}
NSMutableData *data = [NSMutableData data];
CGImageDestinationRef destination = CGImageDestinationCreateWithData((CFMutableDataRef)data, kUTTypePNG, 1, NULL);
//NSDictionary *properties = @{(NSString *)kCGImageDestinationLossyCompressionQuality: @(0.75)};
//CGImageDestinationSetProperties(destination,(CFDictionaryRef)properties);
CGImageRef image = [self generatePreviewForFilter:filter context:context inputImageKeys:inputImageKeys];
CGImageDestinationAddImage(destination, image, NULL);
CGImageDestinationFinalize(destination);
CFRelease(destination);
completion(data,[filter.name stringByAppendingPathExtension:@"png"]);
}
}
+ (NSData *)generateGIFPreviewForFilter:(CIFilter *)filter context:(CIContext *)context duration:(NSTimeInterval)duration frameCallback:(void (^)(NSTimeInterval frameTime))frameCallback {
NSInteger framePerSecond = 30;
NSMutableData *data = [NSMutableData data];
CGImageDestinationRef destination = CGImageDestinationCreateWithData((CFMutableDataRef)data, kUTTypeGIF, (size_t)floor(framePerSecond * duration), NULL);
for (NSInteger frame = 0; frame < framePerSecond * duration; ++frame) {
frameCallback(frame/(double)framePerSecond);
CGImageRef image = [self generatePreviewForFilter:filter context:context inputImageKeys:nil];
NSDictionary *frameProperties = @{(NSString *)kCGImagePropertyGIFDictionary:
@{(NSString *)kCGImagePropertyGIFDelayTime: @(1.0/framePerSecond)}
};
CGImageDestinationAddImage(destination, image, (CFDictionaryRef)frameProperties);
}
NSDictionary *gifProperties = @{(NSString *)kCGImagePropertyGIFDictionary:
@{(NSString *)kCGImagePropertyGIFLoopCount: @(0)}
};
CGImageDestinationSetProperties(destination, (CFDictionaryRef)gifProperties);
CGImageDestinationFinalize(destination);
CFRelease(destination);
return data.copy;
}
+ (void)drawPlusSignInRect:(CGRect)rect context:(CGContextRef)context {
CGFloat size = MIN(CGRectGetWidth(rect), CGRectGetHeight(rect));
CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, center.x - size/2.0, center.y);
CGPathAddLineToPoint(path, NULL, center.x + size/2.0, center.y);
CGPathMoveToPoint(path, NULL, center.x, center.y - size/2.0);
CGPathAddLineToPoint(path, NULL, center.x, center.y + size/2.0);
CGContextAddPath(context, path);
CGContextStrokePath(context);
CGPathRelease(path);
}
+ (void)drawArrowSignInRect:(CGRect)rect context:(CGContextRef)context {
CGFloat size = MIN(CGRectGetWidth(rect), CGRectGetHeight(rect))/2.0;
CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, center.x - size/2.0, center.y - size);
CGPathAddLineToPoint(path, NULL, center.x + size/2.0, center.y);
CGPathAddLineToPoint(path, NULL, center.x - size/2.0, center.y + size);
CGContextAddPath(context, path);
CGContextStrokePath(context);
CGPathRelease(path);
}
+ (CGImageRef)generatePreviewForFilter:(CIFilter *)filter context:(CIContext *)context inputImageKeys:(NSArray *)inputImageKeys {
//filter
NSMutableArray *inputImages = [NSMutableArray array];
for (NSString *inputKey in inputImageKeys) {
id value = [filter valueForKey:inputKey];
if ([value isKindOfClass:[CIImage class]]) {
[inputImages addObject:value];
}
}
CGRect previewRect = CGRectMake(0, 0, (inputImages.count + 1) * YUCIFilterPreviewImageSize.width + inputImages.count * YUCIFilterPreviewImageSpacing + YUCIFilterPreviewAreaEdgeInset * 2.0, YUCIFilterPreviewImageSize.height + YUCIFilterPreviewAreaEdgeInset * 2.0);
CGImageRef image = [self imageByPerformingDrawing:^(CGContextRef cgContext) {
CGContextSetLineWidth(cgContext, 5);
CGFloat color[4] = {0.7,0.7,0.7,1.0};
CGContextSetStrokeColor(cgContext, color);
CGContextSetLineJoin(cgContext, kCGLineJoinRound);
CIImage *backgroundImage = [CIImage imageWithColor:[CIColor colorWithRed:0 green:0 blue:0 alpha:0.05]];
CGFloat x = YUCIFilterPreviewAreaEdgeInset;
for (CIImage *inputImage in inputImages) {
CGImageRef inputCGImage = [context createCGImage:[inputImage imageByCompositingOverImage:backgroundImage] fromRect:YUCIMakeRectWithAspectRatioFillRect(YUCIFilterPreviewImageSize, inputImage.extent)];
CGContextDrawImage(cgContext, (CGRect){{x,YUCIFilterPreviewAreaEdgeInset},YUCIFilterPreviewImageSize}, inputCGImage);
CGImageRelease(inputCGImage);
x += YUCIFilterPreviewImageSize.width;
CGRect spacingRect = CGRectInset(CGRectMake(x, YUCIFilterPreviewAreaEdgeInset, YUCIFilterPreviewImageSpacing, YUCIFilterPreviewImageSize.height), 15, 15);
if (inputImage == inputImages.lastObject) {
//>
[self drawArrowSignInRect:spacingRect context:cgContext];
} else {
//+
[self drawPlusSignInRect:spacingRect context:cgContext];
}
x += YUCIFilterPreviewImageSpacing;
}
{
//render output image
CGImageRef inputCGImage = [context createCGImage:[filter.outputImage imageByCompositingOverImage:backgroundImage] fromRect:YUCIMakeRectWithAspectRatioFillRect(YUCIFilterPreviewImageSize, filter.outputImage.extent)];
CGContextDrawImage(cgContext, (CGRect){{x,YUCIFilterPreviewAreaEdgeInset},YUCIFilterPreviewImageSize}, inputCGImage);
CGImageRelease(inputCGImage);
}
} inRect:previewRect];
return image;
}
+ (CGImageRef)imageByPerformingDrawing:(void (^)(CGContextRef context))drawing inRect:(CGRect)rect {
// Build a context that's the same dimensions as the new size
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
CGContextRef bitmap = CGBitmapContextCreate(NULL,
rect.size.width,
rect.size.height,
8,
0,
colorSpace,
bitmapInfo);
// Set the quality level to use when rescaling
CGContextSetInterpolationQuality(bitmap, kCGInterpolationMedium);
drawing(bitmap);
// Get the resized image from the context and a UIImage
CGImageRef newImageRef = CGBitmapContextCreateImage(bitmap);
// Clean up
CFAutorelease(newImageRef);
CGColorSpaceRelease(colorSpace);
CGContextRelease(bitmap);
return newImageRef;
}
@end