Skip to content

Commit

Permalink
Added razor pixel style
Browse files Browse the repository at this point in the history
  • Loading branch information
dagronf committed May 6, 2024
1 parent 05b97c2 commit 89577f9
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 5 deletions.
Binary file added Art/images/data_razor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Art/paths.pcvd
Binary file not shown.
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ let package = Package(
// A microframework for cleaning handling image conversion
.package(
url: "https://github.com/dagronf/SwiftImageReadWrite",
.upToNextMinor(from: "1.6.1")
.upToNextMinor(from: "1.7.2")
),
],
targets: [
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@ however you can supply a `PixelShape` object to custom-draw the data. There are
|<img src="./Art/images/data_flower.png" width="60"/> |"flower"|`QRCode.PixelShape.Flower`|A 'flower' style|
|<img src="./Art/images/data_horizontal.png" width="60"/> |"horizontal"|`QRCode.PixelShape.Horizontal`|The pixels are horizonally joined to make continuous horizontal bars|
|<img src="./Art/images/data_pointy.png" width="60"/> |"pointy"|`QRCode.PixelShape.Pointy`|A 'pointy' style|
|<img src="./Art/images/data_razor.png" width="60"/> |"razor"|`QRCode.PixelShape.Razor`| A 'razor' style|
|<img src="./Art/images/data_roundedEndIndent.png" width="60"/> |"roundedEndIndent"|`QRCode.PixelShape.RoundedEndIndent`|Rounded path with circular indented ends|
|<img src="./Art/images/data_roundedPath.png" width="60"/> |"roundedPath"|`QRCode.PixelShape.RoundedPath`|A smooth rounded-edge path|
|<img src="./Art/images/data_roundedRect.png" width="60"/> |"roundedRect"|`QRCode.PixelShape.RoundedRect`|A basic rounded rectangle pixel with configurable radius|
Expand Down
5 changes: 3 additions & 2 deletions Sources/QRCode/styles/data/QRCodePixelShapeFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,13 @@ import Foundation
QRCode.PixelShape.Vertical.self,
QRCode.PixelShape.Flower.self,
QRCode.PixelShape.Horizontal.self,
QRCode.PixelShape.RoundedPath.self,
QRCode.PixelShape.Pointy.self,
QRCode.PixelShape.CurvePixel.self,
QRCode.PixelShape.Razor.self,
QRCode.PixelShape.RoundedEndIndent.self,
QRCode.PixelShape.RoundedPath.self,
QRCode.PixelShape.Sharp.self,
QRCode.PixelShape.Star.self,
QRCode.PixelShape.RoundedEndIndent.self,
QRCode.PixelShape.Shiny.self,
].sorted(by: { a, b in a.Title < b.Title })
super.init()
Expand Down
191 changes: 191 additions & 0 deletions Sources/QRCode/styles/data/QRCodePixelShapeRazor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
//
// QRCodePixelShapeRazor.swift
//
// Copyright © 2024 Darren Ford. All rights reserved.
//
// MIT license
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial
// portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
// OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

import Foundation
import CoreGraphics

public extension QRCode.PixelShape {
@objc(QRCodePixelShapeRazor) class Razor: NSObject, QRCodePixelShapeGenerator {
/// The generator name
@objc public static let Name: String = "razor"
/// The generator title
@objc public static var Title: String { "Razor" }
/// Create an instance of this path generator with the specified settings
@objc public static func Create(_ settings: [String: Any]?) -> any QRCodePixelShapeGenerator { Razor() }

/// Make a copy of the object
@objc public func copyShape() -> any QRCodePixelShapeGenerator { Razor() }

static let templateSquare: CGPath = {
CGPath(rect: CGRect(origin: .zero, size: .init(width: 10, height: 10)), transform: nil)
}()

static let templatePointingDown: CGPath = {
let p = CGMutablePath()
p.move(to: CGPoint(x: 0, y: 0))
p.line(to: CGPoint(x: 10, y: 0))
p.line(to: CGPoint(x: 10, y: 10))
p.curve(to: CGPoint(x: 2, y: 8), controlPoint1: CGPoint(x: 10, y: 10), controlPoint2: CGPoint(x: 4.5, y: 9.5))
p.curve(to: CGPoint(x: 0, y: 4), controlPoint1: CGPoint(x: -0.5, y: 6.5), controlPoint2: CGPoint(x: 0, y: 4))
p.line(to: CGPoint(x: 0, y: 0))
p.close()
return p
}()

static let templatePointingUp: CGPath = {
let p = CGMutablePath()
p.move(to: CGPoint(x: 0, y: 0))
p.curve(to: CGPoint(x: 8, y: 2), controlPoint1: CGPoint(x: 0, y: 0), controlPoint2: CGPoint(x: 5.5, y: 0.5))
p.curve(to: CGPoint(x: 10, y: 6), controlPoint1: CGPoint(x: 10.5, y: 3.5), controlPoint2: CGPoint(x: 10, y: 6))
p.line(to: CGPoint(x: 10, y: 10))
p.line(to: CGPoint(x: 0, y: 10))
p.line(to: CGPoint(x: 0, y: 0))
p.close()
return p
}()

static let templatePointingRight: CGPath = {
let p = CGMutablePath()
p.move(to: CGPoint(x: 0, y: 0))
p.line(to: CGPoint(x: 10, y: 0))
p.curve(to: CGPoint(x: 8, y: 8), controlPoint1: CGPoint(x: 10, y: 0), controlPoint2: CGPoint(x: 9.5, y: 5.5))
p.curve(to: CGPoint(x: 4, y: 10), controlPoint1: CGPoint(x: 6.5, y: 10.5), controlPoint2: CGPoint(x: 4, y: 10))
p.line(to: CGPoint(x: 0, y: 10))
p.line(to: CGPoint(x: 0, y: 0))
p.close()
return p
}()

static let templatePointingLeft: CGPath = {
let p = CGMutablePath()
p.move(to: CGPoint(x: 2, y: 2))
p.curve(to: CGPoint(x: 6, y: 0), controlPoint1: CGPoint(x: 3.5, y: -0.5), controlPoint2: CGPoint(x: 6, y: 0))
p.line(to: CGPoint(x: 10, y: 0))
p.line(to: CGPoint(x: 10, y: 10))
p.line(to: CGPoint(x: 0, y: 10))
p.curve(to: CGPoint(x: 2, y: 2), controlPoint1: CGPoint(x: 0, y: 10), controlPoint2: CGPoint(x: 0.5, y: 4.5))

p.close()
return p
}()
}
}

