Skip to content
This repository was archived by the owner on Apr 13, 2024. It is now read-only.

Commit 3cc79d2

Browse files
committed
Fixed icons getting black after a while
1 parent f2199ca commit 3cc79d2

9 files changed

Lines changed: 876 additions & 41 deletions

File tree

CoreTempRemote/BitmapHolder.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Created by Joshua Flanagan
2+
// http://flimflan.com/blog
3+
// May 2004
4+
//
5+
// You may freely use this code as you wish, I only ask that you retain my name in the source code
6+
7+
using System;
8+
using System.IO;
9+
10+
namespace FlimFlan.IconEncoder
11+
{
12+
/// <summary>
13+
/// Provides an in-memory representation of the device independent bitmap format
14+
/// </summary>
15+
/// <remarks>
16+
/// Based on documentation at http://www.whisqu.se/per/docs/graphics52.htm
17+
/// </remarks>
18+
public class BitmapHolder
19+
{
20+
public BITMAPFILEHEADER fileHeader;
21+
public BITMAPINFO info;
22+
public byte[] imageData;
23+
24+
public void Open(string filename)
25+
{
26+
this.Open(File.OpenRead(filename));
27+
}
28+
29+
public void Open(Stream stream)
30+
{
31+
using (BinaryReader br = new BinaryReader(stream))
32+
{
33+
fileHeader.Populate(br);
34+
info.Populate(br);
35+
if (info.infoHeader.biSizeImage > 0)
36+
{
37+
imageData = br.ReadBytes((int)info.infoHeader.biSizeImage);
38+
}
39+
else
40+
{
41+
// can be 0 if the bitmap is in the BI_RGB format
42+
// in which case you just read all of the remaining data
43+
imageData = br.ReadBytes((int)(br.BaseStream.Length - br.BaseStream.Position));
44+
}
45+
}
46+
}
47+
48+
public void Save(string filename)
49+
{
50+
this.Save(File.OpenWrite(filename));
51+
}
52+
public void Save(Stream stream)
53+
{
54+
using (BinaryWriter bw = new BinaryWriter(stream))
55+
{
56+
fileHeader.Save(bw);
57+
info.Save(bw);
58+
bw.Write(imageData);
59+
}
60+
}
61+
}
62+
}

CoreTempRemote/CExtensions.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace CoreTempRemote
8+
{
9+
public static class CExtensions
10+
{
11+
public static string Truncate(this string value, int maxChars)
12+
{
13+
return value.Length <= maxChars ? value : value.Substring(0, maxChars) + "...";
14+
}
15+
}
16+
}

