From 3e27ea3a5299e7f0caaf7b8cfda23839901326ab Mon Sep 17 00:00:00 2001 From: bitflower Date: Fri, 29 Aug 2014 09:10:44 +0200 Subject: [PATCH 1/7] Return image that was barcoded MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Return image that was barcoded as base64 string in the successCallback in the parameter „scanImage“ --- src/ios/CDVBarcodeScanner.mm | 40 ++++++++++++++++++++++++++++-------- www/barcodescanner.js | 10 ++++++++- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/ios/CDVBarcodeScanner.mm b/src/ios/CDVBarcodeScanner.mm index d57bfa1b4..8a5315aad 100644 --- a/src/ios/CDVBarcodeScanner.mm +++ b/src/ios/CDVBarcodeScanner.mm @@ -48,7 +48,7 @@ @interface CDVBarcodeScanner : CDVPlugin {} - (NSString*)isScanNotPossible; - (void)scan:(CDVInvokedUrlCommand*)command; - (void)encode:(CDVInvokedUrlCommand*)command; -- (void)returnSuccess:(NSString*)scannedText format:(NSString*)format cancelled:(BOOL)cancelled flipped:(BOOL)flipped callback:(NSString*)callback; +- (void)returnSuccess:(NSString*)scannedText format:(NSString*)format scanImage:(NSString*)imageBase64 cancelled:(BOOL)cancelled flipped:(BOOL)flipped callback:(NSString*)callback; - (void)returnError:(NSString*)message callback:(NSString*)callback; @end @@ -72,7 +72,7 @@ @interface CDVbcsProcessor : NSObject getText().c_str(); NSString* resultString = [[[NSString alloc] initWithCString:cString encoding:NSUTF8StringEncoding] autorelease]; - [self barcodeScanSucceeded:resultString format:format]; + //DEBUG: Dump succeeded image to Photos + //UIImage *image= [[self getImageFromSample:sampleBuffer] autorelease]; + //[self dumpImage: image]; + + // Write base64 to Javascript + UIImage *image= [[self getImageFromSample:sampleBuffer] autorelease]; + NSString *imageBase64 = @"data:image/jpeg;base64,"; + imageBase64 = [imageBase64 stringByAppendingString:[UIImagePNGRepresentation(image) base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]]; + + // DEVELOPMENT idea 1: Write base64 image directly to DOM element + //NSString *javascript = @"document.getElementById('camera').src = '"; + //javascript = [javascript stringByAppendingString:imageBase64]; + //javascript = [javascript stringByAppendingString:@"';"]; + //NSLog(@"Executing javascript: %@", javascript); + //[self.plugin.webView writeJavascript:javascript]; + //[self.plugin.webView performSelectorOnMainThread:@selector(stringByEvaluatingJavaScriptFromString:) withObject:javascript waitUntilDone:YES]; + + [self barcodeScanSucceeded:resultString format:format scanImage:imageBase64]; } catch (zxing::ReaderException &rex) { diff --git a/www/barcodescanner.js b/www/barcodescanner.js index a2a431d2a..fc7184fc2 100644 --- a/www/barcodescanner.js +++ b/www/barcodescanner.js @@ -70,7 +70,9 @@ * } * @param {Function} errorCallback */ - BarcodeScanner.prototype.scan = function (successCallback, errorCallback) { + BarcodeScanner.prototype.scan = function (successCallback, errorCallback) { + //DEVELOPMENT: videoStream + // BarcodeScanner.prototype.scan = function (successCallback, errorCallback, videoStreamCallback) { if (errorCallback == null) { errorCallback = function () { }; @@ -86,6 +88,12 @@ return; } + //DEVELOPMENT: videoStream + // if (typeof videoStreamCallback != "function") { + // console.log("BarcodeScanner.scan failure: video stream callback parameter must be a function"); + // return; + // } + exec(successCallback, errorCallback, 'BarcodeScanner', 'scan', []); }; From c3e2883b107859ee3a7fc11613f667fe607605f0 Mon Sep 17 00:00:00 2001 From: Matthias Max Date: Fri, 29 Aug 2014 09:13:48 +0200 Subject: [PATCH 2/7] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 2a777ba9a..948b1d459 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,11 @@ A full example could be: "Result: " + result.text + "\n" + "Format: " + result.format + "\n" + "Cancelled: " + result.cancelled); + + // Use image in which the code was recognized in DOM + var scanImage = document.querySelector("#scanImage"); + scanImage.setAttribute( 'src', result.scanImage ); + }, function (error) { alert("Scanning failed: " + error); From 0770de32cb6332f8b919cfe886e324ad94698d91 Mon Sep 17 00:00:00 2001 From: bitflower Date: Fri, 29 Aug 2014 16:50:01 +0200 Subject: [PATCH 3/7] Fixed orientation in returned result image --- src/ios/CDVBarcodeScanner.mm | 18 +- .../CDVBarcodeScanner.mm.bakVersucheRotate.mm | 1269 +++++++++++++++++ 2 files changed, 1277 insertions(+), 10 deletions(-) create mode 100644 src/ios/CDVBarcodeScanner.mm.bakVersucheRotate.mm diff --git a/src/ios/CDVBarcodeScanner.mm b/src/ios/CDVBarcodeScanner.mm index 8a5315aad..066f03edb 100644 --- a/src/ios/CDVBarcodeScanner.mm +++ b/src/ios/CDVBarcodeScanner.mm @@ -339,8 +339,8 @@ - (NSString*)setUpCaptureSession { if (!device) return @"unable to obtain video capture device"; } - + AVCaptureDeviceInput* input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error]; if (!input) return @"unable to obtain video capture device input"; @@ -393,6 +393,11 @@ - (void)captureOutput:(AVCaptureOutput*)captureOutput didOutputSampleBuffer:(CMS if (!self.capturing) return; + // Important: set orientation of AVCaptureCOnnection to that of the device + if ([connection isVideoOrientationSupported]) { + [connection setVideoOrientation:[[UIDevice currentDevice] orientation]]; + } + #if USE_SHUTTER if (!self.viewController.shutterPressed) return; self.viewController.shutterPressed = NO; @@ -418,7 +423,6 @@ - (void)captureOutput:(AVCaptureOutput*)captureOutput didOutputSampleBuffer:(CMS //NSString *javascript = @"CanvasCamera.capture('');"; //[self.plugin.webView performSelectorOnMainThread:@selector(stringByEvaluatingJavaScriptFromString:) withObject:javascript waitUntilDone:YES]; - using namespace zxing; // LuminanceSource is pretty dumb; we have to give it a pointer to @@ -464,14 +468,6 @@ - (void)captureOutput:(AVCaptureOutput*)captureOutput didOutputSampleBuffer:(CMS NSString *imageBase64 = @"data:image/jpeg;base64,"; imageBase64 = [imageBase64 stringByAppendingString:[UIImagePNGRepresentation(image) base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]]; - // DEVELOPMENT idea 1: Write base64 image directly to DOM element - //NSString *javascript = @"document.getElementById('camera').src = '"; - //javascript = [javascript stringByAppendingString:imageBase64]; - //javascript = [javascript stringByAppendingString:@"';"]; - //NSLog(@"Executing javascript: %@", javascript); - //[self.plugin.webView writeJavascript:javascript]; - //[self.plugin.webView performSelectorOnMainThread:@selector(stringByEvaluatingJavaScriptFromString:) withObject:javascript waitUntilDone:YES]; - [self barcodeScanSucceeded:resultString format:format scanImage:imageBase64]; } @@ -601,6 +597,7 @@ - (UIImage*) getImageFromLuminanceSource:(zxing::LuminanceSource*)luminanceSourc // for debugging //-------------------------------------------------------------------------- - (UIImage*)getImageFromSample:(CMSampleBufferRef)sampleBuffer { + CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); CVPixelBufferLockBaseAddress(imageBuffer, 0); @@ -615,6 +612,7 @@ - (UIImage*)getImageFromSample:(CMSampleBufferRef)sampleBuffer { baseAddress = newBaseAddress; CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGContextRef context = CGBitmapContextCreate( baseAddress, width, height, 8, bytesPerRow, diff --git a/src/ios/CDVBarcodeScanner.mm.bakVersucheRotate.mm b/src/ios/CDVBarcodeScanner.mm.bakVersucheRotate.mm new file mode 100644 index 000000000..1968a841b --- /dev/null +++ b/src/ios/CDVBarcodeScanner.mm.bakVersucheRotate.mm @@ -0,0 +1,1269 @@ +/* + * PhoneGap is available under *either* the terms of the modified BSD license *or* the + * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. + * + * Copyright 2011 Matt Kane. All rights reserved. + * Copyright (c) 2011, IBM Corporation + */ + +#import +#import +#import +#import +// MaxM +#import + +//------------------------------------------------------------------------------ +// use the all-in-one version of zxing that we built +//------------------------------------------------------------------------------ +#import "zxing-all-in-one.h" + +#import + + +//------------------------------------------------------------------------------ +// Delegate to handle orientation functions +// +//------------------------------------------------------------------------------ +@protocol CDVBarcodeScannerOrientationDelegate + +- (NSUInteger)supportedInterfaceOrientations; +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation; +- (BOOL)shouldAutorotate; + +@end + +//CGFloat degreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;}; +//CGFloat radiansToDegrees(CGFloat radians) {return radians * 180/M_PI;}; +CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;}; + +@interface UIImage (RotationMethods) +- (UIImage *)imageRotatedByDegrees:(CGFloat)degrees; +@end + +@implementation UIImage (RotationMethods) + +- (UIImage *)imageRotatedByDegrees:(CGFloat)degrees +{ + // calculate the size of the rotated view's containing box for our drawing space + UIView *rotatedViewBox = [[UIView alloc] initWithFrame:CGRectMake(0,0,self.size.width, self.size.height)]; + CGAffineTransform t = CGAffineTransformMakeRotation(DegreesToRadians(degrees)); + rotatedViewBox.transform = t; + CGSize rotatedSize = rotatedViewBox.frame.size; + [rotatedViewBox release]; + + // Create the bitmap context + UIGraphicsBeginImageContext(rotatedSize); // NOT THREAD SAVE ! + CGContextRef bitmap = UIGraphicsGetCurrentContext(); + + // Move the origin to the middle of the image so we will rotate and scale around the center. + CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2); + + // // Rotate the image context + CGContextRotateCTM(bitmap, DegreesToRadians(degrees)); + + // Now, draw the rotated/scaled image into the context + CGContextScaleCTM(bitmap, 1.0, -1.0); + CGContextDrawImage(bitmap, CGRectMake(-self.size.width / 2, -self.size.height / 2, self.size.width, self.size.height), [self CGImage]); + + UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return newImage; + +} + +@end + +//------------------------------------------------------------------------------ +// Adds a shutter button to the UI, and changes the scan from continuous to +// only performing a scan when you click the shutter button. For testing. +//------------------------------------------------------------------------------ +#define USE_SHUTTER 0 + +//------------------------------------------------------------------------------ +@class CDVbcsProcessor; +@class CDVbcsViewController; + +//------------------------------------------------------------------------------ +// plugin class +//------------------------------------------------------------------------------ +@interface CDVBarcodeScanner : CDVPlugin {} +- (NSString*)isScanNotPossible; +- (void)scan:(CDVInvokedUrlCommand*)command; +- (void)encode:(CDVInvokedUrlCommand*)command; +- (void)returnSuccess:(NSString*)scannedText format:(NSString*)format scanImage:(NSString*)imageBase64 cancelled:(BOOL)cancelled flipped:(BOOL)flipped callback:(NSString*)callback; +- (void)returnError:(NSString*)message callback:(NSString*)callback; +@end + +//------------------------------------------------------------------------------ +// class that does the grunt work +//------------------------------------------------------------------------------ +@interface CDVbcsProcessor : NSObject {} +@property (nonatomic, retain) CDVBarcodeScanner* plugin; +@property (nonatomic, retain) NSString* callback; +@property (nonatomic, retain) UIViewController* parentViewController; +@property (nonatomic, retain) CDVbcsViewController* viewController; +@property (nonatomic, retain) AVCaptureSession* captureSession; +@property (nonatomic, retain) AVCaptureVideoPreviewLayer* previewLayer; +@property (nonatomic, retain) NSString* alternateXib; +@property (nonatomic) BOOL is1D; +@property (nonatomic) BOOL is2D; +@property (nonatomic) BOOL capturing; +@property (nonatomic) BOOL isFrontCamera; +@property (nonatomic) BOOL isFlipped; + + +- (id)initWithPlugin:(CDVBarcodeScanner*)plugin callback:(NSString*)callback parentViewController:(UIViewController*)parentViewController alterateOverlayXib:(NSString *)alternateXib; +- (void)scanBarcode; +- (void)barcodeScanSucceeded:(NSString*)text format:(NSString*)format scanImage:(NSString*)imageBase64; +- (void)barcodeScanFailed:(NSString*)message; +- (void)barcodeScanCancelled; +- (void)openDialog; +- (NSString*)setUpCaptureSession; +- (void)captureOutput:(AVCaptureOutput*)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection*)connection; +- (NSString*)formatStringFrom:(zxing::BarcodeFormat)format; +- (UIImage*)getImageFromSample:(CMSampleBufferRef)sampleBuffer; +- (zxing::Ref) getLuminanceSourceFromSample:(CMSampleBufferRef)sampleBuffer imageBytes:(uint8_t**)ptr; +- (UIImage*) getImageFromLuminanceSource:(zxing::LuminanceSource*)luminanceSource; +- (void)dumpImage:(UIImage*)image; +@end + +//------------------------------------------------------------------------------ +// view controller for the ui +//------------------------------------------------------------------------------ +@interface CDVbcsViewController : UIViewController {} +@property (nonatomic, retain) CDVbcsProcessor* processor; +@property (nonatomic, retain) NSString* alternateXib; +@property (nonatomic) BOOL shutterPressed; +@property (nonatomic, retain) IBOutlet UIView* overlayView; +// unsafe_unretained is equivalent to assign - used to prevent retain cycles in the property below +@property (nonatomic, unsafe_unretained) id orientationDelegate; + +- (id)initWithProcessor:(CDVbcsProcessor*)processor alternateOverlay:(NSString *)alternateXib; +- (void)startCapturing; +- (UIView*)buildOverlayView; +- (UIImage*)buildReticleImage; +- (void)shutterButtonPressed; +- (IBAction)cancelButtonPressed:(id)sender; + +@end + +//------------------------------------------------------------------------------ +// plugin class +//------------------------------------------------------------------------------ +@implementation CDVBarcodeScanner + +//-------------------------------------------------------------------------- +- (NSString*)isScanNotPossible { + NSString* result = nil; + + Class aClass = NSClassFromString(@"AVCaptureSession"); + if (aClass == nil) { + return @"AVFoundation Framework not available"; + } + + return result; +} + +//-------------------------------------------------------------------------- +- (void)scan:(CDVInvokedUrlCommand*)command { + CDVbcsProcessor* processor; + NSString* callback; + NSString* capabilityError; + + callback = command.callbackId; + + // We allow the user to define an alternate xib file for loading the overlay. + NSString *overlayXib = nil; + if ( [command.arguments count] >= 1 ) + { + overlayXib = [command.arguments objectAtIndex:0]; + } + + capabilityError = [self isScanNotPossible]; + if (capabilityError) { + [self returnError:capabilityError callback:callback]; + return; + } + + processor = [[CDVbcsProcessor alloc] + initWithPlugin:self + callback:callback + parentViewController:self.viewController + alterateOverlayXib:overlayXib + ]; + [processor retain]; + [processor retain]; + [processor retain]; + // queue [processor scanBarcode] to run on the event loop + [processor performSelector:@selector(scanBarcode) withObject:nil afterDelay:0]; +} + +//-------------------------------------------------------------------------- +- (void)encode:(CDVInvokedUrlCommand*)command { + [self returnError:@"encode function not supported" callback:command.callbackId]; +} + +//-------------------------------------------------------------------------- +- (void)returnSuccess:(NSString*)scannedText format:(NSString*)format scanImage:(NSString*)imageBase64 cancelled:(BOOL)cancelled flipped:(BOOL)flipped callback:(NSString*)callback{ + NSNumber* cancelledNumber = [NSNumber numberWithInt:(cancelled?1:0)]; + + NSMutableDictionary* resultDict = [[[NSMutableDictionary alloc] init] autorelease]; + [resultDict setObject:scannedText forKey:@"text"]; + [resultDict setObject:format forKey:@"format"]; + [resultDict setObject:imageBase64 forKey:@"scanImage"]; + [resultDict setObject:cancelledNumber forKey:@"cancelled"]; + + CDVPluginResult* result = [CDVPluginResult + resultWithStatus: CDVCommandStatus_OK + messageAsDictionary: resultDict + ]; + + NSString* js = [result toSuccessCallbackString:callback]; + if (!flipped) { + [self writeJavascript:js]; + } +} + +//-------------------------------------------------------------------------- +- (void)returnError:(NSString*)message callback:(NSString*)callback { + CDVPluginResult* result = [CDVPluginResult + resultWithStatus: CDVCommandStatus_OK + messageAsString: message + ]; + + NSString* js = [result toErrorCallbackString:callback]; + + [self writeJavascript:js]; +} + +@end + +//------------------------------------------------------------------------------ +// class that does the grunt work +//------------------------------------------------------------------------------ +@implementation CDVbcsProcessor + +@synthesize plugin = _plugin; +@synthesize callback = _callback; +@synthesize parentViewController = _parentViewController; +@synthesize viewController = _viewController; +@synthesize captureSession = _captureSession; +@synthesize previewLayer = _previewLayer; +@synthesize alternateXib = _alternateXib; +@synthesize is1D = _is1D; +@synthesize is2D = _is2D; +@synthesize capturing = _capturing; + +//-------------------------------------------------------------------------- +- (id)initWithPlugin:(CDVBarcodeScanner*)plugin + callback:(NSString*)callback +parentViewController:(UIViewController*)parentViewController + alterateOverlayXib:(NSString *)alternateXib { + self = [super init]; + if (!self) return self; + + self.plugin = plugin; + self.callback = callback; + self.parentViewController = parentViewController; + self.alternateXib = alternateXib; + + self.is1D = YES; + self.is2D = YES; + self.capturing = NO; + + return self; +} + +//-------------------------------------------------------------------------- +- (void)dealloc { + self.plugin = nil; + self.callback = nil; + self.parentViewController = nil; + self.viewController = nil; + self.captureSession = nil; + self.previewLayer = nil; + self.alternateXib = nil; + + self.capturing = NO; + + [super dealloc]; +} + +//-------------------------------------------------------------------------- +- (void)scanBarcode { + +// self.captureSession = nil; +// self.previewLayer = nil; + NSString* errorMessage = [self setUpCaptureSession]; + if (errorMessage) { + [self barcodeScanFailed:errorMessage]; + return; + } + + self.viewController = [[[CDVbcsViewController alloc] initWithProcessor: self alternateOverlay:self.alternateXib] autorelease]; + // here we set the orientation delegate to the MainViewController of the app (orientation controlled in the Project Settings) + self.viewController.orientationDelegate = self.plugin.viewController; + + // delayed [self openDialog]; + [self performSelector:@selector(openDialog) withObject:nil afterDelay:1]; +} + +//-------------------------------------------------------------------------- +- (void)openDialog { + [self.parentViewController + presentModalViewController:self.viewController + animated:YES + ]; +} + +//-------------------------------------------------------------------------- +- (void)barcodeScanDone { + self.capturing = NO; + [self.captureSession stopRunning]; + [self.parentViewController dismissModalViewControllerAnimated: YES]; + + // viewcontroller holding onto a reference to us, release them so they + // will release us + self.viewController = nil; + + // delayed [self release]; + [self performSelector:@selector(release) withObject:nil afterDelay:1]; +} + +//-------------------------------------------------------------------------- +- (void)barcodeScanSucceeded:(NSString*)text format:(NSString*)format scanImage:(NSString*)imageBase64 { + [self barcodeScanDone]; + [self.plugin returnSuccess:text format:format scanImage:imageBase64 cancelled:FALSE flipped:FALSE callback:self.callback]; +} + +//-------------------------------------------------------------------------- +- (void)barcodeScanFailed:(NSString*)message { + [self barcodeScanDone]; + [self.plugin returnError:message callback:self.callback]; +} + +//-------------------------------------------------------------------------- +- (void)barcodeScanCancelled { + [self barcodeScanDone]; + [self.plugin returnSuccess:@"" format:@"" scanImage:@"" cancelled:TRUE flipped:self.isFlipped callback:self.callback]; + if (self.isFlipped) { + self.isFlipped = NO; + } +} + + +- (void)flipCamera +{ + self.isFlipped = YES; + self.isFrontCamera = !self.isFrontCamera; + [self performSelector:@selector(barcodeScanCancelled) withObject:nil afterDelay:0]; + [self performSelector:@selector(scanBarcode) withObject:nil afterDelay:0.1]; +} + +//-------------------------------------------------------------------------- +- (NSString*)setUpCaptureSession { + NSError* error = nil; + + AVCaptureSession* captureSession = [[[AVCaptureSession alloc] init] autorelease]; + self.captureSession = captureSession; + + AVCaptureDevice* __block device = nil; + if (self.isFrontCamera) { + + NSArray* devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; + [devices enumerateObjectsUsingBlock:^(AVCaptureDevice *obj, NSUInteger idx, BOOL *stop) { + if (obj.position == AVCaptureDevicePositionFront) { + device = obj; + } + }]; + } else { + device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; + if (!device) return @"unable to obtain video capture device"; + + } + + + AVCaptureDeviceInput* input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error]; + if (!input) return @"unable to obtain video capture device input"; + + AVCaptureVideoDataOutput* output = [[[AVCaptureVideoDataOutput alloc] init] autorelease]; + if (!output) return @"unable to obtain video capture output"; + + NSDictionary* videoOutputSettings = [NSDictionary + dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA] + forKey:(id)kCVPixelBufferPixelFormatTypeKey + ]; + + output.alwaysDiscardsLateVideoFrames = YES; + output.videoSettings = videoOutputSettings; + + [output setSampleBufferDelegate:self queue:dispatch_get_main_queue()]; + + if (![captureSession canSetSessionPreset:AVCaptureSessionPresetMedium]) { + return @"unable to preset medium quality video capture"; + } + + captureSession.sessionPreset = AVCaptureSessionPresetMedium; + + if ([captureSession canAddInput:input]) { + [captureSession addInput:input]; + } + else { + return @"unable to add video capture device input to session"; + } + + if ([captureSession canAddOutput:output]) { + [captureSession addOutput:output]; + } + else { + return @"unable to add video capture output to session"; + } + + // setup capture preview layer + self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession]; + + // run on next event loop pass [captureSession startRunning] + [captureSession performSelector:@selector(startRunning) withObject:nil afterDelay:0]; + + return nil; +} + +//-------------------------------------------------------------------------- +// this method gets sent the captured frames +//-------------------------------------------------------------------------- +- (void)captureOutput:(AVCaptureOutput*)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection*)connection { + + if (!self.capturing) return; + +#if USE_SHUTTER + if (!self.viewController.shutterPressed) return; + self.viewController.shutterPressed = NO; + + UIView* flashView = [[[UIView alloc] initWithFrame:self.viewController.view.frame] autorelease]; + [flashView setBackgroundColor:[UIColor whiteColor]]; + [self.viewController.view.window addSubview:flashView]; + + [UIView + animateWithDuration:.4f + animations:^{ + [flashView setAlpha:0.f]; + } + completion:^(BOOL finished){ + [flashView removeFromSuperview]; + } + ]; + + // [self dumpImage: [[self getImageFromSample:sampleBuffer] autorelease]]; +#endif + + // Canvas Part: Outputs the image stream to Javascript + //NSString *javascript = @"CanvasCamera.capture('');"; + //[self.plugin.webView performSelectorOnMainThread:@selector(stringByEvaluatingJavaScriptFromString:) withObject:javascript waitUntilDone:YES]; + + + using namespace zxing; + + // LuminanceSource is pretty dumb; we have to give it a pointer to + // a byte array, but then can't get it back out again. We need to + // get it back to free it. Saving it in imageBytes. + uint8_t* imageBytes; + + // NSTimeInterval timeStart = [NSDate timeIntervalSinceReferenceDate]; + + try { + DecodeHints decodeHints; + decodeHints.addFormat(BarcodeFormat_QR_CODE); + decodeHints.addFormat(BarcodeFormat_DATA_MATRIX); + decodeHints.addFormat(BarcodeFormat_UPC_E); + decodeHints.addFormat(BarcodeFormat_UPC_A); + decodeHints.addFormat(BarcodeFormat_EAN_8); + decodeHints.addFormat(BarcodeFormat_EAN_13); + decodeHints.addFormat(BarcodeFormat_CODE_128); + decodeHints.addFormat(BarcodeFormat_CODE_39); + decodeHints.addFormat(BarcodeFormat_ITF); + + // here's the meat of the decode process + Ref luminanceSource ([self getLuminanceSourceFromSample: sampleBuffer imageBytes:&imageBytes]); + // [self dumpImage: [[self getImageFromLuminanceSource:luminanceSource] autorelease]]; + Ref binarizer (new HybridBinarizer(luminanceSource)); + Ref bitmap (new BinaryBitmap(binarizer)); + Ref reader (new MultiFormatReader()); + Ref result (reader->decode(bitmap, decodeHints)); + Ref resultText (result->getText()); + BarcodeFormat formatVal = result->getBarcodeFormat(); + NSString* format = [self formatStringFrom:formatVal]; + + + const char* cString = resultText->getText().c_str(); + NSString* resultString = [[[NSString alloc] initWithCString:cString encoding:NSUTF8StringEncoding] autorelease]; + + //DEBUG: Dump succeeded image to Photos + UIImage *imageDump = [[self getImageFromSample:sampleBuffer orientation:UIInterfaceOrientationPortrait] autorelease]; + [self dumpImage: imageDump]; + + NSLog(@"Executing javascript: %@", captureOutput.connections[0]); + + // Write base64 to Javascript + UIImage *image= [[self getImageFromSample:sampleBuffer orientation:UIInterfaceOrientationPortrait] autorelease]; + NSString *imageBase64 = @"data:image/jpeg;base64,"; + imageBase64 = [imageBase64 stringByAppendingString:[UIImagePNGRepresentation(image) base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]]; + + // DEVELOPMENT idea 1: Write base64 image directly to DOM element + //NSString *javascript = @"document.getElementById('camera').src = '"; + //javascript = [javascript stringByAppendingString:imageBase64]; + //javascript = [javascript stringByAppendingString:@"';"]; + //NSLog(@"Executing javascript: %@", javascript); + //[self.plugin.webView writeJavascript:javascript]; + //[self.plugin.webView performSelectorOnMainThread:@selector(stringByEvaluatingJavaScriptFromString:) withObject:javascript waitUntilDone:YES]; + + [self barcodeScanSucceeded:resultString format:format scanImage:imageBase64]; + + } + catch (zxing::ReaderException &rex) { + // NSString *message = [[[NSString alloc] initWithCString:rex.what() encoding:NSUTF8StringEncoding] autorelease]; + // NSLog(@"decoding: ReaderException: %@", message); + } + catch (zxing::IllegalArgumentException &iex) { + // NSString *message = [[[NSString alloc] initWithCString:iex.what() encoding:NSUTF8StringEncoding] autorelease]; + // NSLog(@"decoding: IllegalArgumentException: %@", message); + } + catch (...) { + // NSLog(@"decoding: unknown exception"); + // [self barcodeScanFailed:@"unknown exception decoding barcode"]; + } + + // NSTimeInterval timeElapsed = [NSDate timeIntervalSinceReferenceDate] - timeStart; + // NSLog(@"decoding completed in %dms", (int) (timeElapsed * 1000)); + + // free the buffer behind the LuminanceSource + if (imageBytes) { + free(imageBytes); + } +} + +//-------------------------------------------------------------------------- +// convert barcode format to string +//-------------------------------------------------------------------------- +- (NSString*)formatStringFrom:(zxing::BarcodeFormat)format { + if (format == zxing::BarcodeFormat_QR_CODE) return @"QR_CODE"; + if (format == zxing::BarcodeFormat_DATA_MATRIX) return @"DATA_MATRIX"; + if (format == zxing::BarcodeFormat_UPC_E) return @"UPC_E"; + if (format == zxing::BarcodeFormat_UPC_A) return @"UPC_A"; + if (format == zxing::BarcodeFormat_EAN_8) return @"EAN_8"; + if (format == zxing::BarcodeFormat_EAN_13) return @"EAN_13"; + if (format == zxing::BarcodeFormat_CODE_128) return @"CODE_128"; + if (format == zxing::BarcodeFormat_CODE_39) return @"CODE_39"; + if (format == zxing::BarcodeFormat_ITF) return @"ITF"; + return @"???"; +} + +//-------------------------------------------------------------------------- +// convert capture's sample buffer (scanned picture) into the thing that +// zxing needs. +//-------------------------------------------------------------------------- +- (zxing::Ref) getLuminanceSourceFromSample:(CMSampleBufferRef)sampleBuffer imageBytes:(uint8_t**)ptr { + CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); + CVPixelBufferLockBaseAddress(imageBuffer, 0); + + size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); + size_t width = CVPixelBufferGetWidth(imageBuffer); + size_t height = CVPixelBufferGetHeight(imageBuffer); + uint8_t* baseAddress = (uint8_t*) CVPixelBufferGetBaseAddress(imageBuffer); + + // only going to get 90% of the min(width,height) of the captured image + size_t greyWidth = 9 * MIN(width, height) / 10; + uint8_t* greyData = (uint8_t*) malloc(greyWidth * greyWidth); + + // remember this pointer so we can free it later + *ptr = greyData; + + if (!greyData) { + CVPixelBufferUnlockBaseAddress(imageBuffer,0); + throw new zxing::ReaderException("out of memory"); + } + + size_t offsetX = (width - greyWidth) / 2; + size_t offsetY = (height - greyWidth) / 2; + + // pixel-by-pixel ... + for (size_t i=0; i luminanceSource ( + new GreyscaleLuminanceSource(greyData, greyWidth, greyWidth, 0, 0, greyWidth, greyWidth) + ); + + return luminanceSource; +} + +//-------------------------------------------------------------------------- +// for debugging +//-------------------------------------------------------------------------- +- (UIImage*) getImageFromLuminanceSource:(zxing::LuminanceSource*)luminanceSource { + unsigned char* bytes = luminanceSource->getMatrix(); + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); + CGContextRef context = CGBitmapContextCreate( + bytes, + luminanceSource->getWidth(), luminanceSource->getHeight(), 8, luminanceSource->getWidth(), + colorSpace, + kCGImageAlphaNone + ); + + CGImageRef cgImage = CGBitmapContextCreateImage(context); + UIImage* image = [[UIImage alloc] initWithCGImage:cgImage]; + + CGContextRelease(context); + CGColorSpaceRelease(colorSpace); + CGImageRelease(cgImage); + free(bytes); + + return image; +} + +//-------------------------------------------------------------------------- +// for debugging +//-------------------------------------------------------------------------- +- (UIImage*)getImageFromSample:(CMSampleBufferRef)sampleBuffer { + UIInterfaceOrientation initialOrientation = UIInterfaceOrientationPortrait; + + return [self getImageFromSample:sampleBuffer orientation:initialOrientation]; +} +#define radians(degrees) (degrees * M_PI/180) +- (UIImage*)getImageFromSample:(CMSampleBufferRef)sampleBuffer orientation:(UIInterfaceOrientation)orientation { + //UIInterfaceOrientation initialOrientation = UIInterfaceOrientationPortrait; + + // Rotate sampleBuffer if necessary + //sampleBuffer = [self rotateBuffer:sampleBuffer]; + + // 2. Rotate Buffer Versuch + //sampleBuffer = [self rotateBuffer:sampleBuffer]; + + // Bringt zwar Hochkant Bild aber mit Seltsamen Pixeln + //UIImage* imageTest = [self imageFromSampleBuffer:sampleBuffer]; + + + CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); + CVPixelBufferLockBaseAddress(imageBuffer, 0); + + size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); + size_t width = CVPixelBufferGetWidth(imageBuffer); + size_t height = CVPixelBufferGetHeight(imageBuffer); + + uint8_t* baseAddress = (uint8_t*) CVPixelBufferGetBaseAddress(imageBuffer); + int length = height * bytesPerRow; + uint8_t* newBaseAddress = (uint8_t*) malloc(length); + memcpy(newBaseAddress, baseAddress, length); + baseAddress = newBaseAddress; + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + + CGContextRef context = CGBitmapContextCreate( + baseAddress, + width, height, 8, bytesPerRow, + colorSpace, + kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst + ); + + + // Rotate context + //CGContextRotateCTM (context, radians(90)); + //CGContextTranslateCTM (context, 0, -height); + + CGImageRef cgImage = CGBitmapContextCreateImage(context); + //CGContextDrawImage(context, CGRectMake(0, 0, height, width), cgImage); + //CGImageRef ref = CGBitmapContextCreateImage(context); + //UIImage* image = [UIImage imageWithCGImage:cgImage]; + + + + // Apple method extracted + // Move the origin to the middle of the image so we will rotate and scale around the center. + //CGContextTranslateCTM(context, height/2, width/2); // rotatedSize.width/2, rotatedSize.height/2 + + // // Rotate the image context + //CGContextRotateCTM(context, DegreesToRadians(90.0)); + + // Now, draw the rotated/scaled image into the context + //CGContextScaleCTM(context, 1.0, -1.0); + + //CGImageRef cgImage = CGBitmapContextCreateImage(context); + + //CGContextDrawImage(context, CGRectMake(-width / 2, -height / 2, width, height), cgImage); + + + + + + + + + //CGImageRef cgImage = CGBitmapContextCreateImage(context); + UIImage* image = [[UIImage alloc] initWithCGImage:cgImage]; + + + // Dreht!!! und ARC Probleme + //image = [self scaleAndRotateImage:image]; + + + + + + + + + // Apple method + //image = [image imageRotatedByDegrees:90.0]; + + //UIImage* image = [[UIImage alloc] imageWithCGImage:cgImage scale:1.0 orientation:UIImageOrientationRight]; + //UIImage * image = [[UIImage imageWithCGImage:cgImage scale:1.0 orientation:UIImageOrientationUp] retain]; + + CVPixelBufferUnlockBaseAddress(imageBuffer,0); + CGContextRelease(context); + CGColorSpaceRelease(colorSpace); + CGImageRelease(cgImage); + + free(baseAddress); + + return image; +} + + + +- (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer +{ + CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); + CVPixelBufferLockBaseAddress(imageBuffer, 0); + + void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer); + + size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); + size_t width = CVPixelBufferGetWidth(imageBuffer); + size_t height = CVPixelBufferGetHeight(imageBuffer); + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + + CGContextRef context = CGBitmapContextCreate(baseAddress, height, width, 8, + bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); + + CGImageRef quartzImage = CGBitmapContextCreateImage(context); + CVPixelBufferUnlockBaseAddress(imageBuffer,0); + + CGContextRelease(context); + CGColorSpaceRelease(colorSpace); + + // From SO + //UIImage *image = [UIImage imageWithCGImage:quartzImage]; + + int frontCameraImageOrientation = UIImageOrientationLeftMirrored; + //int backCameraImageOrientation = UIImageOrientationRight; + + UIImage *image = [[UIImage alloc] initWithCGImage:quartzImage scale:(CGFloat)1.0 orientation:frontCameraImageOrientation]; + + CGImageRelease(quartzImage); + + return (image); +} + + + +- (UIImage *)scaleAndRotateImage:(UIImage *)image +{ + int kMaxResolution = 640; + + CGImageRef imgRef = image.CGImage; + + CGFloat width = CGImageGetWidth(imgRef); + CGFloat height = CGImageGetHeight(imgRef); + + CGAffineTransform transform = CGAffineTransformIdentity; + CGRect bounds = CGRectMake(0, 0, width, height); + if (width > kMaxResolution || height > kMaxResolution) { + CGFloat ratio = width/height; + if (ratio > 1) { + bounds.size.width = kMaxResolution; + bounds.size.height = bounds.size.width / ratio; + } + else { + bounds.size.height = kMaxResolution; + bounds.size.width = bounds.size.height * ratio; + } + } + + CGFloat scaleRatio = bounds.size.width / width; + CGSize imageSize = CGSizeMake(CGImageGetWidth(imgRef), CGImageGetHeight(imgRef)); + CGFloat boundHeight; + UIImageOrientation orient = image.imageOrientation; + orient = UIImageOrientationRight; + switch(orient) { + + case UIImageOrientationUp: //EXIF = 1 + transform = CGAffineTransformIdentity; + break; + + case UIImageOrientationUpMirrored: //EXIF = 2 + transform = CGAffineTransformMakeTranslation(imageSize.width, 0.0); + transform = CGAffineTransformScale(transform, -1.0, 1.0); + break; + + case UIImageOrientationDown: //EXIF = 3 + transform = CGAffineTransformMakeTranslation(imageSize.width, imageSize.height); + transform = CGAffineTransformRotate(transform, M_PI); + break; + + case UIImageOrientationDownMirrored: //EXIF = 4 + transform = CGAffineTransformMakeTranslation(0.0, imageSize.height); + transform = CGAffineTransformScale(transform, 1.0, -1.0); + break; + + case UIImageOrientationLeftMirrored: //EXIF = 5 + boundHeight = bounds.size.height; + bounds.size.height = bounds.size.width; + bounds.size.width = boundHeight; + transform = CGAffineTransformMakeTranslation(imageSize.height, imageSize.width); + transform = CGAffineTransformScale(transform, -1.0, 1.0); + transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0); + break; + + case UIImageOrientationLeft: //EXIF = 6 + boundHeight = bounds.size.height; + bounds.size.height = bounds.size.width; + bounds.size.width = boundHeight; + transform = CGAffineTransformMakeTranslation(0.0, imageSize.width); + transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0); + break; + + case UIImageOrientationRightMirrored: //EXIF = 7 + boundHeight = bounds.size.height; + bounds.size.height = bounds.size.width; + bounds.size.width = boundHeight; + transform = CGAffineTransformMakeScale(-1.0, 1.0); + transform = CGAffineTransformRotate(transform, M_PI / 2.0); + break; + + case UIImageOrientationRight: //EXIF = 8 + boundHeight = bounds.size.height; + bounds.size.height = bounds.size.width; + bounds.size.width = boundHeight; + transform = CGAffineTransformMakeTranslation(imageSize.height, 0.0); + transform = CGAffineTransformRotate(transform, M_PI / 2.0); + break; + + default: + [NSException raise:NSInternalInconsistencyException format:@"Invalid image orientation"]; + + } + + UIGraphicsBeginImageContext(bounds.size); + + CGContextRef context = UIGraphicsGetCurrentContext(); + + if (orient == UIImageOrientationRight || orient == UIImageOrientationLeft) { + CGContextScaleCTM(context, -scaleRatio, scaleRatio); + CGContextTranslateCTM(context, -height, 0); + } + else { + CGContextScaleCTM(context, scaleRatio, -scaleRatio); + CGContextTranslateCTM(context, 0, -height); + } + + CGContextConcatCTM(context, transform); + + CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, width, height), imgRef); + UIImage *imageCopy = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return imageCopy; +} + + + + +- (unsigned char*) rotateBuffer: (CMSampleBufferRef) sampleBuffer +{ + CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); + CVPixelBufferLockBaseAddress(imageBuffer,0); + + size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); + size_t width = CVPixelBufferGetWidth(imageBuffer); + size_t height = CVPixelBufferGetHeight(imageBuffer); + size_t currSize = bytesPerRow*height*sizeof(unsigned char); + size_t bytesPerRowOut = 4*height*sizeof(unsigned char); + + void *srcBuff = CVPixelBufferGetBaseAddress(imageBuffer); + + /* + * rotationConstant: 0 -- rotate 0 degrees (simply copy the data from src to dest) + * 1 -- rotate 90 degrees counterclockwise + * 2 -- rotate 180 degress + * 3 -- rotate 270 degrees counterclockwise + */ + uint8_t rotationConstant = 1; + + unsigned char *outBuff = (unsigned char*)malloc(currSize); + + vImage_Buffer ibuff = { srcBuff, height, width, bytesPerRow}; + vImage_Buffer ubuff = { outBuff, width, height, bytesPerRowOut}; + + vImage_Error err = vImageRotate90_ARGB8888 (&ibuff, &ubuff, rotationConstant, NULL, 0); + if (err != kvImageNoError) NSLog(@"%ld", err); + + return outBuff; +} + + + +- (CVPixelBufferRef) rotateBufferCVPIxel: (CMSampleBufferRef) sampleBuffer +{ + CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); + CVPixelBufferLockBaseAddress(imageBuffer,0); + + size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); + size_t width = CVPixelBufferGetWidth(imageBuffer); + size_t height = CVPixelBufferGetHeight(imageBuffer); + + void *src_buff = CVPixelBufferGetBaseAddress(imageBuffer); + + NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey, + [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey, + nil]; + + CVPixelBufferRef pxbuffer = NULL; + //CVReturn status = CVPixelBufferPoolCreatePixelBuffer (NULL, _pixelWriter.pixelBufferPool, &pxbuffer); + CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, width, + height, kCVPixelFormatType_32BGRA, (CFDictionaryRef) options, + &pxbuffer); + + NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL); + + CVPixelBufferLockBaseAddress(pxbuffer, 0); + void *dest_buff = CVPixelBufferGetBaseAddress(pxbuffer); + NSParameterAssert(dest_buff != NULL); + + int *src = (int*) src_buff ; + int *dest= (int*) dest_buff ; + size_t count = (bytesPerRow * height) / 4 ; + while (count--) { + *dest++ = *src++; + } + + //Test straight copy. + //memcpy(pxdata, baseAddress, width * height * 4) ; + CVPixelBufferUnlockBaseAddress(pxbuffer, 0); + CVPixelBufferUnlockBaseAddress(imageBuffer, 0); + return pxbuffer; +} + +//-------------------------------------------------------------------------- +// for debugging +//-------------------------------------------------------------------------- +- (void)dumpImage:(UIImage*)image { + NSLog(@"writing image to library: %dx%d", (int)image.size.width, (int)image.size.height); + ALAssetsLibrary* assetsLibrary = [[[ALAssetsLibrary alloc] init] autorelease]; + [assetsLibrary + writeImageToSavedPhotosAlbum:image.CGImage + orientation:ALAssetOrientationUp + completionBlock:^(NSURL* assetURL, NSError* error){ + if (error) NSLog(@" error writing image to library"); + else NSLog(@" wrote image to library %@", assetURL); + } + ]; +} + +@end + +//------------------------------------------------------------------------------ +// view controller for the ui +//------------------------------------------------------------------------------ +@implementation CDVbcsViewController +@synthesize processor = _processor; +@synthesize shutterPressed = _shutterPressed; +@synthesize alternateXib = _alternateXib; +@synthesize overlayView = _overlayView; + +//-------------------------------------------------------------------------- +- (id)initWithProcessor:(CDVbcsProcessor*)processor alternateOverlay:(NSString *)alternateXib { + self = [super init]; + if (!self) return self; + + self.processor = processor; + self.shutterPressed = NO; + self.alternateXib = alternateXib; + self.overlayView = nil; + return self; +} + +//-------------------------------------------------------------------------- +- (void)dealloc { + self.view = nil; +// self.processor = nil; + self.shutterPressed = NO; + self.alternateXib = nil; + self.overlayView = nil; + [super dealloc]; +} + +//-------------------------------------------------------------------------- +- (void)loadView { + self.view = [[[UIView alloc] initWithFrame: self.processor.parentViewController.view.frame] autorelease]; + + // setup capture preview layer + AVCaptureVideoPreviewLayer* previewLayer = self.processor.previewLayer; + previewLayer.frame = self.view.bounds; + previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; + + if ([previewLayer isOrientationSupported]) { + [previewLayer setOrientation:AVCaptureVideoOrientationPortrait]; + } + + [self.view.layer insertSublayer:previewLayer below:[[self.view.layer sublayers] objectAtIndex:0]]; + + [self.view addSubview:[self buildOverlayView]]; +} + +//-------------------------------------------------------------------------- +- (void)viewWillAppear:(BOOL)animated { + + // set video orientation to what the camera sees + self.processor.previewLayer.orientation = [[UIApplication sharedApplication] statusBarOrientation]; + + // this fixes the bug when the statusbar is landscape, and the preview layer + // starts up in portrait (not filling the whole view) + self.processor.previewLayer.frame = self.view.bounds; +} + +//-------------------------------------------------------------------------- +- (void)viewDidAppear:(BOOL)animated { + [self startCapturing]; + + [super viewDidAppear:animated]; +} + +//-------------------------------------------------------------------------- +- (void)startCapturing { + self.processor.capturing = YES; +} + +//-------------------------------------------------------------------------- +- (void)shutterButtonPressed { + self.shutterPressed = YES; +} + +//-------------------------------------------------------------------------- +- (IBAction)cancelButtonPressed:(id)sender { + [self.processor performSelector:@selector(barcodeScanCancelled) withObject:nil afterDelay:0]; +} + +- (void)flipCameraButtonPressed:(id)sender +{ + [self.processor performSelector:@selector(flipCamera) withObject:nil afterDelay:0]; +} + +//-------------------------------------------------------------------------- +- (UIView *)buildOverlayViewFromXib +{ + [[NSBundle mainBundle] loadNibNamed:self.alternateXib owner:self options:NULL]; + + if ( self.overlayView == nil ) + { + NSLog(@"%@", @"An error occurred loading the overlay xib. It appears that the overlayView outlet is not set."); + return nil; + } + + return self.overlayView; +} + +//-------------------------------------------------------------------------- +- (UIView*)buildOverlayView { + + if ( nil != self.alternateXib ) + { + return [self buildOverlayViewFromXib]; + } + CGRect bounds = self.view.bounds; + bounds = CGRectMake(0, 0, bounds.size.width, bounds.size.height); + + UIView* overlayView = [[[UIView alloc] initWithFrame:bounds] autorelease]; + overlayView.autoresizesSubviews = YES; + overlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + overlayView.opaque = NO; + + UIToolbar* toolbar = [[[UIToolbar alloc] init] autorelease]; + toolbar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin; + + id cancelButton = [[[UIBarButtonItem alloc] autorelease] + initWithBarButtonSystemItem:UIBarButtonSystemItemCancel + target:(id)self + action:@selector(cancelButtonPressed:) + ]; + + + id flexSpace = [[[UIBarButtonItem alloc] autorelease] + initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace + target:nil + action:nil + ]; + + id flipCamera = [[[UIBarButtonItem alloc] autorelease] + initWithBarButtonSystemItem:UIBarButtonSystemItemCamera + target:(id)self + action:@selector(flipCameraButtonPressed:) + ]; + + +#if USE_SHUTTER + id shutterButton = [[UIBarButtonItem alloc] + initWithBarButtonSystemItem:UIBarButtonSystemItemCamera + target:(id)self + action:@selector(shutterButtonPressed) + ]; + + toolbar.items = [NSArray arrayWithObjects:flexSpace,cancelButton,flexSpace, flipCamera ,shutterButton,nil]; +#else + toolbar.items = [NSArray arrayWithObjects:flexSpace,cancelButton,flexSpace, flipCamera,nil]; +#endif + bounds = overlayView.bounds; + + [toolbar sizeToFit]; + CGFloat toolbarHeight = [toolbar frame].size.height; + CGFloat rootViewHeight = CGRectGetHeight(bounds); + CGFloat rootViewWidth = CGRectGetWidth(bounds); + CGRect rectArea = CGRectMake(0, rootViewHeight - toolbarHeight, rootViewWidth, toolbarHeight); + [toolbar setFrame:rectArea]; + + [overlayView addSubview: toolbar]; + + UIImage* reticleImage = [self buildReticleImage]; + UIView* reticleView = [[[UIImageView alloc] initWithImage: reticleImage] autorelease]; + CGFloat minAxis = MIN(rootViewHeight, rootViewWidth); + + rectArea = CGRectMake( + 0.5 * (rootViewWidth - minAxis), + 0.5 * (rootViewHeight - minAxis), + minAxis, + minAxis + ); + + [reticleView setFrame:rectArea]; + + reticleView.opaque = NO; + reticleView.contentMode = UIViewContentModeScaleAspectFit; + reticleView.autoresizingMask = 0 + | UIViewAutoresizingFlexibleLeftMargin + | UIViewAutoresizingFlexibleRightMargin + | UIViewAutoresizingFlexibleTopMargin + | UIViewAutoresizingFlexibleBottomMargin + ; + + [overlayView addSubview: reticleView]; + + return overlayView; +} + +//-------------------------------------------------------------------------- + +#define RETICLE_SIZE 500.0f +#define RETICLE_WIDTH 10.0f +#define RETICLE_OFFSET 60.0f +#define RETICLE_ALPHA 0.4f + +//------------------------------------------------------------------------- +// builds the green box and red line +//------------------------------------------------------------------------- +- (UIImage*)buildReticleImage { + UIImage* result; + UIGraphicsBeginImageContext(CGSizeMake(RETICLE_SIZE, RETICLE_SIZE)); + CGContextRef context = UIGraphicsGetCurrentContext(); + + if (self.processor.is1D) { + UIColor* color = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:RETICLE_ALPHA]; + CGContextSetStrokeColorWithColor(context, color.CGColor); + CGContextSetLineWidth(context, RETICLE_WIDTH); + CGContextBeginPath(context); + CGFloat lineOffset = RETICLE_OFFSET+(0.5*RETICLE_WIDTH); + CGContextMoveToPoint(context, lineOffset, RETICLE_SIZE/2); + CGContextAddLineToPoint(context, RETICLE_SIZE-lineOffset, 0.5*RETICLE_SIZE); + CGContextStrokePath(context); + } + + if (self.processor.is2D) { + UIColor* color = [UIColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:RETICLE_ALPHA]; + CGContextSetStrokeColorWithColor(context, color.CGColor); + CGContextSetLineWidth(context, RETICLE_WIDTH); + CGContextStrokeRect(context, + CGRectMake( + RETICLE_OFFSET, + RETICLE_OFFSET, + RETICLE_SIZE-2*RETICLE_OFFSET, + RETICLE_SIZE-2*RETICLE_OFFSET + ) + ); + } + + result = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return result; +} + +#pragma mark CDVBarcodeScannerOrientationDelegate + +- (BOOL)shouldAutorotate +{ + return NO; +} + +- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation +{ + return UIInterfaceOrientationPortrait; +} + +- (NSUInteger)supportedInterfaceOrientations +{ + return UIInterfaceOrientationMaskPortrait; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotateToInterfaceOrientation:)]) { + return [self.orientationDelegate shouldAutorotateToInterfaceOrientation:interfaceOrientation]; + } + + return YES; +} + +- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration +{ + [CATransaction begin]; + + self.processor.previewLayer.orientation = orientation; + [self.processor.previewLayer layoutSublayers]; + self.processor.previewLayer.frame = self.view.bounds; + + [CATransaction commit]; + [super willAnimateRotationToInterfaceOrientation:orientation duration:duration]; +} + +@end From b18150fcb60cf0ef0553161316aa4f1d180907ed Mon Sep 17 00:00:00 2001 From: bitflower Date: Fri, 29 Aug 2014 16:51:32 +0200 Subject: [PATCH 4/7] Clean up --- .../CDVBarcodeScanner.mm.bakVersucheRotate.mm | 1269 ----------------- 1 file changed, 1269 deletions(-) delete mode 100644 src/ios/CDVBarcodeScanner.mm.bakVersucheRotate.mm diff --git a/src/ios/CDVBarcodeScanner.mm.bakVersucheRotate.mm b/src/ios/CDVBarcodeScanner.mm.bakVersucheRotate.mm deleted file mode 100644 index 1968a841b..000000000 --- a/src/ios/CDVBarcodeScanner.mm.bakVersucheRotate.mm +++ /dev/null @@ -1,1269 +0,0 @@ -/* - * PhoneGap is available under *either* the terms of the modified BSD license *or* the - * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. - * - * Copyright 2011 Matt Kane. All rights reserved. - * Copyright (c) 2011, IBM Corporation - */ - -#import -#import -#import -#import -// MaxM -#import - -//------------------------------------------------------------------------------ -// use the all-in-one version of zxing that we built -//------------------------------------------------------------------------------ -#import "zxing-all-in-one.h" - -#import - - -//------------------------------------------------------------------------------ -// Delegate to handle orientation functions -// -//------------------------------------------------------------------------------ -@protocol CDVBarcodeScannerOrientationDelegate - -- (NSUInteger)supportedInterfaceOrientations; -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation; -- (BOOL)shouldAutorotate; - -@end - -//CGFloat degreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;}; -//CGFloat radiansToDegrees(CGFloat radians) {return radians * 180/M_PI;}; -CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;}; - -@interface UIImage (RotationMethods) -- (UIImage *)imageRotatedByDegrees:(CGFloat)degrees; -@end - -@implementation UIImage (RotationMethods) - -- (UIImage *)imageRotatedByDegrees:(CGFloat)degrees -{ - // calculate the size of the rotated view's containing box for our drawing space - UIView *rotatedViewBox = [[UIView alloc] initWithFrame:CGRectMake(0,0,self.size.width, self.size.height)]; - CGAffineTransform t = CGAffineTransformMakeRotation(DegreesToRadians(degrees)); - rotatedViewBox.transform = t; - CGSize rotatedSize = rotatedViewBox.frame.size; - [rotatedViewBox release]; - - // Create the bitmap context - UIGraphicsBeginImageContext(rotatedSize); // NOT THREAD SAVE ! - CGContextRef bitmap = UIGraphicsGetCurrentContext(); - - // Move the origin to the middle of the image so we will rotate and scale around the center. - CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2); - - // // Rotate the image context - CGContextRotateCTM(bitmap, DegreesToRadians(degrees)); - - // Now, draw the rotated/scaled image into the context - CGContextScaleCTM(bitmap, 1.0, -1.0); - CGContextDrawImage(bitmap, CGRectMake(-self.size.width / 2, -self.size.height / 2, self.size.width, self.size.height), [self CGImage]); - - UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - return newImage; - -} - -@end - -//------------------------------------------------------------------------------ -// Adds a shutter button to the UI, and changes the scan from continuous to -// only performing a scan when you click the shutter button. For testing. -//------------------------------------------------------------------------------ -#define USE_SHUTTER 0 - -//------------------------------------------------------------------------------ -@class CDVbcsProcessor; -@class CDVbcsViewController; - -//------------------------------------------------------------------------------ -// plugin class -//------------------------------------------------------------------------------ -@interface CDVBarcodeScanner : CDVPlugin {} -- (NSString*)isScanNotPossible; -- (void)scan:(CDVInvokedUrlCommand*)command; -- (void)encode:(CDVInvokedUrlCommand*)command; -- (void)returnSuccess:(NSString*)scannedText format:(NSString*)format scanImage:(NSString*)imageBase64 cancelled:(BOOL)cancelled flipped:(BOOL)flipped callback:(NSString*)callback; -- (void)returnError:(NSString*)message callback:(NSString*)callback; -@end - -//------------------------------------------------------------------------------ -// class that does the grunt work -//------------------------------------------------------------------------------ -@interface CDVbcsProcessor : NSObject {} -@property (nonatomic, retain) CDVBarcodeScanner* plugin; -@property (nonatomic, retain) NSString* callback; -@property (nonatomic, retain) UIViewController* parentViewController; -@property (nonatomic, retain) CDVbcsViewController* viewController; -@property (nonatomic, retain) AVCaptureSession* captureSession; -@property (nonatomic, retain) AVCaptureVideoPreviewLayer* previewLayer; -@property (nonatomic, retain) NSString* alternateXib; -@property (nonatomic) BOOL is1D; -@property (nonatomic) BOOL is2D; -@property (nonatomic) BOOL capturing; -@property (nonatomic) BOOL isFrontCamera; -@property (nonatomic) BOOL isFlipped; - - -- (id)initWithPlugin:(CDVBarcodeScanner*)plugin callback:(NSString*)callback parentViewController:(UIViewController*)parentViewController alterateOverlayXib:(NSString *)alternateXib; -- (void)scanBarcode; -- (void)barcodeScanSucceeded:(NSString*)text format:(NSString*)format scanImage:(NSString*)imageBase64; -- (void)barcodeScanFailed:(NSString*)message; -- (void)barcodeScanCancelled; -- (void)openDialog; -- (NSString*)setUpCaptureSession; -- (void)captureOutput:(AVCaptureOutput*)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection*)connection; -- (NSString*)formatStringFrom:(zxing::BarcodeFormat)format; -- (UIImage*)getImageFromSample:(CMSampleBufferRef)sampleBuffer; -- (zxing::Ref) getLuminanceSourceFromSample:(CMSampleBufferRef)sampleBuffer imageBytes:(uint8_t**)ptr; -- (UIImage*) getImageFromLuminanceSource:(zxing::LuminanceSource*)luminanceSource; -- (void)dumpImage:(UIImage*)image; -@end - -//------------------------------------------------------------------------------ -// view controller for the ui -//------------------------------------------------------------------------------ -@interface CDVbcsViewController : UIViewController {} -@property (nonatomic, retain) CDVbcsProcessor* processor; -@property (nonatomic, retain) NSString* alternateXib; -@property (nonatomic) BOOL shutterPressed; -@property (nonatomic, retain) IBOutlet UIView* overlayView; -// unsafe_unretained is equivalent to assign - used to prevent retain cycles in the property below -@property (nonatomic, unsafe_unretained) id orientationDelegate; - -- (id)initWithProcessor:(CDVbcsProcessor*)processor alternateOverlay:(NSString *)alternateXib; -- (void)startCapturing; -- (UIView*)buildOverlayView; -- (UIImage*)buildReticleImage; -- (void)shutterButtonPressed; -- (IBAction)cancelButtonPressed:(id)sender; - -@end - -//------------------------------------------------------------------------------ -// plugin class -//------------------------------------------------------------------------------ -@implementation CDVBarcodeScanner - -//-------------------------------------------------------------------------- -- (NSString*)isScanNotPossible { - NSString* result = nil; - - Class aClass = NSClassFromString(@"AVCaptureSession"); - if (aClass == nil) { - return @"AVFoundation Framework not available"; - } - - return result; -} - -//-------------------------------------------------------------------------- -- (void)scan:(CDVInvokedUrlCommand*)command { - CDVbcsProcessor* processor; - NSString* callback; - NSString* capabilityError; - - callback = command.callbackId; - - // We allow the user to define an alternate xib file for loading the overlay. - NSString *overlayXib = nil; - if ( [command.arguments count] >= 1 ) - { - overlayXib = [command.arguments objectAtIndex:0]; - } - - capabilityError = [self isScanNotPossible]; - if (capabilityError) { - [self returnError:capabilityError callback:callback]; - return; - } - - processor = [[CDVbcsProcessor alloc] - initWithPlugin:self - callback:callback - parentViewController:self.viewController - alterateOverlayXib:overlayXib - ]; - [processor retain]; - [processor retain]; - [processor retain]; - // queue [processor scanBarcode] to run on the event loop - [processor performSelector:@selector(scanBarcode) withObject:nil afterDelay:0]; -} - -//-------------------------------------------------------------------------- -- (void)encode:(CDVInvokedUrlCommand*)command { - [self returnError:@"encode function not supported" callback:command.callbackId]; -} - -//-------------------------------------------------------------------------- -- (void)returnSuccess:(NSString*)scannedText format:(NSString*)format scanImage:(NSString*)imageBase64 cancelled:(BOOL)cancelled flipped:(BOOL)flipped callback:(NSString*)callback{ - NSNumber* cancelledNumber = [NSNumber numberWithInt:(cancelled?1:0)]; - - NSMutableDictionary* resultDict = [[[NSMutableDictionary alloc] init] autorelease]; - [resultDict setObject:scannedText forKey:@"text"]; - [resultDict setObject:format forKey:@"format"]; - [resultDict setObject:imageBase64 forKey:@"scanImage"]; - [resultDict setObject:cancelledNumber forKey:@"cancelled"]; - - CDVPluginResult* result = [CDVPluginResult - resultWithStatus: CDVCommandStatus_OK - messageAsDictionary: resultDict - ]; - - NSString* js = [result toSuccessCallbackString:callback]; - if (!flipped) { - [self writeJavascript:js]; - } -} - -//-------------------------------------------------------------------------- -- (void)returnError:(NSString*)message callback:(NSString*)callback { - CDVPluginResult* result = [CDVPluginResult - resultWithStatus: CDVCommandStatus_OK - messageAsString: message - ]; - - NSString* js = [result toErrorCallbackString:callback]; - - [self writeJavascript:js]; -} - -@end - -//------------------------------------------------------------------------------ -// class that does the grunt work -//------------------------------------------------------------------------------ -@implementation CDVbcsProcessor - -@synthesize plugin = _plugin; -@synthesize callback = _callback; -@synthesize parentViewController = _parentViewController; -@synthesize viewController = _viewController; -@synthesize captureSession = _captureSession; -@synthesize previewLayer = _previewLayer; -@synthesize alternateXib = _alternateXib; -@synthesize is1D = _is1D; -@synthesize is2D = _is2D; -@synthesize capturing = _capturing; - -//-------------------------------------------------------------------------- -- (id)initWithPlugin:(CDVBarcodeScanner*)plugin - callback:(NSString*)callback -parentViewController:(UIViewController*)parentViewController - alterateOverlayXib:(NSString *)alternateXib { - self = [super init]; - if (!self) return self; - - self.plugin = plugin; - self.callback = callback; - self.parentViewController = parentViewController; - self.alternateXib = alternateXib; - - self.is1D = YES; - self.is2D = YES; - self.capturing = NO; - - return self; -} - -//-------------------------------------------------------------------------- -- (void)dealloc { - self.plugin = nil; - self.callback = nil; - self.parentViewController = nil; - self.viewController = nil; - self.captureSession = nil; - self.previewLayer = nil; - self.alternateXib = nil; - - self.capturing = NO; - - [super dealloc]; -} - -//-------------------------------------------------------------------------- -- (void)scanBarcode { - -// self.captureSession = nil; -// self.previewLayer = nil; - NSString* errorMessage = [self setUpCaptureSession]; - if (errorMessage) { - [self barcodeScanFailed:errorMessage]; - return; - } - - self.viewController = [[[CDVbcsViewController alloc] initWithProcessor: self alternateOverlay:self.alternateXib] autorelease]; - // here we set the orientation delegate to the MainViewController of the app (orientation controlled in the Project Settings) - self.viewController.orientationDelegate = self.plugin.viewController; - - // delayed [self openDialog]; - [self performSelector:@selector(openDialog) withObject:nil afterDelay:1]; -} - -//-------------------------------------------------------------------------- -- (void)openDialog { - [self.parentViewController - presentModalViewController:self.viewController - animated:YES - ]; -} - -//-------------------------------------------------------------------------- -- (void)barcodeScanDone { - self.capturing = NO; - [self.captureSession stopRunning]; - [self.parentViewController dismissModalViewControllerAnimated: YES]; - - // viewcontroller holding onto a reference to us, release them so they - // will release us - self.viewController = nil; - - // delayed [self release]; - [self performSelector:@selector(release) withObject:nil afterDelay:1]; -} - -//-------------------------------------------------------------------------- -- (void)barcodeScanSucceeded:(NSString*)text format:(NSString*)format scanImage:(NSString*)imageBase64 { - [self barcodeScanDone]; - [self.plugin returnSuccess:text format:format scanImage:imageBase64 cancelled:FALSE flipped:FALSE callback:self.callback]; -} - -//-------------------------------------------------------------------------- -- (void)barcodeScanFailed:(NSString*)message { - [self barcodeScanDone]; - [self.plugin returnError:message callback:self.callback]; -} - -//-------------------------------------------------------------------------- -- (void)barcodeScanCancelled { - [self barcodeScanDone]; - [self.plugin returnSuccess:@"" format:@"" scanImage:@"" cancelled:TRUE flipped:self.isFlipped callback:self.callback]; - if (self.isFlipped) { - self.isFlipped = NO; - } -} - - -- (void)flipCamera -{ - self.isFlipped = YES; - self.isFrontCamera = !self.isFrontCamera; - [self performSelector:@selector(barcodeScanCancelled) withObject:nil afterDelay:0]; - [self performSelector:@selector(scanBarcode) withObject:nil afterDelay:0.1]; -} - -//-------------------------------------------------------------------------- -- (NSString*)setUpCaptureSession { - NSError* error = nil; - - AVCaptureSession* captureSession = [[[AVCaptureSession alloc] init] autorelease]; - self.captureSession = captureSession; - - AVCaptureDevice* __block device = nil; - if (self.isFrontCamera) { - - NSArray* devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; - [devices enumerateObjectsUsingBlock:^(AVCaptureDevice *obj, NSUInteger idx, BOOL *stop) { - if (obj.position == AVCaptureDevicePositionFront) { - device = obj; - } - }]; - } else { - device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; - if (!device) return @"unable to obtain video capture device"; - - } - - - AVCaptureDeviceInput* input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error]; - if (!input) return @"unable to obtain video capture device input"; - - AVCaptureVideoDataOutput* output = [[[AVCaptureVideoDataOutput alloc] init] autorelease]; - if (!output) return @"unable to obtain video capture output"; - - NSDictionary* videoOutputSettings = [NSDictionary - dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA] - forKey:(id)kCVPixelBufferPixelFormatTypeKey - ]; - - output.alwaysDiscardsLateVideoFrames = YES; - output.videoSettings = videoOutputSettings; - - [output setSampleBufferDelegate:self queue:dispatch_get_main_queue()]; - - if (![captureSession canSetSessionPreset:AVCaptureSessionPresetMedium]) { - return @"unable to preset medium quality video capture"; - } - - captureSession.sessionPreset = AVCaptureSessionPresetMedium; - - if ([captureSession canAddInput:input]) { - [captureSession addInput:input]; - } - else { - return @"unable to add video capture device input to session"; - } - - if ([captureSession canAddOutput:output]) { - [captureSession addOutput:output]; - } - else { - return @"unable to add video capture output to session"; - } - - // setup capture preview layer - self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession]; - - // run on next event loop pass [captureSession startRunning] - [captureSession performSelector:@selector(startRunning) withObject:nil afterDelay:0]; - - return nil; -} - -//-------------------------------------------------------------------------- -// this method gets sent the captured frames -//-------------------------------------------------------------------------- -- (void)captureOutput:(AVCaptureOutput*)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection*)connection { - - if (!self.capturing) return; - -#if USE_SHUTTER - if (!self.viewController.shutterPressed) return; - self.viewController.shutterPressed = NO; - - UIView* flashView = [[[UIView alloc] initWithFrame:self.viewController.view.frame] autorelease]; - [flashView setBackgroundColor:[UIColor whiteColor]]; - [self.viewController.view.window addSubview:flashView]; - - [UIView - animateWithDuration:.4f - animations:^{ - [flashView setAlpha:0.f]; - } - completion:^(BOOL finished){ - [flashView removeFromSuperview]; - } - ]; - - // [self dumpImage: [[self getImageFromSample:sampleBuffer] autorelease]]; -#endif - - // Canvas Part: Outputs the image stream to Javascript - //NSString *javascript = @"CanvasCamera.capture('');"; - //[self.plugin.webView performSelectorOnMainThread:@selector(stringByEvaluatingJavaScriptFromString:) withObject:javascript waitUntilDone:YES]; - - - using namespace zxing; - - // LuminanceSource is pretty dumb; we have to give it a pointer to - // a byte array, but then can't get it back out again. We need to - // get it back to free it. Saving it in imageBytes. - uint8_t* imageBytes; - - // NSTimeInterval timeStart = [NSDate timeIntervalSinceReferenceDate]; - - try { - DecodeHints decodeHints; - decodeHints.addFormat(BarcodeFormat_QR_CODE); - decodeHints.addFormat(BarcodeFormat_DATA_MATRIX); - decodeHints.addFormat(BarcodeFormat_UPC_E); - decodeHints.addFormat(BarcodeFormat_UPC_A); - decodeHints.addFormat(BarcodeFormat_EAN_8); - decodeHints.addFormat(BarcodeFormat_EAN_13); - decodeHints.addFormat(BarcodeFormat_CODE_128); - decodeHints.addFormat(BarcodeFormat_CODE_39); - decodeHints.addFormat(BarcodeFormat_ITF); - - // here's the meat of the decode process - Ref luminanceSource ([self getLuminanceSourceFromSample: sampleBuffer imageBytes:&imageBytes]); - // [self dumpImage: [[self getImageFromLuminanceSource:luminanceSource] autorelease]]; - Ref binarizer (new HybridBinarizer(luminanceSource)); - Ref bitmap (new BinaryBitmap(binarizer)); - Ref reader (new MultiFormatReader()); - Ref result (reader->decode(bitmap, decodeHints)); - Ref resultText (result->getText()); - BarcodeFormat formatVal = result->getBarcodeFormat(); - NSString* format = [self formatStringFrom:formatVal]; - - - const char* cString = resultText->getText().c_str(); - NSString* resultString = [[[NSString alloc] initWithCString:cString encoding:NSUTF8StringEncoding] autorelease]; - - //DEBUG: Dump succeeded image to Photos - UIImage *imageDump = [[self getImageFromSample:sampleBuffer orientation:UIInterfaceOrientationPortrait] autorelease]; - [self dumpImage: imageDump]; - - NSLog(@"Executing javascript: %@", captureOutput.connections[0]); - - // Write base64 to Javascript - UIImage *image= [[self getImageFromSample:sampleBuffer orientation:UIInterfaceOrientationPortrait] autorelease]; - NSString *imageBase64 = @"data:image/jpeg;base64,"; - imageBase64 = [imageBase64 stringByAppendingString:[UIImagePNGRepresentation(image) base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]]; - - // DEVELOPMENT idea 1: Write base64 image directly to DOM element - //NSString *javascript = @"document.getElementById('camera').src = '"; - //javascript = [javascript stringByAppendingString:imageBase64]; - //javascript = [javascript stringByAppendingString:@"';"]; - //NSLog(@"Executing javascript: %@", javascript); - //[self.plugin.webView writeJavascript:javascript]; - //[self.plugin.webView performSelectorOnMainThread:@selector(stringByEvaluatingJavaScriptFromString:) withObject:javascript waitUntilDone:YES]; - - [self barcodeScanSucceeded:resultString format:format scanImage:imageBase64]; - - } - catch (zxing::ReaderException &rex) { - // NSString *message = [[[NSString alloc] initWithCString:rex.what() encoding:NSUTF8StringEncoding] autorelease]; - // NSLog(@"decoding: ReaderException: %@", message); - } - catch (zxing::IllegalArgumentException &iex) { - // NSString *message = [[[NSString alloc] initWithCString:iex.what() encoding:NSUTF8StringEncoding] autorelease]; - // NSLog(@"decoding: IllegalArgumentException: %@", message); - } - catch (...) { - // NSLog(@"decoding: unknown exception"); - // [self barcodeScanFailed:@"unknown exception decoding barcode"]; - } - - // NSTimeInterval timeElapsed = [NSDate timeIntervalSinceReferenceDate] - timeStart; - // NSLog(@"decoding completed in %dms", (int) (timeElapsed * 1000)); - - // free the buffer behind the LuminanceSource - if (imageBytes) { - free(imageBytes); - } -} - -//-------------------------------------------------------------------------- -// convert barcode format to string -//-------------------------------------------------------------------------- -- (NSString*)formatStringFrom:(zxing::BarcodeFormat)format { - if (format == zxing::BarcodeFormat_QR_CODE) return @"QR_CODE"; - if (format == zxing::BarcodeFormat_DATA_MATRIX) return @"DATA_MATRIX"; - if (format == zxing::BarcodeFormat_UPC_E) return @"UPC_E"; - if (format == zxing::BarcodeFormat_UPC_A) return @"UPC_A"; - if (format == zxing::BarcodeFormat_EAN_8) return @"EAN_8"; - if (format == zxing::BarcodeFormat_EAN_13) return @"EAN_13"; - if (format == zxing::BarcodeFormat_CODE_128) return @"CODE_128"; - if (format == zxing::BarcodeFormat_CODE_39) return @"CODE_39"; - if (format == zxing::BarcodeFormat_ITF) return @"ITF"; - return @"???"; -} - -//-------------------------------------------------------------------------- -// convert capture's sample buffer (scanned picture) into the thing that -// zxing needs. -//-------------------------------------------------------------------------- -- (zxing::Ref) getLuminanceSourceFromSample:(CMSampleBufferRef)sampleBuffer imageBytes:(uint8_t**)ptr { - CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); - CVPixelBufferLockBaseAddress(imageBuffer, 0); - - size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); - size_t width = CVPixelBufferGetWidth(imageBuffer); - size_t height = CVPixelBufferGetHeight(imageBuffer); - uint8_t* baseAddress = (uint8_t*) CVPixelBufferGetBaseAddress(imageBuffer); - - // only going to get 90% of the min(width,height) of the captured image - size_t greyWidth = 9 * MIN(width, height) / 10; - uint8_t* greyData = (uint8_t*) malloc(greyWidth * greyWidth); - - // remember this pointer so we can free it later - *ptr = greyData; - - if (!greyData) { - CVPixelBufferUnlockBaseAddress(imageBuffer,0); - throw new zxing::ReaderException("out of memory"); - } - - size_t offsetX = (width - greyWidth) / 2; - size_t offsetY = (height - greyWidth) / 2; - - // pixel-by-pixel ... - for (size_t i=0; i luminanceSource ( - new GreyscaleLuminanceSource(greyData, greyWidth, greyWidth, 0, 0, greyWidth, greyWidth) - ); - - return luminanceSource; -} - -//-------------------------------------------------------------------------- -// for debugging -//-------------------------------------------------------------------------- -- (UIImage*) getImageFromLuminanceSource:(zxing::LuminanceSource*)luminanceSource { - unsigned char* bytes = luminanceSource->getMatrix(); - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); - CGContextRef context = CGBitmapContextCreate( - bytes, - luminanceSource->getWidth(), luminanceSource->getHeight(), 8, luminanceSource->getWidth(), - colorSpace, - kCGImageAlphaNone - ); - - CGImageRef cgImage = CGBitmapContextCreateImage(context); - UIImage* image = [[UIImage alloc] initWithCGImage:cgImage]; - - CGContextRelease(context); - CGColorSpaceRelease(colorSpace); - CGImageRelease(cgImage); - free(bytes); - - return image; -} - -//-------------------------------------------------------------------------- -// for debugging -//-------------------------------------------------------------------------- -- (UIImage*)getImageFromSample:(CMSampleBufferRef)sampleBuffer { - UIInterfaceOrientation initialOrientation = UIInterfaceOrientationPortrait; - - return [self getImageFromSample:sampleBuffer orientation:initialOrientation]; -} -#define radians(degrees) (degrees * M_PI/180) -- (UIImage*)getImageFromSample:(CMSampleBufferRef)sampleBuffer orientation:(UIInterfaceOrientation)orientation { - //UIInterfaceOrientation initialOrientation = UIInterfaceOrientationPortrait; - - // Rotate sampleBuffer if necessary - //sampleBuffer = [self rotateBuffer:sampleBuffer]; - - // 2. Rotate Buffer Versuch - //sampleBuffer = [self rotateBuffer:sampleBuffer]; - - // Bringt zwar Hochkant Bild aber mit Seltsamen Pixeln - //UIImage* imageTest = [self imageFromSampleBuffer:sampleBuffer]; - - - CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); - CVPixelBufferLockBaseAddress(imageBuffer, 0); - - size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); - size_t width = CVPixelBufferGetWidth(imageBuffer); - size_t height = CVPixelBufferGetHeight(imageBuffer); - - uint8_t* baseAddress = (uint8_t*) CVPixelBufferGetBaseAddress(imageBuffer); - int length = height * bytesPerRow; - uint8_t* newBaseAddress = (uint8_t*) malloc(length); - memcpy(newBaseAddress, baseAddress, length); - baseAddress = newBaseAddress; - - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - - CGContextRef context = CGBitmapContextCreate( - baseAddress, - width, height, 8, bytesPerRow, - colorSpace, - kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst - ); - - - // Rotate context - //CGContextRotateCTM (context, radians(90)); - //CGContextTranslateCTM (context, 0, -height); - - CGImageRef cgImage = CGBitmapContextCreateImage(context); - //CGContextDrawImage(context, CGRectMake(0, 0, height, width), cgImage); - //CGImageRef ref = CGBitmapContextCreateImage(context); - //UIImage* image = [UIImage imageWithCGImage:cgImage]; - - - - // Apple method extracted - // Move the origin to the middle of the image so we will rotate and scale around the center. - //CGContextTranslateCTM(context, height/2, width/2); // rotatedSize.width/2, rotatedSize.height/2 - - // // Rotate the image context - //CGContextRotateCTM(context, DegreesToRadians(90.0)); - - // Now, draw the rotated/scaled image into the context - //CGContextScaleCTM(context, 1.0, -1.0); - - //CGImageRef cgImage = CGBitmapContextCreateImage(context); - - //CGContextDrawImage(context, CGRectMake(-width / 2, -height / 2, width, height), cgImage); - - - - - - - - - //CGImageRef cgImage = CGBitmapContextCreateImage(context); - UIImage* image = [[UIImage alloc] initWithCGImage:cgImage]; - - - // Dreht!!! und ARC Probleme - //image = [self scaleAndRotateImage:image]; - - - - - - - - - // Apple method - //image = [image imageRotatedByDegrees:90.0]; - - //UIImage* image = [[UIImage alloc] imageWithCGImage:cgImage scale:1.0 orientation:UIImageOrientationRight]; - //UIImage * image = [[UIImage imageWithCGImage:cgImage scale:1.0 orientation:UIImageOrientationUp] retain]; - - CVPixelBufferUnlockBaseAddress(imageBuffer,0); - CGContextRelease(context); - CGColorSpaceRelease(colorSpace); - CGImageRelease(cgImage); - - free(baseAddress); - - return image; -} - - - -- (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer -{ - CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); - CVPixelBufferLockBaseAddress(imageBuffer, 0); - - void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer); - - size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); - size_t width = CVPixelBufferGetWidth(imageBuffer); - size_t height = CVPixelBufferGetHeight(imageBuffer); - - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - - CGContextRef context = CGBitmapContextCreate(baseAddress, height, width, 8, - bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); - - CGImageRef quartzImage = CGBitmapContextCreateImage(context); - CVPixelBufferUnlockBaseAddress(imageBuffer,0); - - CGContextRelease(context); - CGColorSpaceRelease(colorSpace); - - // From SO - //UIImage *image = [UIImage imageWithCGImage:quartzImage]; - - int frontCameraImageOrientation = UIImageOrientationLeftMirrored; - //int backCameraImageOrientation = UIImageOrientationRight; - - UIImage *image = [[UIImage alloc] initWithCGImage:quartzImage scale:(CGFloat)1.0 orientation:frontCameraImageOrientation]; - - CGImageRelease(quartzImage); - - return (image); -} - - - -- (UIImage *)scaleAndRotateImage:(UIImage *)image -{ - int kMaxResolution = 640; - - CGImageRef imgRef = image.CGImage; - - CGFloat width = CGImageGetWidth(imgRef); - CGFloat height = CGImageGetHeight(imgRef); - - CGAffineTransform transform = CGAffineTransformIdentity; - CGRect bounds = CGRectMake(0, 0, width, height); - if (width > kMaxResolution || height > kMaxResolution) { - CGFloat ratio = width/height; - if (ratio > 1) { - bounds.size.width = kMaxResolution; - bounds.size.height = bounds.size.width / ratio; - } - else { - bounds.size.height = kMaxResolution; - bounds.size.width = bounds.size.height * ratio; - } - } - - CGFloat scaleRatio = bounds.size.width / width; - CGSize imageSize = CGSizeMake(CGImageGetWidth(imgRef), CGImageGetHeight(imgRef)); - CGFloat boundHeight; - UIImageOrientation orient = image.imageOrientation; - orient = UIImageOrientationRight; - switch(orient) { - - case UIImageOrientationUp: //EXIF = 1 - transform = CGAffineTransformIdentity; - break; - - case UIImageOrientationUpMirrored: //EXIF = 2 - transform = CGAffineTransformMakeTranslation(imageSize.width, 0.0); - transform = CGAffineTransformScale(transform, -1.0, 1.0); - break; - - case UIImageOrientationDown: //EXIF = 3 - transform = CGAffineTransformMakeTranslation(imageSize.width, imageSize.height); - transform = CGAffineTransformRotate(transform, M_PI); - break; - - case UIImageOrientationDownMirrored: //EXIF = 4 - transform = CGAffineTransformMakeTranslation(0.0, imageSize.height); - transform = CGAffineTransformScale(transform, 1.0, -1.0); - break; - - case UIImageOrientationLeftMirrored: //EXIF = 5 - boundHeight = bounds.size.height; - bounds.size.height = bounds.size.width; - bounds.size.width = boundHeight; - transform = CGAffineTransformMakeTranslation(imageSize.height, imageSize.width); - transform = CGAffineTransformScale(transform, -1.0, 1.0); - transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0); - break; - - case UIImageOrientationLeft: //EXIF = 6 - boundHeight = bounds.size.height; - bounds.size.height = bounds.size.width; - bounds.size.width = boundHeight; - transform = CGAffineTransformMakeTranslation(0.0, imageSize.width); - transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0); - break; - - case UIImageOrientationRightMirrored: //EXIF = 7 - boundHeight = bounds.size.height; - bounds.size.height = bounds.size.width; - bounds.size.width = boundHeight; - transform = CGAffineTransformMakeScale(-1.0, 1.0); - transform = CGAffineTransformRotate(transform, M_PI / 2.0); - break; - - case UIImageOrientationRight: //EXIF = 8 - boundHeight = bounds.size.height; - bounds.size.height = bounds.size.width; - bounds.size.width = boundHeight; - transform = CGAffineTransformMakeTranslation(imageSize.height, 0.0); - transform = CGAffineTransformRotate(transform, M_PI / 2.0); - break; - - default: - [NSException raise:NSInternalInconsistencyException format:@"Invalid image orientation"]; - - } - - UIGraphicsBeginImageContext(bounds.size); - - CGContextRef context = UIGraphicsGetCurrentContext(); - - if (orient == UIImageOrientationRight || orient == UIImageOrientationLeft) { - CGContextScaleCTM(context, -scaleRatio, scaleRatio); - CGContextTranslateCTM(context, -height, 0); - } - else { - CGContextScaleCTM(context, scaleRatio, -scaleRatio); - CGContextTranslateCTM(context, 0, -height); - } - - CGContextConcatCTM(context, transform); - - CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, width, height), imgRef); - UIImage *imageCopy = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - return imageCopy; -} - - - - -- (unsigned char*) rotateBuffer: (CMSampleBufferRef) sampleBuffer -{ - CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); - CVPixelBufferLockBaseAddress(imageBuffer,0); - - size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); - size_t width = CVPixelBufferGetWidth(imageBuffer); - size_t height = CVPixelBufferGetHeight(imageBuffer); - size_t currSize = bytesPerRow*height*sizeof(unsigned char); - size_t bytesPerRowOut = 4*height*sizeof(unsigned char); - - void *srcBuff = CVPixelBufferGetBaseAddress(imageBuffer); - - /* - * rotationConstant: 0 -- rotate 0 degrees (simply copy the data from src to dest) - * 1 -- rotate 90 degrees counterclockwise - * 2 -- rotate 180 degress - * 3 -- rotate 270 degrees counterclockwise - */ - uint8_t rotationConstant = 1; - - unsigned char *outBuff = (unsigned char*)malloc(currSize); - - vImage_Buffer ibuff = { srcBuff, height, width, bytesPerRow}; - vImage_Buffer ubuff = { outBuff, width, height, bytesPerRowOut}; - - vImage_Error err = vImageRotate90_ARGB8888 (&ibuff, &ubuff, rotationConstant, NULL, 0); - if (err != kvImageNoError) NSLog(@"%ld", err); - - return outBuff; -} - - - -- (CVPixelBufferRef) rotateBufferCVPIxel: (CMSampleBufferRef) sampleBuffer -{ - CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); - CVPixelBufferLockBaseAddress(imageBuffer,0); - - size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); - size_t width = CVPixelBufferGetWidth(imageBuffer); - size_t height = CVPixelBufferGetHeight(imageBuffer); - - void *src_buff = CVPixelBufferGetBaseAddress(imageBuffer); - - NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey, - [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey, - nil]; - - CVPixelBufferRef pxbuffer = NULL; - //CVReturn status = CVPixelBufferPoolCreatePixelBuffer (NULL, _pixelWriter.pixelBufferPool, &pxbuffer); - CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, width, - height, kCVPixelFormatType_32BGRA, (CFDictionaryRef) options, - &pxbuffer); - - NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL); - - CVPixelBufferLockBaseAddress(pxbuffer, 0); - void *dest_buff = CVPixelBufferGetBaseAddress(pxbuffer); - NSParameterAssert(dest_buff != NULL); - - int *src = (int*) src_buff ; - int *dest= (int*) dest_buff ; - size_t count = (bytesPerRow * height) / 4 ; - while (count--) { - *dest++ = *src++; - } - - //Test straight copy. - //memcpy(pxdata, baseAddress, width * height * 4) ; - CVPixelBufferUnlockBaseAddress(pxbuffer, 0); - CVPixelBufferUnlockBaseAddress(imageBuffer, 0); - return pxbuffer; -} - -//-------------------------------------------------------------------------- -// for debugging -//-------------------------------------------------------------------------- -- (void)dumpImage:(UIImage*)image { - NSLog(@"writing image to library: %dx%d", (int)image.size.width, (int)image.size.height); - ALAssetsLibrary* assetsLibrary = [[[ALAssetsLibrary alloc] init] autorelease]; - [assetsLibrary - writeImageToSavedPhotosAlbum:image.CGImage - orientation:ALAssetOrientationUp - completionBlock:^(NSURL* assetURL, NSError* error){ - if (error) NSLog(@" error writing image to library"); - else NSLog(@" wrote image to library %@", assetURL); - } - ]; -} - -@end - -//------------------------------------------------------------------------------ -// view controller for the ui -//------------------------------------------------------------------------------ -@implementation CDVbcsViewController -@synthesize processor = _processor; -@synthesize shutterPressed = _shutterPressed; -@synthesize alternateXib = _alternateXib; -@synthesize overlayView = _overlayView; - -//-------------------------------------------------------------------------- -- (id)initWithProcessor:(CDVbcsProcessor*)processor alternateOverlay:(NSString *)alternateXib { - self = [super init]; - if (!self) return self; - - self.processor = processor; - self.shutterPressed = NO; - self.alternateXib = alternateXib; - self.overlayView = nil; - return self; -} - -//-------------------------------------------------------------------------- -- (void)dealloc { - self.view = nil; -// self.processor = nil; - self.shutterPressed = NO; - self.alternateXib = nil; - self.overlayView = nil; - [super dealloc]; -} - -//-------------------------------------------------------------------------- -- (void)loadView { - self.view = [[[UIView alloc] initWithFrame: self.processor.parentViewController.view.frame] autorelease]; - - // setup capture preview layer - AVCaptureVideoPreviewLayer* previewLayer = self.processor.previewLayer; - previewLayer.frame = self.view.bounds; - previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; - - if ([previewLayer isOrientationSupported]) { - [previewLayer setOrientation:AVCaptureVideoOrientationPortrait]; - } - - [self.view.layer insertSublayer:previewLayer below:[[self.view.layer sublayers] objectAtIndex:0]]; - - [self.view addSubview:[self buildOverlayView]]; -} - -//-------------------------------------------------------------------------- -- (void)viewWillAppear:(BOOL)animated { - - // set video orientation to what the camera sees - self.processor.previewLayer.orientation = [[UIApplication sharedApplication] statusBarOrientation]; - - // this fixes the bug when the statusbar is landscape, and the preview layer - // starts up in portrait (not filling the whole view) - self.processor.previewLayer.frame = self.view.bounds; -} - -//-------------------------------------------------------------------------- -- (void)viewDidAppear:(BOOL)animated { - [self startCapturing]; - - [super viewDidAppear:animated]; -} - -//-------------------------------------------------------------------------- -- (void)startCapturing { - self.processor.capturing = YES; -} - -//-------------------------------------------------------------------------- -- (void)shutterButtonPressed { - self.shutterPressed = YES; -} - -//-------------------------------------------------------------------------- -- (IBAction)cancelButtonPressed:(id)sender { - [self.processor performSelector:@selector(barcodeScanCancelled) withObject:nil afterDelay:0]; -} - -- (void)flipCameraButtonPressed:(id)sender -{ - [self.processor performSelector:@selector(flipCamera) withObject:nil afterDelay:0]; -} - -//-------------------------------------------------------------------------- -- (UIView *)buildOverlayViewFromXib -{ - [[NSBundle mainBundle] loadNibNamed:self.alternateXib owner:self options:NULL]; - - if ( self.overlayView == nil ) - { - NSLog(@"%@", @"An error occurred loading the overlay xib. It appears that the overlayView outlet is not set."); - return nil; - } - - return self.overlayView; -} - -//-------------------------------------------------------------------------- -- (UIView*)buildOverlayView { - - if ( nil != self.alternateXib ) - { - return [self buildOverlayViewFromXib]; - } - CGRect bounds = self.view.bounds; - bounds = CGRectMake(0, 0, bounds.size.width, bounds.size.height); - - UIView* overlayView = [[[UIView alloc] initWithFrame:bounds] autorelease]; - overlayView.autoresizesSubviews = YES; - overlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - overlayView.opaque = NO; - - UIToolbar* toolbar = [[[UIToolbar alloc] init] autorelease]; - toolbar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin; - - id cancelButton = [[[UIBarButtonItem alloc] autorelease] - initWithBarButtonSystemItem:UIBarButtonSystemItemCancel - target:(id)self - action:@selector(cancelButtonPressed:) - ]; - - - id flexSpace = [[[UIBarButtonItem alloc] autorelease] - initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace - target:nil - action:nil - ]; - - id flipCamera = [[[UIBarButtonItem alloc] autorelease] - initWithBarButtonSystemItem:UIBarButtonSystemItemCamera - target:(id)self - action:@selector(flipCameraButtonPressed:) - ]; - - -#if USE_SHUTTER - id shutterButton = [[UIBarButtonItem alloc] - initWithBarButtonSystemItem:UIBarButtonSystemItemCamera - target:(id)self - action:@selector(shutterButtonPressed) - ]; - - toolbar.items = [NSArray arrayWithObjects:flexSpace,cancelButton,flexSpace, flipCamera ,shutterButton,nil]; -#else - toolbar.items = [NSArray arrayWithObjects:flexSpace,cancelButton,flexSpace, flipCamera,nil]; -#endif - bounds = overlayView.bounds; - - [toolbar sizeToFit]; - CGFloat toolbarHeight = [toolbar frame].size.height; - CGFloat rootViewHeight = CGRectGetHeight(bounds); - CGFloat rootViewWidth = CGRectGetWidth(bounds); - CGRect rectArea = CGRectMake(0, rootViewHeight - toolbarHeight, rootViewWidth, toolbarHeight); - [toolbar setFrame:rectArea]; - - [overlayView addSubview: toolbar]; - - UIImage* reticleImage = [self buildReticleImage]; - UIView* reticleView = [[[UIImageView alloc] initWithImage: reticleImage] autorelease]; - CGFloat minAxis = MIN(rootViewHeight, rootViewWidth); - - rectArea = CGRectMake( - 0.5 * (rootViewWidth - minAxis), - 0.5 * (rootViewHeight - minAxis), - minAxis, - minAxis - ); - - [reticleView setFrame:rectArea]; - - reticleView.opaque = NO; - reticleView.contentMode = UIViewContentModeScaleAspectFit; - reticleView.autoresizingMask = 0 - | UIViewAutoresizingFlexibleLeftMargin - | UIViewAutoresizingFlexibleRightMargin - | UIViewAutoresizingFlexibleTopMargin - | UIViewAutoresizingFlexibleBottomMargin - ; - - [overlayView addSubview: reticleView]; - - return overlayView; -} - -//-------------------------------------------------------------------------- - -#define RETICLE_SIZE 500.0f -#define RETICLE_WIDTH 10.0f -#define RETICLE_OFFSET 60.0f -#define RETICLE_ALPHA 0.4f - -//------------------------------------------------------------------------- -// builds the green box and red line -//------------------------------------------------------------------------- -- (UIImage*)buildReticleImage { - UIImage* result; - UIGraphicsBeginImageContext(CGSizeMake(RETICLE_SIZE, RETICLE_SIZE)); - CGContextRef context = UIGraphicsGetCurrentContext(); - - if (self.processor.is1D) { - UIColor* color = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:RETICLE_ALPHA]; - CGContextSetStrokeColorWithColor(context, color.CGColor); - CGContextSetLineWidth(context, RETICLE_WIDTH); - CGContextBeginPath(context); - CGFloat lineOffset = RETICLE_OFFSET+(0.5*RETICLE_WIDTH); - CGContextMoveToPoint(context, lineOffset, RETICLE_SIZE/2); - CGContextAddLineToPoint(context, RETICLE_SIZE-lineOffset, 0.5*RETICLE_SIZE); - CGContextStrokePath(context); - } - - if (self.processor.is2D) { - UIColor* color = [UIColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:RETICLE_ALPHA]; - CGContextSetStrokeColorWithColor(context, color.CGColor); - CGContextSetLineWidth(context, RETICLE_WIDTH); - CGContextStrokeRect(context, - CGRectMake( - RETICLE_OFFSET, - RETICLE_OFFSET, - RETICLE_SIZE-2*RETICLE_OFFSET, - RETICLE_SIZE-2*RETICLE_OFFSET - ) - ); - } - - result = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - return result; -} - -#pragma mark CDVBarcodeScannerOrientationDelegate - -- (BOOL)shouldAutorotate -{ - return NO; -} - -- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation -{ - return UIInterfaceOrientationPortrait; -} - -- (NSUInteger)supportedInterfaceOrientations -{ - return UIInterfaceOrientationMaskPortrait; -} - -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation -{ - if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotateToInterfaceOrientation:)]) { - return [self.orientationDelegate shouldAutorotateToInterfaceOrientation:interfaceOrientation]; - } - - return YES; -} - -- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration -{ - [CATransaction begin]; - - self.processor.previewLayer.orientation = orientation; - [self.processor.previewLayer layoutSublayers]; - self.processor.previewLayer.frame = self.view.bounds; - - [CATransaction commit]; - [super willAnimateRotationToInterfaceOrientation:orientation duration:duration]; -} - -@end From a2630e569d52f1e7eeaa94388b45ad5250b7923f Mon Sep 17 00:00:00 2001 From: bitflower Date: Fri, 29 Aug 2014 16:53:19 +0200 Subject: [PATCH 5/7] Clean up JS --- www/barcodescanner.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/www/barcodescanner.js b/www/barcodescanner.js index fc7184fc2..4678f4562 100644 --- a/www/barcodescanner.js +++ b/www/barcodescanner.js @@ -71,8 +71,6 @@ * @param {Function} errorCallback */ BarcodeScanner.prototype.scan = function (successCallback, errorCallback) { - //DEVELOPMENT: videoStream - // BarcodeScanner.prototype.scan = function (successCallback, errorCallback, videoStreamCallback) { if (errorCallback == null) { errorCallback = function () { }; @@ -88,12 +86,6 @@ return; } - //DEVELOPMENT: videoStream - // if (typeof videoStreamCallback != "function") { - // console.log("BarcodeScanner.scan failure: video stream callback parameter must be a function"); - // return; - // } - exec(successCallback, errorCallback, 'BarcodeScanner', 'scan', []); }; From c7c672da9c9d422ab90180af1e172925b0ce2a3e Mon Sep 17 00:00:00 2001 From: Matthias Max Date: Fri, 29 Aug 2014 18:07:16 +0200 Subject: [PATCH 6/7] Added note for usage of returned scan image. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 948b1d459..1531359f0 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ The following barcode types are currently supported: * AZTEC * PDF417 -`success` and `fail` are callback functions. Success is passed an object with data, type and cancelled properties. Data is the text representation of the barcode data, type is the type of barcode detected and cancelled is whether or not the user cancelled the scan. +`success` and `fail` are callback functions. Success is passed an object with data, type and cancelled properties. Data is the text representation of the barcode data, type is the type of barcode detected and cancelled is whether or not the user cancelled the scan. In iOS you also get the successfully last-scanned image as base64 string in the success callback. A full example could be: ``` From 37427cc3e8f2da20b015b454963a9cfa543bd28f Mon Sep 17 00:00:00 2001 From: Matthias Max Date: Thu, 4 Dec 2014 16:39:46 +0100 Subject: [PATCH 7/7] Added additional GS1 barcode format identifiers GS1 Data Matrix working GS1 QR (to be finished) GS1 DataBar (to be finished) GS1 128 (to be finished) GS1 Composite (to be finished) + Changed the overlay look to blue with rounded corners --- src/ios/CDVBarcodeScanner.mm | 69 +++++++++++++++++++++++++----------- src/ios/zxing-all-in-one.cpp | 33 ++++++++++++++--- src/ios/zxing-all-in-one.h | 16 +++++++-- 3 files changed, 89 insertions(+), 29 deletions(-) diff --git a/src/ios/CDVBarcodeScanner.mm b/src/ios/CDVBarcodeScanner.mm index 066f03edb..8e8e2d9ff 100644 --- a/src/ios/CDVBarcodeScanner.mm +++ b/src/ios/CDVBarcodeScanner.mm @@ -506,6 +506,11 @@ - (NSString*)formatStringFrom:(zxing::BarcodeFormat)format { if (format == zxing::BarcodeFormat_CODE_128) return @"CODE_128"; if (format == zxing::BarcodeFormat_CODE_39) return @"CODE_39"; if (format == zxing::BarcodeFormat_ITF) return @"ITF"; + if (format == zxing::BarcodeFormat_GS1_DATA_MATRIX) return @"GS1_DATA_MATRIX"; + if (format == zxing::BarcodeFormat_GS1_QR_CODE) return @"GS1_QR_CODE"; + if (format == zxing::BarcodeFormat_GS1_128) return @"GS1_128"; + if (format == zxing::BarcodeFormat_GS1_DATA_BAR) return @"GS1_DATA_BAR"; + if (format == zxing::BarcodeFormat_GS1_COMPOSITE) return @"GS1_COMPOSITE"; return @"???"; } @@ -844,7 +849,8 @@ - (UIView*)buildOverlayView { #define RETICLE_SIZE 500.0f #define RETICLE_WIDTH 10.0f #define RETICLE_OFFSET 60.0f -#define RETICLE_ALPHA 0.4f +#define RETICLE_ALPHA 1.0f +#define RADIUS 75 //------------------------------------------------------------------------- // builds the green box and red line @@ -853,30 +859,51 @@ - (UIImage*)buildReticleImage { UIImage* result; UIGraphicsBeginImageContext(CGSizeMake(RETICLE_SIZE, RETICLE_SIZE)); CGContextRef context = UIGraphicsGetCurrentContext(); - - if (self.processor.is1D) { - UIColor* color = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:RETICLE_ALPHA]; - CGContextSetStrokeColorWithColor(context, color.CGColor); - CGContextSetLineWidth(context, RETICLE_WIDTH); - CGContextBeginPath(context); - CGFloat lineOffset = RETICLE_OFFSET+(0.5*RETICLE_WIDTH); - CGContextMoveToPoint(context, lineOffset, RETICLE_SIZE/2); - CGContextAddLineToPoint(context, RETICLE_SIZE-lineOffset, 0.5*RETICLE_SIZE); - CGContextStrokePath(context); - } + + // Cross line +// if (self.processor.is1D) { +// UIColor* color = [UIColor colorWithRed:0 green:0.55 blue:0.72 alpha:RETICLE_ALPHA]; +// CGContextSetStrokeColorWithColor(context, color.CGColor); +// CGContextSetLineWidth(context, RETICLE_WIDTH); +// CGContextBeginPath(context); +// CGFloat lineOffset = RETICLE_OFFSET+(0.5*RETICLE_WIDTH); +// CGContextMoveToPoint(context, lineOffset, RETICLE_SIZE/2); +// CGContextAddLineToPoint(context, RETICLE_SIZE-lineOffset, 0.5*RETICLE_SIZE); +// CGContextStrokePath(context); +// } if (self.processor.is2D) { - UIColor* color = [UIColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:RETICLE_ALPHA]; + UIColor* color = [UIColor colorWithRed:0.0 green:0.55 blue:0.72 alpha:RETICLE_ALPHA]; CGContextSetStrokeColorWithColor(context, color.CGColor); CGContextSetLineWidth(context, RETICLE_WIDTH); - CGContextStrokeRect(context, - CGRectMake( - RETICLE_OFFSET, - RETICLE_OFFSET, - RETICLE_SIZE-2*RETICLE_OFFSET, - RETICLE_SIZE-2*RETICLE_OFFSET - ) - ); + + // No filling + [[UIColor clearColor] setFill]; + + // rechts unten + CGContextMoveToPoint(context, RETICLE_SIZE - 3*RETICLE_OFFSET, RETICLE_SIZE - RETICLE_OFFSET); + //CGContextAddLineToPoint(context, RETICLE_SIZE-RETICLE_OFFSET, RETICLE_SIZE - RETICLE_OFFSET); + CGContextAddArcToPoint(context, RETICLE_SIZE-RETICLE_OFFSET, RETICLE_SIZE - RETICLE_OFFSET, RETICLE_SIZE - RETICLE_OFFSET, RETICLE_OFFSET, RADIUS); + CGContextAddLineToPoint(context, RETICLE_SIZE-RETICLE_OFFSET, RETICLE_SIZE - 3*RETICLE_OFFSET); + CGContextDrawPath(context, kCGPathFillStroke); + + // rechts oben + CGContextMoveToPoint(context, RETICLE_SIZE - RETICLE_OFFSET, 3*RETICLE_OFFSET); + CGContextAddArcToPoint(context, RETICLE_SIZE - RETICLE_OFFSET, RETICLE_OFFSET, RETICLE_OFFSET, RETICLE_OFFSET, RADIUS); + CGContextAddLineToPoint(context, RETICLE_SIZE-3*RETICLE_OFFSET, RETICLE_OFFSET); + CGContextDrawPath(context, kCGPathFillStroke); + + // links oben + CGContextMoveToPoint(context, 3*RETICLE_OFFSET, RETICLE_OFFSET); + CGContextAddArcToPoint(context, RETICLE_OFFSET, RETICLE_OFFSET, RETICLE_OFFSET, 2*RETICLE_OFFSET, RADIUS); + CGContextAddLineToPoint(context, RETICLE_OFFSET, 3*RETICLE_OFFSET); + CGContextDrawPath(context, kCGPathFillStroke); + + // links unten + CGContextMoveToPoint(context, RETICLE_OFFSET, RETICLE_SIZE - 3*RETICLE_OFFSET); + CGContextAddArcToPoint(context, RETICLE_OFFSET, RETICLE_SIZE - RETICLE_OFFSET, 3*RETICLE_OFFSET, RETICLE_SIZE - RETICLE_OFFSET, RADIUS); + CGContextAddLineToPoint(context, 3*RETICLE_OFFSET, RETICLE_SIZE - RETICLE_OFFSET); + CGContextDrawPath(context, kCGPathFillStroke); } result = UIGraphicsGetImageFromCurrentImageContext(); diff --git a/src/ios/zxing-all-in-one.cpp b/src/ios/zxing-all-in-one.cpp index 2e6e5a510..fac5c0525 100644 --- a/src/ios/zxing-all-in-one.cpp +++ b/src/ios/zxing-all-in-one.cpp @@ -35,7 +35,12 @@ const char *barcodeFormatNames[] = { "EAN_13", "CODE_128", "CODE_39", - "ITF" + "ITF", + "GS1_DATA_MATRIX", + "GS1_QR_CODE", + "GS1_128", + "GS1_DATA_BAR", + "GS1_COMPOSITE" }; } @@ -222,6 +227,11 @@ void DecodeHints::addFormat(BarcodeFormat toadd) { case BarcodeFormat_CODE_128: hints |= BARCODEFORMAT_CODE_128_HINT; break; case BarcodeFormat_CODE_39: hints |= BARCODEFORMAT_CODE_39_HINT; break; case BarcodeFormat_ITF: hints |= BARCODEFORMAT_ITF_HINT; break; +// case BarcodeFormat_GS1_DATA_MATRIX: hints |= BarcodeFormat_GS1_DATA_MATRIX; break; +// case BarcodeFormat_GS1_QR_CODE: hints |= BarcodeFormat_GS1_QR_CODE; break; +// case BarcodeFormat_GS1_128: hints |= BarcodeFormat_GS1_128; break; +// case BarcodeFormat_GS1_DATA_BAR: hints |= BarcodeFormat_GS1_DATA_BAR; break; +// case BarcodeFormat_GS1_COMPOSITE: hints |= BarcodeFormat_GS1_COMPOSITE; break; default: throw IllegalArgumentException("Unrecognizd barcode format"); } } @@ -238,6 +248,11 @@ bool DecodeHints::containsFormat(BarcodeFormat tocheck) const { case BarcodeFormat_CODE_128: checkAgainst = BARCODEFORMAT_CODE_128_HINT; break; case BarcodeFormat_CODE_39: checkAgainst = BARCODEFORMAT_CODE_39_HINT; break; case BarcodeFormat_ITF: checkAgainst = BARCODEFORMAT_ITF_HINT; break; +// case BarcodeFormat_GS1_DATA_MATRIX: checkAgainst = BARCODEFORMAT_GS1_DATA_MATRIX_HINT; break; +// case BarcodeFormat_GS1_QR_CODE: checkAgainst = BARCODEFORMAT_GS1_QR_CODE_HINT; break; +// case BarcodeFormat_GS1_128: checkAgainst = BARCODEFORMAT_GS1_128_HINT; break; +// case BarcodeFormat_GS1_DATA_BAR: checkAgainst = BARCODEFORMAT_GS1_DATA_BAR_HINT; break; +// case BarcodeFormat_GS1_COMPOSITE: checkAgainst = BARCODEFORMAT_GS1_COMPOSITE_HINT; break; default: throw IllegalArgumentException("Unrecognizd barcode format"); } return (hints & checkAgainst); @@ -3877,9 +3892,17 @@ Ref DataMatrixReader::decode(Ref image, DecodeHints hints) #ifdef DEBUG cout << "(4) decoded, have decoderResult " << decoderResult.object_ << "\n" << flush; #endif - - Ref result( - new Result(decoderResult->getText(), decoderResult->getRawBytes(), points, BarcodeFormat_DATA_MATRIX)); + + Ref result; + result = new Result(decoderResult->getText(), decoderResult->getRawBytes(), points, BarcodeFormat_DATA_MATRIX); + + // Detect GS1 Codes + switch (decoderResult->getRawBytes()[0]) { + case 232: + result = new Result(decoderResult->getText(), decoderResult->getRawBytes(), points, BarcodeFormat_GS1_DATA_MATRIX); + break; + } + #ifdef DEBUG cout << "(5) created result " << result.object_ << ", returning\n" << flush; #endif @@ -4783,7 +4806,7 @@ void DecodedBitStreamParser::decodeC40Segment(Ref bits, ostringstream result << C40_SHIFT2_SET_CHARS[cValue]; } } else if (cValue == 27) { // FNC1 - result << ((char) 29); // translate as ASCII 29 + result << ((char) 29); // translate as ASCII 29 } else if (cValue == 30) { // Upper Shift upperShift = true; } else { diff --git a/src/ios/zxing-all-in-one.h b/src/ios/zxing-all-in-one.h index 2718ca9d8..39517d19f 100644 --- a/src/ios/zxing-all-in-one.h +++ b/src/ios/zxing-all-in-one.h @@ -1743,9 +1743,14 @@ namespace zxing { BarcodeFormat_EAN_13, BarcodeFormat_CODE_128, BarcodeFormat_CODE_39, - BarcodeFormat_ITF + BarcodeFormat_ITF, + BarcodeFormat_GS1_DATA_MATRIX, + BarcodeFormat_GS1_QR_CODE, + BarcodeFormat_GS1_128, + BarcodeFormat_GS1_DATA_BAR, + BarcodeFormat_GS1_COMPOSITE } BarcodeFormat; - + /* if you update the enum, please update the name in BarcodeFormat.cpp */ extern const char *barcodeFormatNames[]; } @@ -1903,9 +1908,14 @@ class DecodeHints { static const DecodeHintType BARCODEFORMAT_CODE_128_HINT = 1 << BarcodeFormat_CODE_128; static const DecodeHintType BARCODEFORMAT_CODE_39_HINT = 1 << BarcodeFormat_CODE_39; static const DecodeHintType BARCODEFORMAT_ITF_HINT = 1 << BarcodeFormat_ITF; +// static const DecodeHintType BARCODEFORMAT_GS1_DATA_MATRIX_HINT = 1 << BarcodeFormat_GS1_DATA_MATRIX; +// static const DecodeHintType BARCODEFORMAT_GS1_QR_CODE_HINT = 1 << BarcodeFormat_GS1_QR_CODE; +// static const DecodeHintType BARCODEFORMAT_GS1_128_HINT = 1 << BarcodeFormat_GS1_128; +// static const DecodeHintType BARCODEFORMAT_GS1_DATA_BAR_HINT = 1 << BarcodeFormat_GS1_DATA_BAR; +// static const DecodeHintType BARCODEFORMAT_GS1_COMPOSITE_HINT = 1 << BarcodeFormat_GS1_COMPOSITE; static const DecodeHintType CHARACTER_SET = 1 << 30; static const DecodeHintType TRYHARDER_HINT = 1 << 31; - + static const DecodeHints PRODUCT_HINT; static const DecodeHints ONED_HINT; static const DecodeHints DEFAULT_HINT;