Example #1
0
        private static fcTL Read_fcTL(string png, int seq, int delay)
        {
            BinaryReader br = new BinaryReader(new FileStream(png, FileMode.Open));

            br.BaseStream.Position = 0x10;
            fcTL fctl = new fcTL();

            fctl.length         = BitConverter.GetBytes(26).Reverse().ToArray();
            fctl.id             = Encoding.ASCII.GetBytes(new char[] { 'f', 'c', 'T', 'L' });
            fctl.sequence_numer = BitConverter.GetBytes(seq).Reverse().ToArray();
            fctl.width          = br.ReadBytes(4);
            fctl.height         = br.ReadBytes(4);
            fctl.x_offset       = BitConverter.GetBytes(0);
            fctl.y_offset       = BitConverter.GetBytes(0);
            fctl.delay_num      = BitConverter.GetBytes((ushort)delay).Reverse().ToArray();
            fctl.delay_den      = BitConverter.GetBytes((ushort)1000).Reverse().ToArray();
            fctl.dispose_op     = 1;
            fctl.blend_op       = 0;

            List <Byte> stream = new List <byte>();

            stream.AddRange(fctl.id);
            stream.AddRange(fctl.sequence_numer);
            stream.AddRange(fctl.width);
            stream.AddRange(fctl.height);
            stream.AddRange(fctl.x_offset);
            stream.AddRange(fctl.y_offset);
            stream.AddRange(fctl.delay_num);
            stream.AddRange(fctl.delay_den);
            stream.Add(fctl.dispose_op);
            stream.Add(fctl.blend_op);
            fctl.crc = Helper.CRC32.Calculate(stream.ToArray());
            stream.Clear();

            br.Close();
            return(fctl);
        }
Example #2
0
File: APNG.cs Project: MetLob/tinke
        private static void Write(string apng, byte[] signature, IHDR ihdr, acTL actl, byte[] idat,
            fcTL[] fctl, fdAT[] fdat, IEND iend)
        {
            BinaryWriter bw = new BinaryWriter(new FileStream(apng, FileMode.Create));

            bw.Write(signature);

            bw.Write(ihdr.length);
            bw.Write(ihdr.id);
            bw.Write(ihdr.width);
            bw.Write(ihdr.height);
            bw.Write(ihdr.depth);
            bw.Write(ihdr.colour_type);
            bw.Write(ihdr.compression);
            bw.Write(ihdr.filter);
            bw.Write(ihdr.interlace);
            bw.Write(ihdr.crc);

            bw.Write(actl.length);
            bw.Write(actl.id);
            bw.Write(actl.num_frames);
            bw.Write(actl.num_plays);
            bw.Write(actl.crc);

            bw.Write(fctl[0].length);
            bw.Write(fctl[0].id);
            bw.Write(fctl[0].sequence_numer);
            bw.Write(fctl[0].width);
            bw.Write(fctl[0].height);
            bw.Write(fctl[0].x_offset);
            bw.Write(fctl[0].y_offset);
            bw.Write(fctl[0].delay_num);
            bw.Write(fctl[0].delay_den);
            bw.Write(fctl[0].dispose_op);
            bw.Write(fctl[0].blend_op);
            bw.Write(fctl[0].crc);

            bw.Write(idat);

            for (int i = 0; i < fdat.Length; i++)
            {
                bw.Write(fctl[i + 1].length);
                bw.Write(fctl[i + 1].id);
                bw.Write(fctl[i + 1].sequence_numer);
                bw.Write(fctl[i + 1].width);
                bw.Write(fctl[i + 1].height);
                bw.Write(fctl[i + 1].x_offset);
                bw.Write(fctl[i + 1].y_offset);
                bw.Write(fctl[i + 1].delay_num);
                bw.Write(fctl[i + 1].delay_den);
                bw.Write(fctl[i + 1].dispose_op);
                bw.Write(fctl[i + 1].blend_op);
                bw.Write(fctl[i + 1].crc);

                bw.Write(fdat[i].length);
                bw.Write(fdat[i].id);
                bw.Write(fdat[i].sequence_number);
                bw.Write(fdat[i].data);
                bw.Write(fdat[i].crc);
            }

            bw.Write(iend.length);
            bw.Write(iend.id);
            bw.Write(iend.crc);

            bw.Flush();
            bw.Close();
        }
