Пример #1
0
        /// <summary>
        ///     This will decode a bitmap array, or if data is not an array, return an array with the single bitmap element
        /// </summary>
        /// <param name="data">Data</param>
        /// <returns>Array of <see cref="DecodedBitmap" />, one per bitmap, null if the bitmap could not be decoded</returns>
        public static DecodedBitmap[] DecodeBitmap(byte[] data)
        {
            long pos = 0;
            BitmapArrayHeader bitmapArrayHeader;

            byte[] buffer = new byte[Marshal.SizeOf(typeof(BitmapInfoHeader))];

            List <DecodedBitmap> bitmaps = new List <DecodedBitmap>();

            do
            {
                Array.Copy(data, pos, buffer, 0, buffer.Length);
                bitmapArrayHeader = BigEndianMarshal.ByteArrayToStructureLittleEndian <BitmapArrayHeader>(buffer);
                long remaining;

                if (bitmapArrayHeader.Type == TYPE_BITMAP_ARRAY)
                {
                    remaining = bitmapArrayHeader.Size - Marshal.SizeOf(typeof(BitmapArrayHeader));
                    pos      += Marshal.SizeOf(typeof(BitmapArrayHeader));
                }
                else
                {
                    remaining = 1;
                    pos       = 0;
                    bitmapArrayHeader.Next = 0;
                }

                while (remaining > 0)
                {
                    buffer = new byte[Marshal.SizeOf(typeof(BitmapInfoHeader))];
                    Array.Copy(data, pos, buffer, 0, buffer.Length);
                    BitmapInfoHeader bitmapFileHeader =
                        BigEndianMarshal.ByteArrayToStructureLittleEndian <BitmapInfoHeader>(buffer);

                    // Stop at unknown header
                    if (bitmapFileHeader.Fix != 12)
                    {
                        break;
                    }

                    // Multiplanes not supported
                    if (bitmapFileHeader.Planes != 1)
                    {
                        break;
                    }

                    // TODO: Non paletted?
                    pos += Marshal.SizeOf(typeof(BitmapInfoHeader));
                    Rgb[] palette = new Rgb[1 << bitmapFileHeader.BitsPerPlane];
                    buffer = new byte[Marshal.SizeOf(typeof(Rgb))];
                    for (int i = 0; i < palette.Length; i++)
                    {
                        Array.Copy(data, pos, buffer, 0, buffer.Length);
                        pos       += buffer.Length;
                        palette[i] = BigEndianMarshal.ByteArrayToStructureLittleEndian <Rgb>(buffer);
                    }

                    remaining -= bitmapFileHeader.Fix;
                    // rgb[1];
                    remaining -= 1;

                    // TODO (Optimize): Calculate real data length considering that every line is word-aligned (2-byte)
                    long dataLength = 0;
                    for (int y = 0; y < bitmapFileHeader.Y; y++)
                    {
                        int x = 0;
                        while (x < bitmapFileHeader.X)
                        {
                            for (int k = 8 - bitmapFileHeader.BitsPerPlane; k >= 0;
                                 k -= (int)bitmapFileHeader.BitsPerPlane)
                            {
                                x++;
                            }

                            dataLength++;
                        }

                        dataLength += dataLength % 2;
                    }

                    buffer = new byte[dataLength];
                    Array.Copy(data, bitmapFileHeader.Offset, buffer, 0, buffer.Length);

                    DecodedBitmap bitmap = DecodeBitmap(bitmapFileHeader, palette, buffer);

                    // Mask palette
                    int[] argbPalette = new int[palette.Length];
                    for (int c = 0; c < palette.Length; c++)
                    {
                        argbPalette[c] = (palette[c].Red << 16) + (palette[c].Green << 8) + palette[c].Blue;
                    }

                    // First image, then mask
                    if (bitmapFileHeader.Type == TYPE_ICON || bitmapFileHeader.Type == TYPE_POINTER)
                    {
                        int[] icon = new int[bitmap.Width * bitmap.Height / 2];
                        int[] mask = new int[bitmap.Width * bitmap.Height / 2];
                        Array.Copy(bitmap.Pixels, 0, icon, 0, icon.Length);
                        Array.Copy(bitmap.Pixels, icon.Length, mask, 0, mask.Length);

                        bitmap.Pixels  = new int[bitmap.Width * bitmap.Height / 2];
                        bitmap.Height /= 2;
                        for (int px = 0; px < bitmap.Pixels.Length; px++)
                        {
                            bitmap.Pixels[px] = icon[px] + (mask[px] == argbPalette[0] ? VISIBLE : 0);
                        }
                    }
                    // We got the mask, now we need to read the color
                    else if (bitmapFileHeader.Type == TYPE_COLOR_ICON || bitmapFileHeader.Type == TYPE_COLOR_POINTER)
                    {
                        DecodedBitmap mask = bitmap;

                        buffer = new byte[Marshal.SizeOf(typeof(BitmapInfoHeader))];
                        Array.Copy(data, pos, buffer, 0, buffer.Length);
                        bitmapFileHeader = BigEndianMarshal.ByteArrayToStructureLittleEndian <BitmapInfoHeader>(buffer);

                        // Stop at unknown header
                        if (bitmapFileHeader.Size != Marshal.SizeOf(typeof(BitmapInfoHeader)))
                        {
                            break;
                        }

                        // Multiplanes not supported
                        if (bitmapFileHeader.Planes != 1)
                        {
                            break;
                        }

                        // TODO: Non paletted?
                        pos    += bitmapFileHeader.Size;
                        palette = new Rgb[1 << bitmapFileHeader.BitsPerPlane];
                        buffer  = new byte[Marshal.SizeOf(typeof(Rgb))];
                        for (int i = 0; i < palette.Length; i++)
                        {
                            Array.Copy(data, pos, buffer, 0, buffer.Length);
                            pos       += buffer.Length;
                            palette[i] = BigEndianMarshal.ByteArrayToStructureLittleEndian <Rgb>(buffer);
                        }

                        remaining -= bitmapFileHeader.Fix;
                        // rgb[1];
                        remaining -= 1;

                        // TODO (Optimize): Calculate real data length considering that every line is word-aligned (2-byte)
                        dataLength = 0;
                        for (int y = 0; y < bitmapFileHeader.Y; y++)
                        {
                            int x = 0;
                            while (x < bitmapFileHeader.X)
                            {
                                for (int k = 8 - bitmapFileHeader.BitsPerPlane; k >= 0;
                                     k -= (int)bitmapFileHeader.BitsPerPlane)
                                {
                                    x++;
                                }

                                dataLength++;
                            }

                            dataLength += dataLength % 2;
                        }

                        buffer = new byte[dataLength];
                        Array.Copy(data, bitmapFileHeader.Offset, buffer, 0, buffer.Length);

                        bitmap = DecodeBitmap(bitmapFileHeader, palette, buffer);

                        for (int px = 0; px < bitmap.Pixels.Length; px++)
                        {
                            bitmap.Pixels[px] = bitmap.Pixels[px] +
                                                (mask.Pixels[px + bitmapFileHeader.X * bitmapFileHeader.Y] ==
                                                 argbPalette[0]
                                                     ? VISIBLE
                                                     : 0);
                        }
                    }
                    // Not an icon, all pixels are visible
                    else
                    {
                        for (int px = 0; px < bitmap.Pixels.Length; px++)
                        {
                            bitmap.Pixels[px] = bitmap.Pixels[px] + VISIBLE;
                        }
                    }

                    // Need to reverse first all pixels then by line
                    int[] pixels = bitmap.Pixels.Reverse().ToArray();
                    for (int y = 0; y < bitmap.Height; y++)
                    {
                        for (int x = 0; x < bitmap.Width; x++)
                        {
                            bitmap.Pixels[y * bitmap.Width + (bitmap.Width - x - 1)] = pixels[y * bitmap.Width + x];
                        }
                    }

                    bitmaps.Add(bitmap);
                }

                pos = bitmapArrayHeader.Next;
            }while(bitmapArrayHeader.Next != 0);

            return(bitmaps.ToArray());
        }
