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;
43 @implementation ZXingWidgetController
44 @synthesize result, actions, delegate, soundToPlay;
45 @synthesize overlayView;
46 @synthesize oneDMode, showCancel;
48 - (id)initWithDelegate:(id<ZXingDelegate>)scanDelegate showCancel:(BOOL)shouldShowCancel OneDMode:(BOOL)shouldUseoOneDMode {
49 if (self = [super init]) {
50 [self setDelegate:scanDelegate];
51 self.oneDMode = shouldUseoOneDMode;
52 self.showCancel = shouldShowCancel;
54 self.wantsFullScreenLayout = YES;
55 if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
56 self.sourceType = UIImagePickerControllerSourceTypeCamera;
57 float zoomFactor = CAMERA_SCALAR;
58 if ([self fixedFocus]) {
61 if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
62 self.cameraViewTransform = CGAffineTransformScale(
63 self.cameraViewTransform, zoomFactor, zoomFactor);
64 overlayView = [OverlayView alloc];
65 [overlayView setOneDMode:oneDMode];
66 overlayView = [overlayView initWithCancelEnabled:showCancel frame:[UIScreen mainScreen].bounds];
67 [overlayView setDelegate:self];
68 if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
70 self.showsCameraControls = NO;
71 self.cameraOverlayView = overlayView;
72 self.allowsEditing = NO; // [[NSUserDefaults standardUserDefaults] boolForKey:@"allowEditing"];
80 if (beepSound != -1) {
81 AudioServicesDisposeSystemSoundID(beepSound);
83 self.cameraOverlayView = nil;
84 [overlayView release];
89 NSLog(@"cancelled called in ZXingWidgetController");
91 if (delegate != nil) {
96 - (NSString *)getPlatform {
98 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
99 char *machine = malloc(size);
100 sysctlbyname("hw.machine", machine, &size, NULL, 0);
101 NSString *platform = [NSString stringWithCString:machine encoding:NSASCIIStringEncoding];
107 NSString *platform = [self getPlatform];
108 if ([platform isEqualToString:@"iPhone1,1"] ||
109 [platform isEqualToString:@"iPhone1,2"]) return true;
113 - (void)viewWillAppear:(BOOL)animated {
114 [super viewWillAppear:animated];
115 if ([self soundToPlay] != nil) {
116 OSStatus error = AudioServicesCreateSystemSoundID((CFURLRef)[self soundToPlay], &beepSound);
117 if (error != kAudioServicesNoError) {
118 NSLog(@"Problem loading nearSound.caf");
123 - (void)viewDidAppear:(BOOL)animated {
124 [super viewDidAppear:animated];
125 [overlayView setPoints:nil];
126 wasCancelled = false;
127 [NSTimer scheduledTimerWithTimeInterval: FIRST_TAKE_DELAY
129 selector: @selector(takePicture:)
134 - (CGImageRef)CGImageRotated90:(CGImageRef)imgRef
136 CGFloat angleInRadians = -90 * (M_PI / 180);
137 CGFloat width = CGImageGetWidth(imgRef);
138 CGFloat height = CGImageGetHeight(imgRef);
140 CGRect imgRect = CGRectMake(0, 0, width, height);
141 CGAffineTransform transform = CGAffineTransformMakeRotation(angleInRadians);
142 CGRect rotatedRect = CGRectApplyAffineTransform(imgRect, transform);
144 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
145 CGContextRef bmContext = CGBitmapContextCreate(NULL,
146 rotatedRect.size.width,
147 rotatedRect.size.height,
151 kCGImageAlphaPremultipliedFirst);
152 CGContextSetAllowsAntialiasing(bmContext, FALSE);
153 CGContextSetInterpolationQuality(bmContext, kCGInterpolationNone);
154 CGColorSpaceRelease(colorSpace);
155 // CGContextTranslateCTM(bmContext,
156 // +(rotatedRect.size.width/2),
157 // +(rotatedRect.size.height/2));
158 CGContextScaleCTM(bmContext, rotatedRect.size.width/rotatedRect.size.height, 1.0);
159 CGContextTranslateCTM(bmContext, 0.0, rotatedRect.size.height);
160 CGContextRotateCTM(bmContext, angleInRadians);
161 // CGContextTranslateCTM(bmContext,
162 // -(rotatedRect.size.width/2),
163 // -(rotatedRect.size.height/2));
164 CGContextDrawImage(bmContext, CGRectMake(0, 0,
165 rotatedRect.size.width,
166 rotatedRect.size.height),
169 CGImageRef rotatedImage = CGBitmapContextCreateImage(bmContext);
170 CFRelease(bmContext);
171 [(id)rotatedImage autorelease];
176 - (CGImageRef)CGImageRotated180:(CGImageRef)imgRef
178 CGFloat angleInRadians = M_PI;
179 CGFloat width = CGImageGetWidth(imgRef);
180 CGFloat height = CGImageGetHeight(imgRef);
182 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
183 CGContextRef bmContext = CGBitmapContextCreate(NULL,
189 kCGImageAlphaPremultipliedFirst);
190 CGContextSetAllowsAntialiasing(bmContext, FALSE);
191 CGContextSetInterpolationQuality(bmContext, kCGInterpolationNone);
192 CGColorSpaceRelease(colorSpace);
193 CGContextTranslateCTM(bmContext,
196 CGContextRotateCTM(bmContext, angleInRadians);
197 CGContextTranslateCTM(bmContext,
200 CGContextDrawImage(bmContext, CGRectMake(0, 0, width, height), imgRef);
202 CGImageRef rotatedImage = CGBitmapContextCreateImage(bmContext);
203 CFRelease(bmContext);
204 [(id)rotatedImage autorelease];
209 - (void)takePicture:(NSTimer*)theTimer {
210 CGImageRef capture = UIGetScreenImage();
211 CGRect cropRect = [overlayView cropRect];
213 // let's just give the decoder a vertical band right above the red line
214 cropRect.origin.x = cropRect.origin.x + (cropRect.size.width / 2) - (ONE_D_BAND_HEIGHT + 1);
215 cropRect.size.width = ONE_D_BAND_HEIGHT;
217 CGImageRef croppedImg = CGImageCreateWithImageInRect(capture, cropRect);
218 capture = [self CGImageRotated90:croppedImg];
219 capture = [self CGImageRotated180:capture];
220 // UIImageWriteToSavedPhotosAlbum([UIImage imageWithCGImage:capture], nil, nil, nil);
221 CGImageRelease(croppedImg);
222 cropRect.origin.x = 0.0;
223 cropRect.origin.y = 0.0;
224 cropRect.size.width = CGImageGetWidth(capture);
225 cropRect.size.height = CGImageGetHeight(capture);
227 CGImageRef newImage = CGImageCreateWithImageInRect(capture, cropRect);
228 CGImageRelease(capture);
229 //UIImage *scrn = [UIImage imageWithCGImage:newImage];
230 UIImage *scrn = [[UIImage alloc] initWithCGImage:newImage];
231 CGImageRelease(newImage);
232 Decoder *d = [[Decoder alloc] init];
234 cropRect.origin.x = 0.0;
235 cropRect.origin.y = 0.0;
236 [d decodeImage:scrn cropRect:cropRect];
240 // DecoderDelegate methods
242 - (void)decoder:(Decoder *)decoder willDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset{
244 NSLog(@"DecoderViewController MessageWhileDecodingWithDimensions: Decoding image (%.0fx%.0f) ...", image.size.width, image.size.height);
248 - (void)decoder:(Decoder *)decoder
249 decodingImage:(UIImage *)image
250 usingSubset:(UIImage *)subset
251 progress:(NSString *)message {
254 - (void)presentResultForString:(NSString *)resultString {
255 self.result = [ResultParser parsedResultForString:resultString];
257 if (beepSound != -1) {
258 AudioServicesPlaySystemSound(beepSound);
261 NSLog(@"result string = %@", resultString);
262 NSLog(@"result has %d actions", actions ? 0 : actions.count);
264 // [self updateToolbar];
267 - (void)presentResultPoints:(NSArray *)resultPoints
268 forImage:(UIImage *)image
269 usingSubset:(UIImage *)subset {
270 // simply add the points to the image view
271 [overlayView setPoints:resultPoints];
274 - (void)decoder:(Decoder *)decoder didDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset withResult:(TwoDDecoderResult *)twoDResult {
275 [self presentResultForString:[twoDResult text]];
276 [self presentResultPoints:[twoDResult points] forImage:image usingSubset:subset];
277 // now, in a selector, call the delegate to give this overlay time to show the points
278 [self performSelector:@selector(alertDelegate:) withObject:[[twoDResult text] copy] afterDelay:1.0];
279 decoder.delegate = nil;
283 - (void)alertDelegate:(id)text {
284 if (delegate != nil) {
285 [delegate scanResult:text];
289 - (void)decoder:(Decoder *)decoder failedToDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset reason:(NSString *)reason {
290 decoder.delegate = nil;
292 [overlayView setPoints:nil];
294 [self takePicture:nil];
296 //[self updateToolbar];