dorisoy_HardwareMonitor/LibreHardwareMonitor/Utilities/IconFactory.cs
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;

namespace LibreHardwareMonitor.Utilities
{
    public static class IconFactory
    {
        private struct BITMAPINFOHEADER
        {
            public readonly uint Size;
            public readonly int Width;
            public readonly int Height;
            public readonly ushort Planes;
            public readonly ushort BitCount;
            public readonly uint Compression;
            public readonly uint SizeImage;
            public readonly int XPelsPerMeter;
            public readonly int YPelsPerMeter;
            public readonly uint ClrUsed;
            public readonly uint ClrImportant;

            public BITMAPINFOHEADER(int width, int height, int bitCount)
            {
                Size = 40;
                Width = width;
                Height = height;
                Planes = 1;
                BitCount = (ushort)bitCount;
                Compression = 0;
                SizeImage = 0;
                XPelsPerMeter = 0;
                YPelsPerMeter = 0;
                ClrUsed = 0;
                ClrImportant = 0;
            }

            public void Write(BinaryWriter bw)
            {
                bw.Write(Size);
                bw.Write(Width);
                bw.Write(Height);
                bw.Write(Planes);
                bw.Write(BitCount);
                bw.Write(Compression);
                bw.Write(SizeImage);
                bw.Write(XPelsPerMeter);
                bw.Write(YPelsPerMeter);
                bw.Write(ClrUsed);
                bw.Write(ClrImportant);
            }
        }

        private struct ICONIMAGE
        {
            public BITMAPINFOHEADER Header;
            public readonly byte[] Colors;
            public readonly int MaskSize;

            public ICONIMAGE(int width, int height, byte[] colors)
            {
                Header = new BITMAPINFOHEADER(width, height << 1, (8 * colors.Length) / (width * height));
                Colors = colors;
                MaskSize = (width * height) >> 3;
            }

            public void Write(BinaryWriter bw)
            {
                Header.Write(bw);
                int stride = Header.Width << 2;
                for (int i = (Header.Height >> 1) - 1; i >= 0; i--)
                    bw.Write(Colors, i * stride, stride);
                for (int i = 0; i < 2 * MaskSize; i++)
                    bw.Write((byte)0);
            }
        }

        private struct ICONDIRENTRY
        {
            public readonly byte Width;
            public readonly byte Height;
            public readonly byte ColorCount;
            public readonly byte Reserved;
            public readonly ushort Planes;
            public readonly ushort BitCount;
            public readonly uint BytesInRes;
            public uint ImageOffset;

            public ICONDIRENTRY(ICONIMAGE image, int imageOffset)
            {
                Width = (byte)image.Header.Width;
                Height = (byte)(image.Header.Height >> 1);
                ColorCount = 0;
                Reserved = 0;
                Planes = image.Header.Planes;
                BitCount = image.Header.BitCount;
                BytesInRes = (uint)(image.Header.Size + image.Colors.Length + image.MaskSize + image.MaskSize);
                ImageOffset = (uint)imageOffset;
            }

            public void Write(BinaryWriter bw)
            {
                bw.Write(Width);
                bw.Write(Height);
                bw.Write(ColorCount);
                bw.Write(Reserved);
                bw.Write(Planes);
                bw.Write(BitCount);
                bw.Write(BytesInRes);
                bw.Write(ImageOffset);
            }

            public uint Size
            {
                get { return 16; }
            }
        }

        private struct ICONDIR
        {
            public readonly ushort Reserved;
            public readonly ushort Type;
            public readonly ushort Count;
            public readonly ICONDIRENTRY[] Entries;

            public ICONDIR(ICONDIRENTRY[] entries)
            {
                Reserved = 0;
                Type = 1;
                Count = (ushort)entries.Length;
                Entries = entries;
            }

            public void Write(BinaryWriter bw)
            {
                bw.Write(Reserved);
                bw.Write(Type);
                bw.Write(Count);
                for (int i = 0; i < Entries.Length; i++)
                    Entries[i].Write(bw);
            }

            public uint Size
            {
                get
                {
                    return (uint)(6 + Entries.Length * (Entries.Length > 0 ? Entries[0].Size : 0));
                }
            }
        }

        private static readonly BinaryWriter BinaryWriter = new BinaryWriter(new MemoryStream());

        public static Icon Create(byte[] colors, int width, int height, PixelFormat format)
        {
            if (format != PixelFormat.Format32bppArgb)
                throw new NotImplementedException();

            ICONIMAGE image = new ICONIMAGE(width, height, colors);
            ICONDIR dir = new ICONDIR(new[] { new ICONDIRENTRY(image, 0) });
            dir.Entries[0].ImageOffset = dir.Size;

            BinaryWriter.BaseStream.Position = 0;
            dir.Write(BinaryWriter);
            image.Write(BinaryWriter);

            BinaryWriter.BaseStream.Position = 0;
            Icon icon = new Icon(BinaryWriter.BaseStream);

            return icon;
        }

        [DllImport("user32", SetLastError = true)]
        static extern bool DestroyIcon(IntPtr handle);

        public static Icon Create(Bitmap bitmap)
        {
            IntPtr hIcon = bitmap.GetHicon();
            Icon icon = Icon.FromHandle(hIcon);

            return icon;
        }

        public static void Destroy(this Icon icon)
        {
            DestroyIcon(icon.Handle);
            icon.Dispose();
        }
    }
}
