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>
27 #define CAMERA_SCALAR 1.12412 // scalar = (480 / (2048 / 480))
28 #define FIRST_TAKE_DELAY 1.0
29 #define ONE_D_BAND_HEIGHT 10.0
31 CGImageRef UIGetScreenImage();
33 @implementation ZXingWidgetController
34 @synthesize result, actions, showCancel, delegate, soundToPlay, oneDMode;
36 - (id)initWithDelegate:(id<ZXingDelegate>)scanDelegate {
37 if (self = [super init]) {
38 [self setDelegate:scanDelegate];
40 self.wantsFullScreenLayout = YES;
41 self.sourceType = UIImagePickerControllerSourceTypeCamera;
42 float zoomFactor = CAMERA_SCALAR;
43 if ([self fixedFocus]) {
46 self.cameraViewTransform = CGAffineTransformScale(
47 self.cameraViewTransform, zoomFactor, zoomFactor);
48 overlayView = [OverlayView alloc];
49 [overlayView setOneDMode:oneDMode];
50 overlayView = [overlayView initWithCancelEnabled:showCancel frame:[UIScreen mainScreen].bounds];
51 [overlayView setDelegate:self];
52 self.sourceType = UIImagePickerControllerSourceTypeCamera;
53 self.showsCameraControls = NO;
54 self.cameraOverlayView = overlayView;
55 self.allowsEditing = NO; // [[NSUserDefaults standardUserDefaults] boolForKey:@"allowEditing"];
62 if (beepSound != -1) {
63 AudioServicesDisposeSystemSoundID(beepSound);
65 [overlayView dealloc];
70 NSLog(@"cancelled called in ZXingWidgetController");
72 if (delegate != nil) {
77 - (NSString *)getPlatform {
79 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
80 char *machine = malloc(size);
81 sysctlbyname("hw.machine", machine, &size, NULL, 0);
82 NSString *platform = [NSString stringWithCString:machine encoding:NSASCIIStringEncoding];
88 NSString *platform = [self getPlatform];
89 if ([platform isEqualToString:@"iPhone1,1"] ||
90 [platform isEqualToString:@"iPhone1,2"]) return true;
94 - (void)viewWillAppear:(BOOL)animated {
95 [super viewWillAppear:animated];
96 if ([self soundToPlay] != nil) {
97 OSStatus error = AudioServicesCreateSystemSoundID((CFURLRef)[self soundToPlay], &beepSound);
98 if (error != kAudioServicesNoError) {
99 NSLog(@"Problem loading nearSound.caf");
104 - (void)viewDidAppear:(BOOL)animated {
105 [super viewDidAppear:animated];
106 [overlayView setPoints:nil];
107 wasCancelled = false;
108 [NSTimer scheduledTimerWithTimeInterval: FIRST_TAKE_DELAY
110 selector: @selector(takePicture:)
115 - (CGImageRef)CGImageRotated90:(CGImageRef)imgRef
117 CGFloat angleInRadians = -90 * (M_PI / 180);
118 CGFloat width = CGImageGetWidth(imgRef);
119 CGFloat height = CGImageGetHeight(imgRef);
121 CGRect imgRect = CGRectMake(0, 0, width, height);
122 CGAffineTransform transform = CGAffineTransformMakeRotation(angleInRadians);
123 CGRect rotatedRect = CGRectApplyAffineTransform(imgRect, transform);
125 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
126 CGContextRef bmContext = CGBitmapContextCreate(NULL,
127 rotatedRect.size.width,
128 rotatedRect.size.height,
132 kCGImageAlphaPremultipliedFirst);
133 CGContextSetAllowsAntialiasing(bmContext, FALSE);
134 CGContextSetInterpolationQuality(bmContext, kCGInterpolationNone);
135 CGColorSpaceRelease(colorSpace);
136 // CGContextTranslateCTM(bmContext,
137 // +(rotatedRect.size.width/2),
138 // +(rotatedRect.size.height/2));
139 CGContextScaleCTM(bmContext, rotatedRect.size.width/rotatedRect.size.height, 1.0);
140 CGContextTranslateCTM(bmContext, 0.0, rotatedRect.size.height);
141 CGContextRotateCTM(bmContext, angleInRadians);
142 // CGContextTranslateCTM(bmContext,
143 // -(rotatedRect.size.width/2),
144 // -(rotatedRect.size.height/2));
145 CGContextDrawImage(bmContext, CGRectMake(0, 0,
146 rotatedRect.size.width,
147 rotatedRect.size.height),
150 CGImageRef rotatedImage = CGBitmapContextCreateImage(bmContext);
151 CFRelease(bmContext);
152 [(id)rotatedImage autorelease];
157 - (CGImageRef)CGImageRotated180:(CGImageRef)imgRef
159 CGFloat angleInRadians = M_PI;
160 CGFloat width = CGImageGetWidth(imgRef);
161 CGFloat height = CGImageGetHeight(imgRef);
163 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
164 CGContextRef bmContext = CGBitmapContextCreate(NULL,
170 kCGImageAlphaPremultipliedFirst);
171 CGContextSetAllowsAntialiasing(bmContext, FALSE);
172 CGContextSetInterpolationQuality(bmContext, kCGInterpolationNone);
173 CGColorSpaceRelease(colorSpace);
174 CGContextTranslateCTM(bmContext,
177 CGContextRotateCTM(bmContext, angleInRadians);
178 CGContextTranslateCTM(bmContext,
181 CGContextDrawImage(bmContext, CGRectMake(0, 0, width, height), imgRef);
183 CGImageRef rotatedImage = CGBitmapContextCreateImage(bmContext);
184 CFRelease(bmContext);
185 [(id)rotatedImage autorelease];
190 - (void)takePicture:(NSTimer*)theTimer {
191 CGImageRef capture = UIGetScreenImage();
192 CGRect cropRect = [overlayView cropRect];
194 // let's just give the decoder a vertical band right above the red line
195 cropRect.origin.x = cropRect.origin.x + (cropRect.size.width / 2) - (ONE_D_BAND_HEIGHT + 1);
196 cropRect.size.width = ONE_D_BAND_HEIGHT;
198 CGImageRef croppedImg = CGImageCreateWithImageInRect(capture, cropRect);
199 CGImageRelease(capture);
200 capture = [self CGImageRotated90:croppedImg];
201 capture = [self CGImageRotated180:capture];
202 // UIImageWriteToSavedPhotosAlbum([UIImage imageWithCGImage:capture], nil, nil, nil);
203 CGImageRelease(croppedImg);
204 cropRect.origin.x = 0.0;
205 cropRect.origin.y = 0.0;
206 cropRect.size.width = CGImageGetWidth(capture);
207 cropRect.size.height = CGImageGetHeight(capture);
210 UIImage *scrn = [UIImage imageWithCGImage:CGImageCreateWithImageInRect(capture, cropRect)];
211 Decoder *d = [[Decoder alloc] init];
213 cropRect.origin.x = 0.0;
214 cropRect.origin.y = 0.0;
215 [d decodeImage:scrn cropRect:cropRect];
218 // DecoderDelegate methods
220 - (void)decoder:(Decoder *)decoder willDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset{
222 NSLog(@"DecoderViewController MessageWhileDecodingWithDimensions: Decoding image (%.0fx%.0f) ...", image.size.width, image.size.height);
226 - (void)decoder:(Decoder *)decoder
227 decodingImage:(UIImage *)image
228 usingSubset:(UIImage *)subset
229 progress:(NSString *)message {
232 - (void)presentResultForString:(NSString *)resultString {
233 self.result = [ResultParser parsedResultForString:resultString];
235 if (beepSound != -1) {
236 AudioServicesPlaySystemSound(beepSound);
239 NSLog(@"result string = %@", resultString);
240 NSLog(@"result has %d actions", actions ? 0 : actions.count);
242 // [self updateToolbar];
245 - (void)presentResultPoints:(NSArray *)resultPoints
246 forImage:(UIImage *)image
247 usingSubset:(UIImage *)subset {
248 // simply add the points to the image view
249 [overlayView setPoints:resultPoints];
252 - (void)decoder:(Decoder *)decoder didDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset withResult:(TwoDDecoderResult *)twoDResult {
253 [self presentResultForString:[twoDResult text]];
254 [self presentResultPoints:[twoDResult points] forImage:image usingSubset:subset];
255 // now, in a selector, call the delegate to give this overlay time to show the points
256 [self performSelector:@selector(alertDelegate:) withObject:[[twoDResult text] copy] afterDelay:1.0];
257 decoder.delegate = nil;
261 - (void)alertDelegate:(id)text {
262 if (delegate != nil) {
263 [delegate scanResult:text];
267 - (void)decoder:(Decoder *)decoder failedToDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset reason:(NSString *)reason {
268 decoder.delegate = nil;
270 [overlayView setPoints:nil];
272 [self takePicture:nil];
274 //[self updateToolbar];