0

I'm trying to write a screenshot function for my OpenGL project in Go, I'm using the OpenGL bindings found here:

https://github.com/go-gl/glow

This is the code I use to make a screenshot, or well, it's what I'm working on:

    width, height := r.window.GetSize()
    pixels := make([]byte, 3*width*height)

    // Read the buffer into memory
    var buf unsafe.Pointer
    gl.PixelStorei(gl.UNPACK_ALIGNMENT, 1)
    gl.ReadPixels(0, 0, int32(width), int32(height), gl.RGB, gl.UNSIGNED_BYTE, buf)
    pixels = []byte(&buf) // <-- LINE 99

This triggers the following error during compile time:

video\renderer.go:99: cannot convert &buf (type *unsafe.Pointer) to type []byte.

How do I convert unsafe.Pointer to a byte array?

Jesse Brands
  • 2,637
  • 8
  • 29
  • 36

2 Answers2

7

Since unsafe.Pointer is already a pointer, you can't use a pointer to unsafe.Pointer, but you should use it directly. A simple example:

bytes := []byte{104, 101, 108, 108, 111}

p := unsafe.Pointer(&bytes)
str := *(*string)(p) //cast it to a string pointer and assign the value of this pointer
fmt.Println(str) //prints "hello"
Not_a_Golfer
  • 47,012
  • 14
  • 126
  • 92
  • Thanks, that helped. Can I ask why it has to be written as `*(*[]byte)` and what does that mean? A pointer conversion to a pointer to a byte array? And how does that become []byte? – Jesse Brands Dec 04 '14 at 14:02
  • it's confusing and comes from C: `(*string)(p)` means "cast p to a string pointer". The extra `*` before it means "the value of that pointer". so essentially the line reads: `let "str" be a the value of a string pointer cast of p` – Not_a_Golfer Dec 04 '14 at 14:27
  • 8
    For the purposes of C-interop, `unsafe.Pointer(&bytes)` will create a pointer to the first byte of the slice, which is not the first byte of the *data* (which is usually what C expects)--for this reason, you should use `unsafe.Pointer(&bytes[0])` (of course, you'll need to ensure that you have a 0th element). – weberc2 Dec 04 '14 at 17:54
  • Thanks for the insights you two. I figured out the real solution but I will still mark the answer as valid because it did answer the question. The GLOW library has a gl.Ptr type that automatically converts to the type you supply in it. (f.ex: gl.Ptr(pixels)) – Jesse Brands Dec 05 '14 at 00:23
  • why this is the answer.. I'm here from Google with title `unsafe.Pointer to slice`. but the answer absolutely not, it's `slice to unsafe.Pointer`. – Jiang YD Sep 28 '18 at 04:05
  • 2
    @weberc2 Be very careful that this is a very nasty hack. The []byte type is a slice struct consisting of three fields: ptr, len and capa. The string struct has two fields: ptr and len. The conversion from []byte to string *happens* to be compatible, resulting in a string reflecting the bytes and ignoring the capacity. If you modify the bytes the string also changes, breaking the immutability guarentee. See https://play.golang.org/p/ye9UJts1Zoe – vincent163 Dec 12 '18 at 12:17
  • @HeWenYang That's the point of my comment. You make a pointer to the start of the _data_, not the start of the _slice header_. Also, the question and my comment had nothing to do with strings... – weberc2 Dec 12 '18 at 17:38
0

How do I convert unsafe.Pointer to a byte array?

This is probably an XY problem. You don't really need to convert an unsafe.Pointer to a byte array/slice from what I can see.

The root of the issue lies at your attempt in passing buf to gl.ReadPixels. I'm not familiar with the go-gl package, but it looks like you should be using gl.Ptr(data interface{}) to pass an unsafe.Pointer to an existing buffer (which I assume is what pixels is):

    width, height := r.window.GetSize()
    pixels := make([]byte, 3*width*height)

    // ...

    buf := gl.Ptr(&pixels[0]) // unsafe.Pointer pointing to 1st element in pixels
    gl.ReadPixels(0, 0, int32(width), int32(height), gl.RGB, gl.UNSIGNED_BYTE, buf)
    // Also could try (I believe this requires OpenGL 4.5):
    gl.ReadPixels(0, 0, int32(width), int32(height), gl.RGB, gl.UNSIGNED_BYTE, int32(len(pixels)), buf)
    // Access pixels as normal, no need for conversion.

That said, it is possible to go from an unsafe.Pointer to a byte slice/array back to a byte array/slice. To avoid redundancy, I suggest looking at this existing SO question: How to create an array or a slice from an array unsafe.Pointer in golang?.

Long story short, though, if you have access to Go 1.17 you can simply do the following to get a []byte slice.

pixels = unsafe.Slice((*byte)(buf), desiredSliceLen)
YenForYang
  • 2,998
  • 25
  • 22