Пример #2
0
        // TODO: Mask is not correctly decoded on XGA icons (20x20 and 40x40)...
        static DecodedBitmap DecodeBitmap(BitmapInfoHeader header, IList <Rgb> palette, byte[] data)
        {
            DecodedBitmap bitmap = new DecodedBitmap
            {
                BitsPerPixel = header.BitsPerPlane,
                Height       = header.Y,
                Width        = header.X,
                XHotspot     = header.XHotspot,
                YHostpot     = header.YHostpot,
                Pixels       = new int[header.X * header.Y]
            };

            switch (header.Type)
            {
            case TYPE_ICON:
                bitmap.Type = "Icon";
                break;

            case TYPE_BITMAP:
                bitmap.Type = "Bitmap";
                break;

            case TYPE_POINTER:
                bitmap.Type = "Pointer";
                break;

            case TYPE_COLOR_ICON:
                bitmap.Type = "Color Icon";
                break;

            case TYPE_COLOR_POINTER:
                bitmap.Type = "Color Pointer";
                break;

            default: return(null);
            }

            int[] argbPalette = new int[palette.Count];

            for (int c = 0; c < palette.Count; c++)
            {
                argbPalette[c] = (palette[c].Red << 16) + (palette[c].Green << 8) + palette[c].Blue;
            }

            long pos = 0;

            for (int y = 0; y < bitmap.Height; y++)
            {
                int x = 0;
                while (x < bitmap.Width)
                {
                    for (int k = (int)(8 - bitmap.BitsPerPixel); k >= 0; k -= (int)bitmap.BitsPerPixel)
                    {
                        bitmap.Pixels[y * bitmap.Width + x] =
                            argbPalette[(data[pos] >> k) & ((1 << (int)bitmap.BitsPerPixel) - 1)];
                        x++;

                        if (x == bitmap.Width)
                        {
                            break;
                        }
                    }

                    pos++;
                }

                pos += pos % 2;
            }

            return(bitmap);
        }