Example #1
0
    /// <summary>
    /// Attempts to read 25 bytes of the stream.
    /// </summary>
    internal static IhdrChunk Read(Stream stream)
    {
        var chunk = new IhdrChunk
        {
            Length    = BitHelper.ConvertEndian(stream.ReadUInt32()), //Chunk length, 4 bytes.
            ChunkType = Encoding.ASCII.GetString(stream.ReadBytes(4)) //Chunk type, 4 bytes.
        };

        if (chunk.ChunkType != "IHDR")
        {
            throw new Exception("Missing IHDR chunk.");
        }

        //var pos = stream.Position;
        //chunk.ChunkData = stream.ReadBytes(chunk.Length);
        //stream.Position = pos;

        //Chunk details + CRC, 13 bytes + 4 bytes.
        chunk.Width             = BitHelper.ConvertEndian(stream.ReadUInt32());
        chunk.Height            = BitHelper.ConvertEndian(stream.ReadUInt32());
        chunk.BitDepth          = (byte)stream.ReadByte();
        chunk.ColorType         = (byte)stream.ReadByte();
        chunk.CompressionMethod = (byte)stream.ReadByte();
        chunk.FilterMethod      = (byte)stream.ReadByte();
        chunk.InterlaceMethod   = (byte)stream.ReadByte();
        chunk.Crc = BitHelper.ConvertEndian(stream.ReadUInt32());

        return(chunk);
    }
Example #2
0
        private static IEnumerable <byte[]> GetData(Stream ms)
        {
            ms.Position = 8 + 25;

            var list = new List <byte[]>();

            while (ms.CanRead)
            {
                var length    = BitHelper.ConvertEndian(ms.ReadUInt32());
                var chunkType = Encoding.ASCII.GetString(ms.ReadBytes(4));
                var data      = ms.ReadBytes(length);

                if (chunkType == "IDAT")
                {
                    list.Add(data);
                }

                if (chunkType == "IEND")
                {
                    break;
                }

                ms.ReadUInt32();
            }

            return(list);
        }
Example #3
0
 internal void Write(Stream stream)
 {
     stream.WriteUInt32(BitHelper.ConvertEndian(Length));                                                                                 //4 bytes.
     stream.WriteBytes(Encoding.ASCII.GetBytes(ChunkType));                                                                               //4 bytes.
     stream.WriteBytes(ChunkData);                                                                                                        //XX bytes.
     stream.WriteUInt32(BitHelper.ConvertEndian(CrcHelper.Calculate(stream.PeekBytes(stream.Position - (Length + 4), (int)Length + 4)))); //CRC, 4 bytes.
 }
Example #4
0
    /// <summary>
    /// Attempts to read 16 bytes of the stream.
    /// </summary>
    internal static ActlChunk Read(Stream stream)
    {
        var chunk = new ActlChunk
        {
            Length    = BitHelper.ConvertEndian(stream.ReadUInt32()), //Chunk length, 4 bytes.
            ChunkType = Encoding.ASCII.GetString(stream.ReadBytes(4)) //Chunk type, 4 bytes.
        };

        //If the second chunk is not the animation control (acTL), it means that this is a normal PNG.
        if (chunk.ChunkType != "acTL")
        {
            return(null);
        }

        //var pos = stream.Position;
        //chunk.ChunkData = stream.ReadBytes(chunk.Length);
        //stream.Position = pos;

        //Chunk details + CRC, 8 bytes + 4 bytes.
        chunk.NumFrames = BitHelper.ConvertEndian(stream.ReadUInt32());
        chunk.NumPlays  = BitHelper.ConvertEndian(stream.ReadUInt32());
        chunk.Crc       = BitHelper.ConvertEndian(stream.ReadUInt32());

        return(chunk);
    }
Example #5
0
        public void Dispose()
        {
            //Write down the image bytes.

            InternalStream.WriteUInt32(BitHelper.ConvertEndian((uint)0)); //Global mask information size, 4 bytes.

            //Aditional info.

            //Precomposed image data?
        }
