9 Commits

Author SHA1 Message Date
Haik Aslanyan 63a209fb0c Update README.md 2020-06-23 21:41:58 +04:00
Haik Aslanyan 2fa33d0e88 updated readme 2020-05-09 13:30:36 +04:00
Haik Aslanyan 25d734d87d updated readme file 2020-05-07 21:19:32 +04:00
Haik Aslanyan a95fc1265c Merge pull request #7 from aslanyanhaik/develop
Develop
2020-05-07 21:11:29 +04:00
Haik Aslanyan e02e761c2e changed version number to 1.3.0 2020-05-07 21:10:48 +04:00
Haik Aslanyan 9e5ddf0082 updated example project 2020-05-07 21:10:04 +04:00
Haik Aslanyan aace0fd52d added ScanningMode parameter 2020-05-07 21:08:40 +04:00
Haik Aslanyan 63fd478b58 modified scanning logic for dark background images 2020-05-07 21:08:08 +04:00
Haik Aslanyan 783ac44550 moved decoding calculation into different queue 2020-05-07 17:07:24 +04:00
9 changed files with 42 additions and 36 deletions
+2
View File
@@ -64,5 +64,7 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIUserInterfaceStyle</key>
<string>Light</string>
</dict>
</plist>
+1 -2
View File
@@ -75,6 +75,7 @@ extension ViewController {
@IBAction func scan(_ sender: Any) {
let vc = RCCameraViewController()
coder.scanningMode = .lightBackground
vc.coder = coder
vc.delegate = self
present(vc, animated: true)
@@ -82,12 +83,10 @@ extension ViewController {
@IBAction func share(_ sender: Any) {
image.size = 1000
image.isTransparent = false
image.contentInsets = UIEdgeInsets(top: 50, left: 50, bottom: 50, right: 50)
let uiImage = try? coder.encode(image)
image.size = 300
image.contentInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
image.isTransparent = true
let vc = UIActivityViewController.init(activityItems: [uiImage!], applicationActivities: nil)
present(vc, animated: true)
}
+14 -12
View File
@@ -5,10 +5,8 @@
[![Twitter: @aslanyanhaik](https://img.shields.io/badge/Contact-Twitter-blue.svg?style=flat)](https://twitter.com/aslanyanhaik)
RoundCode is a Facebook messenger like custom QR code with lots of customization.
In addition to encoder the RoundCode also includes convenient camera scanner and decoder.
RoundCode is custom circular QR code with lots of customization.
Similar to Facebook messenger and Apple's App Clip Codes the RoundCode includes convenient camera scanner and decoder.
<h3 align="center">
<img src="appearance.png" alt="Different styles of RoundCode for iOS"/>
@@ -81,14 +79,14 @@ You can also decode a UIImage like this
```swift
let coder = RCCoder()
do {
messageLabel.text = try coder.decode(UIImage(named: code)!)
} catch {
//handle errors
}
do {
messageLabel.text = try coder.decode(UIImage(named: code)!)
} catch {
//handle errors
}
```
### Appearance
## Appearance
You can change the appearance like this
@@ -100,10 +98,14 @@ image.size = 300
image.gradientType = .linear(angle: CGFloat.pi)
image.tintColors = [.red, .black]
```
If image is on dark background you should change scanning mode to `darkBackground`
⚠️ When choosing colors or transparent background you should keep in mind that decoder will detect the code on light background with dark colors ⚠️
```swift
let coder = RCCoder()
coder.scanningMode = .darkBackground
```
### Advanced
## Advanced coding configuration
You can provide custom coding configuration in order to encode long text by reducing number of characters
+1 -1
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'RoundCode'
s.version = '1.2.0'
s.version = '1.3.0'
s.summary = 'Facebook messenger style custom barcode.'
s.description = <<-DESC
Encode and decode data into custom stylish barcode.
+2 -1
View File
@@ -28,5 +28,6 @@ struct RCConstants {
static let dotSizeScale: CGFloat = 0.08
static let dotPatterns: [CGFloat] = [6, 4, 2]
static let dotPointRange = (Float(1.3)...Float(2.5))
static let pixelThreshold = (UInt8(0)...UInt8(180))
static let lightBackgroundRange = UInt8(0)...UInt8(180)
static let darkBackgroundRange = UInt8(100)...UInt8(255)
}
-3
View File
@@ -27,7 +27,6 @@ public enum RCError: String, LocalizedError {
case longText
case decoding
case wrongImageSize
case wrongColors
public var errorDescription: String? {
switch self {
@@ -39,8 +38,6 @@ public enum RCError: String, LocalizedError {
return "Error decoding"
case .wrongImageSize:
return "Error decoding. Image width and height must be a equal"
case .wrongColors:
return "The tint colors should be in a range of \(RCConstants.pixelThreshold) grayscale with 1.0 alpha"
}
}
}
+5 -5
View File
@@ -27,6 +27,7 @@ struct RCImageDecoder {
internal let configuration: RCCoderConfiguration
internal var size = 720
internal var bytesPerRow = 720
internal var pixelThreshold = RCConstants.lightBackgroundRange
}
extension RCImageDecoder {
@@ -37,7 +38,7 @@ extension RCImageDecoder {
let transform = calculateTransform(from: points)
let mapper = RCPointMapper(transform: transform, size: size)
let locations = mapper.map(points: calculateBitLocations())
let bits = locations.map { RCConstants.pixelThreshold.contains(data[Int($0.x), Int($0.y)]) ? RCBit.one : RCBit.zero }
let bits = locations.map { pixelThreshold.contains(data[Int($0.x), Int($0.y)]) ? RCBit.one : RCBit.zero }
return bits
}
@@ -73,9 +74,8 @@ extension RCImageDecoder {
return points
}
private func scanPixelPattern(for mode: ScanMode, data: PixelContainer) -> [PixelPattern] {
var lastPattern = PixelPattern.init(bit: RCConstants.pixelThreshold.contains((data[0, 0])) ? RCBit.one : RCBit.zero, x: 0, y: 0, count: 0)
var lastPattern = PixelPattern(bit: pixelThreshold.contains((data[0, 0])) ? RCBit.one : RCBit.zero, x: 0, y: 0, count: 0)
var pixelPatterns = [lastPattern]
var count = 0
let maxSize = size * size
@@ -84,7 +84,7 @@ extension RCImageDecoder {
while count < maxSize {
let x = count % size
let y = count / size
let bit = RCConstants.pixelThreshold.contains(data[x, y]) ? RCBit.one : RCBit.zero
let bit = pixelThreshold.contains(data[x, y]) ? RCBit.one : RCBit.zero
if lastPattern.y == y, lastPattern.bit == bit {
lastPattern.count += 1
pixelPatterns[pixelPatterns.count - 1] = lastPattern
@@ -98,7 +98,7 @@ extension RCImageDecoder {
while count < maxSize {
let x = count / size
let y = count % size
let bit = RCConstants.pixelThreshold.contains(data[x, y]) ? RCBit.one : RCBit.zero
let bit = pixelThreshold.contains(data[x, y]) ? RCBit.one : RCBit.zero
if lastPattern.x == x, lastPattern.bit == bit {
lastPattern.count += 1
pixelPatterns[pixelPatterns.count - 1] = lastPattern
+5 -3
View File
@@ -160,7 +160,7 @@ extension RCCameraViewController {
input.device.activeVideoMaxFrameDuration = CMTimeMake(value: 1, timescale: 30)
captureSession.addInput(input)
let videoOutput = AVCaptureVideoDataOutput()
videoOutput.setSampleBufferDelegate(self, queue: .main)
videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.global(qos: .userInteractive))
videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: NSNumber(value: kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)]
captureSession.addOutput(videoOutput)
} catch {
@@ -197,8 +197,10 @@ extension RCCameraViewController: AVCaptureVideoDataOutputSampleBufferDelegate {
coder.imageDecoder.bytesPerRow = bytesPerRow
if let message = try? coder.decode(buffer: lumaCopy.assumingMemoryBound(to: UInt8.self)) {
captureSession.stopRunning()
delegate?.cameraViewController(didFinishScanning: message)
dismiss(animated: true)
DispatchQueue.main.async {[weak self] in
self?.delegate?.cameraViewController(didFinishScanning: message)
self?.dismiss(animated: true)
}
}
lumaCopy.deallocate()
CVPixelBufferUnlockBaseAddress(pixelBuffer, .readOnly)
+12 -9
View File
@@ -25,6 +25,11 @@ import UIKit
public final class RCCoder {
public let configuration: RCCoderConfiguration
public var scanningMode = ScanningMode.lightBackground {
didSet {
imageDecoder.pixelThreshold = scanningMode == .lightBackground ? RCConstants.lightBackgroundRange : RCConstants.darkBackgroundRange
}
}
internal lazy var imageDecoder = RCImageDecoder(configuration: self.configuration)
internal lazy var imageEncoder = RCImageEncoder(configuration: self.configuration)
internal lazy var bitCoder = RCBitCoder(configuration: self.configuration)
@@ -53,15 +58,12 @@ public extension RCCoder {
func validate(_ text: String) -> Bool {
configuration.validate(text)
}
func validateForBlackBackground(colors: [UIColor]) -> Bool {
colors.allSatisfy { color in
var white: CGFloat = 0
var alpha: CGFloat = 0
guard color.getWhite(&white, alpha: &alpha) else { return false }
guard alpha == 1.0 else { return false }
return RCConstants.pixelThreshold.contains(UInt8(white * 255))
}
}
public extension RCCoder {
enum ScanningMode {
case lightBackground
case darkBackground
}
}
@@ -73,3 +75,4 @@ extension RCCoder {
return message
}
}