Search code examples
swiftswift3sprite-kituiimageskshapenode

Recreating image from pixel data


I have the following code:

GameScene.swift

import SpriteKit
import GameKit

class GameScene : SKScene {

    var circleHolder:CircleHolder = CircleHolder()

    override func didMove(to view: SKView) {
        self.addChild(circleHolder)
    }
}

CircleHolder.swift

import Foundation
import SpriteKit

class CircleHolder : SKNode {
    var circles:[Circle] = [Circle]()

    override init() {
        super.init()

        var image:UIImage = UIImage(named: "Circles")!

        let width = Int(image.size.width)
        let height = Int(image.size.height)

        if let cfData = image.cgImage?.dataProvider?.data, let pointer = CFDataGetBytePtr(cfData) {
            for x in 0..<width {
                for y in 0..<height {
                    let pixelAddress = ((Int(image.size.width) * y) + x ) * 4

                        let r = CGFloat(pointer.advanced(by: pixelAddress).pointee) / CGFloat(255.0)
                        let g = CGFloat(pointer.advanced(by: pixelAddress + 1).pointee) / CGFloat(255.0)
                        let b = CGFloat(pointer.advanced(by: pixelAddress + 2).pointee) / CGFloat(255.0)
                        let a = CGFloat(pointer.advanced(by: pixelAddress + 3).pointee) / CGFloat(255.0)
                        let color = UIColor(red: r, green: g, blue: b, alpha: a)
                        var c = Circle(x: CGFloat(x), y: CGFloat(y), color: color)
                        circles.append(c)
                        self.addChild(c)
                }
            }
        }
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

Circle.swift

import Foundation
import SpriteKit

class Circle : SKNode {
    var circle:SKShapeNode?
    init(x:CGFloat, y:CGFloat, color:UIColor) {
        super.init()

        circle = SKShapeNode(circleOfRadius: 1)
        circle?.fillColor = color
        circle?.position.x = x
        circle?.position.y = y
        circle?.lineWidth = 0
        self.addChild(circle!)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

I'm trying to place solid circles on my scene to 'recreate' the image in UIImage. This is the image I'm trying to recreate:

enter image description here

but I end up with this:

enter image description here

Can anyone point out the error in my logic?


Solution

  • It looks like your image doesn't have the properties you're expecting. It may have an alpha channel or something else that's changing the byte dimensions.

    Instead of assuming 4 bytes per pixel, query the image for the actual properties:

    let bytesPerRow = image.cgImage!.bytesPerRow
    let bitsPerPixel = image.cgImage!.bitsPerPixel
    let bitsPerComponent = image.cgImage!.bitsPerComponent
    let bytesPerPixel = bitsPerPixel / bitsPerComponent
    

    Then use those properties to calculate the pixel address:

    let pixelAddress = y * bytesPerRow + x * bytesPerPixel