ZXIBarcodeReader.mm 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. // Copyright 2022 KURZ Digital Solutions GmbH
  2. //
  3. // SPDX-License-Identifier: Apache-2.0
  4. #import "ZXIBarcodeReader.h"
  5. #import "ReadBarcode.h"
  6. #import "ImageView.h"
  7. #import "Barcode.h"
  8. #import "GTIN.h"
  9. #import "ZXIFormatHelper.h"
  10. #import "ZXIPosition+Helper.h"
  11. #import "ZXIErrors.h"
  12. using namespace ZXing;
  13. NSString *stringToNSString(const std::string &text) {
  14. return [[NSString alloc]initWithBytes:text.data() length:text.size() encoding:NSUTF8StringEncoding];
  15. }
  16. ZXIGTIN *getGTIN(const Result &result) {
  17. try {
  18. auto country = GTIN::LookupCountryIdentifier(result.text(TextMode::Plain), result.format());
  19. auto addOn = GTIN::EanAddOn(result);
  20. return country.empty()
  21. ? nullptr
  22. : [[ZXIGTIN alloc]initWithCountry:stringToNSString(country)
  23. addOn:stringToNSString(addOn)
  24. price:stringToNSString(GTIN::Price(addOn))
  25. issueNumber:stringToNSString(GTIN::IssueNr(addOn))];
  26. } catch (std::exception e) {
  27. // Because invalid GTIN data can lead to exceptions, in which case
  28. // we don't want to discard the whole result.
  29. return nullptr;
  30. }
  31. }
  32. @interface ZXIReaderOptions()
  33. @property(nonatomic) ZXing::ReaderOptions cppOpts;
  34. @end
  35. @interface ZXIBarcodeReader()
  36. @property (nonatomic, strong) CIContext* ciContext;
  37. @end
  38. @implementation ZXIBarcodeReader
  39. - (instancetype)init {
  40. return [self initWithOptions: [[ZXIReaderOptions alloc] init]];
  41. }
  42. - (instancetype)initWithOptions:(ZXIReaderOptions*)options{
  43. self = [super init];
  44. self.ciContext = [[CIContext alloc] initWithOptions:@{kCIContextWorkingColorSpace: [NSNull new]}];
  45. self.options = options;
  46. return self;
  47. }
  48. - (NSArray<ZXIResult *> *)readCVPixelBuffer:(nonnull CVPixelBufferRef)pixelBuffer
  49. error:(NSError *__autoreleasing _Nullable *)error {
  50. OSType pixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer);
  51. // We tried to work with all luminance based formats listed in kCVPixelFormatType
  52. // but only the following ones seem to be supported on iOS.
  53. switch (pixelFormat) {
  54. case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
  55. case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
  56. NSInteger cols = CVPixelBufferGetWidth(pixelBuffer);
  57. NSInteger rows = CVPixelBufferGetHeight(pixelBuffer);
  58. NSInteger bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
  59. CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
  60. const uint8_t * bytes = static_cast<const uint8_t *>(CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0));
  61. ImageView imageView = ImageView(
  62. static_cast<const uint8_t *>(bytes),
  63. static_cast<int>(cols),
  64. static_cast<int>(rows),
  65. ImageFormat::Lum,
  66. static_cast<int>(bytesPerRow),
  67. 0);
  68. NSArray* results = [self readImageView:imageView error:error];
  69. CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
  70. return results;
  71. }
  72. // If given pixel format is not a supported type with a luminance channel we just use the
  73. // default method
  74. return [self readCIImage:[[CIImage alloc] initWithCVImageBuffer:pixelBuffer] error:error];
  75. }
  76. - (NSArray<ZXIResult *> *)readCIImage:(nonnull CIImage *)image
  77. error:(NSError *__autoreleasing _Nullable *)error {
  78. CGImageRef cgImage = [self.ciContext createCGImage:image fromRect:image.extent];
  79. auto results = [self readCGImage:cgImage error:error];
  80. CGImageRelease(cgImage);
  81. return results;
  82. }
  83. - (NSArray<ZXIResult *> *)readCGImage:(nonnull CGImageRef)image
  84. error:(NSError *__autoreleasing _Nullable *)error {
  85. CGFloat cols = CGImageGetWidth(image);
  86. CGFloat rows = CGImageGetHeight(image);
  87. NSMutableData *data = [NSMutableData dataWithLength: cols * rows];
  88. CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
  89. CGContextRef contextRef = CGBitmapContextCreate(data.mutableBytes,// Pointer to backing data
  90. cols, // Width of bitmap
  91. rows, // Height of bitmap
  92. 8, // Bits per component
  93. cols, // Bytes per row
  94. colorSpace, // Colorspace
  95. kCGBitmapByteOrderDefault); // Bitmap info flags
  96. CGColorSpaceRelease(colorSpace);
  97. CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image);
  98. CGContextRelease(contextRef);
  99. ImageView imageView = ImageView(
  100. static_cast<const uint8_t *>(data.bytes),
  101. static_cast<int>(cols),
  102. static_cast<int>(rows),
  103. ImageFormat::Lum);
  104. return [self readImageView:imageView error:error];
  105. }
  106. - (NSArray<ZXIResult*> *)readImageView:(ImageView)imageView
  107. error:(NSError *__autoreleasing _Nullable *)error {
  108. try {
  109. Barcodes results = ReadBarcodes(imageView, self.options.cppOpts);
  110. NSMutableArray* zxiResults = [NSMutableArray array];
  111. for (auto result: results) {
  112. [zxiResults addObject:
  113. [[ZXIResult alloc] init:stringToNSString(result.text())
  114. format:ZXIFormatFromBarcodeFormat(result.format())
  115. bytes:[[NSData alloc] initWithBytes:result.bytes().data() length:result.bytes().size()]
  116. position:[[ZXIPosition alloc]initWithPosition: result.position()]
  117. orientation:result.orientation()
  118. ecLevel:stringToNSString(result.ecLevel())
  119. symbologyIdentifier:stringToNSString(result.symbologyIdentifier())
  120. sequenceSize:result.sequenceSize()
  121. sequenceIndex:result.sequenceIndex()
  122. sequenceId:stringToNSString(result.sequenceId())
  123. readerInit:result.readerInit()
  124. lineCount:result.lineCount()
  125. gtin:getGTIN(result)]
  126. ];
  127. }
  128. return zxiResults;
  129. } catch(std::exception &e) {
  130. SetNSError(error, ZXIReaderError, e.what());
  131. return nil;
  132. } catch (...) {
  133. SetNSError(error, ZXIReaderError, "An unknown error occurred");
  134. return nil;
  135. }
  136. }
  137. @end