How do I turn an array into a bitmap image?


#1

For one of my programs, I need to take an array of Floats and use the
underlying bytes as the RGBA data for a bitmap image. In particular, the 32
bits of each Float must be treated as the color of the corresponding pixel.
So the result is an image of height 1 and width the length of the array.

In particular, I need to be able to save the image as a PNG file, then
later load it from disk and reconstruct each Float from its pixel, with no
loss or change to the value of the Floats. So, how do I go from [Float] to
a bitmap image suitable for saving to disk, without altering the underlying
bytes?

(Yes, I understand this is an odd sort of thing to do. I can explain the
purpose if anyone is interested.)

Thanks,
Nevin


(Jens Alfke) #2

For one of my programs, I need to take an array of Floats and use the underlying bytes as the RGBA data for a bitmap image. In particular, the 32 bits of each Float must be treated as the color of the corresponding pixel. So the result is an image of height 1 and width the length of the array.

Make a pixmap, an array of UInt8 that’s 4x longer than your original array, where the elements are interpreted as R, G, B, A, R, G, B, A… Then fill it in based on the original float values.

In particular, I need to be able to save the image as a PNG file, then later load it from disk and reconstruct each Float from its pixel, with no loss or change to the value of the Floats. So, how do I go from [Float] to a bitmap image suitable for saving to disk, without altering the underlying bytes?

You’ll need to call some external image-encoding library, passing it your pixmap. If you’re on an Apple platform try ImageIO. If not, or if you want to be cross-platform, try libpng.

—Jens

···

On Mar 15, 2017, at 9:56 AM, Nevin Brackett-Rozinsky via swift-users <swift-users@swift.org> wrote:


#3

Right, I understand conceptually what needs to happen. It is the actual
implementation where I’m getting stuck.

Ideally I’d like to create the bitmap image without having to allocate a
second buffer. The sole purpose of the Float array is for creating this
image, so I’d like to directly use its bytes as the pixels data. I expect
this will involve some Unsafe[…] APIs in Swift, though I don’t know exactly
how.

And yes, I am on an Apple platform. Despite reading boatloads of Core
Graphics documentation, I still can’t make heads or tails out of how to
turn a pre-existing array into the backing data for a bitmap image. This
part is only Swift-related in the sense that I am trying to do it in Swift,
so if there’s a better place to ask/learn about it I’d appreciate being
pointed there.

Thanks again,
Nevin

···

On Wed, Mar 15, 2017 at 1:44 PM, Jens Alfke <jens@mooseyard.com> wrote:

On Mar 15, 2017, at 9:56 AM, Nevin Brackett-Rozinsky via swift-users < > swift-users@swift.org> wrote:

For one of my programs, I need to take an array of Floats and use the
underlying bytes as the RGBA data for a bitmap image. In particular, the 32
bits of each Float must be treated as the color of the corresponding pixel.
So the result is an image of height 1 and width the length of the array.

Make a pixmap, an array of UInt8 that’s 4x longer than your original
array, where the elements are interpreted as R, G, B, A, R, G, B, A… Then
fill it in based on the original float values.

In particular, I need to be able to save the image as a PNG file, then
later load it from disk and reconstruct each Float from its pixel, with no
loss or change to the value of the Floats. So, how do I go from [Float] to
a bitmap image suitable for saving to disk, without altering the underlying
bytes?

You’ll need to call some external image-encoding library, passing it your
pixmap. If you’re on an Apple platform try ImageIO. If not, or if you want
to be cross-platform, try libpng.

—Jens


(Jens Alfke) #4

Ideally I’d like to create the bitmap image without having to allocate a second buffer. The sole purpose of the Float array is for creating this image, so I’d like to directly use its bytes as the pixels data. I expect this will involve some Unsafe[…] APIs in Swift, though I don’t know exactly how.

AFAIK there aren’t any pixmap formats that use floats. If you want to produce ARGB pixels, then emit your data in that format, i.e. as a [UInt8]. No unsafe stuff needed.

And yes, I am on an Apple platform. Despite reading boatloads of Core Graphics documentation, I still can’t make heads or tails out of how to turn a pre-existing array into the backing data for a bitmap image. This part is only Swift-related in the sense that I am trying to do it in Swift, so if there’s a better place to ask/learn about it I’d appreciate being pointed there.

Either Apple’s developer forums, or the cocoa-dev mailing list hosted at http://lists.apple.com <http://lists.apple.com/>.