Example #6
0
        public void Dispose()
        {
            //IEND: The end of the Png datastream. 0 bytes (Length + Type + CRC, 4 bytes each) = 12 bytes.
            InternalStream.WriteUInt32(BitHelper.ConvertEndian(0u));                                                                            //Length, 4 bytes.
            InternalStream.WriteBytes(Encoding.ASCII.GetBytes("IEND"));                                                                         //Chunk type, 4 bytes.
            InternalStream.WriteUInt32(BitHelper.ConvertEndian(CrcHelper.Calculate(InternalStream.PeekBytes(InternalStream.Position - 4, 4)))); //CRC, 4 bytes.

            InternalStream.Flush();
            //Resets the stream position to save afterwards.
            InternalStream.Position = 0;
        }
Example #7
0
        internal void Write(Stream stream, bool writeAsIdat = true)
        {
            stream.WriteUInt32(BitHelper.ConvertEndian(Length));                          //4 bytes.
            stream.WriteBytes(Encoding.ASCII.GetBytes(writeAsIdat ? "IDAT" : ChunkType)); //4 bytes.

            if (!writeAsIdat)
            {
                stream.WriteUInt32(BitHelper.ConvertEndian(SequenceNumber));                                                                                                             //4 bytes.
            }
            stream.WriteBytes(FrameData);                                                                                                                                                //XX bytes.
            stream.WriteUInt32(BitHelper.ConvertEndian(CrcHelper.Calculate(stream.PeekBytes(stream.Position - (Length + (writeAsIdat ? 4 : 8)), (int)Length + (writeAsIdat ? 4 : 8))))); //CRC, 4 bytes.
        }
Example #8
0
 /// <summary>
 /// Write the IHDR chunk to the stream.
 /// If a custom size is given, that's what is written.
 /// </summary>
 internal void Write(Stream stream, uint?width = null, uint?height = null)
 {
     stream.WriteUInt32(BitHelper.ConvertEndian(Length));                                                                                 //4 bytes.
     stream.WriteBytes(Encoding.ASCII.GetBytes(ChunkType));                                                                               //4 bytes.
     stream.WriteUInt32(BitHelper.ConvertEndian(width ?? Width));                                                                         //4 bytes.
     stream.WriteUInt32(BitHelper.ConvertEndian(height ?? Height));                                                                       //4 bytes.
     stream.WriteByte(BitDepth);                                                                                                          //1 byte.
     stream.WriteByte(ColorType);                                                                                                         //1 byte.
     stream.WriteByte(CompressionMethod);                                                                                                 //1 byte.
     stream.WriteByte(FilterMethod);                                                                                                      //1 byte.
     stream.WriteByte(InterlaceMethod);                                                                                                   //1 byte.
     stream.WriteUInt32(BitHelper.ConvertEndian(CrcHelper.Calculate(stream.PeekBytes(stream.Position - (Length + 4), (int)Length + 4)))); //CRC, 4 bytes.
 }
Example #9
0
        /// <summary>
        /// Attempts to read XX bytes of the stream.
        /// </summary>
        internal static Chunk Read(Stream stream, int sequence)
        {
            var chunk = new Chunk
            {
                MasterSequence = sequence,
                Length         = BitHelper.ConvertEndian(stream.ReadUInt32()), //Chunk length, 4 bytes.
                ChunkType      = Encoding.ASCII.GetString(stream.ReadBytes(4)) //Chunk type, 4 bytes.
            };

            //Chunk details + CRC, XX bytes + 4 bytes.
            chunk.ChunkData = stream.ReadBytes(chunk.Length);
            chunk.Crc       = BitHelper.ConvertEndian(stream.ReadUInt32());

            return(chunk);
        }
Example #10
0
        /// <summary>
        /// Attempts to read XX bytes of the stream.
        /// </summary>
        internal static FdatChunk Read(uint length, byte[] array)
        {
            var chunk = new FdatChunk
            {
                Length    = length, //Chunk length, 4 bytes.
                ChunkType = "fdAT"  //Chunk type, 4 bytes.
            };

            using (var stream = new MemoryStream(array))
            {
                //Chunk details, 4 bytes + XX bytes.
                chunk.SequenceNumber = BitHelper.ConvertEndian(stream.ReadUInt32());
                chunk.FrameData      = stream.ReadBytes(length - 4); //Minus 4 because that's the size of the sequence number.
            }

            return(chunk);
        }
