* TreeView.cs: Don't draw the selected node when we lose
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / ImageListStreamer.cs
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 //
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 //
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Copyright (c) 2002-2005 Novell, Inc.
21 //
22 // Authors:
23 //      Jackson Harper (jackson@ximian.com)
24 //
25 // Based on work done by:
26 //   Dennis Hayes (dennish@Raytek.com)
27 //   Aleksey Ryabchuk (ryabchuk@yahoo.com)
28
29 using System.IO;
30 using System.Drawing;
31 using System.Collections;
32 using System.Drawing.Imaging;
33 using System.Runtime.Serialization;
34 using System.Runtime.InteropServices;
35
36
37 namespace System.Windows.Forms {
38
39         [Serializable]
40         public sealed class ImageListStreamer : ISerializable {
41
42                 private static byte [] signature = new byte [] {77 , 83 , 70 , 116};
43
44                 private readonly ImageList.ImageCollection imageCollection;
45                 private Image [] images;
46                 private Size image_size;
47                 private Color back_color;
48
49                 internal ImageListStreamer (ImageList.ImageCollection imageCollection) {
50                         this.imageCollection = imageCollection;
51                 }
52                 
53                 private ImageListStreamer (SerializationInfo info, StreamingContext context) {
54
55                         byte [] data = (byte [])info.GetValue ("Data", typeof (byte []));
56                         if (data == null || data.Length <= signature.Length)
57                                 return;
58                         // check the signature ( 'MSFt' )
59                         if (data [0] != signature [0] || data [1] != signature [1] ||
60                                         data [2] != signature [2] ||  data [3] != signature [3])
61                                 return;
62
63                         // calulate size of array needed for decomressed data
64                         int i = 0;
65                         int real_byte_count = 0;
66                         for (i = signature.Length; i < data.Length; i += 2)
67                                 real_byte_count += data [i];
68
69                         if (real_byte_count == 0)
70                                 return;
71                         
72                         int j = 0;
73                         byte [] decompressed = new byte [real_byte_count];
74
75                         for (i = signature.Length; i < data.Length; i += 2) {
76                                 for (int k = 0; k < data [i]; k++)
77                                         decompressed [j++] = data [i + 1];
78                         }
79
80                         MemoryStream stream = new MemoryStream (decompressed);
81                         BinaryReader reader = new BinaryReader (stream);
82
83                         try {
84                                 // read image list header
85                                 reader.ReadUInt16 ();   // usMagic
86                                 reader.ReadUInt16 ();   // usVersion
87                                 ushort cCurImage = reader.ReadUInt16 ();
88                                 reader.ReadUInt16 ();   // cMaxImage
89                                 reader.ReadUInt16 ();   // cGrow
90                                 ushort cx        = reader.ReadUInt16 ();
91                                 ushort cy        = reader.ReadUInt16 ();
92                                 uint   bkcolor   = reader.ReadUInt32 ();
93                                 reader.ReadUInt16 ();   // flags
94
95                                 short [] ovls = new short [4];
96                                 for (i = 0; i < ovls.Length; i++) {
97                                         ovls[i] = reader.ReadInt16 ();
98                                 }
99
100                                 image_size = new Size (cx, cy);
101                                 back_color = Color.FromArgb ((int) bkcolor);
102                                                 
103                                 MemoryStream start = new MemoryStream (decompressed,
104                                                 (int) stream.Position,
105                                                 (int) stream.Length - (int) stream.Position,
106                                                 false);
107
108                                 Image image = Image.FromStream (start);
109
110                                 // Holy calamity. This is what happens on MS
111                                 // if the background colour is 0xFFFFFFFF (CLR_NONE)
112                                 // the mask is set to the color at pixel 0, 0
113                                 Bitmap bmp = image as Bitmap;
114                                 if (bkcolor == 0xFFFFFFFF && bmp != null)
115                                         back_color = bmp.GetPixel (0, 0);
116
117                                 int step = image.Width / cx;
118                                 images = new Image [cCurImage];
119
120                                 Rectangle dest_rect = new Rectangle (0, 0, cx, cy);
121                                 for (int r = 0 ; r < cCurImage ; r++) {
122                                         Rectangle area = new Rectangle (
123                                                 (r % step) * cx,
124                                                 (r / step) * cy,
125                                                 cx, cy);
126                                         Bitmap b = new Bitmap (cx, cy);
127                                         using (Graphics g = Graphics.FromImage (b)) {
128                                                 g.DrawImage (image, dest_rect, area, 
129                                                                 GraphicsUnit.Pixel);
130                                         }
131                                         b.MakeTransparent (back_color);
132                                         images [r] = b;
133                                 }
134
135                         } catch (Exception e) {
136
137                         }
138                 }
139
140                 [MonoTODO ("RLE is broken")]
141                 public void GetObjectData (SerializationInfo info, StreamingContext context)
142                 {
143                         MemoryStream stream = new MemoryStream ();
144                         BinaryWriter writer = new BinaryWriter (stream);
145
146                         writer.Write (signature);
147                         writer.Write (GetStreamData ());
148                         
149                         info.AddValue ("Data", stream.ToArray (), typeof (byte []));
150                 }
151
152                 [MonoTODO ("Images should be written to the stream")]
153                 private byte [] GetStreamData ()
154                 {
155                         MemoryStream stream = new MemoryStream ();
156                         BinaryWriter writer = new BinaryWriter (stream);
157                         Image [] images = (imageCollection != null) ? imageCollection.ToArray () : this.images;
158
159                         int cols = 4;
160                         int rows = images.Length / cols;
161                         if (images.Length % cols > 0)
162                                 ++rows;
163
164                         Bitmap main = new Bitmap (cols * ImageSize.Width, rows * ImageSize.Height);
165                         using (Graphics g = Graphics.FromImage (main)) {
166                                 g.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (BackColor), 0, 0, cols * ImageSize.Width, rows * ImageSize.Height);
167                                 for (int i = 0; i < images.Length; i++) {
168                                         g.DrawImage (images [i], (i % cols) * ImageSize.Width,
169                                                         (i / cols) * ImageSize.Height);
170                                 }
171                         }
172
173                         writer.Write ((ushort) (('L' << 8) | 'I'));    // magic
174                         writer.Write ((ushort) 0x101);                 // version
175                         writer.Write ((ushort) images.Length);
176                         writer.Write ((ushort) images.Length);
177                         writer.Write ((ushort) (rows * cols));
178                         writer.Write ((ushort) 0x4);                    // grow....not sure this should be hard coded
179                         writer.Write ((ushort) image_size.Width);
180                         writer.Write ((ushort) image_size.Height);
181                         writer.Write (BackColor.ToArgb ());
182                         writer.Write ((ushort) 0x1009);                // flags
183
184                         for (int i = 0; i < 4; i++)
185                                 writer.Write ((short) -1);  // ovls
186
187                         return RLEncodeData (stream.ToArray ());
188                 }
189
190                 // TODO: This is broken
191                 private byte [] RLEncodeData (byte [] data)
192                 {
193                         MemoryStream stream = new MemoryStream ();
194                         BinaryWriter writer = new BinaryWriter (stream);
195
196                         for (int i = 0; i < data.Length; i += 2) {
197                                 int seq = 0;
198                                 byte item  = data [i];
199                                 while (data [i++] == item && i < data.Length)
200                                         seq++;
201                                 writer.Write ((byte) seq);
202                                 writer.Write (item);
203                         }
204
205                         return stream.ToArray ();
206
207                 }
208
209                 internal Image [] Images {
210                         get { return images; }
211                 }
212
213                 internal Size ImageSize {
214                         get { return image_size; }
215                 }
216
217                 internal ColorDepth ColorDepth {
218                         get { return ColorDepth.Depth32Bit; }
219                 }
220
221                 internal Color BackColor {
222                         get { return back_color; }
223                 }
224         }
225 }
226