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