Drop useless comment marker
[TortoiseGit.git] / src / Utils / IconExtractor.cpp
blob9fc3b04910b4c263bc87c051a3a8cf7ad8d3144a
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2018-2019 - 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.
20 #include "stdafx.h"
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);
35 if (!hRsrc)
36 return GetLastError();
38 HGLOBAL hGlobal = nullptr;
39 if ((hGlobal = LoadResource(hResource, hRsrc)) == nullptr)
40 return GetLastError();
42 LPMEMICONDIR lpIcon = nullptr;
43 if ((lpIcon = static_cast<LPMEMICONDIR>(LockResource(hGlobal))) == nullptr)
44 return GetLastError();
46 LPICONRESOURCE lpIR = nullptr;
47 if ((lpIR = static_cast<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;
52 SCOPE_EXIT
54 for (UINT i = 0; i < lpIR->nNumImages; ++i)
55 free(lpIR->IconImages[i].lpBits);
56 free(lpIR);
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 = static_cast<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);
87 // open the file
88 if (!hFile)
89 return GetLastError();
91 // Write the header
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 = static_cast<BYTE>(lpIR->IconImages[i].Width);
102 ide.bHeight = static_cast<BYTE>(lpIR->IconImages[i].Height);
103 if (ide.bHeight == 0) // 256x256 icon, both width and height must be 0
104 ide.bWidth = 0;
105 ide.bReserved = 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)
110 ide.bColorCount = 0;
111 else
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))
136 bError = true;
138 if (dwBytesWritten != lpIR->IconImages[i].dwNumBytes)
139 bError = true;
141 // set it back
142 lpIR->IconImages[i].lpbi->bmiHeader.biSizeImage = dwTemp;
143 if (bError)
144 return GetLastError();
146 return NO_ERROR;
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;
159 return dwSize;
162 DWORD CIconExtractor::WriteICOHeader(HANDLE hFile, UINT nNumEntries) const
164 WORD Output = 0;
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)
174 Output = 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 = static_cast<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();
188 return NO_ERROR;
191 BOOL CIconExtractor::AdjustIconImagePointers(LPICONIMAGE lpImage)
193 if (!lpImage)
194 return FALSE;
196 // BITMAPINFO is at beginning of bits
197 lpImage->lpbi = reinterpret_cast<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;
202 // How many colors?
203 lpImage->Colors = lpImage->lpbi->bmiHeader.biPlanes * lpImage->lpbi->bmiHeader.biBitCount;
204 // XOR bits follow the header and color table
205 lpImage->lpXOR = reinterpret_cast<PBYTE>(FindDIBBits(reinterpret_cast<LPSTR>(lpImage->lpbi)));
206 // AND bits follow the XOR bits
207 lpImage->lpAND = lpImage->lpXOR + (lpImage->Height * BytesPerLine(reinterpret_cast<LPBITMAPINFOHEADER>(lpImage->lpbi)));
209 return TRUE;
212 LPSTR CIconExtractor::FindDIBBits(LPSTR lpbi)
214 return (lpbi + *reinterpret_cast<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 = reinterpret_cast<LPBITMAPINFOHEADER>(lpbi)->biClrUsed;
231 if (dwClrUsed)
232 return static_cast<WORD>(dwClrUsed);
234 WORD wBitCount = reinterpret_cast<LPBITMAPINFOHEADER>(lpbi)->biBitCount;
236 switch (wBitCount)
238 case 1:
239 return 2;
240 case 4:
241 return 16;
242 case 8:
243 return 256;
244 default:
245 return 0;
247 //return 0;