Removed DeflateStream.UnmanagedRead Read loop. Fixes #19313.
[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-2006 Novell, Inc.
21 //
22 // Authors:
23 //      Jackson Harper (jackson@ximian.com)
24 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
25 //
26 // Based on work done by:
27 //   Dennis Hayes (dennish@Raytek.com)
28 //   Aleksey Ryabchuk (ryabchuk@yahoo.com)
29
30 using System.IO;
31 using System.Drawing;
32 using System.Collections;
33 using System.Drawing.Imaging;
34 using System.Runtime.Serialization;
35 using System.Runtime.InteropServices;
36
37
38 namespace System.Windows.Forms {
39         [Serializable]
40         public sealed class ImageListStreamer : ISerializable {
41                 readonly ImageList.ImageCollection imageCollection;
42                 Image [] images;
43                 Size image_size;
44                 Color back_color;
45
46                 internal ImageListStreamer (ImageList.ImageCollection imageCollection)
47                 {
48                         this.imageCollection = imageCollection;
49                 }
50
51                 private ImageListStreamer (SerializationInfo info, StreamingContext context)
52                 {
53                         byte [] data = (byte []) info.GetValue ("Data", typeof (byte []));
54                         if (data == null || data.Length <= 4) { // 4 is the signature
55                                 return;
56                         }
57
58                         // check the signature ( 'MSFt' )
59                         if (data [0] != 77 || data [1] != 83 || data [2] != 70 || data [3] != 116) {
60                                 return;
61                         }
62
63                         MemoryStream decoded = GetDecodedStream (data, 4, data.Length - 4);
64                         decoded.Position = 4; // jumps over 'magic' and 'version', which are 16-bits each
65
66                         BinaryReader reader = new BinaryReader (decoded);
67                         ushort nimages = reader.ReadUInt16 ();
68                         reader.ReadUInt16 ();   // cMaxImage
69                         ushort grow = reader.ReadUInt16 (); // cGrow
70                         ushort cx = reader.ReadUInt16 ();
71                         ushort cy = reader.ReadUInt16 ();
72                         uint bkcolor = reader.ReadUInt32 ();
73                         back_color = Color.FromArgb ((int) bkcolor);
74                         reader.ReadUInt16 ();   // flags
75
76                         short [] ovls = new short [4];
77                         for (int i = 0; i < 4; i++) {
78                                 ovls[i] = reader.ReadInt16 ();
79                         }
80
81                         byte [] decoded_buffer = decoded.GetBuffer ();
82                         int bmp_offset = 28;
83                         // FileSize field from the bitmap file header
84                         int filesize = decoded_buffer [bmp_offset + 2] + (decoded_buffer [bmp_offset + 3] << 8) +
85                                         (decoded_buffer [bmp_offset + 4] << 16) + (decoded_buffer [bmp_offset + 5] << 24);
86                         // ImageSize field from the info header (can be 0)
87                         int imagesize = decoded_buffer [bmp_offset + 34] + (decoded_buffer [bmp_offset + 35] << 8) +
88                                         (decoded_buffer [bmp_offset + 36] << 16) + (decoded_buffer [bmp_offset + 37] << 24);
89
90                         int bmp_length = imagesize + filesize;
91                         MemoryStream bmpms = new MemoryStream (decoded_buffer, bmp_offset, bmp_length);
92                         Bitmap bmp = null;
93                         Bitmap mask = null;
94                         bmp = new Bitmap (bmpms);
95                         MemoryStream mask_stream = new MemoryStream (decoded_buffer,
96                                                         bmp_offset + bmp_length,
97                                                         (int) (decoded.Length - bmp_offset - bmp_length));
98
99                         if (mask_stream.Length > 0)
100                                 mask = new Bitmap (mask_stream);
101
102                         if (bkcolor == 0xFFFFFFFF)
103                                 back_color = bmp.GetPixel (0, 0);
104
105                         if (mask != null) {
106                                 int width = bmp.Width;
107                                 int height = bmp.Height;
108                                 Bitmap newbmp = new Bitmap (bmp);
109                                 for (int y = 0; y < height; y++) {
110                                         for (int x = 0; x < width; x++) {
111                                                 Color mcolor = mask.GetPixel (x, y);
112                                                 if (mcolor.B != 0) {
113                                                         newbmp.SetPixel (x, y, Color.Transparent);
114                                                 }
115                                         }
116                                 }
117                                 bmp.Dispose ();
118                                 bmp = newbmp;
119                                 mask.Dispose ();
120                                 mask = null;
121                         }
122                         images = new Image [nimages];
123                         image_size = new Size (cx, cy);
124                         Rectangle dest_rect = new Rectangle (0, 0, cx, cy);
125                         if (grow * bmp.Width > cx) // Some images store a wrong 'grow' factor
126                                 grow = (ushort) (bmp.Width / cx);
127
128                         for (int r = 0 ; r < nimages ; r++) {
129                                 int col = r % grow;
130                                 int row = r / grow;
131                                 Rectangle area = new Rectangle (col * cx, row * cy, cx, cy);
132                                 Bitmap b = new Bitmap (cx, cy);
133                                 using (Graphics g = Graphics.FromImage (b)) {
134                                         g.DrawImage (bmp, dest_rect, area, GraphicsUnit.Pixel);
135                                 }
136
137                                 images [r] = b;
138                         }
139                         bmp.Dispose ();
140                 }
141
142                 /*
143                 static void WriteToFile (MemoryStream st)
144                 {
145                         st.Position = 0;
146                         FileStream fs = File.OpenWrite (Path.GetTempFileName ());
147                         Console.WriteLine ("Writing to {0}", fs.Name);
148                         st.WriteTo (fs);
149                         fs.Close ();
150                 }
151                 */
152
153                 static byte [] header = new byte []{ 77, 83, 70, 116, 73, 76, 3, 0 };
154                 public void GetObjectData (SerializationInfo si, StreamingContext context)
155                 {
156                         MemoryStream stream = new MemoryStream ();
157                         BinaryWriter writer = new BinaryWriter (stream);
158                         writer.Write (header);
159
160                         Image [] images = (imageCollection != null) ? imageCollection.ToArray () : this.images;
161                         int cols = 4;
162                         int rows = images.Length / cols;
163                         if (images.Length % cols > 0)
164                                 ++rows;
165
166                         writer.Write ((ushort) images.Length);
167                         writer.Write ((ushort) images.Length);
168                         writer.Write ((ushort) 0x4);
169                         writer.Write ((ushort) (images [0].Width));
170                         writer.Write ((ushort) (images [0].Height));
171                         writer.Write (0xFFFFFFFF); //BackColor.ToArgb ()); //FIXME: should set the right one here.
172                         writer.Write ((ushort) 0x1009);
173                         for (int i = 0; i < 4; i++)
174                                 writer.Write ((short) -1);
175
176                         Bitmap main = new Bitmap (cols * ImageSize.Width, rows * ImageSize.Height);
177                         using (Graphics g = Graphics.FromImage (main)) {
178                                 g.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (BackColor), 0, 0,
179                                                 main.Width, main.Height);
180                                 for (int i = 0; i < images.Length; i++) {
181                                         g.DrawImage (images [i], (i % cols) * ImageSize.Width,
182                                                         (i / cols) * ImageSize.Height);
183                                 }
184                         }
185
186                         MemoryStream tmp = new MemoryStream ();
187                         main.Save (tmp, ImageFormat.Bmp);
188                         tmp.WriteTo (stream);
189
190                         Bitmap mask = Get1bppMask (main);
191                         main.Dispose ();
192                         main = null;
193
194                         tmp = new MemoryStream ();
195                         mask.Save (tmp, ImageFormat.Bmp);
196                         tmp.WriteTo (stream);
197                         mask.Dispose ();
198
199                         stream = GetRLEStream (stream, 4);
200                         si.AddValue ("Data", stream.ToArray (), typeof (byte []));
201                 }
202
203                 unsafe Bitmap Get1bppMask (Bitmap main)
204                 {
205                         Rectangle rect = new Rectangle (0, 0, main.Width, main.Height);
206                         Bitmap result = new Bitmap (main.Width, main.Height, PixelFormat.Format1bppIndexed);
207                         BitmapData dresult = result.LockBits (rect, ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed);
208
209                         int w = images [0].Width;
210                         int h = images [0].Height;
211                         byte *scan = (byte *) dresult.Scan0.ToPointer ();
212                         int stride = dresult.Stride;
213                         Bitmap current = null;
214                         for (int idx = 0; idx < images.Length; idx++) {
215                                 current = (Bitmap) images [idx];
216                                 // Hack for newly added images.
217                                 // Probably has to be done somewhere else.
218                                 Color c1 = current.GetPixel (0, 0);
219                                 if (c1.A != 0 && c1 == back_color)
220                                         current.MakeTransparent (back_color);
221                                 //
222                         }
223
224                         int yidx = 0;
225                         int imgidx = 0;
226                         int localy = 0;
227                         int localx = 0;
228                         int factor_y = 0;
229                         int factor_x = 0;
230                         for (int y = 0; y < main.Height; y++) {
231                                 if (localy == h) {
232                                         localy = 0;
233                                         factor_y += 4;
234                                 }
235                                 factor_x = 0;
236                                 localx = 0;
237                                 for (int x = 0; x < main.Width; x++) {
238                                         if (localx == w) {
239                                                 localx = 0;
240                                                 factor_x++;
241                                         }
242                                         imgidx = factor_y + factor_x;
243                                         if (imgidx >= images.Length)
244                                                 break;
245                                         current = (Bitmap) images [imgidx];
246                                         Color color = current.GetPixel (localx, localy);
247                                         if (color.A == 0) {
248                                                 int ptridx = yidx + (x >> 3);
249                                                 scan [ptridx] |= (byte) (0x80 >> (x & 7));
250                                         }
251                                         localx++;
252                                 }
253                                 if (imgidx >= images.Length)
254                                         break;
255                                 yidx += stride;
256                                 localy++;
257                         }
258                         result.UnlockBits (dresult);
259
260                         return result;
261                 }
262
263                 static MemoryStream GetDecodedStream (byte [] bytes, int offset, int size)
264                 {
265                         byte [] buffer = new byte [512];
266                         int position = 0;
267                         int count, data;
268                         MemoryStream result = new MemoryStream ();
269                         while (size > 0) {
270                                 count = (int) bytes [offset++];
271                                 data = (int) bytes [offset++];
272                                 if ((512 - count) < position) {
273                                         result.Write (buffer, 0, position);
274                                         position = 0;
275                                 }
276
277                                 for (int i = 0; i < count; i++)
278                                         buffer [position++] = (byte) data;
279                                 size -= 2;
280                         }
281
282                         if (position > 0)
283                                 result.Write (buffer, 0, position);
284
285                         result.Position = 0;
286                         return result;
287                 }
288
289                 //TODO: OptimizeMe
290                 static MemoryStream GetRLEStream (MemoryStream input, int start)
291                 {
292                         MemoryStream result = new MemoryStream ();
293                         byte [] ibuffer = input.GetBuffer ();
294                         result.Write (ibuffer, 0, start);
295                         input.Position = start;
296
297                         int prev = -1;
298                         int count = 0;
299                         int current;
300                         while ((current = input.ReadByte ()) != -1) {
301                                 if (prev != current || count == 255) {
302                                         if (prev != -1) {
303                                                 result.WriteByte ((byte) count);
304                                                 result.WriteByte ((byte) prev);
305                                         }
306                                         prev = current;
307                                         count = 0;
308                                 }
309                                 count++;
310                         }
311
312                         if (count > 0) {
313                                 result.WriteByte ((byte) count);
314                                 result.WriteByte ((byte) current);
315                         }
316
317                         return result;
318                 }
319
320                 internal Image [] Images {
321                         get { return images; }
322                 }
323
324                 internal Size ImageSize {
325                         get { return image_size; }
326                 }
327
328                 internal ColorDepth ColorDepth {
329                         get { return ColorDepth.Depth32Bit; }
330                 }
331
332                 internal Color BackColor {
333                         get { return back_color; }
334                 }
335         }
336 }
337