Created
January 21, 2023 04:46
-
-
Save CocoaBob/7eff06420a71a235e17d4ae7292a8202 to your computer and use it in GitHub Desktop.
Get UIImage from PCX data, supports both monochrome and color formats, need to install `pod "BinaryDataScanner", '1.0.3'`. Ref: https://github.com/actumn/playground/tree/master/swift/swift_load_PCX
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import UIKit | |
struct PCXhead { | |
var identity: UInt8 = 0 | |
var pcxver: UInt8 = 0 | |
var compression: UInt8 = 0 | |
var bpp: UInt8 = 0 | |
var minX: UInt16 = 0 | |
var minY: UInt16 = 0 | |
var maxX: UInt16 = 0 | |
var maxY: UInt16 = 0 | |
var hDPI: UInt16 = 0 | |
var vDPI: UInt16 = 0 | |
var colormap: Data? // 16 * 3 | |
var reserved1: UInt8 = 0 | |
var np: UInt8 = 0 | |
var bpl: UInt16 = 0 | |
var pallinfo: UInt16 = 0 | |
var width: UInt16 = 0 | |
var height: UInt16 = 0 | |
var reserved2: Data? // 54 | |
} | |
public struct PixelData { | |
var r: UInt8 | |
var g: UInt8 | |
var b: UInt8 | |
var a: UInt8 = 255 | |
} | |
private let rgbColorSpace = CGColorSpaceCreateDeviceRGB() | |
private let bitmapInfo:CGBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue) | |
public func imageFromARGB32Bitmap(pixels:[PixelData], width: Int, height: Int) -> UIImage? { | |
let bitsPerComponent: Int = 8 | |
let bitsPerPixel: Int = 32 | |
assert(pixels.count == Int(width * height)) | |
var data = pixels | |
let providerRef = CGDataProvider(data: NSData(bytes: &data, length: data.count * MemoryLayout<PixelData>.size))! | |
if let cgImage = CGImage(width: width, | |
height: height, | |
bitsPerComponent: bitsPerComponent, | |
bitsPerPixel: bitsPerPixel, | |
bytesPerRow: width * MemoryLayout<PixelData>.size, | |
space: rgbColorSpace, | |
bitmapInfo: bitmapInfo, | |
provider: providerRef, | |
decode: nil, | |
shouldInterpolate: true, | |
intent: CGColorRenderingIntent.defaultIntent) { | |
return UIImage(cgImage: cgImage) | |
} | |
return nil | |
} | |
func pcxDataToImage(rawData: Data) -> UIImage? { | |
let data = BinaryDataScanner(data: rawData, endian: CFByteOrderLittleEndian) | |
var bytes = [UInt8]() | |
var palettes = [PixelData]() | |
var pixels = [PixelData]() | |
do { | |
let pcxHead = try PCXhead( | |
identity : data.read8(), | |
pcxver : data.read8(), | |
compression : data.read8(), | |
bpp : data.read8(), // The number of bits constituting one plane. Most often 1, 2, 4 or 8 | |
minX : data.read16(), // The minimum x co-ordinate of the image position | |
minY : data.read16(), // The minimum y co-ordinate of the image position | |
maxX : data.read16(), // The maximum x co-ordinate of the image position | |
maxY : data.read16(), // The maximum y co-ordinate of the image position | |
hDPI : data.read16(), // The horizontal image resolution in DPI | |
vDPI : data.read16(), // The vertical image resolution in DPI | |
colormap : data.data(count: 48),// The EGA palette for 16-color images | |
reserved1 : data.read8(), // The first reserved field, usually set to zero | |
np : data.read8(), // The number of color planes constituting the pixel data. Mostly chosen to be 1, 3, or 4 | |
bpl : data.read16(), // The number of bytes of one color plane representing a single scan line | |
pallinfo : data.read16(), // The mode in which to construe the palette, | |
// 1: The palette contains monochrome or color information | |
// 2: The palette contains grayscale information | |
width : data.read16(), // The horizontal resolution of the source system's screen | |
height : data.read16(), // The vertical resolution of the source system's screen | |
reserved2 : data.data(count: 54)// The second reserved field, intended for future extensions, and usually set to zero bytes | |
) | |
let rowSize = Int(pcxHead.bpl) * Int(pcxHead.np) | |
let xInit = Int(pcxHead.minX) | |
let yInit = Int(pcxHead.minY) | |
let width = Int(pcxHead.maxX - pcxHead.minX + 1) | |
let height = Int(pcxHead.maxY - pcxHead.minY + 1) | |
var tx = xInit | |
var ty = yInit | |
var byte: UInt8 // for data.read8() | |
var runlen: Int // for run-length encoding | |
var drawing = true | |
while (drawing) { | |
byte = try data.read8() | |
/* check for run */ | |
if((byte & 0xc0) == 0xc0) { | |
runlen = Int(byte & 0x3f) | |
byte = try data.read8() | |
} else { | |
runlen = 1 | |
} | |
while( drawing && runlen > 0 ) { | |
if pcxHead.bpp == 1 { | |
for i in 0..<8 { | |
if (tx * 8 + i) < width { | |
let bit = ((byte >> (7-i)) & 1) | |
bytes.append(bit * 255) | |
} | |
} | |
tx += 1 | |
} else { | |
if ( tx < width * 4 ) { | |
bytes.append(byte) | |
} | |
tx += 1 | |
} | |
runlen -= 1 | |
if ( tx >= rowSize ) { | |
tx = xInit | |
ty += 1 | |
runlen = 0 | |
} | |
if ( ty >= height ) { | |
drawing = false | |
} | |
} | |
} | |
/* Setting Palette */ | |
let hasPalette = try? data.read8() | |
if ( hasPalette == 12 ) { | |
for _ in 0 ..< 256 { | |
let r = try data.read8() | |
let g = try data.read8() | |
let b = try data.read8() | |
palettes.append(PixelData(r: r, g: g, b: b)) | |
} | |
} | |
if pcxHead.np == 1 { | |
if palettes.isEmpty { | |
for byte in bytes { | |
pixels.append(PixelData(r: byte, g: byte, b: byte)) | |
} | |
} else { | |
for i in bytes { | |
pixels.append(palettes[Int(i)]) | |
} | |
} | |
} else if pcxHead.np == 3 { | |
var pos: Int = 0 | |
while pos < bytes.count { | |
for i in 0..<min(Int(pcxHead.bpl), width) { | |
pixels.append(PixelData(r: bytes[pos + i], | |
g: bytes[pos + i + Int(pcxHead.bpl)], | |
b: bytes[pos + i + Int(pcxHead.bpl) * 2], | |
a: 255)) | |
} | |
pos += rowSize | |
} | |
} else if pcxHead.np == 4 { | |
var pos: Int = 0 | |
while pos < bytes.count { | |
for i in 0..<min(Int(pcxHead.bpl), width) { | |
pixels.append(PixelData(r: bytes[pos + i], | |
g: bytes[pos + i + Int(pcxHead.bpl)], | |
b: bytes[pos + i + Int(pcxHead.bpl) * 2], | |
a: bytes[pos + i + Int(pcxHead.bpl) * 3])) | |
} | |
pos += rowSize | |
} | |
} | |
return imageFromARGB32Bitmap(pixels: pixels, width: width, height: height) | |
} catch let e { | |
print(e) | |
return nil | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment