2

I've tried to build reading RGB pixel data in Swift. Getting the basic Image Information is no problem, but I think I've got something wrong with the pointer and the byte-offset:

let provider:CGDataProviderRef = CGImageGetDataProvider(inImage)
let data:NSData = CGDataProviderCopyData(provider)
let bytes: COpaquePointer = data.bytes // Needs to be changed to ConstUnsafePointer<()> in xCode beta3
let bytePointer = UnsafePointer<UInt8>(bytes)

println("Pixel Data:\n")

var printString:String = ""

    for var row:Int = 0; row < Int(height); row++ {
    for var col:Int = 0; col < Int(width); col++ {

        var pixel:UInt8 = bytePointer[row * Int(bytesPerRow) + col * Int(bytesPerPixel)]
        printString += "("

        for var x = 0; x < Int(bitsPerPixel); x++ {
            printString += pixel[x]
        }

        printString += ")"

        if col < Int(width) - 1 {
            printString += ","
        }

    }
    printString += "\n"
}

println(printString)

(I cast Int to the variables bytesPerRow, etc because they are UInt8)

I got a 4x2px Image wich contains two rows of following pixels: Red, Green, Blue, White. So I expect this output (There is no alpha):

(ff0000), (00ff00), (0000ff), (ffffff)
(ff0000), (00ff00), (0000ff), (ffffff)

I get a error when compiling at printString += pixel[x]

'UInt8' does not have a member named 'subscript'

I thought it could just be appended to a string in swift. I've tried to convert it to a string but this won't work either. So maybe I'm doing something wrong with the pointers.

How do i get a proper output?

edit:

@David got me on a right track I think. But after the first 8 Byte weird thing happen. Might this be padding byted @heinrich-giesen suggested?

My raw image data for 3 tests looks like this:

Pic1: 3Byte/Pixel, no Alpha, 4 pixelWidth, 2 pixelHeight, [red, green, blue white; red, green, blue white]
Raw Data: <ff000000 ff000000 ffffffff ff000000 ff000000 ffffffff>

Pic2: 4Byte/Pixel, Alpha, 4 pixelWidth, 2 pixelHeight, [red, green, blue white (alpha); red, green, blue white]
Raw Data: <ff000000 00ff0000 0000ff00 ffffff00 ff0000ff 00ff00ff 0000ffff ffffffff>

If you look at the raw data its pretty clear that I expect a output like:

Pic1
(ff,00,00), (00,ff,00), (00,00,ff), (ff,ff,ff)
(ff,00,00), (00,ff,00), (00,00,ff), (ff,ff,ff)

Pic2
(ff,00,00,00), (00,ff,00,00), (00,00,ff,00), (ff,ff,ff,00)
(ff,00,00,ff), (00,ff,00,ff), (00,00,ff,ff), (ff,ff,ff,ff)

But the real output my code generates is this:

Pic1   
((ff, 00, 00, ), (00, 00, ff, ), (00, ff, ff, ), (ff, ff, 00, ))
((ff, 00, 00, ), (00, 00, ff, ), (00, ff, 00, ), (ff, 00, 00, ))

Pic2 
((ff, 00, 00, ff, ), (00, 00, ff, ff, ), (00, ff, ff, 00, ), (ff, ff, 00, 00, )) 
((ff, 00, 00, ff, ), (00, 00, ff, 00, ), (00, ff, 00, 00, ), (ff, 00, 00, 00, ))

The code to build the output is this now:

    for var row:Int = 0; row < Int(height) ; row++ {

        printString += "("

        for var col:Int = 0; col < Int(width); col++ {

            printString += "("

            for var x = 0; x < Int(bytesPerPixel); x++ {

                var pixel:UInt8 = bytePointer[row * Int(bytesPerRow) + col * Int(bytesPerPixel) + x * Int(bytesPerPixel)]
                printString += NSString(format:"%02x, ", pixel)

            }

            printString += ")"

            if col < Int(width) - 1 {
                printString += ", "
            }

        }
        printString += ")\n"
    }

println(printString)

So is there a padding (Like the space in the output of the raw data)? I've tried to skip one and two after the initial 8 but this won't yield less confusing outputs. Something breaks after 8 Bytes. Suggestions?

Community
  • 1
  • 1
D.icon
  • 396
  • 3
  • 14

2 Answers2

1

Your code looks a bit strange. Are you sure the line:

    for var x = 0; x < Int(bitsPerPixel); x++ {

is correct? Did you really mean bitsPerPixel and not bytesPerPixel ? (or samplesPerPixel or whatever it is in swift). And are you sure a component (a sample) has 8 bits? And a pixel has no padding bytes?

Another point: what are height and width? Are they the components of the size of an image? that is wrong. You have to use pixelsWide and pixelsHigh. The size of an image says only how great --expressed in inch or centimeter or (1/72)of an inch (called a point)-- an image shall be depicted. That is independent of the number of pixels. But you work on pixels!

Heinrich Giesen
  • 501
  • 1
  • 3
  • 11
  • Yes, it should be bytes per pixel. I fixed that. I'm sure a component has 8 bits. The raw-data shows that very beautiful. Look at my comment on @David answer for more details. But there is still a problem. ( The height and width das the pixelWidth and pixelHeight. I was just a bit lazy so I gave them shorter names: `let width:UInt = CGImageGetWidth(inImage) let height:UInt = CGImageGetHeight(inImage)` ) – D.icon Jul 20 '14 at 22:09
  • In the for var x = 0; - loop the last multiplication x*bytePerPixel is wrong. Replace it with x only, because x iterates over the components within a pixel, not over pixels. – Heinrich Giesen Jul 21 '14 at 07:58
0

Change bytePointer to UnsafePointer<UInt32> and update the loop to access the individual bytes of the word using shift (>>) and mask (& 0xff).

    var pixel:UInt32 = bytePointer[row * Int(bytesPerRow) + col * Int(bytesPerPixel)]
    printString += NSString(withFormat:"(%08x)", pixel)

That'll give you the alpha channel as well, but you get the drift.

David Berry
  • 40,941
  • 12
  • 84
  • 95
  • Thanks, thats works beautiful for the first 16 Byte. With shifting its basically the example from the Swift-Book, if there is alpha: `let redComponent = (pixel & 0xFF000000) >> 24 let greenComponent = (pixel & 0x00FF0000) >> 16 let blueComponent = (pixel & 0x0000FF00) >> 8 let blueComponent = pixel & 0x000000FF` But after those 16 Byte, things get weird. Might you look on my edit on the question? – D.icon Jul 20 '14 at 22:40