[iphone] Memory leak fixed. Widget does not crash anymore after a few seconds. No...
[zxing.git] / iphone / ZXingWidget / Classes / Decoder.mm
1 //
2 //  Decoder.m
3 //  ZXing
4 //
5 //  Created by Christian Brunschen on 31/03/2008.
6 //
7 /*
8  * Copyright 2008 ZXing authors
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  *      http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  */
22
23 #import "Decoder.h"
24 #import "TwoDDecoderResult.h"
25 #import "FormatReader.h"
26
27 #include <zxing/BinaryBitmap.h>
28 #include <zxing/ReaderException.h>
29 #include <zxing/common/IllegalArgumentException.h>
30 #include <zxing/common/GlobalHistogramBinarizer.h>
31 #include "GrayBytesMonochromeBitmapSource.h"
32
33 using namespace zxing;
34
35 @implementation Decoder
36
37 @synthesize image;
38 @synthesize cropRect;
39 @synthesize subsetImage;
40 @synthesize subsetData;
41 @synthesize subsetWidth;
42 @synthesize subsetHeight;
43 @synthesize subsetBytesPerRow;
44 @synthesize delegate;
45 @synthesize operationQueue;
46
47
48 - (id)init {
49   if (self = [super init]) {
50     NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];
51     self.operationQueue = opQueue;
52     [opQueue release];
53   }
54   return self;
55 }
56 - (void)willDecodeImage {
57   if ([self.delegate respondsToSelector:@selector(decoder:willDecodeImage:usingSubset:)]) {
58     [self.delegate decoder:self willDecodeImage:self.image usingSubset:self.subsetImage];
59   }
60 }
61
62 - (void)progressDecodingImage:(NSString *)progress {
63   if ([self.delegate respondsToSelector:@selector(decoder:decodingImage:usingSubset:progress:)]) {
64     [self.delegate decoder:self decodingImage:self.image usingSubset:self.subsetImage progress:progress];
65   }
66 }
67
68 - (void)didDecodeImage:(TwoDDecoderResult *)result {
69
70   if ([self.delegate respondsToSelector:@selector(decoder:didDecodeImage:usingSubset:withResult:)]) {
71     [self.delegate decoder:self didDecodeImage:self.image usingSubset:self.subsetImage withResult:result];
72   }
73         
74   [result release];
75 }
76
77 - (void)failedToDecodeImage:(NSString *)reason {
78
79   if ([self.delegate respondsToSelector:@selector(decoder:failedToDecodeImage:usingSubset:reason:)]) {
80     [self.delegate decoder:self failedToDecodeImage:self.image usingSubset:self.subsetImage reason:reason];
81   }
82 }
83
84 #define SUBSET_SIZE 320.0
85 - (void) prepareSubset {
86   CGSize size = [image size];
87 #ifdef DEBUG
88   NSLog(@"decoding: image is (%.1f x %.1f), cropRect is (%.1f,%.1f)x(%.1f,%.1f)", size.width, size.height,
89       cropRect.origin.x, cropRect.origin.y, cropRect.size.width, cropRect.size.height);
90 #endif
91   float scale = fminf(1.0f, fmaxf(SUBSET_SIZE / cropRect.size.width, SUBSET_SIZE / cropRect.size.height));
92   CGPoint offset = CGPointMake(-cropRect.origin.x, -cropRect.origin.y);
93 #ifdef DEBUG
94   NSLog(@"  offset = (%.1f, %.1f), scale = %.3f", offset.x, offset.y, scale);
95 #endif
96   
97   subsetWidth = cropRect.size.width * scale;
98   subsetHeight = cropRect.size.height * scale;
99   
100   subsetBytesPerRow = ((subsetWidth + 0xf) >> 4) << 4;
101 #ifdef DEBUG
102   NSLog(@"decoding: image to decode is (%d x %d) (%d bytes/row)", subsetWidth, subsetHeight, subsetBytesPerRow);
103 #endif
104   
105   subsetData = (unsigned char *)malloc(subsetBytesPerRow * subsetHeight);
106 #ifdef DEBUG
107   NSLog(@"allocated %d bytes of memory", subsetBytesPerRow * subsetHeight);
108 #endif
109   
110   CGColorSpaceRef grayColorSpace = CGColorSpaceCreateDeviceGray();
111   
112   CGContextRef ctx = 
113   CGBitmapContextCreate(subsetData, subsetWidth, subsetHeight, 
114               8, subsetBytesPerRow, grayColorSpace, 
115               kCGImageAlphaNone);
116   CGColorSpaceRelease(grayColorSpace);
117   CGContextSetInterpolationQuality(ctx, kCGInterpolationNone);
118   CGContextSetAllowsAntialiasing(ctx, false);
119   // adjust the coordinate system
120   CGContextTranslateCTM(ctx, 0.0, subsetHeight);
121   CGContextScaleCTM(ctx, 1.0, -1.0);  
122   
123 #ifdef DEBUG
124   NSLog(@"created %dx%d bitmap context", subsetWidth, subsetHeight);
125 #endif
126   
127   UIGraphicsPushContext(ctx);
128   CGRect rect = CGRectMake(offset.x * scale, offset.y * scale, scale * size.width, scale * size.height);
129 #ifdef DEBUG
130   NSLog(@"rect for image = (%.1f,%.1f)x(%.1f,%.1f)", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
131 #endif
132   [image drawInRect:rect];
133   UIGraphicsPopContext();
134   
135 #ifdef DEBUG
136   NSLog(@"drew image into %d(%d)x%d  bitmap context", subsetWidth, subsetBytesPerRow, subsetHeight);
137 #endif
138   CGContextFlush(ctx);
139 #ifdef DEBUG
140   NSLog(@"flushed context");
141 #endif
142     
143   CGImageRef subsetImageRef = CGBitmapContextCreateImage(ctx);
144 #ifdef DEBUG
145   NSLog(@"created CGImage from context");
146 #endif
147   
148   self.subsetImage = [UIImage imageWithCGImage:subsetImageRef];
149   CGImageRelease(subsetImageRef);
150   
151   CGContextRelease(ctx);
152 #ifdef DEBUG
153   NSLog(@"released context");  
154 #endif
155 }  
156
157 - (void)decode:(id)arg {
158   
159   NSAutoreleasePool* mainpool = [[NSAutoreleasePool alloc] init];
160   { 
161
162     NSSet *formatReaders = [FormatReader formatReaders];
163     
164     Ref<LuminanceSource> source (new GrayBytesMonochromeBitmapSource(subsetData, subsetWidth, subsetHeight, subsetBytesPerRow));
165     Ref<Binarizer> binarizer (new GlobalHistogramBinarizer(source));
166     Ref<BinaryBitmap> grayImage (new BinaryBitmap(binarizer));
167 #ifdef DEBUG
168     NSLog(@"created GrayBytesMonochromeBitmapSource", subsetWidth, subsetHeight);
169     NSLog(@"grayImage count = %d", grayImage->count());
170 #endif
171     
172     TwoDDecoderResult *decoderResult = nil;
173     
174 #ifdef TRY_ROTATIONS
175     for (int i = 0; !decoderResult && i < 4; i++) {
176 #endif
177       for (FormatReader *reader in formatReaders) {
178         NSAutoreleasePool *secondarypool = [[NSAutoreleasePool alloc] init];
179         try {
180   #ifdef DEBUG
181           NSLog(@"decoding gray image");
182   #endif  
183           Ref<Result> result([reader decode:grayImage]);
184   #ifdef DEBUG
185           NSLog(@"gray image decoded");
186   #endif
187           
188           Ref<String> resultText(result->getText());
189           const char *cString = resultText->getText().c_str();
190           const std::vector<Ref<ResultPoint> > &resultPoints = result->getResultPoints();
191           NSMutableArray *points = 
192             [NSMutableArray arrayWithCapacity:resultPoints.size()];
193           
194           for (size_t i = 0; i < resultPoints.size(); i++) {
195             const Ref<ResultPoint> &rp = resultPoints[i];
196             CGPoint p = CGPointMake(rp->getX(), rp->getY());
197             [points addObject:[NSValue valueWithCGPoint:p]];
198           }
199           
200           NSString *resultString = [NSString stringWithCString:cString
201                                 encoding:NSUTF8StringEncoding];
202           
203           decoderResult = [[TwoDDecoderResult resultWithText:resultString
204                                                      points:points] retain];
205         } catch (ReaderException &rex) {
206           NSLog(@"failed to decode, caught ReaderException '%s'",
207               rex.what());
208         } catch (IllegalArgumentException &iex) {
209           NSLog(@"failed to decode, caught IllegalArgumentException '%s'", 
210               iex.what());
211         } catch (...) {
212           NSLog(@"Caught unknown exception!");
213         }
214         [secondarypool release];
215       }
216       
217 #ifdef TRY_ROTATIONS
218       if (!decoderResult) {
219 #ifdef DEBUG
220         NSLog(@"rotating gray image");
221 #endif
222         grayImage = grayImage->rotateCounterClockwise();
223 #ifdef DEBUG
224         NSLog(@"gray image rotated");
225 #endif
226       }
227     }
228 #endif
229           
230         free(subsetData);
231         self.subsetData = NULL;
232           
233     if (decoderResult) {
234       [self performSelectorOnMainThread:@selector(didDecodeImage:)
235                    withObject:decoderResult
236                 waitUntilDone:NO];
237     } else {
238       [self performSelectorOnMainThread:@selector(failedToDecodeImage:)
239                    withObject:NSLocalizedString(@"Decoder BarcodeDetectionFailure", @"No barcode detected.")
240                 waitUntilDone:NO];
241     }
242   }
243   
244   
245 #ifdef DEBUG
246   NSLog(@"finished decoding.");
247 #endif
248   [mainpool release];
249 }
250
251 - (void) decodeImage:(UIImage *)i {
252   [self decodeImage:i cropRect:CGRectMake(0.0f, 0.0f, i.size.width, i.size.height)];
253 }
254
255 - (void) decodeImage:(UIImage *)i cropRect:(CGRect)cr {
256   self.image = i;
257   self.cropRect = cr;
258   
259   [self prepareSubset];
260   [self willDecodeImage];
261   [self performSelectorOnMainThread:@selector(progressDecodingImage:)
262                withObject:NSLocalizedString(@"Decoder MessageWhileDecoding", @"Decoding ...")
263             waitUntilDone:NO];
264   NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(decode:) object:nil];
265   [operationQueue addOperation:op];
266   [op release];
267   //[self performSelectorInBackground:@selector(decode:) withObject:nil];
268
269  
270   //[self performSelector:@selector(decode:) onThread:decodingThread withObject:nil waitUntilDone:NO];
271   /*[NSThread detachNewThreadSelector:@selector(decode:) 
272                toTarget:self 
273                withObject:nil];*/
274 }
275
276 - (void) dealloc {
277
278   [image release];
279   [subsetImage release];
280   if (subsetData) free(subsetData);
281   [operationQueue release];
282   [super dealloc];
283 }
284
285 @end