@@ -7,18 +7,47 @@ import (
7
7
"image/color"
8
8
"image/png"
9
9
"io"
10
+ "math"
10
11
"os"
11
12
)
12
13
14
+ //TODO: Potentially change this to type Pixel []uint16
13
15
type Pixel struct {
14
- R uint8
15
- G uint8
16
- B uint8
17
- A uint8
16
+ Channels []uint16
18
17
}
19
18
20
- func LoadImage (imgPath string ) (pixels [][]Pixel , e error ) {
21
- image .RegisterFormat ("png" , "png" , png .Decode , png .DecodeConfig )
19
+ type FmtInfo struct {
20
+ Model color.Model
21
+ ChannelsPerPix uint8
22
+ BitsPerChannel uint8
23
+ }
24
+
25
+ func (info * FmtInfo ) BytesPerChannel () uint8 {
26
+ return uint8 (math .Ceil (float64 (info .BitsPerChannel / bitsPerByte )))
27
+ }
28
+
29
+ func (info * FmtInfo ) String () string {
30
+ return fmt .Sprintf ("{%v %d %d}" , colorModelToStr (info .Model ), info .ChannelsPerPix , info .BitsPerChannel )
31
+ }
32
+
33
+ type ImgInfo struct {
34
+ W , H uint
35
+ Format FmtInfo
36
+ }
37
+
38
+ const (
39
+ bitsPerByte uint8 = 8
40
+ pngHeader string = "\x89 PNG\r \n \x1a \n "
41
+ )
42
+
43
+ type unknownColourModelError struct {}
44
+
45
+ func (e unknownColourModelError ) Error () string {
46
+ return "The colour model of the provided Image is unknown."
47
+ }
48
+
49
+ func LoadImage (imgPath string ) (pixels * []Pixel , info ImgInfo , e error ) {
50
+ image .RegisterFormat ("png" , pngHeader , png .Decode , png .DecodeConfig )
22
51
23
52
imgFile , err := os .Open (imgPath )
24
53
@@ -27,68 +56,204 @@ func LoadImage(imgPath string) (pixels [][]Pixel, e error) {
27
56
return
28
57
}
29
58
30
- defer imgFile .Close ()
59
+ defer func () {
60
+ if err = imgFile .Close (); err != nil {
61
+ fmt .Println ("Error closing the file:" , err .Error ())
62
+ }
63
+ }()
31
64
32
- pixels , err = readPixels (imgFile )
65
+ pixels , info , err = readPixels (imgFile )
33
66
//fmt.Println(pixels)
34
67
35
68
if err != nil {
36
- fmt .Println ("The image couldn't be decoded. " , err .Error ())
69
+ fmt .Println ("The image couldn't be decoded: " , err .Error ())
37
70
return
38
71
}
39
72
40
73
return
41
74
}
42
75
43
- func readPixels (imgFile io.Reader ) (pixels [][] Pixel , e error ) {
76
+ func readPixels (imgFile io.Reader ) (pixels * [] Pixel , info ImgInfo , e error ) {
44
77
img , _ , err := image .Decode (imgFile )
78
+ //img, err := png.Decode(imgFile)
45
79
46
80
if err != nil {
47
- return nil , err
81
+ return nil , ImgInfo {}, err
48
82
}
49
83
50
84
dims := img .Bounds ()
51
85
w , h := dims .Max .X , dims .Max .Y
52
- //TODO: Potentially check for 0x0 dims
53
86
54
- for y := 0 ; y < h ; y ++ {
55
- row := make ([]Pixel , 0 , w )
56
- for x := 0 ; x < w ; x ++ {
57
- row = append (row , rgbaToPixel (img .At (x , y ).RGBA ()))
58
- }
59
- pixels = append (pixels , row )
60
- }
87
+ info = ImgInfo {W : uint (w ), H :uint (h )}
61
88
89
+ fmt .Println ("Colour model:" , colorModelToStr (img .ColorModel ()))
90
+ //Handle each image type independently, parsing out the pixel channel values
91
+ switch img .(type ) {
92
+ case * image.Alpha16 :
93
+ info .Format = FmtInfo {color .Alpha16Model , 4 , 16 }
94
+ simg := img .(* image.Alpha16 )
95
+ pixels = imgPixToPixels (& simg .Pix , info .Format )
96
+ case * image.Alpha :
97
+ info .Format = FmtInfo {color .AlphaModel , 4 , 8 }
98
+ simg := img .(* image.Alpha )
99
+ pixels = imgPixToPixels (& simg .Pix , info .Format )
100
+ case * image.CMYK :
101
+ info .Format = FmtInfo {color .CMYKModel , 4 , 8 }
102
+ simg := img .(* image.CMYK )
103
+ pixels = imgPixToPixels (& simg .Pix , info .Format )
104
+ case * image.Gray16 :
105
+ info .Format = FmtInfo {color .Gray16Model , 4 , 16 }
106
+ simg := img .(* image.Gray16 )
107
+ pixels = imgPixToPixels (& simg .Pix , info .Format )
108
+ case * image.Gray :
109
+ info .Format = FmtInfo {color .GrayModel , 4 , 8 }
110
+ simg := img .(* image.Gray )
111
+ pixels = imgPixToPixels (& simg .Pix , info .Format )
112
+ case * image.NRGBA64 :
113
+ info .Format = FmtInfo {color .NRGBA64Model , 4 , 16 }
114
+ simg := img .(* image.NRGBA64 )
115
+ pixels = imgPixToPixels (& simg .Pix , info .Format )
116
+ case * image.NRGBA :
117
+ info .Format = FmtInfo {color .NRGBAModel , 4 , 8 }
118
+ simg := img .(* image.NRGBA )
119
+ pixels = imgPixToPixels (& simg .Pix , info .Format )
120
+ case * image.RGBA64 :
121
+ info .Format = FmtInfo {color .RGBA64Model , 4 , 16 }
122
+ simg := img .(* image.RGBA64 )
123
+ pixels = imgPixToPixels (& simg .Pix , info .Format )
124
+ case * image.RGBA :
125
+ info .Format = FmtInfo {color .RGBAModel , 4 , 8 }
126
+ simg := img .(* image.RGBA )
127
+ pixels = imgPixToPixels (& simg .Pix , info .Format )
128
+ default :
129
+ return nil , info , unknownColourModelError {}
130
+ }
62
131
return
63
132
}
64
133
65
- func WriteImage (pixels * [][]Pixel , outPath string ) {
66
- w , h := len ((* pixels )[0 ]), len (* pixels )
134
+ func imgPixToPixels (pix * []uint8 , info FmtInfo ) * []Pixel {
135
+ bytesPerChannel := info .BitsPerChannel / bitsPerByte
136
+ pixels := make ([]Pixel , len (* pix ) / int (info .ChannelsPerPix * bytesPerChannel ))
137
+ for i := range pixels {
138
+ pixels [i ] = Pixel {Channels :make ([]uint16 , info .ChannelsPerPix )}
139
+ for j := uint8 (0 ); j < info .ChannelsPerPix ; j ++ {
140
+ //Image raw Pix arrays store multi-byte channel values in big-endian format
141
+ //across separate indices (https://golang.org/src/image/image.go?s=8222:8528#L380)
142
+ for k := uint8 (0 ); k < info .BytesPerChannel (); k ++ {
143
+ pixels [i ].Channels [j ] <<= bitsPerByte
144
+ pixels [i ].Channels [j ] += uint16 ((* pix )[i * int (info .ChannelsPerPix * bytesPerChannel ) + int (j * bytesPerChannel )])
145
+ }
146
+ }
147
+ }
148
+ return & pixels
149
+ }
67
150
68
- img := image .NewRGBA (image.Rectangle {Max : image.Point {w , h }})
69
- for y := 0 ; y < h ; y ++ {
70
- for x := 0 ; x < w ; x ++ {
71
- img .Set (x , y , pixelToRgba ((* pixels )[y ][x ]))
151
+ func updatePixWithPixels (pix * []uint8 , pixels * []Pixel , info FmtInfo ) {
152
+ bytes := info .BytesPerChannel ()
153
+ for i := range * pixels {
154
+ for j := range (* pixels )[i ].Channels {
155
+ for k := uint8 (0 ); k < bytes ; k ++ {
156
+ (* pix )[(i * int (info .ChannelsPerPix ) + j ) * int (bytes ) + int (k )] =
157
+ uint8 (ReadFrom ((* pixels )[i ].Channels [j ], (bytes - 1 - k ) * bitsPerByte , bitsPerByte ))
158
+ }
72
159
}
73
160
}
161
+ }
162
+
163
+ func WriteImage (pixels * []Pixel , info ImgInfo , outPath string ) {
164
+ var img image.Image
165
+ switch info .Format .Model {
166
+ case color .Alpha16Model :
167
+ simg := image .NewAlpha16 (image.Rectangle {Max : image.Point {int (info .W ), int (info .H )}})
168
+ updatePixWithPixels (& simg .Pix , pixels , info .Format )
169
+ img = simg
170
+ case color .AlphaModel :
171
+ simg := image .NewAlpha (image.Rectangle {Max : image.Point {int (info .W ), int (info .H )}})
172
+ updatePixWithPixels (& simg .Pix , pixels , info .Format )
173
+ img = simg
174
+ case color .CMYKModel :
175
+ simg := image .NewCMYK (image.Rectangle {Max : image.Point {int (info .W ), int (info .H )}})
176
+ updatePixWithPixels (& simg .Pix , pixels , info .Format )
177
+ img = simg
178
+ case color .Gray16Model :
179
+ simg := image .NewGray16 (image.Rectangle {Max : image.Point {int (info .W ), int (info .H )}})
180
+ updatePixWithPixels (& simg .Pix , pixels , info .Format )
181
+ img = simg
182
+ case color .GrayModel :
183
+ simg := image .NewGray (image.Rectangle {Max : image.Point {int (info .W ), int (info .H )}})
184
+ updatePixWithPixels (& simg .Pix , pixels , info .Format )
185
+ img = simg
186
+ case color .NRGBA64Model :
187
+ simg := image .NewNRGBA64 (image.Rectangle {Max : image.Point {int (info .W ), int (info .H )}})
188
+ updatePixWithPixels (& simg .Pix , pixels , info .Format )
189
+ img = simg
190
+ case color .NRGBAModel :
191
+ simg := image .NewNRGBA (image.Rectangle {Max : image.Point {int (info .W ), int (info .H )}})
192
+ updatePixWithPixels (& simg .Pix , pixels , info .Format )
193
+ img = simg
194
+ case color .RGBA64Model :
195
+ simg := image .NewRGBA64 (image.Rectangle {Max : image.Point {int (info .W ), int (info .H )}})
196
+ updatePixWithPixels (& simg .Pix , pixels , info .Format )
197
+ img = simg
198
+ case color .RGBAModel :
199
+ simg := image .NewRGBA (image.Rectangle {Max : image.Point {int (info .W ), int (info .H )}})
200
+ updatePixWithPixels (& simg .Pix , pixels , info .Format )
201
+ img = simg
202
+ default :
203
+ fmt .Println ("Unknown image format." )
204
+ return
205
+ }
74
206
75
207
f , err := os .Create (outPath )
76
208
if err != nil {
77
209
fmt .Printf ("There was an error creating the file '%v': %v\n " , outPath , err .Error ())
78
210
}
211
+ defer func () {
212
+ if err = f .Close (); err != nil {
213
+ fmt .Println ("Error closing the file:" , err .Error ())
214
+ }
215
+ }()
79
216
80
- err = png .Encode (f , img )
217
+ //TODO: Add support for additional format exports
218
+ encoder := png.Encoder {CompressionLevel :png .BestCompression }
219
+ err = encoder .Encode (f , img )
81
220
if err != nil {
82
221
fmt .Printf ("There was an error encoding the image to the new file: %v\n " , err .Error ())
83
222
}
84
223
}
85
224
86
- func rgbaToPixel (r , g , b , a uint32 ) Pixel {
87
- return Pixel {uint8 (r ), uint8 (g ), uint8 (b ), uint8 (a )}
88
- }
225
+ /*func pixelToRgba(pix Pixel) color.Color {
226
+ //fmt.Println(pix)
227
+ return color.NRGBA{pix.R, pix.G, pix.B, pix.A}
228
+ }*/
89
229
90
- func pixelToRgba (pix Pixel ) color.Color {
91
- return color.RGBA {pix .R , pix .G , pix .B , pix .A }
230
+ func colorModelToStr (model color.Model ) string {
231
+ switch model {
232
+ case color .Alpha16Model :
233
+ return "Alpha16"
234
+ case color .AlphaModel :
235
+ return "Alpha"
236
+ case color .CMYKModel :
237
+ return "CMYK"
238
+ case color .Gray16Model :
239
+ return "Gray16"
240
+ case color .GrayModel :
241
+ return "Gray"
242
+ case color .NRGBA64Model :
243
+ return "NRGBA64"
244
+ case color .NRGBAModel :
245
+ return "NRGBA"
246
+ case color .RGBA64Model :
247
+ return "RGBA64"
248
+ case color .RGBAModel :
249
+ return "RGBA"
250
+ case color .NYCbCrAModel :
251
+ return "NYCbCrA"
252
+ case color .YCbCrModel :
253
+ return "YCbCr"
254
+ default :
255
+ return "<Unknown>"
256
+ }
92
257
}
93
258
94
259
//PCB = Pixel, Channel, Bit #
@@ -135,14 +300,14 @@ func HashPatternFile(patternPath string) int64 {
135
300
136
301
//Bit manipulation functions
137
302
138
- func GetMask (index , size uint8 ) uint8 {
303
+ func GetMask (index , size uint8 ) uint16 {
139
304
return ((1 << size ) - 1 ) << index
140
305
}
141
306
142
- func ReadFrom (data uint8 , index , size uint8 ) uint8 {
307
+ func ReadFrom (data uint16 , index , size uint8 ) uint16 {
143
308
return (data & GetMask (index , size )) >> index
144
309
}
145
310
146
- func WriteTo (data uint8 , index , size uint8 , value uint8 ) uint8 {
311
+ func WriteTo (data uint16 , index , size uint8 , value uint16 ) uint16 {
147
312
return (data & (^ GetMask (index , size ))) | (value << index )
148
313
}
0 commit comments