Example #11
0
        /// <summary>
        /// Attempts to read 26 bytes of the stream.
        /// </summary>
        internal static FctlChunk Read(uint length, byte[] array)
        {
            var chunk = new FctlChunk
            {
                Length    = length, //Chunk length, 4 bytes.
                ChunkType = "fcTL"  //Chunk type, 4 bytes.
            };

            using (var stream = new MemoryStream(array))
            {
                //Chunk details, 26 bytes.
                chunk.SequenceNumber = BitHelper.ConvertEndian(stream.ReadUInt32());
                chunk.Width          = BitHelper.ConvertEndian(stream.ReadUInt32());
                chunk.Height         = BitHelper.ConvertEndian(stream.ReadUInt32());
                chunk.XOffset        = BitHelper.ConvertEndian(stream.ReadUInt32());
                chunk.YOffset        = BitHelper.ConvertEndian(stream.ReadUInt32());
                chunk.DelayNum       = BitHelper.ConvertEndian(stream.ReadUInt16());
                chunk.DelayDen       = BitHelper.ConvertEndian(stream.ReadUInt16());
                chunk.DisposeOp      = (Apng.DisposeOps)stream.ReadByte();
                chunk.BlendOp        = (Apng.BlendOps)stream.ReadByte();
            }

            return(chunk);
        }
Example #12
0
        internal void Encode()
        {
            //Psd Header: 26 bytes.
            InternalStream.WriteBytes(Encoding.ASCII.GetBytes("8BPS"));        //Chunk type, 4 bytes.
            InternalStream.WriteUInt16(BitHelper.ConvertEndian((ushort)1));    //File version, 1 - PSD, 2 - PSB, 2 bytes.
            InternalStream.Position += 6;                                      //Must be zero, 6 bytes.
            InternalStream.WriteUInt16(BitHelper.ConvertEndian((ushort)4));    //Number of channels, ARGB, 2 bytes.
            InternalStream.WriteUInt32(BitHelper.ConvertEndian((uint)Height)); //Height of the image, 4 bytes.
            InternalStream.WriteUInt32(BitHelper.ConvertEndian((uint)Width));  //Width of the image, 4 bytes.
            InternalStream.WriteUInt16(BitHelper.ConvertEndian((ushort)8));    //Number of bits per channel, 2 bytes.
            InternalStream.WriteUInt16(BitHelper.ConvertEndian((ushort)3));    //The color mode of the file, 3 - RGB, 2 bytes.

            //Color mode data. 4 bytes.
            InternalStream.WriteUInt32(BitHelper.ConvertEndian(0u)); //The size of the color mode data block, 0 bytes for RGB mode, 4 bytes.

            //Image resources. XX bytes.
            InternalStream.WriteBytes(ImageResources.Content);

            //LayerAndMaskInformation. 4 + XX bytes.
            var layerAndMask = LayerAndMask.Content;

            InternalStream.WriteUInt32(BitHelper.ConvertEndian((uint)layerAndMask.Length)); //Length of the LayerAndMask block, 4 bytes.
            InternalStream.WriteBytes(layerAndMask);                                        //Content of the LayerAndMask block.

            //ImageData. XX bytes.
            if (Compress)
            {
                InternalStream.WriteUInt16(BitHelper.ConvertEndian((ushort)1)); //The type of encoding, PackBit/RLE, 2 bytes.
                foreach (var layer in LayerAndMask.LayerInfo.ImageChannelDataList)
                {
                    //Writes all byte counts for all the scan lines (rows * channels), with each count stored as a two-byte value.
                    foreach (var channel in layer.ChannelList)
                    {
                        foreach (var b in channel.RleCompressedContent)
                        {
                            InternalStream.WriteInt16(BitHelper.ConvertEndian((short)b.Length));
                        }
                    }

                    //Writes down each layer, in planar order: AAA RRR GGG BBB.
                    foreach (var channel in layer.ChannelList)
                    {
                        foreach (var b in channel.RleCompressedContent)
                        {
                            InternalStream.WriteBytes(b);
                        }
                    }

                    break;
                }
            }
            else
            {
                InternalStream.WriteUInt16(BitHelper.ConvertEndian((ushort)0)); //The type of encoding, Raw data, 2 bytes.
                foreach (var layer in LayerAndMask.LayerInfo.ImageChannelDataList)
                {
                    //Writes down each layer, in planar order: AAA RRR GGG BBB.
                    foreach (var channel in layer.ChannelList)
                    {
                        InternalStream.WriteBytes(channel.RawContent);
                    }

                    break;
                }
            }
        }