Example #3
0
File: APNG.cs Project: MetLob/tinke
        private static fcTL Read_fcTL(string png, int seq, int delay)
        {
            BinaryReader br = new BinaryReader(new FileStream(png, FileMode.Open));
            br.BaseStream.Position = 0x10;
            fcTL fctl = new fcTL();

            fctl.length = BitConverter.GetBytes(26).Reverse().ToArray();
            fctl.id = Encoding.ASCII.GetBytes(new char[] { 'f', 'c', 'T', 'L' });
            fctl.sequence_numer = BitConverter.GetBytes(seq).Reverse().ToArray();
            fctl.width = br.ReadBytes(4);
            fctl.height = br.ReadBytes(4);
            fctl.x_offset = BitConverter.GetBytes(0);
            fctl.y_offset = BitConverter.GetBytes(0);
            fctl.delay_num = BitConverter.GetBytes((ushort)delay).Reverse().ToArray();
            fctl.delay_den = BitConverter.GetBytes((ushort)1000).Reverse().ToArray();
            fctl.dispose_op = 1;
            fctl.blend_op = 0;

            List<Byte> stream = new List<byte>();
            stream.AddRange(fctl.id);
            stream.AddRange(fctl.sequence_numer);
            stream.AddRange(fctl.width);
            stream.AddRange(fctl.height);
            stream.AddRange(fctl.x_offset);
            stream.AddRange(fctl.y_offset);
            stream.AddRange(fctl.delay_num);
            stream.AddRange(fctl.delay_den);
            stream.Add(fctl.dispose_op);
            stream.Add(fctl.blend_op);
            fctl.crc = Helper.CRC32.Calculate(stream.ToArray());
            stream.Clear();

            br.Close();
            return fctl;
        }
