Search code examples
iosswiftcore-graphics

Swift - mapping text to grid


My objective is to map a character to a n x n grid - like an LED matrix. My current approach is to:

  1. Convert the string to an image using UIGraphicsBeginImageContextWithOptions()
  2. Iterate over each pixel of the image and extract the RGB colours to a 2D array
  3. Create a new grid of UIViews and apply the colour to each coordinate.
//1
func textToImage() -> UIImage? {
   let text = "A"
   let attributes = [
       NSAttributedString.Key.foregroundColor: UIColor.white,
       NSAttributedString.Key.font: UIFont.systemFont(ofSize: 10)
   ]
   let textSize = text.size(withAttributes: attributes)

   UIGraphicsBeginImageContextWithOptions(textSize, false, 0)
   text.draw(at: CGPoint.zero, withAttributes: attributes)
   let image = UIGraphicsGetImageFromCurrentImageContext()
   UIGraphicsEndImageContext()
   return image
}

//2
func imageToMapping(_ inputImage: UIImage) -> [[UIColor]] {

   guard let cgImage = inputImage.cgImage,
       let data = cgImage.dataProvider?.data,
       let pixelPointer = CFDataGetBytePtr(data) else {
       fatalError("Couldn't access image data")
   }

   let bytesPerPixel = cgImage.bitsPerPixel / cgImage.bitsPerComponent
   let byteCount = CFDataGetLength(data)
/*
   let matrix = Pixel.createPixelMatrix(cgImage.width, cgImage.height)
   return matrix.compactMap { yPixels in
       return yPixels.compactMap { xPixels in
           return xPixels.colorByPixel(pixelPointer)
       }
   }
*/
// update with for loops
    let matrix = Pixel.createPixelMatrix(Int(cgImage.width), Int(cgImage.height))
    var ledGrid = [[UIColor?]](repeating: [UIColor?](repeating: nil, count: 36), count: 21)
    for (indexY, yPixels) in matrix.enumerated() {
        for (indexX, xPixels) in yPixels.enumerated() {
            ledGrid[indexY][indexX] = xPixels.colorByPixel(pixelPointer)
        }
    }
}

//3
func ledMap(colorMap: [[UIColor]], imageWidth: Int, imageHeight: Int) {

    let yIndex = colorMap.count
    let xIndex = colorMap[0].count
    //        cgImage size 177 36
    let ledHeight = Int(view.frame.height) / imageHeight
    let ledWidth = ledHeight


    var y = 0
    var x = 0
    while y < colorMap.count {
        while x < colorMap[y].count {

            let led = UIView()
            led.frame = CGRect(origin: CGPoint(x: 11 * x, y: 11 * y), size: CGSize(width: 11, height: 11))
            led.backgroundColor = colorMap[y][x]
            view.addSubview(led)
            x = x + 1
        }
        x = 0
        y = y + 1
    }
}
struct Pixel {
    /** The number of bytes a pixel occupies. 1 byte per channel (RGBA). */
    static let bytesPerPixel = 4

    fileprivate let offset: Int
    fileprivate init(_ offset: Int) { self.offset = offset }

    static func createPixelMatrix(_ width: Int, _ height: Int) -> [[Pixel]]
    {
        return (0..<height).map { row in
            (0..<width).map { col in
                let offset = (width * row + col) * Pixel.bytesPerPixel
                return Pixel(offset)
            }
        }
    }
}

Here you can see the output of the grid and not the left-center the actual UIImage of the string

output

As you can see it's not the result I'm look for. The output of the grid should map to the letter A. Any advice?

Updated with for loops however simulator still brings the same result


Solution

  • The solution was that the offset was incorrect, the following code will replace the colorByPixel method.

    for (indexY, yPixels) in matrix.enumerated() {
        for (indexX, xPixels) in yPixels.enumerated() {
            let offset = (indexY * cgImage.bytesPerRow) + (indexX * bytesPerPixel)
            let components = (r: pixelPointer[offset], g: pixelPointer[offset + 1], b: pixelPointer[offset + 2])
            ledGrid[indexY][indexX] = UIColor(red: CGFloat(components.r/255), green: CGFloat(components.g), blue: CGFloat(components.b), alpha: 1.0)
            ledGrid[indexY][indexX] = xPixels.colorByPixel(pixelPointer)
        }
    }
    

    The colour isn't perfect but the shape is definitely there.

    result