2014-08-13 10:39:27 +01:00
|
|
|
// 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 ();
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
2014-09-04 09:07:35 +01:00
|
|
|
static byte [] header = new byte []{ 77, 83, 70, 116, 73, 76, 1, 1 };
|
2014-08-13 10:39:27 +01:00
|
|
|
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.
|
2014-09-04 09:07:35 +01:00
|
|
|
writer.Write ((ushort) 0x21);
|
2014-08-13 10:39:27 +01:00
|
|
|
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);
|
|
|
|
result.WriteByte ((byte) current);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal Image [] Images {
|
|
|
|
get { return images; }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal Size ImageSize {
|
|
|
|
get { return image_size; }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal ColorDepth ColorDepth {
|
|
|
|
get { return ColorDepth.Depth32Bit; }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal Color BackColor {
|
|
|
|
get { return back_color; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|