From b14c2c0484c3d513c12157e704a008e73785bb3b Mon Sep 17 00:00:00 2001 From: markpmlim Date: Mon, 17 Sep 2018 08:51:42 +0800 Subject: [PATCH] Convert Apple IIGS graphics Converts a file with the Apple IIGS Graphic Format $C0/$0000 to a macOS NSImage object. --- ConvertBitMap.playground/Contents.swift | 11 + .../Resources/ANGELFISH.SHR | Bin 0 -> 32768 bytes .../Sources/Converter.swift | 210 ++++++++++++++++++ .../contents.xcplayground | 4 + .../contents.xcworkspacedata | 7 + .../UserInterfaceState.xcuserstate | Bin 0 -> 12326 bytes ConvertBitMap.playground/timeline.xctimeline | 6 + Readme.md | 14 ++ 8 files changed, 252 insertions(+) create mode 100644 ConvertBitMap.playground/Contents.swift create mode 100644 ConvertBitMap.playground/Resources/ANGELFISH.SHR create mode 100644 ConvertBitMap.playground/Sources/Converter.swift create mode 100644 ConvertBitMap.playground/contents.xcplayground create mode 100644 ConvertBitMap.playground/playground.xcworkspace/contents.xcworkspacedata create mode 100644 ConvertBitMap.playground/playground.xcworkspace/xcuserdata/marklim.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 ConvertBitMap.playground/timeline.xctimeline create mode 100644 Readme.md diff --git a/ConvertBitMap.playground/Contents.swift b/ConvertBitMap.playground/Contents.swift new file mode 100644 index 0000000..3747177 --- /dev/null +++ b/ConvertBitMap.playground/Contents.swift @@ -0,0 +1,11 @@ +/* + A simple Playground demo to load an unpacked Apple IIGS Super-Hires graphic file ($C1/$0000) and display it. + Written in Swift 3.x + Requires XCode 8.x or later. + */ +import Cocoa +import PlaygroundSupport // for the live preview + +let viewFrame = NSMakeRect(0, 0, 640, 400) +let imageView = ImageView(frame: viewFrame) +PlaygroundPage.current.liveView = imageView diff --git a/ConvertBitMap.playground/Resources/ANGELFISH.SHR b/ConvertBitMap.playground/Resources/ANGELFISH.SHR new file mode 100644 index 0000000000000000000000000000000000000000..413f0f1cac6e7d9702ea7f2aacaaeaa161be0a71 GIT binary patch literal 32768 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 a_#djReQbSyxcbD0zrQiE^W^`cW&S^?xq?Xm literal 0 HcmV?d00001 diff --git a/ConvertBitMap.playground/Sources/Converter.swift b/ConvertBitMap.playground/Sources/Converter.swift new file mode 100644 index 0000000..8c33bcb --- /dev/null +++ b/ConvertBitMap.playground/Sources/Converter.swift @@ -0,0 +1,210 @@ +import AppKit +import MetalKit + +/* + Loads an unpacked Apple IIGS graphic ($C0/$0000) and make available + the generated macOS bit map. + */ +public class Converter { + public var bmData: [UInt8] + + public init?(_ url: URL) { + + var SCB = [UInt8](repeating: 0, count: 256) + // Each scanline is 160 bytes. Each byte consists of 2 "pixels". + // There are 200 scanlines in a 320x200 IIGS graphic + var iigsBitmap = [UInt8](repeating: 0, count: 160*200) + // First load the entire file + var rawData: Data? = nil + do { + try rawData = Data(contentsOf: url) + } + catch let error { + print("Error", error) + return nil + } + + // 1. Extract 160x200 = 32 000 bytes - this is the IIGS bitmap + var range = Range(0..<32000) + rawData?.copyBytes(to: &iigsBitmap, from: range) + + // 2. Extract 256 bytes - this is the SCB table (only 200 required). + range = Range(32000..<32256) + rawData?.copyBytes(to: &SCB, from: range) + + // 3. Extract 16 color tables = 16 x 32 bytes = 512 bytes. + range = Range(32256..<32768) + var buffer512 = [UInt8](repeating:0, count: 512) + rawData?.copyBytes(to: &buffer512, from: range) + // Declare a 2D array for easier access and copy data from buffer. + var colorTables = [[UInt16]](repeating: [UInt16](repeating: 0, count: 16), + count: 16) + var k = 0; + for row in 0..<16 { + for col in 0..<16 { + colorTables[row][col] = UInt16(buffer512[k]) + (UInt16(buffer512[k+1]) << 8) + k = k + 2 + // Checked - color table entries are correct. + //print(colorTables[row][col], terminator: " ") + } + //print() + } + + bmData = Converter.convert(bitMap: iigsBitmap, + colorTables: colorTables, + scbs: SCB) + } + + // Convert a IIGS bitmap to a macOS bitmap. + // Each IIGS byte which has 2 "pixels" becomes 8 bytes. + // The "pixels" are actually an index (0-15) to a color entry. + // The color table to be used for a particular scanline is given + // by its corresponding scanline control byte obtained from the SCBs table + static func convert(bitMap:[UInt8], + colorTables: [[UInt16]], + scbs: [UInt8]) -> [UInt8] { + // The width and height of the macOS bitmap. + let width = 320 + let height = 200 + let bytesPerPixel = 4 + var macOSBitmap = [UInt8](repeating: 0, + count: width*height*bytesPerPixel) + + for row in 0..> 4) & 0x0f) // bits 4-7 - pixel # 0 + var whichColor = colorTables[whichColorTable][colorEntry] + var r = ((whichColor & 0x0f00) >> 8) * 17 // 0-15 --> 0, 17, ...238, 255 + var g = ((whichColor & 0x00f0) >> 4) * 17 + var b = (whichColor & 0x000f) * 17 + // Convert the first "pixel" to a true pixel. + let bmIndex = baseIndex + 8*col + macOSBitmap[bmIndex+0] = UInt8(r) + macOSBitmap[bmIndex+1] = UInt8(g) + macOSBitmap[bmIndex+2] = UInt8(b) + macOSBitmap[bmIndex+3] = UInt8(255) + + // Convert the second "pixel" to a true pixel. + colorEntry = Int(pixels & 0x0f) // bits 3-0 - pixel # 1 + whichColor = colorTables[whichColorTable][colorEntry] + r = ((whichColor & 0x0f00) >> 8) * 17 // 0-15 --> 0, 17, ...238, 255 + g = ((whichColor & 0x00f0) >> 4) * 17 + b = (whichColor & 0x000f) * 17 + macOSBitmap[bmIndex+4] = UInt8(r) + macOSBitmap[bmIndex+5] = UInt8(g) + macOSBitmap[bmIndex+6] = UInt8(b) + macOSBitmap[bmIndex+7] = UInt8(255) + } + } + // Return the raw bit map for further processing. + return macOSBitmap + } +} + +/* + An object of this class is used to display the image. + */ +public class ImageView: NSView { + var nsImage: NSImage? + + override public init(frame frameRect: NSRect) { + super.init(frame: frameRect) + let myBundle = Bundle.main + let assetURL = myBundle.url(forResource: "ANGELFISH", + withExtension:"SHR") + let graphicsConverter = Converter(assetURL!)! + let bmData = graphicsConverter.bmData + // Create a CGImage object from the raw bit map. + let cgImage = cgImageFromRawBitmap(bitMapData: bmData) + let size = NSSize(width: 320, height: 200) + nsImage = NSImage(cgImage: cgImage!, size: size) + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func cgImageFromRawBitmap(bitMapData: UnsafeRawPointer) -> CGImage? { + let width = 320 + let height = 200 + let bitsPerComponent = 8 + let bytesPerPixel = 4 // Four 8-bit components viz RGBA + let colorSpace = NSDeviceRGBColorSpace + // Create an instance of NSBitmapImageRep; pass nil as the first parameter + // to tell macOS to allocate enough memory hold the image. + let bir = NSBitmapImageRep(bitmapDataPlanes: nil, + pixelsWide: width, + pixelsHigh: height, + bitsPerSample: bitsPerComponent, + samplesPerPixel: bytesPerPixel, + hasAlpha: true, + isPlanar: false, + colorSpaceName: colorSpace, + bytesPerRow: width*bytesPerPixel, + bitsPerPixel: bitsPerComponent*bytesPerPixel) + memcpy(bir?.bitmapData, bitMapData, width*height*bytesPerPixel) + guard let bmImageRep = bir + else { + return nil + } + return bmImageRep.cgImage + } + + func imageRect() -> NSRect { + let bounds = self.bounds + return self.nsImage!.proportionalRectForTargetRect(bounds) + } + + + override public func draw(_ dirtyRect: NSRect) { + let image = self.nsImage + NSGraphicsContext.saveGraphicsState() + image!.draw(in: imageRect(), + from: NSZeroRect, + operation: .sourceOver, + fraction: 1.0) + NSGraphicsContext.restoreGraphicsState() + } +} + +extension NSImage +{ + func proportionalRectForTargetRect(_ targetRect: NSRect) ->NSRect { + if NSEqualSizes(self.size, targetRect.size) { + return targetRect + } + let imageSize = self.size + let sourceWidth = imageSize.width + let sourceHeight = imageSize.height + + let widthAdjust = targetRect.size.width / sourceWidth + let heightAdjust = targetRect.size.height / sourceHeight + var scaleFactor: CGFloat = 1.0 + + if (widthAdjust < heightAdjust) { + scaleFactor = widthAdjust + } + else { + scaleFactor = heightAdjust + } + + let finalWidth = sourceWidth * scaleFactor + let finalHeight = sourceHeight * scaleFactor + let finalSize = NSMakeSize(finalWidth, finalHeight) + + var finalRect = NSRect() + finalRect.size = finalSize + + finalRect.origin = targetRect.origin + finalRect.origin.x += (targetRect.size.width - finalWidth) * 0.5 + finalRect.origin.y += (targetRect.size.height - finalHeight) * 0.5 + + return NSIntegralRect(finalRect) + } +} + diff --git a/ConvertBitMap.playground/contents.xcplayground b/ConvertBitMap.playground/contents.xcplayground new file mode 100644 index 0000000..63b6dd8 --- /dev/null +++ b/ConvertBitMap.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/ConvertBitMap.playground/playground.xcworkspace/contents.xcworkspacedata b/ConvertBitMap.playground/playground.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/ConvertBitMap.playground/playground.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ConvertBitMap.playground/playground.xcworkspace/xcuserdata/marklim.xcuserdatad/UserInterfaceState.xcuserstate b/ConvertBitMap.playground/playground.xcworkspace/xcuserdata/marklim.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..1ae67bbee6a59485885fc93512c0c9994aaf7ae9 GIT binary patch literal 12326 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 J_-Eqo{{m9kwMPH| literal 0 HcmV?d00001 diff --git a/ConvertBitMap.playground/timeline.xctimeline b/ConvertBitMap.playground/timeline.xctimeline new file mode 100644 index 0000000..bf468af --- /dev/null +++ b/ConvertBitMap.playground/timeline.xctimeline @@ -0,0 +1,6 @@ + + + + + diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..47d6811 --- /dev/null +++ b/Readme.md @@ -0,0 +1,14 @@ +A simple Playground demonstration on how to + +a) load an Apple IIGS graphic file with a file type and auxiliary type of $C0 and $0000, + +b) extract data from the loaded file and use the information to instantiate a raw bitmap which can be used by macOS to create a CGImage object, + +c) create an instance of NSImage from the CGImage object, and, + +d) display the NSImage object in a Playground view. + +Requirements: + +Written in Swift 3.x later +Xcode 8.x or later