Exemple #1
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);
    }
Exemple #2
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);
        }