forked from davidbyttow/govips
-
Notifications
You must be signed in to change notification settings - Fork 0
/
image.go
240 lines (201 loc) · 5.82 KB
/
image.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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
package vips
// #cgo pkg-config: vips
// #include "bridge.h"
import "C"
import (
"errors"
"io"
"io/ioutil"
"runtime"
"unsafe"
)
// ImageRef contains a libvips image and manages its lifecycle. You should
// close an image when done or it will leak until the next GC
type ImageRef struct {
image *C.VipsImage
format ImageType
// NOTE(d): We keep a reference to this so that the input buffer is
// never garbage collected during processing. Some image loaders use random
// access transcoding and therefore need the original buffer to be in memory.
buf []byte
}
type LoadOption func(o *vipsLoadOptions)
func WithAccessMode(a Access) LoadOption {
return func(o *vipsLoadOptions) {
switch a {
case AccessRandom:
o.cOpts.access = C.VIPS_ACCESS_RANDOM
case AccessSequential:
o.cOpts.access = C.VIPS_ACCESS_SEQUENTIAL
case AccessSequentialUnbuffered:
o.cOpts.access = C.VIPS_ACCESS_SEQUENTIAL_UNBUFFERED
default:
o.cOpts.access = C.VIPS_ACCESS_RANDOM
}
}
}
// LoadImage loads an ImageRef from the given reader
func LoadImage(r io.Reader, opts ...LoadOption) (*ImageRef, error) {
startupIfNeeded()
buf, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
return NewImageFromBuffer(buf, opts...)
}
// NewImageFromFile loads an image from file and creates a new ImageRef
func NewImageFromFile(file string, opts ...LoadOption) (*ImageRef, error) {
startupIfNeeded()
buf, err := ioutil.ReadFile(file)
if err != nil {
return nil, err
}
return NewImageFromBuffer(buf, opts...)
}
// NewImageFromBuffer loads an image buffer and creates a new Image
func NewImageFromBuffer(buf []byte, opts ...LoadOption) (*ImageRef, error) {
startupIfNeeded()
image, format, err := vipsLoadFromBuffer(buf, opts...)
if err != nil {
return nil, err
}
ref := NewImageRef(image, format)
ref.buf = buf
return ref, nil
}
// NewImageFromMemory loads an image from memory and creates a new Image
func NewImageFromMemory(buf []byte, width int, height int, bands int) (*ImageRef, error) {
startupIfNeeded()
image, err := vipsLoadFromMemory(buf, width, height, bands)
if err != nil {
return nil, err
}
ref := NewImageRef(image, ImageTypeUnknown)
ref.buf = buf
return ref, nil
}
func NewImageRef(vipsImage *C.VipsImage, format ImageType) *ImageRef {
stream := &ImageRef{
image: vipsImage,
format: format,
}
runtime.SetFinalizer(stream, finalizeImage)
return stream
}
func finalizeImage(ref *ImageRef) {
ref.Close()
}
// SetImage resets the image for this image and frees the previous one
func (ref *ImageRef) SetImage(image *C.VipsImage) {
if ref.image != nil {
defer C.g_object_unref(C.gpointer(ref.image))
}
ref.image = image
}
// Format returns the initial format of the vips image when loaded
func (ref *ImageRef) Format() ImageType {
return ref.format
}
// Close closes an image and frees internal memory associated with it
func (ref *ImageRef) Close() {
ref.SetImage(nil)
ref.buf = nil
}
// Image returns a handle to the internal vips image, just in case
func (ref *ImageRef) Image() *C.VipsImage {
return ref.image
}
// Width returns the width of this image
func (ref *ImageRef) Width() int {
return int(ref.image.Xsize)
}
// Height returns the height of this iamge
func (ref *ImageRef) Height() int {
return int(ref.image.Ysize)
}
// Bands returns the number of bands for this image
func (ref *ImageRef) Bands() int {
return int(ref.image.Bands)
}
// ResX returns the X resolution
func (ref *ImageRef) ResX() float64 {
return float64(ref.image.Xres)
}
// ResY returns the Y resolution
func (ref *ImageRef) ResY() float64 {
return float64(ref.image.Yres)
}
// OffsetX returns the X offset
func (ref *ImageRef) OffsetX() int {
return int(ref.image.Xoffset)
}
// OffsetY returns the Y offset
func (ref *ImageRef) OffsetY() int {
return int(ref.image.Yoffset)
}
// BandFormat returns the current band format
func (ref *ImageRef) BandFormat() BandFormat {
return BandFormat(int(ref.image.BandFmt))
}
// Coding returns the image coding
func (ref *ImageRef) Coding() Coding {
return Coding(int(ref.image.Coding))
}
// Interpretation returns the current interpretation
func (ref *ImageRef) Interpretation() Interpretation {
return Interpretation(int(ref.image.Type))
}
// Composite overlays the given image over this one
func (ref *ImageRef) Composite(overlay *ImageRef, mode BlendMode) error {
out, err := vipsComposite([]*C.VipsImage{ref.image, overlay.image}, mode)
if err != nil {
return err
}
ref.SetImage(out)
return nil
}
// Join joins this image with another in the direction specified
func (ref *ImageRef) Join(in *ImageRef, dir Direction) error {
out, err := vipsJoin(ref.image, in.image, dir)
if err != nil {
return err
}
ref.SetImage(out)
return nil
}
// ArrayJoin joins an array of images together wrapping at each n images
func (ref *ImageRef) ArrayJoin(images []*ImageRef, across int) error {
allImages := append([]*ImageRef{ref}, images...)
inputs := make([]*C.VipsImage, len(allImages))
for i := range inputs {
inputs[i] = allImages[i].image
}
out, err := vipsArrayJoin(inputs, across)
if err != nil {
return err
}
ref.SetImage(out)
return nil
}
// Export exports the image
func (ref *ImageRef) Export(params ExportParams) ([]byte, ImageType, error) {
if params.Format == ImageTypeUnknown {
params.Format = ref.format
}
return vipsExportBuffer(ref.image, ¶ms)
}
// HasProfile returns if the image has an ICC profile embedded.
func (ref *ImageRef) HasProfile() bool {
return vipsHasProfile(ref.image)
}
// ToBytes writes the image to memory in VIPs format and returns the raw bytes, useful for storage.
func (ref *ImageRef) ToBytes() ([]byte, error) {
var cSize C.size_t
cData := C.vips_image_write_to_memory(ref.image, &cSize)
if cData == nil {
return nil, errors.New("Failed to write image to memory")
}
defer C.free(cData)
bytes := C.GoBytes(unsafe.Pointer(cData), C.int(cSize))
return bytes, nil
}