CoreTempRemote/Converter.cs

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
// Created by Joshua Flanagan
2+
// http://flimflan.com/blog
3+
// May 2004
4+
//
5+
// You may freely use this code as you wish, I only ask that you retain my name in the source code
6+
7+
// Modified by Pavel Janda
8+
// - added support for 32bpp bitmaps
9+
// November 2006
10+
11+
using System;
12+
using System.IO;
13+
using System.Collections;
14+
using System.Drawing;
15+
16+
namespace FlimFlan.IconEncoder
17+
{
18+
/// <summary>
19+
/// Provides methods for converting between the bitmap and icon formats
20+
/// </summary>
21+
public class Converter
22+
{
23+
private Converter(){}
24+
public static Icon BitmapToIcon(Bitmap b)
25+
{
26+
IconHolder ico = BitmapToIconHolder(b);
27+
Icon newIcon;
28+
using (BinaryWriter bw = new BinaryWriter(new MemoryStream()))
29+
{
30+
ico.Save(bw);
31+
bw.BaseStream.Position = 0;
32+
newIcon = new Icon(bw.BaseStream);
33+
}
34+
return newIcon;
35+
}
36+
37+
public static IconHolder BitmapToIconHolder(Bitmap b)
38+
{
39+
BitmapHolder bmp = new BitmapHolder();;
40+
using (MemoryStream stream = new MemoryStream())
41+
{
42+
b.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);
43+
stream.Position = 0;
44+
bmp.Open(stream);
45+
}
46+
return BitmapToIconHolder(bmp);
47+
}
48+
49+
public static IconHolder BitmapToIconHolder(BitmapHolder bmp)
50+
{
51+
bool mapColors = (bmp.info.infoHeader.biBitCount <= 24);
52+
int maximumColors = 1 << bmp.info.infoHeader.biBitCount;
53+
//Hashtable uniqueColors = new Hashtable(maximumColors);
54+
// actual colors is probably nowhere near maximum, so dont try to initialize the hashtable
55+
Hashtable uniqueColors = new Hashtable();
56+
57+
int sourcePosition = 0;
58+
int numPixels = bmp.info.infoHeader.biHeight * bmp.info.infoHeader.biWidth;
59+
byte[] indexedImage = new byte[numPixels];
60+
byte colorIndex;
61+
62+
if (mapColors)
63+
{
64+
for (int i=0; i < indexedImage.Length; i++)
65+
{
66+
//TODO: currently assumes source bitmap is 24bit color
67+
//read 3 bytes, convert to color
68+
byte[] pixel = new byte[3];
69+
Array.Copy(bmp.imageData, sourcePosition, pixel, 0, 3);
70+
sourcePosition += 3;
71+
72+
RGBQUAD color = new RGBQUAD(pixel);
73+
if (uniqueColors.Contains(color))
74+
{
75+
colorIndex = Convert.ToByte(uniqueColors[color]);
76+
}
77+
else
78+
{
79+
if (uniqueColors.Count > byte.MaxValue)
80+
{
81+
throw new NotSupportedException(String.Format("The source image contains more than {0} colors.", byte.MaxValue));
82+
}
83+
colorIndex = Convert.ToByte(uniqueColors.Count);
84+
uniqueColors.Add(color, colorIndex);
85+
}
86+
// store pixel as an index into the color table
87+
indexedImage[i] = colorIndex;
88+
}
89+
}
90+
else
91+
{
92+
// added by Pavel Janda on 14/11/2006
93+
if (bmp.info.infoHeader.biBitCount == 32)
94+
{
95+
for (int i=0; i < indexedImage.Length; i++)
96+
{
97+
//TODO: currently assumes source bitmap is 32bit color with alpha set to zero
98+
//ignore first byte, read another 3 bytes, convert to color
99+
byte[] pixel = new byte[4];
100+
Array.Copy(bmp.imageData, sourcePosition, pixel, 0, 4);
101+
sourcePosition += 4;
102+
103+
RGBQUAD color = new RGBQUAD(pixel[0], pixel[1], pixel[2], pixel[3]);
104+
if (uniqueColors.Contains(color))
105+
{
106+
colorIndex = Convert.ToByte(uniqueColors[color]);
107+
}
108+
else
109+
{
110+
if (uniqueColors.Count > byte.MaxValue)
111+
{
112+
throw new NotSupportedException(String.Format("The source image contains more than {0} colors.", byte.MaxValue));
113+
}
114+
colorIndex = Convert.ToByte(uniqueColors.Count);
115+
uniqueColors.Add(color, colorIndex);
116+
}
117+
// store pixel as an index into the color table
118+
indexedImage[i] = colorIndex;
119+
}
120+
// end of addition
121+
}
122+
else
123+
{
124+
//TODO: implement converting an indexed bitmap
125+
throw new NotImplementedException("Unable to convert indexed bitmaps.");
126+
}
127+
}
128+
129+
ushort bitCount = getBitCount(uniqueColors.Count);
130+
// *** Build Icon ***
131+
IconHolder ico = new IconHolder();
132+
ico.iconDirectory.Entries = new ICONDIRENTRY[1];
133+
//TODO: is it really safe to assume the bitmap width/height are bytes?
134+
ico.iconDirectory.Entries[0].Width = (byte) bmp.info.infoHeader.biWidth;
135+
ico.iconDirectory.Entries[0].Height = (byte) bmp.info.infoHeader.biHeight;
136+
ico.iconDirectory.Entries[0].BitCount = bitCount; // maybe 0?
137+
ico.iconDirectory.Entries[0].ColorCount = (uniqueColors.Count > byte.MaxValue) ? (byte)0 : (byte)uniqueColors.Count;
138+
//HACK: safe to assume that the first imageoffset is always 22
139+
ico.iconDirectory.Entries[0].ImageOffset = 22;
140+
ico.iconDirectory.Entries[0].Planes = 0;
141+
ico.iconImages[0].Header.biBitCount = bitCount;
142+
ico.iconImages[0].Header.biWidth = ico.iconDirectory.Entries[0].Width;
143+
// height is doubled in header, to account for XOR and AND
144+
ico.iconImages[0].Header.biHeight = ico.iconDirectory.Entries[0].Height << 1;
145+
ico.iconImages[0].XOR = new byte[ico.iconImages[0].numBytesInXor()];
146+
ico.iconImages[0].AND = new byte[ico.iconImages[0].numBytesInAnd()];
147+
ico.iconImages[0].Header.biSize = 40; // always
148+
ico.iconImages[0].Header.biSizeImage = (uint)ico.iconImages[0].XOR.Length;
149+
ico.iconImages[0].Header.biPlanes = 1;
150+
ico.iconImages[0].Colors = buildColorTable(uniqueColors, bitCount);
151+
//BytesInRes = biSize + colors * 4 + XOR + AND
152+
ico.iconDirectory.Entries[0].BytesInRes = (uint)(ico.iconImages[0].Header.biSize
153+
+ (ico.iconImages[0].Colors.Length * 4)
154+
+ ico.iconImages[0].XOR.Length
155+
+ ico.iconImages[0].AND.Length);
156+
157+
// copy image data
158+
int bytePosXOR = 0;
159+
int bytePosAND = 0;
160+
byte transparentIndex = 0;
161+
transparentIndex = indexedImage[0];
162+
//initialize AND
163+
ico.iconImages[0].AND[0] = byte.MaxValue;
164+
165+
int pixelsPerByte;
166+
int bytesPerRow; // must be a long boundary (multiple of 4)
167+
int[] shiftCounts;
168+
169+
switch (bitCount)
170+
{
171+
case 1:
172+
pixelsPerByte = 8;
173+
shiftCounts = new int[] {7, 6, 5, 4, 3, 2, 1, 0};
174+
break;
175+
case 4:
176+
pixelsPerByte = 2;
177+
shiftCounts = new int[] {4, 0};
178+
break;
179+
case 8:
180+
pixelsPerByte = 1;
181+
shiftCounts = new int[] {0};
182+
break;
183+
default:
184+
throw new NotSupportedException("Bits per pixel must be 1, 4, or 8");
185+
}
186+
bytesPerRow = ico.iconDirectory.Entries[0].Width / pixelsPerByte;
187+
int padBytes = bytesPerRow % 4;
188+
if (padBytes > 0)
189+
padBytes = 4 - padBytes;
190+
191+
byte currentByte;
192+
sourcePosition = 0;
193+
for (int row=0; row < ico.iconDirectory.Entries[0].Height; ++row)
194+
{
195+
for (int rowByte=0; rowByte < bytesPerRow; ++rowByte)
196+
{
197+
currentByte = 0;
198+
for (int pixel=0; pixel < pixelsPerByte; ++pixel)
199+
{
200+
byte index = indexedImage[sourcePosition++];
201+
byte shiftedIndex = (byte)(index << shiftCounts[pixel]);
202+
currentByte |= shiftedIndex;
203+
}
204+
ico.iconImages[0].XOR[bytePosXOR] = currentByte;
205+
++bytePosXOR;
206+
}
207+
// make sure each scan line ends on a long boundary
208+
bytePosXOR += padBytes;
209+
}
210+
211+
for(int i=0; i<indexedImage.Length; i++)
212+
{
213+
byte index = indexedImage[i];
214+
int bitPosAND = 128 >> (i % 8);
215+
if (index != transparentIndex)
216+
ico.iconImages[0].AND[bytePosAND] ^= (byte)bitPosAND;
217+
if (bitPosAND == 1)
218+
{
219+
// need to start another byte for next pixel
220+
if (bytePosAND % 2 ==1)
221+
{
222+
//TODO: fix assumption that icon is 16px wide
223+
//skip some bytes so that scanline ends on a long barrier
224+
bytePosAND += 3;
225+
}
226+
else
227+
{
228+
bytePosAND += 1;
229+
}
230+
if (bytePosAND < ico.iconImages[0].AND.Length)
231+
ico.iconImages[0].AND[bytePosAND] = byte.MaxValue;
232+
}
233+
}
234+
return ico;
235+
}
236+
237+
private static ushort getBitCount(int uniqueColorCount)
238+
{
239+
if (uniqueColorCount <= 2)
240+
{
241+
return 1;
242+
}
243+
if (uniqueColorCount <= 16)
244+
{
245+
return 4;
246+
}
247+
if (uniqueColorCount <= 256)
248+
{
249+
return 8;
250+
}
251+
return 24;
252+
}
253+
254+
private static RGBQUAD[] buildColorTable(Hashtable colors, ushort bpp)
255+
{
256+
//RGBQUAD[] colorTable = new RGBQUAD[colors.Count];
257+
//HACK: it looks like the color array needs to be the max size based on bitcount
258+
int numColors = 1 << bpp;
259+
RGBQUAD[] colorTable = new RGBQUAD[numColors];
260+
foreach(RGBQUAD color in colors.Keys)
261+
{
262+
int colorIndex = Convert.ToInt32( colors[color] );
263+
colorTable[ colorIndex ] = color;
264+
}
265+
return colorTable;
266+
}
267+
268+
// public static BitmapHolder IconToBitmap(IconHolder ico)
269+
// {
270+
// //TODO: implement
271+
// return new BitmapHolder();
272+
// }
273+
}
274+
}

CoreTempRemote/CoreTempRemote.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,10 @@
5252
<Reference Include="System.Xml" />
5353
</ItemGroup>
5454
<ItemGroup>
55+
<Compile Include="BitmapHolder.cs" />
5556
<Compile Include="CConfig.cs" />
57+
<Compile Include="CExtensions.cs" />
58+
<Compile Include="Converter.cs" />
5659
<Compile Include="DoubleBufferedTreeView.cs">
5760
<SubType>Component</SubType>
5861
</Compile>
@@ -68,6 +71,8 @@
6871
<Compile Include="FormSettings.Designer.cs">
6972
<DependentUpon>FormSettings.cs</DependentUpon>
7073
</Compile>
74+
<Compile Include="IconHolder.cs" />
75+
<Compile Include="ImageStructs.cs" />
7176
<Compile Include="JValues.cs" />
7277
<Compile Include="Program.cs" />
7378
<Compile Include="Properties\AssemblyInfo.cs" />

0 commit comments

Comments
 (0)