@@ -11,6 +11,7 @@ import (
11
11
"errors"
12
12
"fmt"
13
13
"image"
14
+ "image/color"
14
15
"image/png"
15
16
"os"
16
17
"syscall"
@@ -37,36 +38,44 @@ type ICONINFO struct {
37
38
HbmColor uintptr
38
39
}
39
40
40
- // BITMAP mirrors the Win32 BITMAP struct for GetObject
41
- type BITMAP struct {
42
- Type int32
43
- Width int32
44
- Height int32
45
- WidthBytes int32
46
- Planes uint16
47
- BitsPixel uint16
48
- Bits uintptr
41
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/dd183376.aspx
42
+ type BITMAPINFOHEADER struct {
43
+ BiSize uint32
44
+ BiWidth int32
45
+ BiHeight int32
46
+ BiPlanes uint16
47
+ BiBitCount uint16
48
+ BiCompression uint32
49
+ BiSizeImage uint32
50
+ BiXPelsPerMeter int32
51
+ BiYPelsPerMeter int32
52
+ BiClrUsed uint32
53
+ BiClrImportant uint32
49
54
}
50
55
51
- // BITMAPINFOHEADER mirrors the Win32 BITMAPINFOHEADER
52
- type BITMAPINFOHEADER struct {
53
- Size uint32
54
- Width int32
55
- Height int32
56
- Planes uint16
57
- BitCount uint16
58
- Compression uint32
59
- SizeImage uint32
60
- XPelsPerMeter int32
61
- YPelsPerMeter int32
62
- ClrUsed uint32
63
- ClrImportant uint32
56
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/dd162938.aspx
57
+ type RGBQUAD struct {
58
+ RgbBlue byte
59
+ RgbGreen byte
60
+ RgbRed byte
61
+ RgbReserved byte
64
62
}
65
63
66
- // BITMAPINFO wraps a header plus color table
64
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/dd183375.aspx
67
65
type BITMAPINFO struct {
68
- Header BITMAPINFOHEADER
69
- Colors [1 ]uint32
66
+ BmiHeader BITMAPINFOHEADER
67
+ BmiColors * RGBQUAD
68
+ }
69
+
70
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/dd183371.aspx
71
+ type BITMAP struct {
72
+ BmType int32
73
+ BmWidth int32
74
+ BmHeight int32
75
+ BmWidthBytes int32
76
+ BmPlanes uint16
77
+ BmBitsPixel uint16
78
+ BmBits unsafe.Pointer
70
79
}
71
80
72
81
const (
@@ -105,71 +114,111 @@ func ExtractIcon(fileName string, index int) (*Icon, error) {
105
114
return ico , err
106
115
}
107
116
108
- // SaveHIconAsPNG extracts the color bitmap from an HICON and writes it to a PNG file.
109
- func SaveHIconAsPNG (hIcon w32.HICON , destPath string ) error {
110
- // 1) Get the ICONINFO structure (which contains two HBITMAPs)
111
- var ii ICONINFO
112
- if ret , _ , err := procGetIconInfo .Call (
117
+ func SaveHIconAsPNG (hIcon w32.HICON , filePath string ) error {
118
+ // Load necessary DLLs
119
+ user32 := syscall .NewLazyDLL ("user32.dll" )
120
+ gdi32 := syscall .NewLazyDLL ("gdi32.dll" )
121
+
122
+ // Get procedures
123
+ getIconInfo := user32 .NewProc ("GetIconInfo" )
124
+ getObject := gdi32 .NewProc ("GetObjectW" )
125
+ createCompatibleDC := gdi32 .NewProc ("CreateCompatibleDC" )
126
+ selectObject := gdi32 .NewProc ("SelectObject" )
127
+ getDIBits := gdi32 .NewProc ("GetDIBits" )
128
+ deleteObject := gdi32 .NewProc ("DeleteObject" )
129
+ deleteDC := gdi32 .NewProc ("DeleteDC" )
130
+
131
+ // Get icon info
132
+ var iconInfo ICONINFO
133
+ ret , _ , err := getIconInfo .Call (
113
134
uintptr (hIcon ),
114
- uintptr (unsafe .Pointer (& ii )),
115
- ); ret == 0 {
135
+ uintptr (unsafe .Pointer (& iconInfo )),
136
+ )
137
+ if ret == 0 {
116
138
return err
117
139
}
118
- // Make sure we free the bitmaps when done
119
- defer procDeleteObject .Call (ii .HbmMask )
120
- defer procDeleteObject .Call (ii .HbmColor )
121
-
122
- // 2) Render the color bitmap (HbmColor) to RGBA pixels and save
123
- return saveHBitmapAsPNG (w32 .HBITMAP (ii .HbmColor ), destPath )
124
- }
140
+ defer deleteObject .Call (uintptr (iconInfo .HbmMask ))
141
+ defer deleteObject .Call (uintptr (iconInfo .HbmColor ))
125
142
126
- func saveHBitmapAsPNG (hBmp w32.HBITMAP , destPath string ) error {
127
- // 1) Fetch the BITMAP header
143
+ // Get bitmap info
128
144
var bmp BITMAP
129
- if ret , _ , err := procGetObject .Call (
130
- uintptr (hBmp ),
145
+ ret , _ , err = getObject .Call (
146
+ uintptr (iconInfo . HbmColor ),
131
147
unsafe .Sizeof (bmp ),
132
148
uintptr (unsafe .Pointer (& bmp )),
133
- ); ret == 0 {
149
+ )
150
+ if ret == 0 {
134
151
return err
135
152
}
136
- w , h := int (bmp .Width ), int (bmp .Height )
137
-
138
- // 2) Prepare BITMAPINFO for 32-bit, top-down DIB
139
- var bmi BITMAPINFO
140
- bmi .Header = BITMAPINFOHEADER {
141
- Size : uint32 (unsafe .Sizeof (BITMAPINFOHEADER {})),
142
- Width : int32 (w ),
143
- Height : - int32 (h ), // negative = top-down
144
- Planes : 1 ,
145
- BitCount : 32 ,
146
- Compression : BI_RGB ,
147
- }
148
153
149
- // 3) Allocate a buffer and pull the bits
150
- buf := make ([]byte , w * h * 4 )
151
- if ret , _ , err := procGetDIBits .Call (
152
- 0 ,
153
- uintptr (hBmp ),
154
+ // Create DC
155
+ hdc , _ , _ := createCompatibleDC .Call (0 )
156
+ if hdc == 0 {
157
+ return syscall .EINVAL
158
+ }
159
+ defer deleteDC .Call (hdc )
160
+
161
+ // Select bitmap into DC
162
+ oldBitmap , _ , _ := selectObject .Call (hdc , uintptr (iconInfo .HbmColor ))
163
+ defer selectObject .Call (hdc , oldBitmap )
164
+
165
+ // Prepare bitmap info header
166
+ var bi BITMAPINFO
167
+ bi .BmiHeader .BiSize = uint32 (unsafe .Sizeof (bi .BmiHeader ))
168
+ bi .BmiHeader .BiWidth = bmp .BmWidth
169
+ bi .BmiHeader .BiHeight = bmp .BmHeight
170
+ bi .BmiHeader .BiPlanes = 1
171
+ bi .BmiHeader .BiBitCount = 32
172
+ bi .BmiHeader .BiCompression = BI_RGB
173
+
174
+ // Allocate memory for bitmap bits
175
+ width , height := int (bmp .BmWidth ), int (bmp .BmHeight )
176
+ bufferSize := width * height * 4
177
+ bits := make ([]byte , bufferSize )
178
+
179
+ // Get bitmap bits
180
+ ret , _ , err = getDIBits .Call (
181
+ hdc ,
182
+ uintptr (iconInfo .HbmColor ),
154
183
0 ,
155
- uintptr (h ),
156
- uintptr (unsafe .Pointer (& buf [0 ])),
157
- uintptr (unsafe .Pointer (& bmi )),
184
+ uintptr (bmp . BmHeight ),
185
+ uintptr (unsafe .Pointer (& bits [0 ])),
186
+ uintptr (unsafe .Pointer (& bi )),
158
187
DIB_RGB_COLORS ,
159
- ); ret == 0 {
188
+ )
189
+ if ret == 0 {
160
190
return err
161
191
}
162
192
163
- // 4) Copy into an image.RGBA and write PNG
164
- img := image .NewRGBA (image .Rect (0 , 0 , w , h ))
165
- copy (img .Pix , buf )
193
+ // Create Go image
194
+ img := image .NewRGBA (image .Rect (0 , 0 , width , height ))
195
+
196
+ // Convert DIB to RGBA
197
+ for y := 0 ; y < height ; y ++ {
198
+ for x := 0 ; x < width ; x ++ {
199
+ // DIB is bottom-up, so we need to invert Y
200
+ dibIndex := ((height - 1 - y )* width + x ) * 4
166
201
167
- f , err := os .Create (destPath )
202
+ // BGRA to RGBA
203
+ b := bits [dibIndex ]
204
+ g := bits [dibIndex + 1 ]
205
+ r := bits [dibIndex + 2 ]
206
+ a := bits [dibIndex + 3 ]
207
+
208
+ // Set pixel in the image
209
+ img .Set (x , y , color.RGBA {R : r , G : g , B : b , A : a })
210
+ }
211
+ }
212
+
213
+ // Create output file
214
+ outFile , err := os .Create (filePath )
168
215
if err != nil {
169
216
return err
170
217
}
171
- defer f .Close ()
172
- return png .Encode (f , img )
218
+ defer outFile .Close ()
219
+
220
+ // Encode and save the image
221
+ return png .Encode (outFile , img )
173
222
}
174
223
175
224
func (ic * Icon ) Destroy () bool {
0 commit comments