Example #13
0
        internal ApngFrame GetFrame(int index)
        {
            //Build each frame using:
            //Starting blocks: IHDR, tIME, zTXt, tEXt, iTXt, pHYs, sPLT, (iCCP | sRGB), sBIT, gAMA, cHRM, PLTE, tRNS, hIST, bKGD.
            //Image data: IDAT.
            //End block: IEND.

            var chunks      = Chunks.Where(w => w.FrameGroupId == index).ToList();
            var otherChunks = Chunks.Where(w => w.FrameGroupId == -1 && w.ChunkType != "IDAT").ToList();

            if (!chunks.Any())
            {
                return(null);
            }

            var frame = new ApngFrame();

            //First frame • Second frame
            //Default image is part of the animation:       fcTL + IDAT • fcTL + fdAT
            //Default image isn't part of the animation:    IDAT • fcTL + fdAT

            if (chunks[0].ChunkType == "fcTL")
            {
                var fctl = FctlChunk.Read(chunks[0].Length, chunks[0].ChunkData);
                frame.Delay     = fctl.DelayNum == 0 ? 10 : (int)(fctl.DelayNum / (fctl.DelayDen == 0 ? 100d : fctl.DelayDen) * 1000d);
                frame.Width     = fctl.Width;
                frame.Height    = fctl.Height;
                frame.Left      = fctl.XOffset;
                frame.Top       = fctl.YOffset;
                frame.ColorType = Ihdr.ColorType;
                frame.BitDepth  = Ihdr.BitDepth;
                frame.DisposeOp = fctl.DisposeOp;
                frame.BlendOp   = fctl.BlendOp;

                using (var stream = new MemoryStream())
                {
                    //Png signature, 8 bytes.
                    stream.WriteBytes(new byte[] { 137, 80, 78, 71, 13, 10, 26, 10 });

                    //Image header chunk. 25 bytes.
                    Ihdr.Write(stream, fctl.Width, fctl.Height);

                    //Any other auxiliar chunks.
                    foreach (var other in otherChunks)
                    {
                        other.Write(stream);
                    }

                    //Frame has multiple chunks.
                    if (chunks.Count > 2)
                    {
                        var datas = new List <byte[]>();

                        //Data chunks.
                        for (var i = 1; i < chunks.Count; i++)
                        {
                            switch (chunks[i].ChunkType)
                            {
                            case "fdAT":
                            {
                                var fdat = FdatChunk.Read(chunks[i].Length, chunks[i].ChunkData);
                                datas.Add(fdat.FrameData);
                                break;
                            }

                            case "IDAT":
                            {
                                var idat = IdatChunk.Read(chunks[i].Length, chunks[i].ChunkData);
                                datas.Add(idat.FrameData);
                                break;
                            }
                            }
                        }

                        //Write combined frame data.
                        var length = datas.Sum(s => s.Length);

                        stream.WriteUInt32(BitHelper.ConvertEndian((uint)length));                                                                      //4 bytes.
                        stream.WriteBytes(Encoding.ASCII.GetBytes("IDAT"));                                                                             //4 bytes.
                        stream.WriteBytes(datas.SelectMany(s => s).ToArray());                                                                          //XX bytes.
                        stream.WriteUInt32(BitHelper.ConvertEndian(CrcHelper.Calculate(stream.PeekBytes(stream.Position - (length + 4), length + 4)))); //CRC, 4 bytes.
                    }
                    else
                    {
                        switch (chunks[1].ChunkType)
                        {
                        case "fdAT":
                        {
                            var fdat = FdatChunk.Read(chunks[1].Length, chunks[1].ChunkData);
                            fdat.Write(stream);
                            break;
                        }

                        case "IDAT":
                        {
                            var idat = IdatChunk.Read(chunks[1].Length, chunks[1].ChunkData);
                            idat.Write(stream);
                            break;
                        }
                        }
                    }

                    //End chunk.
                    stream.WriteUInt32(BitHelper.ConvertEndian(0u));                                                            //Chunk length, 4 bytes.
                    stream.WriteBytes(Encoding.ASCII.GetBytes("IEND"));                                                         //Chunk type, 4 bytes.
                    stream.WriteUInt32(BitHelper.ConvertEndian(CrcHelper.Calculate(stream.PeekBytes(stream.Position - 4, 4)))); //CRC, 4 bytes.

                    //Gets the whole Png.
                    frame.ImageData = stream.ToArray();
                }
            }
            else
            {
                //This is not supposed to happen.
                //All chunks with an FrameGroupId are grouped with a starting fcTL, ending with a IDAT or fdAT chunk.
                LogWriter.Log(new Exception("Missing fcTL on frame number " + index), $"It was not possible to read frame number {index}");
                return(null);
            }

            return(frame);
        }
