context: // Permission is hereby granted, free of charge, to any person obtaining
  // a copy of this software and associated documentation files (the
  // "Software"), to deal in the Software without restriction, including
  // without limitation the rights to use, copy, modify, merge, publish,
  // distribute, sublicense, and/or sell copies of the Software, and to
  // permit persons to whom the Software is furnished to do so, subject to
  // the following conditions:
  //
  // The above copyright notice and this permission notice shall be
  // included in all copies or substantial portions of the Software.
  //
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  //
  // Copyright (c) 2002-2006 Novell, Inc.
  //
  // Authors:
  //	Jackson Harper (jackson@ximian.com)
  //	Gonzalo Paniagua Javier (gonzalo@ximian.com)
  //
  // Based on work done by:
  //   Dennis Hayes (dennish@Raytek.com)
  //   Aleksey Ryabchuk (ryabchuk@yahoo.com)
  
  using System.IO;
  using System.Drawing;
  using System.Collections;
  using System.Drawing.Imaging;
  using System.Runtime.Serialization;
  using System.Runtime.InteropServices;
  
  
  namespace System.Windows.Forms {
  	[Serializable]
  	public sealed class ImageListStreamer : ISerializable {
  		readonly ImageList.ImageCollection imageCollection;
  		Image [] images;
  		Size image_size;
  		Color back_color;
  
  		internal ImageListStreamer (ImageList.ImageCollection imageCollection)
  		{
  			this.imageCollection = imageCollection;
  		}
  
  		private ImageListStreamer (SerializationInfo info, StreamingContext context)
  		{
  			byte [] data = (byte []) info.GetValue ("Data", typeof (byte []));
  			if (data == null || data.Length <= 4) { // 4 is the signature
  				return;
  			}
  
  			// check the signature ( 'MSFt' )
  			if (data [0] != 77 || data [1] != 83 || data [2] != 70 || data [3] != 116) {
  				return;
  			}
  
  			MemoryStream decoded = GetDecodedStream (data, 4, data.Length - 4);
  			decoded.Position = 4; // jumps over 'magic' and 'version', which are 16-bits each
  
  			BinaryReader reader = new BinaryReader (decoded);
  			ushort nimages = reader.ReadUInt16 ();
  			reader.ReadUInt16 ();	// cMaxImage
  			ushort grow = reader.ReadUInt16 (); // cGrow
  			ushort cx = reader.ReadUInt16 ();
  			ushort cy = reader.ReadUInt16 ();
  			uint bkcolor = reader.ReadUInt32 ();
  			back_color = Color.FromArgb ((int) bkcolor);
  			reader.ReadUInt16 ();	// flags
  
  			short [] ovls = new short [4];
  			for (int i = 0; i < 4; i++) {
  				ovls[i] = reader.ReadInt16 ();
  			}
  
  			byte [] decoded_buffer = decoded.GetBuffer ();
  			int bmp_offset = 28;
  			// FileSize field from the bitmap file header
  			int filesize = decoded_buffer [bmp_offset + 2] + (decoded_buffer [bmp_offset + 3] << 8) +
  					(decoded_buffer [bmp_offset + 4] << 16) + (decoded_buffer [bmp_offset + 5] << 24);
  			// ImageSize field from the info header (can be 0)
  			int imagesize = decoded_buffer [bmp_offset + 34] + (decoded_buffer [bmp_offset + 35] << 8) +
  					(decoded_buffer [bmp_offset + 36] << 16) + (decoded_buffer [bmp_offset + 37] << 24);
  
  			int bmp_length = imagesize + filesize;
  			MemoryStream bmpms = new MemoryStream (decoded_buffer, bmp_offset, bmp_length);
  			Bitmap bmp = null;
  			Bitmap mask = null;
  			bmp = new Bitmap (bmpms);
  			MemoryStream mask_stream = new MemoryStream (decoded_buffer,
  							bmp_offset + bmp_length,
  							(int) (decoded.Length - bmp_offset - bmp_length));
  
  			if (mask_stream.Length > 0)
  				mask = new Bitmap (mask_stream);
  
  			if (bkcolor == 0xFFFFFFFF)
  				back_color = bmp.GetPixel (0, 0);
  
  			if (mask != null) {
  				int width = bmp.Width;
  				int height = bmp.Height;
  				Bitmap newbmp = new Bitmap (bmp);
  				for (int y = 0; y < height; y++) {
  					for (int x = 0; x < width; x++) {
  						Color mcolor = mask.GetPixel (x, y);
  						if (mcolor.B != 0) {
  							newbmp.SetPixel (x, y, Color.Transparent);
  						}
  					}
  				}
  				bmp.Dispose ();
  				bmp = newbmp;
  				mask.Dispose ();
  				mask = null;
  			}
  			images = new Image [nimages];
  			image_size = new Size (cx, cy);
  			Rectangle dest_rect = new Rectangle (0, 0, cx, cy);
  			if (grow * bmp.Width > cx) // Some images store a wrong 'grow' factor
  				grow = (ushort) (bmp.Width / cx);
  
  			for (int r = 0 ; r < nimages ; r++) {
  				int col = r % grow;
  				int row = r / grow;
  				Rectangle area = new Rectangle (col * cx, row * cy, cx, cy);
  				Bitmap b = new Bitmap (cx, cy);
  				using (Graphics g = Graphics.FromImage (b)) {
  					g.DrawImage (bmp, dest_rect, area, GraphicsUnit.Pixel);
  				}
  
  				images [r] = b;
  			}
  			bmp.Dispose ();
  		}
  
  		/*
  		static void WriteToFile (MemoryStream st)
  		{
  			st.Position = 0;
  			FileStream fs = File.OpenWrite (Path.GetTempFileName ());
  			Console.WriteLine ("Writing to {0}", fs.Name);
  			st.WriteTo (fs);
  			fs.Close ();
  		}
  		*/
  
  		static byte [] header = new byte []{ 77, 83, 70, 116, 73, 76, 3, 0 };
  		public void GetObjectData (SerializationInfo si, StreamingContext context)
  		{
  			MemoryStream stream = new MemoryStream ();
  			BinaryWriter writer = new BinaryWriter (stream);
  			writer.Write (header);
  
  			Image [] images = (imageCollection != null) ? imageCollection.ToArray () : this.images;
  			int cols = 4;
  			int rows = images.Length / cols;
  			if (images.Length % cols > 0)
  				++rows;
  
  			writer.Write ((ushort) images.Length);
  			writer.Write ((ushort) images.Length);
  			writer.Write ((ushort) 0x4);
  			writer.Write ((ushort) (images [0].Width));
  			writer.Write ((ushort) (images [0].Height));
  			writer.Write (0xFFFFFFFF); //BackColor.ToArgb ()); //FIXME: should set the right one here.
  			writer.Write ((ushort) 0x1009);
  			for (int i = 0; i < 4; i++)
  				writer.Write ((short) -1);
  
  			Bitmap main = new Bitmap (cols * ImageSize.Width, rows * ImageSize.Height);
  			using (Graphics g = Graphics.FromImage (main)) {
  				g.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (BackColor), 0, 0,
  						main.Width, main.Height);
  				for (int i = 0; i < images.Length; i++) {
  					g.DrawImage (images [i], (i % cols) * ImageSize.Width,
  							(i / cols) * ImageSize.Height);
  				}
  			}
  
  			MemoryStream tmp = new MemoryStream ();
  			main.Save (tmp, ImageFormat.Bmp);
  			tmp.WriteTo (stream);
  
  			Bitmap mask = Get1bppMask (main);
  			main.Dispose ();
  			main = null;
  
  			tmp = new MemoryStream ();
  			mask.Save (tmp, ImageFormat.Bmp);
  			tmp.WriteTo (stream);
  			mask.Dispose ();
  
  			stream = GetRLEStream (stream, 4);
  			si.AddValue ("Data", stream.ToArray (), typeof (byte []));
  		}
  
  		unsafe Bitmap Get1bppMask (Bitmap main)
  		{
  			Rectangle rect = new Rectangle (0, 0, main.Width, main.Height);
  			Bitmap result = new Bitmap (main.Width, main.Height, PixelFormat.Format1bppIndexed);
  			BitmapData dresult = result.LockBits (rect, ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed);
  
  			int w = images [0].Width;
  			int h = images [0].Height;
  			byte *scan = (byte *) dresult.Scan0.ToPointer ();
  			int stride = dresult.Stride;
  			Bitmap current = null;
  			for (int idx = 0; idx < images.Length; idx++) {
  				current = (Bitmap) images [idx];
  				// Hack for newly added images.
  				// Probably has to be done somewhere else.
  				Color c1 = current.GetPixel (0, 0);
  				if (c1.A != 0 && c1 == back_color)
  					current.MakeTransparent (back_color);
  				//
  			}
  
  			int yidx = 0;
  			int imgidx = 0;
  			int localy = 0;
  			int localx = 0;
  			int factor_y = 0;
  			int factor_x = 0;
  			for (int y = 0; y < main.Height; y++) {
  				if (localy == h) {
  					localy = 0;
  					factor_y += 4;
  				}
  				factor_x = 0;
  				localx = 0;
  				for (int x = 0; x < main.Width; x++) {
  					if (localx == w) {
  						localx = 0;
  						factor_x++;
  					}
  					imgidx = factor_y + factor_x;
  					if (imgidx >= images.Length)
  						break;
  					current = (Bitmap) images [imgidx];
  					Color color = current.GetPixel (localx, localy);
  					if (color.A == 0) {
  						int ptridx = yidx + (x >> 3);
  						scan [ptridx] |= (byte) (0x80 >> (x & 7));
  					}
  					localx++;
  				}
  				if (imgidx >= images.Length)
  					break;
  				yidx += stride;
  				localy++;
  			}
  			result.UnlockBits (dresult);
  
  			return result;
  		}
  
  		static MemoryStream GetDecodedStream (byte [] bytes, int offset, int size)
  		{
  			byte [] buffer = new byte [512];
  			int position = 0;
  			int count, data;
  			MemoryStream result = new MemoryStream ();
  			while (size > 0) {
  				count = (int) bytes [offset++];
  				data = (int) bytes [offset++];
  				if ((512 - count) < position) {
  					result.Write (buffer, 0, position);
  					position = 0;
  				}
  
  				for (int i = 0; i < count; i++)
  					buffer [position++] = (byte) data;
  				size -= 2;
  			}
  
  			if (position > 0)
  				result.Write (buffer, 0, position);
  
  			result.Position = 0;
  			return result;
  		}
  
  		//TODO: OptimizeMe
  		static MemoryStream GetRLEStream (MemoryStream input, int start)
  		{
  			MemoryStream result = new MemoryStream ();
  			byte [] ibuffer = input.GetBuffer ();
  			result.Write (ibuffer, 0, start);
  			input.Position = start;
  
  			int prev = -1;
  			int count = 0;
  			int current;
  			while ((current = input.ReadByte ()) != -1) {
  				if (prev != current || count == 255) {
  					if (prev != -1) {
  						result.WriteByte ((byte) count);
  						result.WriteByte ((byte) prev);
  					}
  					prev = current;
  					count = 0;
  				}
  				count++;
  			}
  
  			if (count > 0) {
  				result.WriteByte ((byte) count);
  
gt: 				result.WriteByte ((byte) current);