-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathimage.go
211 lines (185 loc) · 4.12 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
package ui
import (
"fmt"
"image"
"image/jpeg"
"image/png"
"os"
"path/filepath"
"strings"
"git.kirsle.net/go/render"
"golang.org/x/image/bmp"
)
// ImageType for supported image formats.
type ImageType string
// Supported image formats.
const (
BMP ImageType = "bmp"
PNG = "png"
JPEG = "jpg"
)
// Image is a widget that is backed by an image file.
type Image struct {
BaseWidget
// Configurable fields for the constructor.
Type ImageType
Image image.Image // a Go image version
texture render.Texturer // (SDL2) Texture, lazy inited on Present.
}
// NewImage creates a new Image.
func NewImage(c Image) *Image {
w := &Image{
Type: c.Type,
}
if w.Type == "" {
w.Type = BMP
}
w.IDFunc(func() string {
return fmt.Sprintf(`Image<"%s">`, w.Type)
})
return w
}
// ImageFromImage creates an Image from a Go standard library image.Image.
func ImageFromImage(im image.Image) (*Image, error) {
return &Image{
Type: PNG,
Image: im,
}, nil
}
// ImageFromTexture creates an Image from a texture.
func ImageFromTexture(tex render.Texturer) *Image {
return &Image{
texture: tex,
Image: tex.Image(),
}
}
// ImageFromFile creates an Image by opening a file from disk.
func ImageFromFile(filename string) (*Image, error) {
fh, err := os.Open(filename)
if err != nil {
return nil, err
}
img, err := jpeg.Decode(fh)
if err != nil {
return nil, err
}
return &Image{
Image: img,
}, nil
}
// ReplaceFromImage replaces the image with a new image.
func (w *Image) ReplaceFromImage(im image.Image) error {
// Free the old texture.
if w.texture != nil {
if err := w.texture.Free(); err != nil {
return err
}
w.texture = nil
}
w.Image = im
return nil
}
// OpenImage initializes an Image with a given file name.
//
// The file extension is important and should be a supported ImageType.
func OpenImage(e render.Engine, filename string) (*Image, error) {
w := &Image{}
switch strings.ToLower(filepath.Ext(filename)) {
case ".bmp":
w.Type = BMP
case ".png":
w.Type = PNG
case ".jpg":
w.Type = JPEG
case ".jpeg":
w.Type = JPEG
default:
return nil, fmt.Errorf("OpenImage: %s: not a supported image type", filename)
}
// Open the file from disk.
fh, err := os.Open(filename)
if err != nil {
return nil, err
}
// Parse it.
switch w.Type {
case PNG:
img, err := png.Decode(fh)
if err != nil {
return nil, err
}
w.Image = img
case JPEG:
img, err := jpeg.Decode(fh)
if err != nil {
return nil, err
}
w.Image = img
case BMP:
img, err := bmp.Decode(fh)
if err != nil {
return nil, err
}
w.Image = img
}
return w, nil
}
// GetRGBA returns an image.RGBA from the image data.
func (w *Image) GetRGBA() *image.RGBA {
var bounds = w.Image.Bounds()
var rgba = image.NewRGBA(bounds)
for x := bounds.Min.X; x < bounds.Max.X; x++ {
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
color := w.Image.At(x, y)
rgba.Set(x, y, color)
}
}
return rgba
}
// Size returns the dimensions of the image which is also the widget's size.
func (w *Image) Size() render.Rect {
if w.Image != nil {
bounds := w.Image.Bounds().Canon()
return render.Rect{
W: bounds.Max.X,
H: bounds.Max.Y,
}
}
return w.BaseWidget.Size()
}
// Counter for unique SDL2 texture names.
var __imageID int
// Present the widget. This should be called on your main thread
// if using SDL2 in case it needs to generate textures.
func (w *Image) Present(e render.Engine, p render.Point) {
// Lazy load the (e.g. SDL2) texture from the stored bitmap.
if w.texture == nil {
if w.Image == nil {
return
}
__imageID++
tex, err := e.StoreTexture(fmt.Sprintf("ui.Image(%d).png", __imageID), w.Image)
if err != nil {
fmt.Printf("ui.Image.Present(): could not make texture: %s\n", err)
return
}
w.texture = tex
}
size := w.texture.Size()
dst := render.Rect{
X: p.X,
Y: p.Y,
W: size.W,
H: size.H,
}
e.Copy(w.texture, size, dst)
// Call the BaseWidget Present in case we have subscribers.
w.BaseWidget.Present(e, p)
}
// Destroy cleans up the image and releases textures.
func (w *Image) Destroy() {
if w.texture != nil {
w.texture.Free()
w.texture = nil
}
}