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