2 * Copyright 2009 Jeff Verkoeyen
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #import "ZXingWidgetController.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>
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
30 CGImageRef UIGetScreenImage(void);
32 @interface ZXingWidgetController ()
34 @property BOOL showCancel;
35 @property BOOL oneDMode;
37 @property (nonatomic, retain) UIImagePickerController* imagePicker;
45 @implementation ZXingWidgetController
46 @synthesize result, actions, delegate, soundToPlay;
47 @synthesize overlayView;
48 @synthesize oneDMode, showCancel;
49 @synthesize imagePicker;
52 -(void)loadImagePicker {
55 [imagePicker release];
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]) {
69 if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
70 imagePicker.cameraViewTransform = CGAffineTransformScale(
71 imagePicker.cameraViewTransform, zoomFactor, zoomFactor);
72 if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
74 imagePicker.showsCameraControls = NO;
75 imagePicker.cameraOverlayView = overlayView;
76 imagePicker.allowsEditing = NO;
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;
87 OverlayView *theOverLayView = [[OverlayView alloc] initWithFrame:[UIScreen mainScreen].bounds
88 cancelEnabled:showCancel
90 [theOverLayView setDelegate:self];
91 self.overlayView = theOverLayView;
92 [theOverLayView release];
99 if (beepSound != -1) {
100 AudioServicesDisposeSystemSoundID(beepSound);
102 imagePicker.cameraOverlayView = nil;
103 [imagePicker release];
104 [overlayView release];
109 NSLog(@"cancelled called in ZXingWidgetController");
110 [[UIApplication sharedApplication] setStatusBarHidden:NO];
112 if (delegate != nil) {
113 [delegate cancelled];
117 - (NSString *)getPlatform {
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];
128 NSString *platform = [self getPlatform];
129 if ([platform isEqualToString:@"iPhone1,1"] ||
130 [platform isEqualToString:@"iPhone1,2"]) return true;
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");
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;
154 [overlayView setPoints:nil];
155 wasCancelled = false;
156 if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
158 [NSTimer scheduledTimerWithTimeInterval: FIRST_TAKE_DELAY
160 selector: @selector(takePicture:)
166 - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
167 //self.wantsFullScreenLayout = NO;
168 [UIApplication sharedApplication].statusBarHidden = NO;
173 - (CGImageRef)CGImageRotated90:(CGImageRef)imgRef
175 CGFloat angleInRadians = -90 * (M_PI / 180);
176 CGFloat width = CGImageGetWidth(imgRef);
177 CGFloat height = CGImageGetHeight(imgRef);
179 CGRect imgRect = CGRectMake(0, 0, width, height);
180 CGAffineTransform transform = CGAffineTransformMakeRotation(angleInRadians);
181 CGRect rotatedRect = CGRectApplyAffineTransform(imgRect, transform);
183 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
184 CGContextRef bmContext = CGBitmapContextCreate(NULL,
185 rotatedRect.size.width,
186 rotatedRect.size.height,
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),
208 CGImageRef rotatedImage = CGBitmapContextCreateImage(bmContext);
209 CFRelease(bmContext);
210 [(id)rotatedImage autorelease];
215 - (CGImageRef)CGImageRotated180:(CGImageRef)imgRef
217 CGFloat angleInRadians = M_PI;
218 CGFloat width = CGImageGetWidth(imgRef);
219 CGFloat height = CGImageGetHeight(imgRef);
221 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
222 CGContextRef bmContext = CGBitmapContextCreate(NULL,
228 kCGImageAlphaPremultipliedFirst);
229 CGContextSetAllowsAntialiasing(bmContext, FALSE);
230 CGContextSetInterpolationQuality(bmContext, kCGInterpolationNone);
231 CGColorSpaceRelease(colorSpace);
232 CGContextTranslateCTM(bmContext,
235 CGContextRotateCTM(bmContext, angleInRadians);
236 CGContextTranslateCTM(bmContext,
239 CGContextDrawImage(bmContext, CGRectMake(0, 0, width, height), imgRef);
241 CGImageRef rotatedImage = CGBitmapContextCreateImage(bmContext);
242 CFRelease(bmContext);
243 [(id)rotatedImage autorelease];
248 - (void)takePicture:(NSTimer*)theTimer {
249 CGImageRef capture = UIGetScreenImage();
250 CGRect cropRect = [overlayView cropRect];
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;
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);
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];
273 cropRect.origin.x = 0.0;
274 cropRect.origin.y = 0.0;
275 [d decodeImage:scrn cropRect:cropRect];
279 // DecoderDelegate methods
281 - (void)decoder:(Decoder *)decoder willDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset{
283 NSLog(@"DecoderViewController MessageWhileDecodingWithDimensions: Decoding image (%.0fx%.0f) ...", image.size.width, image.size.height);
287 - (void)decoder:(Decoder *)decoder
288 decodingImage:(UIImage *)image
289 usingSubset:(UIImage *)subset
290 progress:(NSString *)message {
293 - (void)presentResultForString:(NSString *)resultString {
294 self.result = [ResultParser parsedResultForString:resultString];
296 if (beepSound != -1) {
297 AudioServicesPlaySystemSound(beepSound);
300 NSLog(@"result string = %@", resultString);
301 NSLog(@"result has %d actions", actions ? 0 : actions.count);
303 // [self updateToolbar];
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];
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;
322 - (void)alertDelegate:(id)text {
323 if (delegate != nil) {
324 [delegate scanResult:text];
328 - (void)decoder:(Decoder *)decoder failedToDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset reason:(NSString *)reason {
329 decoder.delegate = nil;
331 [overlayView setPoints:nil];
333 [self takePicture:nil];
335 //[self updateToolbar];