Example #14
0
        internal void AddFrame(string path, Int32Rect rect, int delay = 66)
        {
            using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                if (IsFirstFrame)
                {
                    //Png Header: 8 bytes.
                    InternalStream.WriteBytes(stream.ReadBytes(8));

                    //IHDR chunk. 13 bytes (Length + Type + CRC, 4 bytes each) = 25 bytes.
                    InternalStream.WriteBytes(stream.ReadBytes(25));

                    //acTL: Animation control chunk. 8 bytes (Length + Type + CRC, 4 bytes each) = 20 bytes.
                    InternalStream.WriteUInt32(BitHelper.ConvertEndian(8u));                                                                              //Length, 4 bytes.
                    InternalStream.WriteBytes(Encoding.ASCII.GetBytes("acTL"));                                                                           //Chunk type, 4 bytes.
                    InternalStream.WriteUInt32(BitHelper.ConvertEndian((uint)FrameCount));                                                                //NumFrames, 4 bytes.
                    InternalStream.WriteUInt32(BitHelper.ConvertEndian((uint)RepeatCount));                                                               //NumPlays, 4 bytes.
                    InternalStream.WriteUInt32(BitHelper.ConvertEndian(CrcHelper.Calculate(InternalStream.PeekBytes(InternalStream.Position - 12, 12)))); //CRC, 4 bytes.
                }

                //fcTL: Frame control chunk. 26 bytes (Length + Type + CRC, 4 bytes each) = 38 bytes.
                InternalStream.WriteUInt32(BitHelper.ConvertEndian(26u));                    //Length, 4 bytes.
                InternalStream.WriteBytes(Encoding.ASCII.GetBytes("fcTL"));                  //Chunk type, 4 bytes.
                InternalStream.WriteUInt32(BitHelper.ConvertEndian((uint)SequenceNumber++)); //SequenceNumber, 4 bytes.
                InternalStream.WriteUInt32(BitHelper.ConvertEndian((uint)rect.Width));       //Width, 4 bytes.
                InternalStream.WriteUInt32(BitHelper.ConvertEndian((uint)rect.Height));      //Height, 4 bytes.
                InternalStream.WriteUInt32(BitHelper.ConvertEndian((uint)rect.X));           //OffsetX, 4 bytes.
                InternalStream.WriteUInt32(BitHelper.ConvertEndian((uint)rect.Y));           //OffsetY, 4 bytes.
                InternalStream.WriteUInt16(BitHelper.ConvertEndian((ushort)delay));          //Delay numerator, 2 bytes.
                InternalStream.WriteUInt16(BitHelper.ConvertEndian((ushort)1000));           //Delay denominator, 2 bytes.

                if (IsFirstFrame)
                {
                    InternalStream.WriteByte((byte)DisposeOps.None); //DisposeOp, 1 byte.
                    InternalStream.WriteByte((byte)BlendOps.Source); //BlendOp, 1 byte.
                }
                else
                {
                    InternalStream.WriteByte((byte)DisposeOps.None); //DisposeOp, 1 byte.
                    InternalStream.WriteByte((byte)BlendOps.Over);   //BlendOp, 1 byte.
                }

                InternalStream.WriteUInt32(BitHelper.ConvertEndian(CrcHelper.Calculate(InternalStream.PeekBytes(InternalStream.Position - 30, 30)))); //CRC, 4 bytes.

                //fdAT: Frame data chunk. 4 + n bytes (Length + Type + CRC, 4 bytes each) = 16 + n bytes, where n is the frame data.
                var dataList = GetData(stream);

                foreach (var data in dataList)
                {
                    if (IsFirstFrame)
                    {
                        InternalStream.WriteUInt32(BitHelper.ConvertEndian((uint)data.Length));                                                                                           //Length, 4 bytes.
                        InternalStream.WriteBytes(Encoding.ASCII.GetBytes("IDAT"));                                                                                                       //Chunk type, 4 bytes.
                        InternalStream.WriteBytes(data);                                                                                                                                  //Frame data, n bytes.
                        InternalStream.WriteUInt32(BitHelper.ConvertEndian(CrcHelper.Calculate(InternalStream.PeekBytes(InternalStream.Position - (data.Length + 4), data.Length + 4)))); //CRC, 4 bytes.
                    }
                    else
                    {
                        InternalStream.WriteUInt32(BitHelper.ConvertEndian(4 + (uint)data.Length));                                                                                       //Length, 4 bytes.
                        InternalStream.WriteBytes(Encoding.ASCII.GetBytes("fdAT"));                                                                                                       //Chunk type, 4 bytes.
                        InternalStream.WriteUInt32(BitHelper.ConvertEndian((uint)SequenceNumber++));                                                                                      //SequenceNumber, 4 bytes.
                        InternalStream.WriteBytes(data);                                                                                                                                  //Frame data, n bytes.
                        InternalStream.WriteUInt32(BitHelper.ConvertEndian(CrcHelper.Calculate(InternalStream.PeekBytes(InternalStream.Position - (data.Length + 8), data.Length + 8)))); //CRC, 4 bytes.
                    }
                }

                IsFirstFrame = false;
            }
        }
