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;
53 -(void)loadImagePicker {
56 [imagePicker release];
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]) {
70 if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
71 imagePicker.cameraViewTransform = CGAffineTransformScale(
72 imagePicker.cameraViewTransform, zoomFactor, zoomFactor);
73 if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
75 imagePicker.showsCameraControls = NO;
76 imagePicker.cameraOverlayView = overlayView;
77 imagePicker.allowsEditing = NO;
81 - (void)unloadImagePicker {
84 [imagePicker release];
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;
96 OverlayView *theOverLayView = [[OverlayView alloc] initWithFrame:[UIScreen mainScreen].bounds
97 cancelEnabled:showCancel
99 [theOverLayView setDelegate:self];
100 self.overlayView = theOverLayView;
101 [theOverLayView release];
108 if (beepSound != -1) {
109 AudioServicesDisposeSystemSoundID(beepSound);
111 imagePicker.cameraOverlayView = nil;
112 [imagePicker release];
113 [overlayView release];
119 [[UIApplication sharedApplication] setStatusBarHidden:NO];
121 if (delegate != nil) {
122 [delegate zxingControllerDidCancel:self];
126 - (NSString *)getPlatform {
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];
137 NSString *platform = [self getPlatform];
138 if ([platform isEqualToString:@"iPhone1,1"] ||
139 [platform isEqualToString:@"iPhone1,2"]) return YES;
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");
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;
163 [overlayView setPoints:nil];
165 if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
167 [NSTimer scheduledTimerWithTimeInterval: FIRST_TAKE_DELAY
169 selector: @selector(takePicture:)
175 - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
176 //self.wantsFullScreenLayout = NO;
177 [UIApplication sharedApplication].statusBarHidden = NO;
182 - (CGImageRef)CGImageRotated90:(CGImageRef)imgRef
184 CGFloat angleInRadians = -90 * (M_PI / 180);
185 CGFloat width = CGImageGetWidth(imgRef);
186 CGFloat height = CGImageGetHeight(imgRef);
188 CGRect imgRect = CGRectMake(0, 0, width, height);
189 CGAffineTransform transform = CGAffineTransformMakeRotation(angleInRadians);
190 CGRect rotatedRect = CGRectApplyAffineTransform(imgRect, transform);
192 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
193 CGContextRef bmContext = CGBitmapContextCreate(NULL,
194 rotatedRect.size.width,
195 rotatedRect.size.height,
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),
217 CGImageRef rotatedImage = CGBitmapContextCreateImage(bmContext);
218 CFRelease(bmContext);
219 [(id)rotatedImage autorelease];
224 - (CGImageRef)CGImageRotated180:(CGImageRef)imgRef
226 CGFloat angleInRadians = M_PI;
227 CGFloat width = CGImageGetWidth(imgRef);
228 CGFloat height = CGImageGetHeight(imgRef);
230 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
231 CGContextRef bmContext = CGBitmapContextCreate(NULL,
237 kCGImageAlphaPremultipliedFirst);
238 CGContextSetAllowsAntialiasing(bmContext, FALSE);
239 CGContextSetInterpolationQuality(bmContext, kCGInterpolationNone);
240 CGColorSpaceRelease(colorSpace);
241 CGContextTranslateCTM(bmContext,
244 CGContextRotateCTM(bmContext, angleInRadians);
245 CGContextTranslateCTM(bmContext,
248 CGContextDrawImage(bmContext, CGRectMake(0, 0, width, height), imgRef);
250 CGImageRef rotatedImage = CGBitmapContextCreateImage(bmContext);
251 CFRelease(bmContext);
252 [(id)rotatedImage autorelease];
257 - (void)takePicture:(NSTimer*)theTimer {
258 CGImageRef capture = UIGetScreenImage();
259 CGRect cropRect = [overlayView cropRect];
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;
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);
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];
283 cropRect.origin.x = 0.0;
284 cropRect.origin.y = 0.0;
285 [d decodeImage:scrn cropRect:cropRect];
289 // DecoderDelegate methods
291 - (void)decoder:(Decoder *)decoder willDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset{
293 NSLog(@"DecoderViewController MessageWhileDecodingWithDimensions: Decoding image (%.0fx%.0f) ...", image.size.width, image.size.height);
297 - (void)decoder:(Decoder *)decoder
298 decodingImage:(UIImage *)image
299 usingSubset:(UIImage *)subset
300 progress:(NSString *)message {
303 - (void)presentResultForString:(NSString *)resultString {
304 self.result = [ResultParser parsedResultForString:resultString];
306 if (beepSound != -1) {
307 AudioServicesPlaySystemSound(beepSound);
310 NSLog(@"result string = %@", resultString);
311 NSLog(@"result has %d actions", actions ? 0 : actions.count);
313 // [self updateToolbar];
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];
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;
332 - (void)alertDelegate:(id)text {
333 [[UIApplication sharedApplication] setStatusBarHidden:NO];
334 if (delegate != nil) {
335 [delegate zxingController:self didScanResult:text];
339 - (void)decoder:(Decoder *)decoder failedToDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset reason:(NSString *)reason {
340 decoder.delegate = nil;
342 [overlayView setPoints:nil];
344 [self takePicture:nil];
346 //[self updateToolbar];