-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathpng.go
85 lines (74 loc) · 1.9 KB
/
png.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package imagecoding
import (
"bytes"
"errors"
"fmt"
"image"
"unsafe"
)
// -Wno-incompatible-pointer-types: ignore a warning from cgo https://github.com/golang/go/issues/19832
// #cgo pkg-config: libpng
// #cgo CFLAGS: -Wno-incompatible-pointer-types
// #include <stdlib.h>
// #include <png.h>
// int cpng_image_data_size(const png_image *image) {
// return PNG_IMAGE_DATA_SIZE(*image);
// }
import "C"
// EncodePng will encode an image.Gray to PNG bytes, using libpng's simplified API for performance
func EncodePng(buf *bytes.Buffer, img image.Image) ([]byte, error) {
var pix []uint8
var stride int
var format C.png_uint_32
switch v := img.(type) {
case *image.Gray:
pix = v.Pix
stride = v.Stride
format = C.PNG_FORMAT_GRAY
case *RGBImage:
pix = v.Pix
stride = v.Stride
format = C.PNG_FORMAT_RGB
case *image.RGBA:
pix = v.Pix
stride = v.Stride
format = C.PNG_FORMAT_RGBA
default:
return nil, errors.New("unsupported image type")
}
var op C.png_controlp
info := &C.png_image{
opaque: op,
version: C.PNG_IMAGE_VERSION,
width: C.uint(img.Bounds().Dx()),
height: C.uint(img.Bounds().Dy()),
format: format,
flags: C.PNG_IMAGE_FLAG_FAST,
colormap_entries: 0,
}
defer C.png_image_free(info)
// Get memory size
var result C.int
var imageBytes C.png_alloc_size_t
// Make a buffer of sufficient size
maxSize := C.cpng_image_data_size(info)
// Prepare a buffer
buf.Reset()
buf.Grow(int(maxSize))
imageBytes = C.png_alloc_size_t(maxSize)
buf.WriteByte(0)
// Write the actual PNG
result = C.png_image_write_to_memory(
info,
unsafe.Pointer(&(buf.Bytes())[0]),
&imageBytes,
0,
unsafe.Pointer(&pix[0]),
C.int(stride),
nil,
)
if result == 0 {
return nil, fmt.Errorf("libpng threw an error (%x) %q", info.warning_or_error, C.GoString(&info.message[:][0]))
}
return buf.Bytes()[:int(imageBytes)], nil
}