diff options
Diffstat (limited to '')
-rw-r--r-- | iphone/ZBarCVImage.m | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/iphone/ZBarCVImage.m b/iphone/ZBarCVImage.m new file mode 100644 index 0000000..63065dd --- /dev/null +++ b/iphone/ZBarCVImage.m @@ -0,0 +1,195 @@ +//------------------------------------------------------------------------ +// Copyright 2010 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#import "ZBarCVImage.h" +#define MODULE ZBarCVImage +#import "debug.h" + +static NSOperationQueue *conversionQueue; + +static const void* +asyncProvider_getBytePointer (void *info) +{ + // block until data is available + ZBarCVImage *image = info; + assert(image); + [image waitUntilConverted]; + void *buf = image.rgbBuffer; + assert(buf); + return(buf); +} + +static const CGDataProviderDirectCallbacks asyncProvider = { + .version = 0, + .getBytePointer = asyncProvider_getBytePointer, + .releaseBytePointer = NULL, + .getBytesAtPosition = NULL, + .releaseInfo = (void*)CFRelease, +}; + +@implementation ZBarCVImage + +@synthesize pixelBuffer, rgbBuffer; + +- (void) dealloc +{ + self.pixelBuffer = NULL; + if(rgbBuffer) { + free(rgbBuffer); + rgbBuffer = NULL; + } + [conversion release]; + conversion = nil; + [super dealloc]; +} + +- (void) setPixelBuffer: (CVPixelBufferRef) newbuf +{ + CVPixelBufferRef oldbuf = pixelBuffer; + if(newbuf) + CVPixelBufferRetain(newbuf); + pixelBuffer = newbuf; + if(oldbuf) + CVPixelBufferRelease(oldbuf); +} + +- (void) waitUntilConverted +{ + // operation will at least have been queued already + NSOperation *op = [conversion retain]; + if(!op) + return; + [op waitUntilFinished]; + [op release]; +} + +- (UIImage*) UIImageWithOrientation: (UIImageOrientation) orient +{ + if(!conversion && !rgbBuffer) { + // start format conversion in separate thread + NSOperationQueue *queue = conversionQueue; + if(!queue) { + queue = conversionQueue = [NSOperationQueue new]; + queue.maxConcurrentOperationCount = 1; + } + else + [queue waitUntilAllOperationsAreFinished]; + + conversion = [[NSInvocationOperation alloc] + initWithTarget: self + selector: @selector(convertCVtoRGB) + object: nil]; + [queue addOperation: conversion]; + [conversion release]; + } + + // create UIImage before converted data is available + CGSize size = self.size; + int w = size.width; + int h = size.height; + + CGDataProviderRef datasrc = + CGDataProviderCreateDirect([self retain], 3 * w * h, &asyncProvider); + CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB(); + CGImageRef cgimg = + CGImageCreate(w, h, 8, 24, 3 * w, cs, + kCGBitmapByteOrderDefault, datasrc, + NULL, YES, kCGRenderingIntentDefault); + CGColorSpaceRelease(cs); + CGDataProviderRelease(datasrc); + + UIImage *uiimg = + [UIImage imageWithCGImage: cgimg + scale: 1 + orientation: orient]; + CGImageRelease(cgimg); + + return(uiimg); +} + +// convert video frame to a CGImage compatible RGB format +// FIXME this is temporary until we can find the native way... +- (void) convertCVtoRGB +{ + timer_start; + unsigned long format = self.format; + assert(format == zbar_fourcc('C','V','2','P')); + if(format != zbar_fourcc('C','V','2','P')) + return; + + CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly); + int w = CVPixelBufferGetWidth(pixelBuffer); + int h = CVPixelBufferGetHeight(pixelBuffer); + int dy = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0); + int duv = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1); + uint8_t *py = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0); + uint8_t *puv = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1); + if(!py || !puv || dy < w || duv < w) + goto error; + + int datalen = 3 * w * h; + // Quartz accesses some undocumented amount past allocated data? + // ...allocate extra to compensate + uint8_t *pdst = rgbBuffer = malloc(datalen + 3 * w); + if(!pdst) + goto error; + [self setData: rgbBuffer + withLength: datalen]; + + for(int y = 0; y < h; y++) { + const uint8_t *qy = py; + const uint8_t *quv = puv; + for(int x = 0; x < w; x++) { + int Y1 = *(qy++) - 16; + int Cb = *(quv) - 128; + int Cr = *(quv + 1) - 128; + Y1 *= 4769; + quv += (x & 1) << 1; + int r = (Y1 + 6537 * Cr + 2048) / 4096; + int g = (Y1 - 1604 * Cb - 3329 * Cr + 2048) / 4096; + int b = (Y1 + 8263 * Cb + 2048) / 4096; + + r = (r | -!!(r >> 8)) & -((r >> 8) >= 0); + g = (g | -!!(g >> 8)) & -((g >> 8) >= 0); + b = (b | -!!(b >> 8)) & -((b >> 8) >= 0); + + *(pdst++) = r; + *(pdst++) = g; + *(pdst++) = b; + } + py += dy; + if(y & 1) + puv += duv; + } + +error: + CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly); + zlog(@"convert time %gs", timer_elapsed(t_start, timer_now())); + + // release buffer as soon as conversion is complete + self.pixelBuffer = NULL; + + conversion = nil; +} + +@end |