1
// TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2018 - TortoiseGit
4 // Copyright (C) 2010-2012, 2014-2015 - TortoiseSVN
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "IconExtractor.h"
22 #include "SmartHandle.h"
24 #define WIDTHBYTES(bits) ((((bits) + 31) >> 5) << 2)
26 CIconExtractor::CIconExtractor()
30 DWORD
CIconExtractor::ExtractIcon(HINSTANCE hResource
, LPCTSTR id
, LPCTSTR TargetICON
)
32 // Find the group icon resource
33 HRSRC hRsrc
= FindResource(hResource
, id
, RT_GROUP_ICON
);
36 return GetLastError();
38 HGLOBAL hGlobal
= nullptr;
39 if ((hGlobal
= LoadResource(hResource
, hRsrc
)) == nullptr)
40 return GetLastError();
42 LPMEMICONDIR lpIcon
= nullptr;
43 if ((lpIcon
= (LPMEMICONDIR
)LockResource(hGlobal
)) == nullptr)
44 return GetLastError();
46 LPICONRESOURCE lpIR
= nullptr;
47 if ((lpIR
= (LPICONRESOURCE
)malloc(sizeof(ICONRESOURCE
) + ((lpIcon
->idCount
- 1) * sizeof(ICONIMAGE
)))) == nullptr)
48 return GetLastError();
49 SecureZeroMemory(lpIR
, sizeof(ICONRESOURCE
) + ((lpIcon
->idCount
- 1) * sizeof(ICONIMAGE
)));
51 lpIR
->nNumImages
= lpIcon
->idCount
;
54 for (UINT i
= 0; i
< lpIR
->nNumImages
; ++i
)
55 free(lpIR
->IconImages
[i
].lpBits
);
59 // Go through all the icons
60 for (UINT i
= 0; i
< lpIR
->nNumImages
; ++i
)
62 // Get the individual icon
63 if ((hRsrc
= FindResource(hResource
, MAKEINTRESOURCE(lpIcon
->idEntries
[i
].nID
), RT_ICON
)) == nullptr)
64 return GetLastError();
65 if ((hGlobal
= LoadResource(hResource
, hRsrc
)) == nullptr)
66 return GetLastError();
68 // Store a copy of the resource locally
69 lpIR
->IconImages
[i
].dwNumBytes
= SizeofResource(hResource
, hRsrc
);
70 lpIR
->IconImages
[i
].lpBits
= (LPBYTE
)malloc(lpIR
->IconImages
[i
].dwNumBytes
);
71 if (!lpIR
->IconImages
[i
].lpBits
)
72 return GetLastError();
74 memcpy(lpIR
->IconImages
[i
].lpBits
, LockResource(hGlobal
), lpIR
->IconImages
[i
].dwNumBytes
);
76 // Adjust internal pointers
77 if (!AdjustIconImagePointers(&(lpIR
->IconImages
[i
])))
78 return GetLastError();
81 return WriteIconToICOFile(lpIR
, TargetICON
);
84 DWORD
CIconExtractor::WriteIconToICOFile(LPICONRESOURCE lpIR
, LPCTSTR szFileName
)
86 CAutoFile hFile
= ::CreateFile(szFileName
, GENERIC_WRITE
, FILE_SHARE_READ
, nullptr, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, nullptr);
89 return GetLastError();
92 if (WriteICOHeader(hFile
, lpIR
->nNumImages
))
93 return GetLastError();
95 // Write the ICONDIRENTRY's
96 for (UINT i
= 0; i
< lpIR
->nNumImages
; ++i
)
98 ICONDIRENTRY ide
= { 0 };
100 // Convert internal format to ICONDIRENTRY
101 ide
.bWidth
= (BYTE
)lpIR
->IconImages
[i
].Width
;
102 ide
.bHeight
= (BYTE
)lpIR
->IconImages
[i
].Height
;
103 if (ide
.bHeight
== 0) // 256x256 icon, both width and height must be 0
106 ide
.wPlanes
= lpIR
->IconImages
[i
].lpbi
->bmiHeader
.biPlanes
;
107 ide
.wBitCount
= lpIR
->IconImages
[i
].lpbi
->bmiHeader
.biBitCount
;
109 if ((ide
.wPlanes
* ide
.wBitCount
) >= 8)
112 ide
.bColorCount
= 1 << (ide
.wPlanes
* ide
.wBitCount
);
113 ide
.dwBytesInRes
= lpIR
->IconImages
[i
].dwNumBytes
;
114 ide
.dwImageOffset
= CalculateImageOffset(lpIR
, i
);
116 // Write the ICONDIRENTRY to disk
117 DWORD dwBytesWritten
= 0;
118 if (!WriteFile(hFile
, &ide
, sizeof(ICONDIRENTRY
), &dwBytesWritten
, nullptr))
119 return GetLastError();
121 if (dwBytesWritten
!= sizeof(ICONDIRENTRY
))
122 return GetLastError();
125 // Write the image bits for each image
126 for (UINT i
= 0; i
< lpIR
->nNumImages
; ++i
)
128 DWORD dwTemp
= lpIR
->IconImages
[i
].lpbi
->bmiHeader
.biSizeImage
;
129 bool bError
= false; // fix size even on error
131 // Set the sizeimage member to zero, but not if the icon is PNG
132 if (lpIR
->IconImages
[i
].lpbi
->bmiHeader
.biCompression
!= 65536)
133 lpIR
->IconImages
[i
].lpbi
->bmiHeader
.biSizeImage
= 0;
134 DWORD dwBytesWritten
= 0;
135 if (!WriteFile(hFile
, lpIR
->IconImages
[i
].lpBits
, lpIR
->IconImages
[i
].dwNumBytes
, &dwBytesWritten
, nullptr))
138 if (dwBytesWritten
!= lpIR
->IconImages
[i
].dwNumBytes
)
142 lpIR
->IconImages
[i
].lpbi
->bmiHeader
.biSizeImage
= dwTemp
;
144 return GetLastError();
149 DWORD
CIconExtractor::CalculateImageOffset(LPICONRESOURCE lpIR
, UINT nIndex
) const
151 // Calculate the ICO header size
152 DWORD dwSize
= 3 * sizeof(WORD
);
153 // Add the ICONDIRENTRY's
154 dwSize
+= lpIR
->nNumImages
* sizeof(ICONDIRENTRY
);
155 // Add the sizes of the previous images
156 for (UINT i
= 0; i
< nIndex
; ++i
)
157 dwSize
+= lpIR
->IconImages
[i
].dwNumBytes
;
162 DWORD
CIconExtractor::WriteICOHeader(HANDLE hFile
, UINT nNumEntries
) const
165 DWORD dwBytesWritten
= 0;
167 // Write 'reserved' WORD
168 if (!WriteFile(hFile
, &Output
, sizeof(WORD
), &dwBytesWritten
, nullptr))
169 return GetLastError();
170 // Did we write a WORD?
171 if (dwBytesWritten
!= sizeof(WORD
))
172 return GetLastError();
173 // Write 'type' WORD (1)
175 if (!WriteFile(hFile
, &Output
, sizeof(WORD
), &dwBytesWritten
, nullptr))
176 return GetLastError();
177 // Did we write a WORD?
178 if (dwBytesWritten
!= sizeof(WORD
))
179 return GetLastError();
180 // Write Number of Entries
181 Output
= (WORD
)nNumEntries
;
182 if (!WriteFile(hFile
, &Output
, sizeof(WORD
), &dwBytesWritten
, nullptr))
183 return GetLastError();
184 // Did we write a WORD?
185 if (dwBytesWritten
!= sizeof(WORD
))
186 return GetLastError();
191 BOOL
CIconExtractor::AdjustIconImagePointers(LPICONIMAGE lpImage
)
196 // BITMAPINFO is at beginning of bits
197 lpImage
->lpbi
= (LPBITMAPINFO
)lpImage
->lpBits
;
198 // Width - simple enough
199 lpImage
->Width
= lpImage
->lpbi
->bmiHeader
.biWidth
;
200 // Icons are stored in funky format where height is doubled - account for it
201 lpImage
->Height
= (lpImage
->lpbi
->bmiHeader
.biHeight
) / 2;
203 lpImage
->Colors
= lpImage
->lpbi
->bmiHeader
.biPlanes
* lpImage
->lpbi
->bmiHeader
.biBitCount
;
204 // XOR bits follow the header and color table
205 lpImage
->lpXOR
= (PBYTE
)FindDIBBits((LPSTR
)lpImage
->lpbi
);
206 // AND bits follow the XOR bits
207 lpImage
->lpAND
= lpImage
->lpXOR
+ (lpImage
->Height
* BytesPerLine((LPBITMAPINFOHEADER
)(lpImage
->lpbi
)));
212 LPSTR
CIconExtractor::FindDIBBits(LPSTR lpbi
)
214 return (lpbi
+ *(LPDWORD
)lpbi
+ PaletteSize(lpbi
));
217 WORD
CIconExtractor::PaletteSize(LPSTR lpbi
)
219 return (DIBNumColors(lpbi
) * sizeof(RGBQUAD
));
222 DWORD
CIconExtractor::BytesPerLine(LPBITMAPINFOHEADER lpBMIH
) const
224 return WIDTHBYTES(lpBMIH
->biWidth
* lpBMIH
->biPlanes
* lpBMIH
->biBitCount
);
227 WORD
CIconExtractor::DIBNumColors(LPSTR lpbi
) const
229 DWORD dwClrUsed
= ((LPBITMAPINFOHEADER
)lpbi
)->biClrUsed
;
232 return (WORD
)dwClrUsed
;
234 WORD wBitCount
= ((LPBITMAPINFOHEADER
)lpbi
)->biBitCount
;