3f709e388c009ab9ebad839fe099eed801a5358e
[zxing.git] / iphone / ZXingWidget / ZXingWidgetController.m
1 /**
2  * Copyright 2009 Jeff Verkoeyen
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *    http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #import "ZXingWidgetController.h"
18 #import "Decoder.h"
19 #import "NSString+HTML.h"
20 #import "ResultParser.h"
21 #import "ParsedResult.h"
22 #import "ResultAction.h"
23 #include <sys/types.h>
24 #include <sys/sysctl.h>
25 #include "UKImage.h"
26
27 #define CAMERA_SCALAR 1.12412 // scalar = (480 / (2048 / 480))
28 #define FIRST_TAKE_DELAY 1.0
29 #define ONE_D_BAND_HEIGHT 10.0
30
31 CGImageRef UIGetScreenImage();
32
33 @implementation ZXingWidgetController
34 @synthesize result, actions, showCancel, delegate, soundToPlay, oneDMode;
35
36 - (id)initWithDelegate:(id<ZXingDelegate>)scanDelegate {
37         if (self = [super init]) {
38                 [self setDelegate:scanDelegate];
39                 beepSound = -1;
40                 self.wantsFullScreenLayout = YES;
41                 self.sourceType = UIImagePickerControllerSourceTypeCamera;
42                 float zoomFactor = CAMERA_SCALAR;
43                 if ([self fixedFocus]) {
44                         zoomFactor *= 2.0;
45                 }
46                 self.cameraViewTransform = CGAffineTransformScale(
47                                         self.cameraViewTransform, zoomFactor, zoomFactor);
48                 overlayView = [OverlayView alloc];
49                 [overlayView setOneDMode:oneDMode];
50                 overlayView = [overlayView initWithCancelEnabled:showCancel frame:[UIScreen mainScreen].bounds];
51                 [overlayView setDelegate:self];
52                 self.sourceType = UIImagePickerControllerSourceTypeCamera;
53                 self.showsCameraControls = NO;
54                 self.cameraOverlayView = overlayView;
55                 self.allowsEditing = NO; // [[NSUserDefaults standardUserDefaults] boolForKey:@"allowEditing"];
56         }
57         
58         return self;
59 }
60
61 - (void)dealloc {
62         if (beepSound != -1) {
63                 AudioServicesDisposeSystemSoundID(beepSound);
64         }
65         [overlayView dealloc];
66         [super dealloc];
67 }
68
69 - (void)cancelled {
70         NSLog(@"cancelled called in ZXingWidgetController");
71         wasCancelled = true;
72         if (delegate != nil) {
73                 [delegate cancelled];
74         }
75 }
76
77 - (NSString *)getPlatform {
78         size_t size;
79     sysctlbyname("hw.machine", NULL, &size, NULL, 0);
80     char *machine = malloc(size);
81     sysctlbyname("hw.machine", machine, &size, NULL, 0);
82     NSString *platform = [NSString stringWithCString:machine encoding:NSASCIIStringEncoding];
83     free(machine);
84         return platform;
85 }
86
87 - (BOOL)fixedFocus {
88         NSString *platform = [self getPlatform];
89         if ([platform isEqualToString:@"iPhone1,1"] ||
90                 [platform isEqualToString:@"iPhone1,2"]) return true;
91         return false;
92 }
93
94 - (void)viewWillAppear:(BOOL)animated {
95         [super viewWillAppear:animated];
96         if ([self soundToPlay] != nil) {
97                 OSStatus error = AudioServicesCreateSystemSoundID((CFURLRef)[self soundToPlay], &beepSound);
98                 if (error != kAudioServicesNoError) {
99                         NSLog(@"Problem loading nearSound.caf");
100                 }
101         }
102 }
103
104 - (void)viewDidAppear:(BOOL)animated {
105     [super viewDidAppear:animated];
106         [overlayView setPoints:nil];
107         wasCancelled = false;
108         [NSTimer scheduledTimerWithTimeInterval: FIRST_TAKE_DELAY
109                                                                          target: self
110                                                                    selector: @selector(takePicture:)
111                                                                    userInfo: nil
112                                                                         repeats: NO];
113 }
114
115 - (CGImageRef)CGImageRotated90:(CGImageRef)imgRef
116 {
117         CGFloat angleInRadians = -90 * (M_PI / 180);
118         CGFloat width = CGImageGetWidth(imgRef);
119         CGFloat height = CGImageGetHeight(imgRef);
120         
121         CGRect imgRect = CGRectMake(0, 0, width, height);
122         CGAffineTransform transform = CGAffineTransformMakeRotation(angleInRadians);
123         CGRect rotatedRect = CGRectApplyAffineTransform(imgRect, transform);
124         
125         CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
126         CGContextRef bmContext = CGBitmapContextCreate(NULL,
127                                                                                                    rotatedRect.size.width,
128                                                                                                    rotatedRect.size.height,
129                                                                                                    8,
130                                                                                                    0,
131                                                                                                    colorSpace,
132                                                                                                    kCGImageAlphaPremultipliedFirst);
133         CGContextSetAllowsAntialiasing(bmContext, FALSE);
134         CGContextSetInterpolationQuality(bmContext, kCGInterpolationNone);
135         CGColorSpaceRelease(colorSpace);
136 //      CGContextTranslateCTM(bmContext,
137 //                                                +(rotatedRect.size.width/2),
138 //                                                +(rotatedRect.size.height/2));
139         CGContextScaleCTM(bmContext, rotatedRect.size.width/rotatedRect.size.height, 1.0);
140         CGContextTranslateCTM(bmContext, 0.0, rotatedRect.size.height);
141         CGContextRotateCTM(bmContext, angleInRadians);
142 //      CGContextTranslateCTM(bmContext,
143 //                                                -(rotatedRect.size.width/2),
144 //                                                -(rotatedRect.size.height/2));
145         CGContextDrawImage(bmContext, CGRectMake(0, 0,
146                                                                                          rotatedRect.size.width,
147                                                                                          rotatedRect.size.height),
148                                            imgRef);
149         
150         CGImageRef rotatedImage = CGBitmapContextCreateImage(bmContext);
151         CFRelease(bmContext);
152         [(id)rotatedImage autorelease];
153         
154         return rotatedImage;
155 }
156
157 - (CGImageRef)CGImageRotated180:(CGImageRef)imgRef
158 {
159         CGFloat angleInRadians = M_PI;
160         CGFloat width = CGImageGetWidth(imgRef);
161         CGFloat height = CGImageGetHeight(imgRef);
162                 
163         CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
164         CGContextRef bmContext = CGBitmapContextCreate(NULL,
165                                                                                                    width,
166                                                                                                    height,
167                                                                                                    8,
168                                                                                                    0,
169                                                                                                    colorSpace,
170                                                                                                    kCGImageAlphaPremultipliedFirst);
171         CGContextSetAllowsAntialiasing(bmContext, FALSE);
172         CGContextSetInterpolationQuality(bmContext, kCGInterpolationNone);
173         CGColorSpaceRelease(colorSpace);
174         CGContextTranslateCTM(bmContext,
175                                                           +(width/2),
176                                                           +(height/2));
177         CGContextRotateCTM(bmContext, angleInRadians);
178         CGContextTranslateCTM(bmContext,
179                                                           -(width/2),
180                                                           -(height/2));
181         CGContextDrawImage(bmContext, CGRectMake(0, 0, width, height), imgRef);
182         
183         CGImageRef rotatedImage = CGBitmapContextCreateImage(bmContext);
184         CFRelease(bmContext);
185         [(id)rotatedImage autorelease];
186         
187         return rotatedImage;
188 }
189
190 - (void)takePicture:(NSTimer*)theTimer {
191         CGImageRef capture = UIGetScreenImage();
192         CGRect cropRect = [overlayView cropRect];
193         if (oneDMode) {
194                 // let's just give the decoder a vertical band right above the red line
195                 cropRect.origin.x = cropRect.origin.x + (cropRect.size.width / 2) - (ONE_D_BAND_HEIGHT + 1);
196                 cropRect.size.width = ONE_D_BAND_HEIGHT;
197                 // do a rotate
198                 CGImageRef croppedImg = CGImageCreateWithImageInRect(capture, cropRect);
199                 CGImageRelease(capture);
200                 capture = [self CGImageRotated90:croppedImg];
201                 capture = [self CGImageRotated180:capture];
202 //              UIImageWriteToSavedPhotosAlbum([UIImage imageWithCGImage:capture], nil, nil, nil);
203                 CGImageRelease(croppedImg);
204                 cropRect.origin.x = 0.0;
205                 cropRect.origin.y = 0.0;
206                 cropRect.size.width = CGImageGetWidth(capture);
207                 cropRect.size.height = CGImageGetHeight(capture);
208         }
209         
210         UIImage *scrn = [UIImage imageWithCGImage:CGImageCreateWithImageInRect(capture, cropRect)];
211         Decoder *d = [[Decoder alloc] init];
212         d.delegate = self;
213         cropRect.origin.x = 0.0;
214         cropRect.origin.y = 0.0;
215         [d decodeImage:scrn cropRect:cropRect];
216 }
217
218 // DecoderDelegate methods
219
220 - (void)decoder:(Decoder *)decoder willDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset{
221 #ifdef DEBUG
222         NSLog(@"DecoderViewController MessageWhileDecodingWithDimensions: Decoding image (%.0fx%.0f) ...", image.size.width, image.size.height);
223 #endif
224 }
225
226 - (void)decoder:(Decoder *)decoder
227   decodingImage:(UIImage *)image
228     usingSubset:(UIImage *)subset
229        progress:(NSString *)message {
230 }
231
232 - (void)presentResultForString:(NSString *)resultString {
233         self.result = [ResultParser parsedResultForString:resultString];
234         
235         if (beepSound != -1) {
236                 AudioServicesPlaySystemSound(beepSound);
237         }
238 #ifdef DEBUG
239         NSLog(@"result string = %@", resultString);
240         NSLog(@"result has %d actions", actions ? 0 : actions.count);
241 #endif
242         //      [self updateToolbar];
243 }
244
245 - (void)presentResultPoints:(NSArray *)resultPoints
246                    forImage:(UIImage *)image
247                 usingSubset:(UIImage *)subset {
248         // simply add the points to the image view
249         [overlayView setPoints:resultPoints];
250 }
251
252 - (void)decoder:(Decoder *)decoder didDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset withResult:(TwoDDecoderResult *)twoDResult {
253         [self presentResultForString:[twoDResult text]];
254         [self presentResultPoints:[twoDResult points] forImage:image usingSubset:subset];
255         // now, in a selector, call the delegate to give this overlay time to show the points
256         [self performSelector:@selector(alertDelegate:) withObject:[[twoDResult text] copy] afterDelay:1.0];
257         decoder.delegate = nil;
258         [decoder release];
259 }
260
261 - (void)alertDelegate:(id)text {        
262         if (delegate != nil) {
263                 [delegate scanResult:text];
264         }
265 }
266
267 - (void)decoder:(Decoder *)decoder failedToDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset reason:(NSString *)reason {
268         decoder.delegate = nil;
269         [decoder release];
270         [overlayView setPoints:nil];
271         if (!wasCancelled) {
272                 [self takePicture:nil];
273         }
274         //[self updateToolbar];
275 }
276
277 @end