public extension QRCode.PixelShape.Razor {
/// Generate a CGPath from the matrix contents
/// - Parameters:
/// - matrix: The matrix to generate
/// - size: The size of the resulting CGPath
/// - Returns: A path
@objc func generatePath(from matrix: BoolMatrix, size: CGSize) -> CGPath {
let dx = size.width / CGFloat(matrix.dimension)
let dy = size.height / CGFloat(matrix.dimension)
let dm = min(dx, dy)

let xoff = (size.width - (CGFloat(matrix.dimension) * dm)) / 2.0
let yoff = (size.height - (CGFloat(matrix.dimension) * dm)) / 2.0

// The scale required to convert our template paths to output path size
let w = QRCode.PixelShape.RoundedPath.DefaultSize.width
let scaleTransform = CGAffineTransform(scaleX: dm / w, y: dm / w)

let path = CGMutablePath()

for row in 0 ..< matrix.dimension {
for col in 0 ..< matrix.dimension {
guard matrix[row, col] == true else { continue }

let hasLeft: Bool = {
if col == 0 { return false }
return (col - 1) >= 0 ? matrix[row, col - 1] : false
}()
let hasRight: Bool = {
if col == (matrix.dimension - 1) { return false }
return (col + 1) < matrix.dimension ? matrix[row, col + 1] : false
}()
let hasTop: Bool = {
if row == 0 { return false }
return (row - 1) >= 0 ? matrix[row - 1, col] : false
}()
let hasBottom: Bool = {
if row == (matrix.dimension - 1) { return false }
return (row + 1) < matrix.dimension ? matrix[row + 1, col] : false
}()

let translate = CGAffineTransform(translationX: CGFloat(col) * dm + xoff, y: CGFloat(row) * dm + yoff)

if !hasLeft, !hasRight, !hasTop, !hasBottom {
// isolated block
path.addPath(
Self.templateSquare,
transform: scaleTransform.concatenating(translate)
)
}
else if !hasLeft, !hasRight, !hasTop, hasBottom {
// pointing up block
path.addPath(
Self.templatePointingUp,
transform: scaleTransform.concatenating(translate)
)
}
else if !hasLeft, !hasRight, !hasBottom, hasTop {
// pointing down block
path.addPath(
Self.templatePointingDown,
transform: scaleTransform.concatenating(translate)
)
}
else if !hasTop, !hasRight, !hasBottom, hasLeft {
// pointing right block
path.addPath(
Self.templatePointingRight,
transform: scaleTransform.concatenating(translate)
)
}
else if !hasTop, !hasLeft, !hasBottom, hasRight {
// pointing left block
path.addPath(
Self.templatePointingLeft,
transform: scaleTransform.concatenating(translate)
)
}
else {
path.addPath(
Self.templateSquare,
transform: scaleTransform.concatenating(translate)
)
}
}
}
return path
}
}

// MARK: - Settings

public extension QRCode.PixelShape.Razor {
/// Does the shape generator support setting values for a particular key?
@objc func supportsSettingValue(forKey key: String) -> Bool { false }
/// Returns a storable representation of the shape handler
@objc func settings() -> [String: Any] { return [:] }
/// Set a configuration value for a particular setting string
@objc func setSettingValue(_ value: Any?, forKey key: String) -> Bool { false }
}
4 changes: 2 additions & 2 deletions Sources/QRCode/views/QRCodeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ import UIKit
self.rebuildQRCode()
}

#if os(iOS) || os(visionOS) || os(tvOS)
#if os(iOS)
public override func didMoveToWindow() {
super.didMoveToWindow()
if self.supportsDrag {
Expand Down Expand Up @@ -370,7 +370,7 @@ extension QRCodeView: NSPasteboardItemDataProvider {
}
#endif

#if os(iOS) || os(tvOS) || os(visionOS)
#if os(iOS)

extension QRCodeView: UIDragInteractionDelegate {
public func dragInteraction(_ interaction: UIDragInteraction, itemsForBeginning session: UIDragSession) -> [UIDragItem] {
Expand Down

0 comments on commit 89577f9

Please sign in to comment.