예제 #1
0
파일: Apng.cs 프로젝트: znatz/ScreenToGif
        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;
            }
        }
예제 #2
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;
                }
            }
        }
예제 #3
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());
                }
            }
        }