—Jens

···

On Mar 15, 2017, at 11:07 AM, Nevin Brackett-Rozinsky via swift-users <swift-users@swift.org> wrote:


#5

I think I might not be explaining this very well.

I want to create a bitmap image where each pixel is RGBA with 8 bits
(UInt8) per channel, and I want to specify the exact color for every pixel.
I do not know how to do that.

Also, for my specific application, I need to take an array of floating
point numbers and say, “Hey, the underlying bits that make up the IEEE-754
representation of these numbers are exactly the bits that I want to use for
the colors of the pixels.” I do not know how to do that in Swift.

In C I would malloc a buffer, write to it as (float*), then pass it to a
function which takes (char*) and saves a PNG. Thus there is only one
allocation, the buffer is filled with float values, and the exact
bit-pattern is interpreted as RGBA pixels.

How can I do the equivalent in Swift?

Nevin

···

On Wed, Mar 15, 2017 at 2:43 PM, Jens Alfke <jens@mooseyard.com> wrote:

On Mar 15, 2017, at 11:07 AM, Nevin Brackett-Rozinsky via swift-users < > swift-users@swift.org> wrote:

Ideally I’d like to create the bitmap image without having to allocate a
second buffer. The sole purpose of the Float array is for creating this
image, so I’d like to directly use its bytes as the pixels data. I expect
this will involve some Unsafe[…] APIs in Swift, though I don’t know exactly
how.

AFAIK there aren’t any pixmap formats that use floats. If you want to
produce ARGB pixels, then emit your data in that format, i.e. as a [UInt8].
No unsafe stuff needed.

And yes, I am on an Apple platform. Despite reading boatloads of Core
Graphics documentation, I still can’t make heads or tails out of how to
turn a pre-existing array into the backing data for a bitmap image. This
part is only Swift-related in the sense that I am trying to do it in Swift,
so if there’s a better place to ask/learn about it I’d appreciate being
pointed there.

Either Apple’s developer forums, or the cocoa-dev mailing list hosted at
http://lists.apple.com.

—Jens


(Brent Royal-Gordon) #6

Without touching the broader question of bitmap formats:

Also, for my specific application, I need to take an array of floating point numbers and say, “Hey, the underlying bits that make up the IEEE-754 representation of these numbers are exactly the bits that I want to use for the colors of the pixels.” I do not know how to do that in Swift.

In C I would malloc a buffer, write to it as (float*), then pass it to a function which takes (char*) and saves a PNG. Thus there is only one allocation, the buffer is filled with float values, and the exact bit-pattern is interpreted as RGBA pixels.

How can I do the equivalent in Swift?

Create and fill an Array<Float> (actually, a ContiguousArray<Float> would be a little better for this use) with your data. Then use `withUnsafeBytes` to access the raw bytes as an `UnsafeRawBufferPointer`, a type which behaves sort of like a fixed-size array of `UInt8`.

You could access the bytes one at at time by looping (or indexing, or doing many other things):

  myFloats.withUnsafeBytes { buffer in
    for byte in buffer {
      putchar(byte)
    }
  }

Or you can pull out the start pointer and count and use them:

  myFloats.withUnsafeBytes { buffer in
    guard write(fd, buffer.baseAddress, buffer.count) != -1 else { throw MyError.IOError(errno) }
  }
    
Or you can copy it into a `Data` or `Array` and return it to the outer context:

  let bytes = myFloats.withUnsafeBytes { buffer in
    return Data(buffer: buffer) // or Array(buffer)
  }

One thing you should *not* do is hold on to `buffer` or its `baseAddress` beyond the closing bracket. Once `withUnsafeBytes` returns, the `Array` or `ContiguousArray` is free to move or delete its data, so you can no longer depend on the pointer to be correct. Copy the data to an `Array` or `Data`, or allocate and copy it to your own `UnsafeRawBufferPointer`, if you want to hold on to it longer.

···

On Mar 15, 2017, at 12:01 PM, Nevin Brackett-Rozinsky via swift-users <swift-users@swift.org> wrote:

--
Brent Royal-Gordon
Architechies


#7

Thanks Brent, that helps a lot.

I eventually found a site (here <http://flylib.com/books/en/3.310.1.43/1/>
if anyone’s interested) that describes (albeit in Objective-C) how to make
a CGImage from a byte buffer through the use of CGDataProvider. Hopefully
I’ll be able to put the pieces together in Swift now.

Thanks again,

Nevin