1// tImageICO.cpp 
2// 
3// This class knows how to load windows icon (ico) files. It loads the data into multiple tPixel arrays, one for each 
4// part (ico files may be multiple images at different resolutions). These arrays may be 'stolen' by tPictures. The 
5// loading code is a modificaton of code from Victor Laskin. In particular the code now: 
6// a) Loads all parts of an ico, not just the biggest one. 
7// b) Supports embedded png images. 
8// c) Supports widths and heights of 256. 
9// 
10// Copyright (c) 2020 Tristan Grimmer. 
11// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby 
12// granted, provided that the above copyright notice and this permission notice appear in all copies. 
13// 
14// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 
15// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 
16// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 
17// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 
18// PERFORMANCE OF THIS SOFTWARE. 
19// 
20// Includes modified version of code from Victor Laskin. Here is Victor Laskin's header/licence in the original ico.cpp: 
21 
22// Code by Victor Laskin (victor.laskin@gmail.com) 
23// Rev 2 - 1bit color was added, fixes for bit mask. 
24// 
25// THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
26// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
27// THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
28// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
29// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
30// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
31// POSSIBILITY OF SUCH DAMAGE. 
32 
33#include <Foundation/tStandard.h> 
34#include <Foundation/tString.h> 
35#include <System/tFile.h> 
36#include "Image/tImageICO.h" 
37#include "Image/tImagePNG.h" 
38using namespace tSystem
39namespace tImage 
40
41 
42  
43// These structs represent how the icon information is stored in an ICO file. 
44struct IconDirEntry 
45
46 uint8 Width; // Width of the image. 
47 uint8 Height; // Height of the image (times 2). 
48 uint8 ColorCount; // Number of colors in image (0 if >=8bpp). 
49 uint8 Reserved
50 uint16 Planes; // Colour planes. 
51 uint16 BitCount; // Bits per pixel. 
52 uint32 BytesInRes; // How many bytes in this resource? 
53 uint32 ImageOffset; // Where in the file is this image. 
54}; 
55 
56 
57struct IconDir 
58
59 uint16 Reserved
60 uint16 Type; // Resource type (1 for icons). 
61 uint16 Count; // How many images? 
62 //IconDirEntrys follow. One for each image. 
63}; 
64 
65 
66// size - 40 bytes 
67struct BitmapInfoHeader 
68
69 uint32 Size
70 uint32 Width
71 uint32 Height; // Icon Height (added height of XOR-Bitmap and AND-Bitmap). 
72 uint16 Planes
73 uint16 BitCount
74 uint32 Compression
75 int32 SizeImage
76 uint32 XPelsPerMeter
77 uint32 YPelsPerMeter
78 uint32 ClrUsed
79 uint32 ClrImportant
80}; 
81 
82 
83struct IconImage 
84
85 BitmapInfoHeader Header; // DIB header. 
86 uint32 Colours[1]; // Color table (short 4 bytes) //RGBQUAD. 
87 uint8 XOR[1]; // DIB bits for XOR mask. 
88 uint8 AND[1]; // DIB bits for AND mask. 
89}; 
90  
91 
92bool tImageICO::Load(const tString& icoFile
93
94 Clear(); 
95 
96 if (tGetFileType(icoFile) != tFileType::ICO
97 return false
98 
99 if (!tFileExists(icoFile)) 
100 return false
101 
102 int numBytes = 0
103 uint8* icoFileInMemory = tLoadFile(icoFile, nullptr, &numBytes); 
104 bool success = PopulateFrames(icoFileInMemory, numBytes);  
105 delete[] icoFileInMemory
106 
107 return success
108
109 
110 
111bool tImageICO::PopulateFrames(const uint8* buffer, int numBytes
112
113 IconDir* icoDir = (IconDir*)buffer
114 int iconsCount = icoDir->Count
115 
116 if (icoDir->Reserved != 0
117 return false
118  
119 if (icoDir->Type != 1
120 return false
121 
122 if (iconsCount == 0
123 return false
124  
125 if (iconsCount > 20
126 return false
127 
128 const uint8* cursor = buffer
129 cursor += 6
130 IconDirEntry* dirEntry = (IconDirEntry*)cursor
131 
132 for (int i = 0; i < iconsCount; i++) 
133
134 int w = dirEntry->Width
135 if (w == 0
136 w = 256
137  
138 int h = dirEntry->Height
139 if (h == 0
140 h = 256
141  
142 int offset = dirEntry->ImageOffset
143 if (!offset || (offset >= numBytes)) 
144 continue
145  
146 Frame* newFrame = CreateFrame(buffer+offset, w, h, numBytes); 
147 if (!newFrame
148 continue
149 
150 Frames.Append(newFrame); 
151 dirEntry++; 
152
153 
154 return !Frames.IsEmpty(); 
155
156 
157 
158tImageICO::Frame* tImageICO::CreateFrame(const uint8* cursor, int width, int height, int numBytes
159
160 IconImage* icon = (IconImage*)cursor
161  
162 // ICO files may have embedded pngs. 
163 if (icon->Header.Size == 0x474e5089
164
165 tImagePNG pngImage(cursor, numBytes); 
166 if (!pngImage.IsValid()) 
167 return nullptr
168 
169 width = pngImage.GetWidth(); 
170 height = pngImage.GetHeight(); 
171 tAssert((width > 0) && (height > 0)); 
172 
173 tPixel* pixels = pngImage.StealPixels(); 
174 bool isOpaque = pngImage.IsOpaque(); 
175  
176 Frame* newFrame = new Frame
177 newFrame->SrcPixelFormat = isOpaque ? tPixelFormat::R8G8B8 : tPixelFormat::R8G8B8A8
178 newFrame->Width = width
179 newFrame->Height = height
180 newFrame->Pixels = pixels
181 return newFrame
182
183  
184 int realBitsCount = int(icon->Header.BitCount); 
185 bool hasAndMask = (realBitsCount < 32) && (height != icon->Header.Height); 
186 
187 cursor += 40
188 int numPixels = width * height
189  
190 tPixel* pixels = new tPixel[numPixels]; 
191 uint8* image = (uint8*)pixels
192 tPixelFormat srcPixelFormat = tPixelFormat::Invalid
193  
194 // rgba + vertical swap 
195 if (realBitsCount == 32
196
197 srcPixelFormat = tPixelFormat::R8G8B8A8
198 for (int x = 0; x < width; x++) 
199
200 for (int y = 0; y < height; y++) 
201
202 int shift = 4 * (x + y * width); 
203  
204 // Rows from bottom to top. 
205 // int shift2 = 4 * (x + (height - y - 1) * width); 
206 int shift2 = 4 * (x + y * width); 
207  
208 image[shift] = cursor[shift2 +2]; 
209 image[shift+1] = cursor[shift2 +1]; 
210 image[shift+2] = cursor[shift2 ]; 
211 image[shift+3] = cursor[shift2 +3]; 
212
213
214
215 
216 if (realBitsCount == 24
217
218 srcPixelFormat = tPixelFormat::R8G8B8
219 for (int x = 0; x < width; x++) 
220
221 for (int y = 0; y < height; y++) 
222
223 int shift = 4 * (x + y * width); 
224  
225 // Rows from bottom to top. 
226 // int shift2 = 3 * (x + (height - y - 1) * width); 
227 int shift2 = 3 * (x + y * width); 
228  
229 image[shift] = cursor[shift2 +2]; 
230 image[shift+1] = cursor[shift2 +1]; 
231 image[shift+2] = cursor[shift2 ]; 
232 image[shift+3] = 255
233
234
235
236 
237 if (realBitsCount == 8
238
239 // 256 colour palette. 
240 srcPixelFormat = tPixelFormat::PAL_8BIT
241  
242 uint8* colors = (uint8*)cursor
243 cursor += 256 * 4
244 for (int x = 0; x < width; x++) 
245
246 for (int y = 0; y < height; y++) 
247
248 int shift = 4 * (x + y * width); 
249  
250 // Rows from bottom to top. 
251 // int shift2 = (x + (height - y - 1) * width); 
252 int shift2 = (x + y * width); 
253  
254 int index = 4 * cursor[shift2]; 
255 image[shift] = colors[index + 2]; 
256 image[shift+1] = colors[index + 1]; 
257 image[shift+2] = colors[index ]; 
258 image[shift+3] = 255
259
260
261
262 
263 if (realBitsCount == 4
264
265 // 16 colour palette. 
266 srcPixelFormat = tPixelFormat::PAL_4BIT
267  
268 uint8* colors = (uint8*)cursor
269 cursor += 16 * 4
270 for (int x = 0; x < width; x++) 
271
272 for (int y = 0; y < height; y++) 
273
274 int shift = 4 * (x + y * width); 
275 
276 // Rows from bottom to top. 
277 // int shift2 = (x + (height - y - 1) * width); 
278 int shift2 = (x + y * width); 
279 
280 uint8 index = cursor[shift2 / 2]; 
281 if (shift2 % 2 == 0
282 index = (index >> 4) & 0xF
283 else 
284 index = index & 0xF
285 index *= 4
286 
287 image[shift] = colors[index + 2]; 
288 image[shift+1] = colors[index + 1]; 
289 image[shift+2] = colors[index ]; 
290 image[shift+3] = 255
291
292
293
294 
295 if (realBitsCount == 1
296
297 // 2 colour palette. 
298 srcPixelFormat = tPixelFormat::PAL_1BIT
299 
300 uint8* colors = (uint8*)cursor
301 cursor += 2 * 4
302 
303 int boundary = width; //!!! 32 bit boundary (http://www.daubnet.com/en/file-format-ico) 
304 while (boundary % 32 != 0
305 boundary++; 
306 
307 for (int x = 0; x < width; x++) 
308
309 for (int y = 0; y < height; y++) 
310
311 int shift = 4 * (x + y * width); 
312 
313 // Rows from bottom to top. 
314 // int shift2 = (x + (height - y - 1) * boundary); 
315 int shift2 = (x + y * boundary); 
316  
317 uint8 index = cursor[shift2 / 8]; 
318 
319 // select 1 bit only 
320 uint8 bit = 7 - (x % 8); 
321 index = (index >> bit) & 0x01
322 index *= 4
323  
324 image[shift] = colors[index + 2]; 
325 image[shift+1] = colors[index + 1]; 
326 image[shift+2] = colors[index ]; 
327 image[shift+3] = 255
328
329
330
331 
332 // Read AND mask after base color data - 1 BIT MASK 
333 if (hasAndMask
334
335 int boundary = width * realBitsCount; //!!! 32 bit boundary (http://www.daubnet.com/en/file-format-ico) 
336 while (boundary % 32 != 0
337 boundary++; 
338 cursor += boundary * height / 8
339 
340 boundary = width
341 while (boundary % 32 != 0
342 boundary++; 
343 
344 for (int y = 0; y < height; y++) 
345
346 for (int x = 0; x < width; x++) 
347
348 int shift = 4 * (x + y * width) + 3
349 uint8 bit = 7 - (x % 8); 
350 
351 // Rows from bottom to top. 
352 // int shift2 = (x + (height - y - 1) * boundary) / 8; 
353 int shift2 = (x + y * boundary) / 8
354 
355 int mask = (0x01 & ((unsigned char)cursor[shift2] >> bit)); 
356 image[shift] *= 1 - mask
357
358
359
360  
361 Frame* newFrame = new Frame
362 newFrame->SrcPixelFormat = srcPixelFormat
363 newFrame->Width = width
364 newFrame->Height = height
365 newFrame->Pixels = pixels
366 
367 return newFrame
368
369 
370 
371
372