Skip to content

Commit

Permalink
Update ZPLImageEncoder
Browse files Browse the repository at this point in the history
  • Loading branch information
scchn committed May 4, 2024
1 parent 272d25b commit 7b43a2e
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 91 deletions.
29 changes: 15 additions & 14 deletions Sources/ZPLBuilder/Commands/GraphicField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,22 @@ import CoreGraphics
public struct GraphicField: ZPLCommandConvertible {
private let encoder: ZPLImageEncoder

public var cgImage: CGImage?
public var size: CGSize?
public var image: ZPLImageReader?
public var command: String {
guard let cgImage,
let encoded = encoder.encode(cgImage: cgImage, targetSize: size)
guard let image = image, let encoded = encoder.encode(imageReader: image)
else {
return ""
}
return "^GFA,\(encoded.totalBytes),\(encoded.totalBytes),\(encoded.bytesPerRow),\(encoded.data)"
}

private init(size: CGSize?, encoder: ZPLImageEncoder) {
private init(encoder: ZPLImageEncoder) {
self.encoder = encoder
}

public init(cgImage: CGImage, size: CGSize? = nil, encoder: ZPLImageEncoder = .default) {
public init(imageReader: ZPLImageReader, encoder: ZPLImageEncoder = .default) {
self.encoder = encoder
self.cgImage = cgImage
self.size = size
self.image = imageReader
}
}
#endif
Expand All @@ -44,10 +41,12 @@ import AppKit

extension GraphicField {
public init(image: NSImage, size: CGSize? = nil, encoder: ZPLImageEncoder = .default) {
if let cgImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil) {
self.init(cgImage: cgImage, size: size, encoder: encoder)
if let cgImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil),
let image = ZPLCGImageReader(cgImage: cgImage, targetSize: size)
{
self.init(imageReader: image, encoder: encoder)
} else {
self.init(size: size, encoder: encoder)
self.init(encoder: encoder)
}
}
}
Expand All @@ -56,10 +55,12 @@ import UIKit

extension GraphicField {
public init(image: UIImage, size: CGSize? = nil, encoder: ZPLImageEncoder = .default) {
if let cgImage = image.cgImage {
self.init(cgImage: cgImage, size: size, encoder: encoder)
if let cgImage = image.cgImage,
let image = ZPLCGImageReader(cgImage: cgImage, targetSize: size)
{
self.init(imageReader: image, encoder: encoder)
} else {
self.init(size: size, encoder: encoder)
self.init(encoder: encoder)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/ZPLBuilder/Commands/HostDirectoryList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// HostDirectoryList.swift
//
//
// Created by 陳世爵 on 2023/12/27.
// Created by chen on 2023/12/27.
//

import Foundation
Expand Down
2 changes: 1 addition & 1 deletion Sources/ZPLBuilder/Commands/PrintDirectoryLabel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// PrintDirectoryLabel.swift
//
//
// Created by 陳世爵 on 2023/12/4.
// Created by chen on 2023/12/4.
//

import Foundation
Expand Down
2 changes: 1 addition & 1 deletion Sources/ZPLBuilder/Commands/UseFontNameToCallFont.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// UseFontNameToCallFont.swift
//
//
// Created by 陳世爵 on 2023/12/4.
// Created by chen on 2023/12/4.
//

import Foundation
Expand Down
75 changes: 75 additions & 0 deletions Sources/ZPLBuilder/Utils/ZPLImageEncoder/ZPLCGImageReader.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//
// ZPLCGImageReader.swift
//
//
// Created by chen on 2024/5/4.
//

#if canImport(CoreGraphics)
import Foundation
import CoreGraphics
import OSLog

public struct ZPLCGImageReader: ZPLImageReader {
private let imageData: CFData
private let imagePtr: UnsafePointer<UInt8>
private let bytesPerPixel: Int
private let bytesPerRow: Int

public var width: Int
public var height: Int

public init?(cgImage: CGImage, targetSize: CGSize?) {
guard cgImage.width > 0, cgImage.height > 0 else {
return nil
}

self.width = {
if let targetWidth = targetSize?.width {
return Int(targetWidth)
} else {
return cgImage.width
}
}()
self.height = {
if let targetHeight = targetSize?.height {
return Int(targetHeight)
} else {
return cgImage.height
}
}()

guard
self.width > 0, self.height > 0,
let cgImage = width == cgImage.width && height == cgImage.height
? cgImage
: cgImage.resized(width: width, height: height),
let imageData = cgImage.dataProvider?.data,
let imagePtr = CFDataGetBytePtr(imageData)
else {
return nil
}

self.imageData = imageData
self.imagePtr = imagePtr
self.bytesPerPixel = cgImage.bitsPerPixel / 8
self.bytesPerRow = cgImage.bytesPerRow
}

private func offset(x: Int, y: Int) -> Int {
y * bytesPerRow + x * bytesPerPixel
}

public func getRed(x: Int, y: Int) -> UInt8 {
imagePtr[offset(x: x, y: y)]
}

public func getGreen(x: Int, y: Int) -> UInt8 {
imagePtr[offset(x: x, y: y) + 1]
}

public func getBlue(x: Int, y: Int) -> UInt8 {
imagePtr[offset(x: x, y: y) + 2]
}
}
#endif
14 changes: 14 additions & 0 deletions Sources/ZPLBuilder/Utils/ZPLImageEncoder/ZPLImage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// File.swift
//
//
// Created by chen on 2024/5/4.
//

import Foundation

public struct ZPLImage {
public var bytesPerRow: Int
public var totalBytes: Int
public var data: String
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,7 @@
// Created by chen on 2023/11/18.
//

#if canImport(CoreGraphics)
import Foundation
import CoreGraphics
import OSLog

@available(macOS 11.0, iOS 14.0, *)
private let logger = Logger(subsystem: "ZPLBuidler", category: "ZPLImageEncoder")

public struct ZPLImage {
public var bytesPerRow: Int
public var totalBytes: Int
public var data: String
}

public class ZPLImageEncoder {
/// The default encoder of ``GraphicField``.
Expand All @@ -32,79 +20,35 @@ public class ZPLImageEncoder {

}

public func encode(cgImage: CGImage, targetSize: CGSize?) -> ZPLImage? {
let width: Int
let height: Int

if let targetWidth = targetSize?.width {
width = Int(targetWidth)
} else {
width = cgImage.width
}

if let targetHeight = targetSize?.height {
height = Int(targetHeight)
} else {
height = cgImage.height
}

guard width > 0, height > 0, cgImage.width > 0, cgImage.height > 0 else {
if #available(macOS 11.0, iOS 14.0, *) {
logger.notice("Invalid image size or target size.")
}
return nil
}
guard let cgImage = width == cgImage.width && height == cgImage.height
? cgImage
: cgImage.resized(width: width, height: height)
else {
if #available(macOS 11.0, iOS 14.0, *) {
logger.notice("Failed to resize image.")
}
return nil
}
guard let data = isCompressed ? makeCompressedData(cgImage: cgImage) : makeData(cgImage: cgImage),
public func encode(imageReader: ZPLImageReader) -> ZPLImage? {
guard let data = isCompressed ? makeCompressedData(imageReader: imageReader) : makeData(imageReader: imageReader),
!data.isEmpty
else {
if #available(macOS 11.0, iOS 14.0, *) {
logger.notice("Failed to encode image.")
}
return nil
}

let bytesPerRow = width / Self.componentValueSize + (width.isMultiple(of: Self.componentValueSize) ? 0 : 1)
let totalBytes = bytesPerRow * height
let bytesPerRow = imageReader.width / Self.componentValueSize + (imageReader.width.isMultiple(of: Self.componentValueSize) ? 0 : 1)
let totalBytes = bytesPerRow * imageReader.height

return ZPLImage(bytesPerRow: bytesPerRow, totalBytes: totalBytes, data: data)
}

private func enumerateComponents(cgImage: CGImage, _ block: (Int, Int, String, Bool) -> Void) {
guard let imageData = cgImage.dataProvider?.data,
let imagePtr = CFDataGetBytePtr(imageData)
else {
return
}

let width = cgImage.width
let height = cgImage.height
let bytesPerPixel = cgImage.bitsPerPixel / 8
let bytesPerRow = cgImage.bytesPerRow
private func enumerateComponents(imageReader: ZPLImageReader, _ block: (Int, Int, String, Bool) -> Void) {
let width = imageReader.width
let height = imageReader.height

for y in 0..<height {
let baseOffset = y * bytesPerRow
var componentValue = 0
var pos = 0

for x in 0..<width {
let offset = baseOffset + (x * bytesPerPixel)
let r = Int(imagePtr[offset])
let g = Int(imagePtr[offset + 1])
let b = Int(imagePtr[offset + 2])
let sum = r + g + b
let color = Int(imageReader.getRed(x: x, y: y))
+ Int(imageReader.getGreen(x: x, y: y))
+ Int(imageReader.getBlue(x: x, y: y))

componentValue <<= 1

if sum <= whiteThreshold {
if color <= whiteThreshold {
componentValue += 1
}

Expand Down Expand Up @@ -133,10 +77,10 @@ public class ZPLImageEncoder {
}

extension ZPLImageEncoder {
private func makeData(cgImage: CGImage) -> String? {
private func makeData(imageReader: ZPLImageReader) -> String? {
var body = ""

enumerateComponents(cgImage: cgImage) { x, y, components, _ in
enumerateComponents(imageReader: imageReader) { x, y, components, _ in
body.append(components)
}

Expand All @@ -145,17 +89,17 @@ extension ZPLImageEncoder {
}

extension ZPLImageEncoder {
private func makeCompressedData(cgImage: CGImage) -> String? {
let divisable = cgImage.width.isMultiple(of: Self.componentValueSize)
let bytesPerRow = cgImage.width / Self.componentValueSize + (divisable ? 0 : 1)
private func makeCompressedData(imageReader: ZPLImageReader) -> String? {
let divisable = imageReader.width.isMultiple(of: Self.componentValueSize)
let bytesPerRow = imageReader.width / Self.componentValueSize + (divisable ? 0 : 1)
let maxRepeatCount = bytesPerRow * 2
var repeatCount = 1
var aux: Character?
var currLine: [Character] = []
var prevLine: [Character] = []
var body: [Character] = []

enumerateComponents(cgImage: cgImage) { x, y, components, isEOL in
enumerateComponents(imageReader: imageReader) { x, y, components, isEOL in
for component in components {
guard let _aux = aux else {
aux = component
Expand Down Expand Up @@ -256,4 +200,3 @@ private let encodingTable: [Int: Character] = [
380: "y",
400: "z",
]
#endif
17 changes: 17 additions & 0 deletions Sources/ZPLBuilder/Utils/ZPLImageEncoder/ZPLImageReader.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// ZPLImageReader.swift
//
//
// Created by chen on 2024/5/4.
//

import Foundation

public protocol ZPLImageReader {
var width: Int { get }
var height: Int { get }

func getRed(x: Int, y: Int) -> UInt8
func getGreen(x: Int, y: Int) -> UInt8
func getBlue(x: Int, y: Int) -> UInt8
}

0 comments on commit 7b43a2e

Please sign in to comment.