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