Example #4
0
        /// <summary>
        /// PNGファイルを展開します。
        /// </summary>
        /// <param name="path">展開するPNGファイルのパスを設定します。</param>
        /// <param name="imageList">展開した画像データを格納するコレクションを設定します。</param>
        /// <returns>画像のサイズとアニメーションの再生回数を返します。</returns>
        public (int width, int height, int times) Decode(string path, List <FrameModel> imageList)
        {
            var chunks = new List <string>();
            var images = new List <byte[]>();
            var frames = new List <fcTL>();

            byte[] buffer;

            bool hasIHDR = false;
            bool hasPLTE = false;
            bool hasACTL = false;
            bool hasIEND = false;

            var ImageHeader      = new IHDR();
            var AnimationControl = new acTL();

            bool isIdatFrame = false;
            int  sequence    = 0;
            bool isFCTL      = false;

            imageList.Clear();

            using (var stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                // PNGヘッダをスキップ
                stream.Seek(Signature.Length, SeekOrigin.Begin);
                var offset = Signature.Length;

                while (!hasIEND)
                {
                    // チャンクを読み取り
                    var length = ReadUINT(stream);
                    buffer = ReadBytes(stream, (int)(length + 4));
                    var type = Encoding.ASCII.GetString(buffer.Take(4).ToArray());
                    var crc  = ReadUINT(stream);

                    // CRCチェック
                    var calcCRC = new CRC32();
                    calcCRC.SlurpBlock(buffer, 0, (int)(length + 4));

                    if (crc != (uint)calcCRC.Crc32Result)
                    {
                        throw CreateEx(type, offset, "CRCが一致していません");
                    }

                    switch (type)
                    {
                    case "IHDR":
                        // イメージヘッダ読み取り
                        if (hasIHDR)
                        {
                            throw CreateEx(type, offset, "チャンクが複数存在しています");
                        }
                        if (chunks.Count > 0)
                        {
                            throw CreateEx(type, offset, "チャンクの位置が不正です");
                        }

                        ImageHeader.Width             = ReadUINT(buffer, 4);
                        ImageHeader.Height            = ReadUINT(buffer, 8);
                        ImageHeader.BitDepth          = buffer[12];
                        ImageHeader.ColorType         = (ColorType)buffer[13];
                        ImageHeader.CompressionMethod = buffer[14];
                        ImageHeader.FilterMethod      = buffer[15];
                        ImageHeader.InterlaceMethod   = (InterlaceMethod)buffer[16];

                        if (ImageHeader.Width == 0 || ImageHeader.Height == 0)
                        {
                            throw CreateEx(type, offset, "画像のサイズが不正です");
                        }
                        if (!Enum.IsDefined(typeof(ColorType), ImageHeader.ColorType) ||
                            !AllowBitDepth.ContainsKey(ImageHeader.ColorType) ||
                            !AllowBitDepth[ImageHeader.ColorType].Contains(ImageHeader.BitDepth))
                        {
                            throw CreateEx(type, offset, "カラーモードとビット深度の組み合わせが不正です");
                        }
                        if (ImageHeader.CompressionMethod != 0)
                        {
                            throw CreateEx(type, offset, "圧縮手法の値が不正です");
                        }
                        if (ImageHeader.FilterMethod != 0)
                        {
                            throw CreateEx(type, offset, "フィルター手法の値が不正です");
                        }
                        if (!Enum.IsDefined(typeof(InterlaceMethod), ImageHeader.InterlaceMethod))
                        {
                            throw CreateEx(type, offset, "インターレース手法の値が不正です");
                        }

                        hasIHDR = true;
                        break;

                    case "PLTE":
                        // パレット読み取り
                        if (hasPLTE)
                        {
                            throw CreateEx(type, offset, "チャンクが複数存在しています");
                        }

                        //
                        // パレット読み込み処理
                        //

                        hasPLTE = true;
                        break;

                    case "IDAT":
                        // イメージデータ読み取り
                        var idat = buffer.Skip(4).Take(buffer.Length - 4).ToArray();
                        if (chunks[chunks.Count - 1] != type)
                        {
                            if (images.Count != 0)
                            {
                                throw CreateEx(type, offset, "非連続なチャンクが存在しています");
                            }

                            images.Add(idat);

                            if (hasACTL && frames.Count == 1)
                            {
                                isIdatFrame = true;
                            }
                        }
                        else
                        {
                            images[images.Count - 1] = images[images.Count - 1].Concat(idat).ToArray();
                        }

                        if (hasACTL)
                        {
                            isFCTL = false;
                        }
                        break;

                    case "acTL":
                        // アニメーションコントロール読み取り
                        if (hasACTL)
                        {
                            throw CreateEx(type, offset, "チャンクが複数存在しています");
                        }
                        if (images.Count != 0)
                        {
                            throw CreateEx(type, offset, "チャンクの位置が不正です");
                        }

                        AnimationControl.NumFrames = ReadUINT(buffer, 4);
                        AnimationControl.NumPlays  = ReadUINT(buffer, 8);

                        if (AnimationControl.NumFrames == 0)
                        {
                            throw CreateEx(type, offset, "アニメーションフレーム数が0です");
                        }

                        hasACTL = true;
                        break;

                    case "fcTL":
                        // フレームコントロール読み取り
                        if (isFCTL)
                        {
                            throw CreateEx(type, offset, "フレームコントロールが連続しています");
                        }

                        var fctl = new fcTL()
                        {
                            SequenceNumber = ReadUINT(buffer, 4),
                            Width          = ReadUINT(buffer, 8),
                            Height         = ReadUINT(buffer, 12),
                            XOffset        = ReadUINT(buffer, 16),
                            YOffset        = ReadUINT(buffer, 20),
                            DelayNum       = ReadWORD(buffer, 24),
                            DelayDen       = ReadWORD(buffer, 26),
                            DisposeOp      = (DisposeOp)buffer[28],
                            BlendOp        = (BlendOp)buffer[29]
                        };

                        if (frames.Count == 0 && sequence != 0)
                        {
                            throw CreateEx(type, offset, "最初のチャンクの位置が不正です");
                        }
                        if (fctl.SequenceNumber != sequence)
                        {
                            throw CreateEx(type, offset, "シーケンス番号が不正です");
                        }
                        if (frames.Count == 0 && (fctl.Width != ImageHeader.Width || fctl.Height != ImageHeader.Height || fctl.XOffset != 0 || fctl.YOffset != 0))
                        {
                            throw CreateEx(type, offset, "1枚目のアニメーションフレームのサイズが不正です");
                        }
                        if (fctl.Width == 0 || fctl.Height == 0 || fctl.XOffset + fctl.Width > ImageHeader.Width || fctl.YOffset + fctl.Height > ImageHeader.Height)
                        {
                            throw CreateEx(type, offset, "アニメーションフレームのサイズが不正です");
                        }
                        if (!Enum.IsDefined(typeof(DisposeOp), fctl.DisposeOp))
                        {
                            throw CreateEx(type, offset, "フレーム描画後の処理方法の値が不正です");
                        }
                        if (!Enum.IsDefined(typeof(BlendOp), fctl.BlendOp))
                        {
                            throw CreateEx(type, offset, "フレーム描画方法の値が不正です");
                        }

                        if (fctl.DelayDen == 0)
                        {
                            fctl.DelayDen = 100;
                        }

                        frames.Add(fctl);
                        sequence++;
                        isFCTL = true;
                        break;

                    case "fdAT":
                        // フレームデータ読み取り
                        if (!isFCTL)
                        {
                            throw CreateEx(type, offset, "対応するフレームコントロールが存在しません");
                        }

                        var seq = ReadUINT(buffer, 4);

                        if (seq != sequence)
                        {
                            throw CreateEx(type, offset, "シーケンス番号が不正です");
                        }

                        var fdat = buffer.Skip(8).Take(buffer.Length - 8).ToArray();
                        if (chunks[chunks.Count - 1] != type)
                        {
                            images.Add(fdat);
                        }
                        else
                        {
                            images[images.Count - 1] = images[images.Count - 1].Concat(fdat).ToArray();
                        }

                        sequence++;
                        isFCTL = false;
                        break;

                    case "IEND":
                        hasIEND = true;
                        break;
                    }

                    chunks.Add(type);
                    offset += buffer.Length + 8;
                }
            }

            // 必須チャンクチェック
            if (!hasIHDR || !hasIEND || (!hasPLTE && ImageHeader.ColorType == ColorType.Palette))
            {
                throw new InvalidOperationException("必須チャンクが存在しません");
            }

            // 不要チャンクチェック
            if (hasPLTE && (ImageHeader.ColorType == ColorType.Grayscale || ImageHeader.ColorType == ColorType.GrayscaleAlpha))
            {
                throw new InvalidOperationException("不要なチャンクが存在しています");
            }

            // フレーム数チェック
            if (hasACTL && (AnimationControl.NumFrames != frames.Count || AnimationControl.NumFrames != images.Count - (isIdatFrame ? 0 : 1)))
            {
                throw new InvalidOperationException("アニメーションフレーム数が不正です");
            }

            // イメージデータチェック
            if (images.Count == 0)
            {
                throw new InvalidOperationException("イメージデータが存在しません");
            }

            // イメージデータ展開
            if (hasACTL)
            {
                // APNG
                for (var i = 0; i < frames.Count; i++)
                {
                    imageList.Add(new FrameModel()
                    {
                        XOffset = (int)frames[i].XOffset,
                        YOffset = (int)frames[i].YOffset,
                        Width   = (int)frames[i].Width,
                        Height  = (int)frames[i].Height,
                        Delay   = frames[i].DelayNum * 1000 / frames[i].DelayDen,
                        Dispose = frames[i].DisposeOp == DisposeOp.APNG_DISPOSE_OP_NONE ? FrameModel.DisposeMode.None : frames[i].DisposeOp == DisposeOp.APNG_DISPOSE_OP_BACKGROUND ? FrameModel.DisposeMode.Background : FrameModel.DisposeMode.Previous,
                        Blend   = frames[i].BlendOp == BlendOp.APNG_BLEND_OP_SOURCE ? FrameModel.BlendMode.Normal : FrameModel.BlendMode.AlphaBlending,
                        Data    = Unfilter(ZlibStream.UncompressBuffer(images[i + (isIdatFrame ? 0 : 1)]), (int)frames[i].Width, (int)frames[i].Height, ImageHeader.ColorType)
                    });
                }
            }
            else
            {
                // PNG
                imageList.Add(new FrameModel()
                {
                    Width  = (int)ImageHeader.Width,
                    Height = (int)ImageHeader.Height,
                    Data   = Unfilter(ZlibStream.UncompressBuffer(images[0]), (int)ImageHeader.Width, (int)ImageHeader.Height, ImageHeader.ColorType)
                });
            }

            return(width : (int)ImageHeader.Width, height : (int)ImageHeader.Height, times : (int)(hasACTL ? AnimationControl.NumPlays : 0));
Example #5
0
 public IHDR(fcTL fctl)
 {
 }