cf9a9213b3bc6d0fe1c494bf27637b86347755e2
[zxing.git] / iphone / ZXingWidget / Classes / 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
26 #define CAMERA_SCALAR 1.12412 // scalar = (480 / (2048 / 480))
27 #define FIRST_TAKE_DELAY 1.0
28 #define ONE_D_BAND_HEIGHT 10.0
29
30 CGImageRef UIGetScreenImage(void);
31
32 @interface ZXingWidgetController ()
33
34 @property BOOL showCancel;
35 @property BOOL oneDMode;
36
37 @property (nonatomic, retain) UIImagePickerController* imagePicker;
38
39 @end
40
41
42
43
44
45 @implementation ZXingWidgetController
46 @synthesize result, actions, delegate, soundToPlay;
47 @synthesize overlayView;
48 @synthesize oneDMode, showCancel;
49 @synthesize imagePicker;
50 @synthesize readers;
51
52
53 -(void)loadImagePicker {
54   if (self.imagePicker)
55   {
56     [imagePicker release];
57     imagePicker = nil;
58   }
59   UIImagePickerController* imController = [[UIImagePickerController alloc] init];
60   self.imagePicker = imController;
61   imagePicker.delegate = self;
62   [imController release];
63   imagePicker.wantsFullScreenLayout = YES;
64   if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
65     imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
66   float zoomFactor = CAMERA_SCALAR;
67   if ([self fixedFocus]) {
68     zoomFactor *= 2.0;
69   }
70   if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
71     imagePicker.cameraViewTransform = CGAffineTransformScale(
72                                                              imagePicker.cameraViewTransform, zoomFactor, zoomFactor);
73   if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
74   {
75     imagePicker.showsCameraControls = NO;
76     imagePicker.cameraOverlayView = overlayView;
77     imagePicker.allowsEditing = NO;
78   }
79 }
80
81 - (void)unloadImagePicker {
82   if (self.imagePicker)
83   {
84     [imagePicker release];
85     imagePicker = nil;
86   }
87 }
88
89 - (id)initWithDelegate:(id<ZXingDelegate>)scanDelegate showCancel:(BOOL)shouldShowCancel OneDMode:(BOOL)shouldUseoOneDMode {
90   if (self = [super init]) {
91     [self setDelegate:scanDelegate];
92     self.oneDMode = shouldUseoOneDMode;
93     self.showCancel = shouldShowCancel;
94     self.wantsFullScreenLayout = YES;
95     beepSound = -1;
96     OverlayView *theOverLayView = [[OverlayView alloc] initWithFrame:[UIScreen mainScreen].bounds 
97                                                        cancelEnabled:showCancel 
98                                                             oneDMode:oneDMode];
99     [theOverLayView setDelegate:self];
100     self.overlayView = theOverLayView;
101     [theOverLayView release];
102   }
103   
104   return self;
105 }
106
107 - (void)dealloc {
108   if (beepSound != -1) {
109     AudioServicesDisposeSystemSoundID(beepSound);
110   }
111   imagePicker.cameraOverlayView = nil;
112   [imagePicker release];
113   [overlayView release];
114   [readers release];
115   [super dealloc];
116 }
117
118 - (void)cancelled {
119   [[UIApplication sharedApplication] setStatusBarHidden:NO];
120   wasCancelled = YES;
121   if (delegate != nil) {
122     [delegate zxingControllerDidCancel:self];
123   }
124 }
125
126 - (NSString *)getPlatform {
127   size_t size;
128   sysctlbyname("hw.machine", NULL, &size, NULL, 0);
129   char *machine = malloc(size);
130   sysctlbyname("hw.machine", machine, &size, NULL, 0);
131   NSString *platform = [NSString stringWithCString:machine encoding:NSASCIIStringEncoding];
132   free(machine);
133   return platform;
134 }
135
136 - (BOOL)fixedFocus {
137   NSString *platform = [self getPlatform];
138   if ([platform isEqualToString:@"iPhone1,1"] ||
139       [platform isEqualToString:@"iPhone1,2"]) return YES;
140   return NO;
141 }
142
143 - (void)viewWillAppear:(BOOL)animated {
144   [super viewWillAppear:animated];
145   self.wantsFullScreenLayout = YES;
146   //[[UIApplication sharedApplication] setStatusBarHidden:YES];
147   if ([self soundToPlay] != nil) {
148     OSStatus error = AudioServicesCreateSystemSoundID((CFURLRef)[self soundToPlay], &beepSound);
149     if (error != kAudioServicesNoError) {
150       NSLog(@"Problem loading nearSound.caf");
151     }
152   }
153 }
154
155 - (void)viewDidAppear:(BOOL)animated {
156   NSLog(@"View did appear");
157   [super viewDidAppear:animated];
158   [[UIApplication sharedApplication] setStatusBarHidden:YES];
159   //self.wantsFullScreenLayout = YES;
160   [self loadImagePicker];
161   self.view = imagePicker.view;
162   
163   [overlayView setPoints:nil];
164   wasCancelled = NO;
165   if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
166
167     [NSTimer scheduledTimerWithTimeInterval: FIRST_TAKE_DELAY
168                                      target: self
169                                    selector: @selector(takePicture:)
170                                    userInfo: nil
171                                     repeats: NO];
172   }
173 }
174
175 - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
176   //self.wantsFullScreenLayout = NO;
177   [UIApplication sharedApplication].statusBarHidden = NO;
178   [self cancelled];
179 }
180
181
182 - (CGImageRef)CGImageRotated90:(CGImageRef)imgRef
183 {
184   CGFloat angleInRadians = -90 * (M_PI / 180);
185   CGFloat width = CGImageGetWidth(imgRef);
186   CGFloat height = CGImageGetHeight(imgRef);
187   
188   CGRect imgRect = CGRectMake(0, 0, width, height);
189   CGAffineTransform transform = CGAffineTransformMakeRotation(angleInRadians);
190   CGRect rotatedRect = CGRectApplyAffineTransform(imgRect, transform);
191   
192   CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
193   CGContextRef bmContext = CGBitmapContextCreate(NULL,
194                                                  rotatedRect.size.width,
195                                                  rotatedRect.size.height,
196                                                  8,
197                                                  0,
198                                                  colorSpace,
199                                                  kCGImageAlphaPremultipliedFirst);
200   CGContextSetAllowsAntialiasing(bmContext, FALSE);
201   CGContextSetInterpolationQuality(bmContext, kCGInterpolationNone);
202   CGColorSpaceRelease(colorSpace);
203   //      CGContextTranslateCTM(bmContext,
204   //                                                +(rotatedRect.size.width/2),
205   //                                                +(rotatedRect.size.height/2));
206   CGContextScaleCTM(bmContext, rotatedRect.size.width/rotatedRect.size.height, 1.0);
207   CGContextTranslateCTM(bmContext, 0.0, rotatedRect.size.height);
208   CGContextRotateCTM(bmContext, angleInRadians);
209   //      CGContextTranslateCTM(bmContext,
210   //                                                -(rotatedRect.size.width/2),
211   //                                                -(rotatedRect.size.height/2));
212   CGContextDrawImage(bmContext, CGRectMake(0, 0,
213                                            rotatedRect.size.width,
214                                            rotatedRect.size.height),
215                      imgRef);
216   
217   CGImageRef rotatedImage = CGBitmapContextCreateImage(bmContext);
218   CFRelease(bmContext);
219   [(id)rotatedImage autorelease];
220   
221   return rotatedImage;
222 }
223
224 - (CGImageRef)CGImageRotated180:(CGImageRef)imgRef
225 {
226   CGFloat angleInRadians = M_PI;
227   CGFloat width = CGImageGetWidth(imgRef);
228   CGFloat height = CGImageGetHeight(imgRef);
229   
230   CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
231   CGContextRef bmContext = CGBitmapContextCreate(NULL,
232                                                  width,
233                                                  height,
234                                                  8,
235                                                  0,
236                                                  colorSpace,
237                                                  kCGImageAlphaPremultipliedFirst);
238   CGContextSetAllowsAntialiasing(bmContext, FALSE);
239   CGContextSetInterpolationQuality(bmContext, kCGInterpolationNone);
240   CGColorSpaceRelease(colorSpace);
241   CGContextTranslateCTM(bmContext,
242                         +(width/2),
243                         +(height/2));
244   CGContextRotateCTM(bmContext, angleInRadians);
245   CGContextTranslateCTM(bmContext,
246                         -(width/2),
247                         -(height/2));
248   CGContextDrawImage(bmContext, CGRectMake(0, 0, width, height), imgRef);
249   
250   CGImageRef rotatedImage = CGBitmapContextCreateImage(bmContext);
251   CFRelease(bmContext);
252   [(id)rotatedImage autorelease];
253   
254   return rotatedImage;
255 }
256
257 - (void)takePicture:(NSTimer*)theTimer {
258   CGImageRef capture = UIGetScreenImage();
259   CGRect cropRect = [overlayView cropRect];
260   if (oneDMode) {
261     // let's just give the decoder a vertical band right above the red line
262     cropRect.origin.x = cropRect.origin.x + (cropRect.size.width / 2) - (ONE_D_BAND_HEIGHT + 1);
263     cropRect.size.width = ONE_D_BAND_HEIGHT;
264     // do a rotate
265     CGImageRef croppedImg = CGImageCreateWithImageInRect(capture, cropRect);
266     capture = [self CGImageRotated90:croppedImg];
267     capture = [self CGImageRotated180:capture];
268     //              UIImageWriteToSavedPhotosAlbum([UIImage imageWithCGImage:capture], nil, nil, nil);
269     CGImageRelease(croppedImg);
270     cropRect.origin.x = 0.0;
271     cropRect.origin.y = 0.0;
272     cropRect.size.width = CGImageGetWidth(capture);
273     cropRect.size.height = CGImageGetHeight(capture);
274   }
275   CGImageRef newImage = CGImageCreateWithImageInRect(capture, cropRect);
276   CGImageRelease(capture);
277   //UIImage *scrn = [UIImage imageWithCGImage:newImage];
278   UIImage *scrn = [[UIImage alloc] initWithCGImage:newImage];
279   CGImageRelease(newImage);
280   Decoder *d = [[Decoder alloc] init];
281   d.readers = readers;
282   d.delegate = self;
283   cropRect.origin.x = 0.0;
284   cropRect.origin.y = 0.0;
285   [d decodeImage:scrn cropRect:cropRect];
286   [scrn release];
287 }
288
289 // DecoderDelegate methods
290
291 - (void)decoder:(Decoder *)decoder willDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset{
292 #ifdef DEBUG
293   NSLog(@"DecoderViewController MessageWhileDecodingWithDimensions: Decoding image (%.0fx%.0f) ...", image.size.width, image.size.height);
294 #endif
295 }
296
297 - (void)decoder:(Decoder *)decoder
298   decodingImage:(UIImage *)image
299     usingSubset:(UIImage *)subset
300        progress:(NSString *)message {
301 }
302
303 - (void)presentResultForString:(NSString *)resultString {
304   self.result = [ResultParser parsedResultForString:resultString];
305   
306   if (beepSound != -1) {
307     AudioServicesPlaySystemSound(beepSound);
308   }
309 #ifdef DEBUG
310   NSLog(@"result string = %@", resultString);
311   NSLog(@"result has %d actions", actions ? 0 : actions.count);
312 #endif
313   //      [self updateToolbar];
314 }
315
316 - (void)presentResultPoints:(NSArray *)resultPoints
317                    forImage:(UIImage *)image
318                 usingSubset:(UIImage *)subset {
319   // simply add the points to the image view
320   [overlayView setPoints:resultPoints];
321 }
322
323 - (void)decoder:(Decoder *)decoder didDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset withResult:(TwoDDecoderResult *)twoDResult {
324   [self presentResultForString:[twoDResult text]];
325   [self presentResultPoints:[twoDResult points] forImage:image usingSubset:subset];
326   // now, in a selector, call the delegate to give this overlay time to show the points
327   [self performSelector:@selector(alertDelegate:) withObject:[[twoDResult text] copy] afterDelay:1.0];
328   decoder.delegate = nil;
329   [decoder release];
330 }
331
332 - (void)alertDelegate:(id)text {        
333   [[UIApplication sharedApplication] setStatusBarHidden:NO];
334   if (delegate != nil) {
335     [delegate zxingController:self didScanResult:text];
336   }
337 }
338
339 - (void)decoder:(Decoder *)decoder failedToDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset reason:(NSString *)reason {
340   decoder.delegate = nil;
341   [decoder release];
342   [overlayView setPoints:nil];
343   if (!wasCancelled) {
344     [self takePicture:nil];
345   }
346   //[self updateToolbar];
347 }
348
349 @end