Example #15
0
        internal void AddFrame(string path, int delay = 66)
        {
            using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                if (IsFirstFrame)
                {
                    IsFirstFrame = false;

                    //Psd Header: XX bytes.
                    InternalStream.WriteBytes(Encoding.ASCII.GetBytes("8BPS"));        //Chunk type, 4 bytes.
                    InternalStream.WriteUInt16(BitHelper.ConvertEndian((ushort)1));    //File version, 1 - PSD, 2 - PSB, 2 bytes.
                    InternalStream.Position += 6;                                      //Must be zero, 6 bytes.
                    InternalStream.WriteUInt16(BitHelper.ConvertEndian((ushort)3));    //Number of channels, 2 bytes.
                    InternalStream.WriteUInt32(BitHelper.ConvertEndian((uint)Height)); //Height of the image, 4 bytes.
                    InternalStream.WriteUInt32(BitHelper.ConvertEndian((uint)Width));  //Width of the image, 4 bytes.
                    InternalStream.WriteUInt16(BitHelper.ConvertEndian((ushort)8));    //Number of bits per channel, 2 bytes.
                    InternalStream.WriteUInt16(BitHelper.ConvertEndian((ushort)3));    //The color mode of the file, 3 - RGB, 2 bytes.

                    //Color mode data. 4 bytes.
                    InternalStream.WriteUInt32(BitHelper.ConvertEndian(0u)); //The size of the color mode data block, 0 bytes for RGB mode, 4 bytes.

                    //TODO: Write the image resource block to another stream, then merge with the main one.
                    //TODO: Or save the position and update later.
                    //Image resources. XX bytes.
                    InternalStream.WriteUInt32(BitHelper.ConvertEndian(0u)); //The size of the image resource block, 4 bytes. TODO

                    //InternalStream.WriteBytes(Encoding.ASCII.GetBytes("8BIM")); //Chunk type, 4 bytes.
                    //InternalStream.WriteUInt16(BitHelper.ConvertEndian((ushort)0x0)); //Image Resource Id, 2 bytes.
                    //InternalStream.WriteBytes(new byte[] { 0, 0 });

                    //Save the position

                    //Layers and Masks list. XX bytes.
                    InternalStream.WriteUInt32(BitHelper.ConvertEndian(0u));        //The size of the layer and global mask block, 4 bytes. TODO

                    InternalStream.WriteUInt32(BitHelper.ConvertEndian(0u));        //The size of the layer block, 4 bytes. TODO
                    InternalStream.WriteUInt16(BitHelper.ConvertEndian((ushort)0)); //The layer count, 2 bytes. TODO
                }

                InternalStream.WriteUInt32(BitHelper.ConvertEndian(0u));           //Top, 4 bytes.
                InternalStream.WriteUInt32(BitHelper.ConvertEndian(0u));           //Left, 4 bytes.
                InternalStream.WriteUInt32(BitHelper.ConvertEndian((uint)Height)); //Bottom, 4 bytes.
                InternalStream.WriteUInt32(BitHelper.ConvertEndian((uint)Width));  //Right, 4 bytes.

                InternalStream.WriteInt16(BitHelper.ConvertEndian((short)4));      //Number of channels, 2 bytes.
                for (int i = -1; i < 4; i++)
                {
                    InternalStream.WriteInt16(BitHelper.ConvertEndian((short)i)); //Channel ID, 2 bytes.
                    InternalStream.WriteUInt32(BitHelper.ConvertEndian((uint)0)); //Data lenght of channel, 4 bytes. TODO
                }


                InternalStream.WriteBytes(Encoding.ASCII.GetBytes("8BIM"));     //Blend mode signature, 4 bytes.
                InternalStream.WriteInt32(BitHelper.ConvertEndian(0x6e6f726d)); //Blend mode value, Normal, 4 bytes.
                InternalStream.WriteByte(255);                                  //Opacity, 1 byte.
                InternalStream.WriteByte(0);                                    //Clipping, 1 byte.
                InternalStream.WriteByte(10);                                   //Flags, Visible = true, 1 byte.
                InternalStream.WriteByte(0);                                    //Filler, 1 byte.

                InternalStream.WriteUInt32(BitHelper.ConvertEndian((uint)0));   //Extra data lenght, 4 bytes. TODO
                InternalStream.WriteInt32(BitHelper.ConvertEndian(0));          //Layer mask size, 4 bytes.
                InternalStream.WriteInt32(BitHelper.ConvertEndian(0));          //Blending ranges size, 4 bytes. TODO: Check if it's possible o have this as zero.

                var name = $"Frame {0}".Truncate(255);
                InternalStream.WriteByte((byte)name.Length); //Layer name size, 1 byte.
                InternalStream.WriteString1252(name);        //Layer name size, 1 byte.

                var padding = 4 - (name.Length + 1) % 4;
                if (padding != 4)  //There's zero padding if equals to 4.
                {
                    InternalStream.Position += padding;
                }

                //For each Aditional Layer Information:
                //InternalStream.WriteBytes(Encoding.ASCII.GetBytes("8BIM")); //Aditional Layer Information signature, 4 bytes.
                //InternalStream.WriteBytes(Encoding.ASCII.GetBytes("shmd")); //ALI ID, 4 bytes.

                var a = new TiffBitmapEncoder {
                    Compression = TiffCompressOption.None
                };
                a.Frames.Add(BitmapFrame.Create(path.SourceFrom()));

                using (var ms = new MemoryStream())
                {
                    a.Save(ms);
                    ImageDataList.Add(ms.ToArray());
                }
            }
        }