PNG第3版に対応する便利クラス


// CPngKit.h
#pragma once

#ifdef CPNGKIT_EXPORTS
#define CPNGKIT_API __declspec(dllexport)
#else
#define CPNGKIT_API __declspec(dllimport)
#endif

#include <vector>
#include <string>
#include <cstdint>

// C#側で扱うためのPNGファイルハンドル(実体はC++のPngFileクラスへのポインタ)
typedef void* HPNG;

extern "C" {

    // --- エラーコード定義 ---
    enum CPNGKIT_RESULT {
        SUCCESS = 0,
        ERROR_INVALID_SIGNATURE = -1,
        ERROR_CRC_MISMATCH = -2,
        ERROR_INVALID_ARGUMENT = -3,
        ERROR_FILE_IO = -4,
        ERROR_NULL_POINTER = -5,
        ERROR_UNKNOWN = -100
    };

    // --- データ構造体 ---
    // C#とやり取りするためのIHDRチャンク情報
#pragma pack(push, 1) // メモリのアライメントを1バイトに設定し、構造体のサイズを固定
    struct IhdrData {
        uint32_t Width;
        uint32_t Height;
        uint8_t BitDepth;
        uint8_t ColorType;
        uint8_t CompressionMethod;
        uint8_t FilterMethod;
        uint8_t InterlaceMethod;
    };

    // APNGのアニメーション制御情報 (acTL)
    struct AcTlData {
        uint32_t NumFrames;
        uint32_t NumPlays;
    };

    // APNGのフレーム制御情報 (fcTL)
    struct FcTlData {
        uint32_t SequenceNumber;
        uint32_t Width;
        uint32_t Height;
        uint32_t XOffset;
        uint32_t YOffset;
        uint16_t DelayNum;
        uint16_t DelayDen;
        uint8_t DisposeOp;
        uint8_t BlendOp;
    };

    // HDRのカラープロファイル情報 (cICP)
    struct CicpData {
        uint8_t ColorPrimaries;
        uint8_t TransferFunction;
        uint8_t MatrixCoefficients;
        uint8_t VideoFullRangeFlag;
    };
#pragma pack(pop)

    // --- PNGファイルの読み込み・解放 ---

    /// <summary>
    /// 指定されたファイルパスからPNGファイルを読み込み、ハンドルを返す。
    /// </summary>
    /// <param name="filePath">読み込むPNGファイルのパス(UTF-8文字列)。</param>
    /// <param name="pngHandle">成功した場合、生成されたPNGファイルハンドルが格納されるポインタ。</param>
    /// <returns>処理結果を示すエラーコード(SUCCESS_CODEなら成功)。</returns>
    CPNGKIT_API int PngFile_ReadFromFile(const char* filePath, HPNG* pngHandle);

    /// <summary>
    /// PngFile_ReadFromFileで確保されたPNGファイルハンドルを解放する。
    /// </summary>
    /// <param name="pngHandle">解放するPNGファイルハンドル。</param>
    CPNGKIT_API void PngFile_Release(HPNG pngHandle);

    // --- PNG情報の取得 ---

    /// <summary>
    /// PNGファイルのIHDRチャンク情報を取得する。
    /// </summary>
    /// <param name="pngHandle">対象のPNGファイルハンドル。</param>
    /// <param name="ihdr">IHDRデータを格納する構造体へのポインタ。</param>
    /// <returns>処理結果。IHDRが見つからない場合はエラー。</returns>
    CPNGKIT_API int PngFile_GetIhdr(HPNG pngHandle, IhdrData* ihdr);

    /// <summary>
    /// PNGがアニメーションPNG(APNG)かどうかを判定する。
    /// </summary>
    /// <param name="pngHandle">対象のPNGファイルハンドル。</param>
    /// <returns>APNGの場合は1、そうでない場合は0を返す。</returns>
    CPNGKIT_API int PngFile_IsAnimated(HPNG pngHandle);

    /// <summary>
    /// アニメーション情報を取得する(acTLチャンク)。
    /// </summary>
    /// <param name="pngHandle">対象のPNGファイルハンドル。</param>
    /// <param name="actl">acTLデータを格納する構造体へのポインタ。</param>
    /// <returns>処理結果。acTLが見つからない場合はエラー。</returns>
    CPNGKIT_API int PngFile_GetAnimationInfo(HPNG pngHandle, AcTlData* actl);

    /// <summary>
    /// 指定したインデックスのフレーム制御情報(fcTL)を取得する。
    /// </summary>
    /// <param name="pngHandle">対象のPNGファイルハンドル。</param>
    /// <param name="frameIndex">取得するフレームのインデックス(0から)。</param>
    /// <param name="fctl">fcTLデータを格納する構造体へのポインタ。</param>
    /// <returns>処理結果。指定インデックスのフレームが見つからない場合はエラー。</returns>
    CPNGKIT_API int PngFile_GetFrameInfo(HPNG pngHandle, int frameIndex, FcTlData* fctl);
    
    /// <summary>
    /// eXIfチャンクのデータを取得する。
    /// </summary>
    /// <param name="pngHandle">対象のPNGファイルハンドル。</param>
    /// <param name="buffer">eXIfデータを格納するバッファへのポインタ。事前にサイズを確保しておく必要がある。</param>
    /// <param name="bufferSize">バッファのサイズ。</param>
    /// <param name="dataSize">実際に書き込まれたデータのサイズが格納される。</param>
    /// <returns>処理結果。eXIfチャンクがない場合はエラー。</returns>
    CPNGKIT_API int PngFile_GetExifData(HPNG pngHandle, unsigned char* buffer, int bufferSize, int* dataSize);

    /// <summary>
    /// cICPチャンクのデータを取得する。
    /// </summary>
    /// <param name="pngHandle">対象のPNGファイルハンドル。</param>
    /// <param name="cicp">cICPデータを格納する構造体へのポインタ。</param>
    /// <returns>処理結果。cICPチャンクがない場合はエラー。</returns>
    CPNGKIT_API int PngFile_GetCICPData(HPNG pngHandle, CicpData* cicp);

    // --- PNGファイルの書き込み ---

    /// <summary>
    /// PNGファイルハンドルを新しいPNGファイルとして保存する。
    /// </summary>
    /// <param name="pngHandle">保存するPNGファイルハンドル。</param>
    /// <param name="filePath">保存先のファイルパス(UTF-8文字列)。</param>
    /// <returns>処理結果。</returns>
    CPNGKIT_API int PngFile_WriteToFile(HPNG pngHandle, const char* filePath);
}


// CPngKit.cpp
#include "pch.h" // プリコンパイル済みヘッダー (Visual Studioのプロジェクト作成時に自動生成される)
#include "CPngKit.h"
#include <fstream>
#include <iostream>
#include <algorithm>
#include <numeric>

// --- 内部実装 ---

// C#のPngHelperに相当する機能
namespace PngUtil {
    // ネットワークバイトオーダー(ビッグエンディアン)とホストバイトオーダー(通常リトルエンディアン)の変換
    uint32_t SwapEndian(uint32_t val) {
        return (val << 24) | ((val << 8) & 0x00ff0000) | ((val >> 8) & 0x0000ff00) | (val >> 24);
    }
    uint16_t SwapEndian(uint16_t val) {
        return (val << 8) | (val >> 8);
    }

    // ストリームからの読み書き
    uint32_t ReadUInt32BigEndian(std::istream& stream) {
        uint32_t val;
        stream.read(reinterpret_cast<char*>(&val), 4);
        return SwapEndian(val);
    }
    uint16_t ReadUInt16BigEndian(std::istream& stream) {
        uint16_t val;
        stream.read(reinterpret_cast<char*>(&val), 2);
        return SwapEndian(val);
    }
    void WriteUInt32BigEndian(std::ostream& stream, uint32_t val) {
        uint32_t swapped = SwapEndian(val);
        stream.write(reinterpret_cast<const char*>(&swapped), 4);
    }
    void WriteUInt16BigEndian(std::ostream& stream, uint16_t val) {
        uint16_t swapped = SwapEndian(val);
        stream.write(reinterpret_cast<const char*>(&swapped), 2);
    }
}

// C#のCrc32に相当する機能
class Crc32 {
private:
    static std::vector<uint32_t> table;
    uint32_t crc_val = 0xFFFFFFFF;

    static void generate_table() {
        if (!table.empty()) return;
        table.resize(256);
        constexpr uint32_t poly = 0xEDB88320;
        for (uint32_t i = 0; i < 256; ++i) {
            uint32_t c = i;
            for (int k = 0; k < 8; ++k) {
                c = (c & 1) ? (poly ^ (c >> 1)) : (c >> 1);
            }
            table[i] = c;
        }
    }

public:
    Crc32() {
        generate_table();
    }

    void update(const std::vector<uint8_t>& data) {
        for (uint8_t byte : data) {
            crc_val = table[(crc_val ^ byte) & 0xFF] ^ (crc_val >> 8);
        }
    }
    
    uint32_t get_value() {
        return crc_val ^ 0xFFFFFFFF;
    }

    static uint32_t calculate(const std::string& type, const std::vector<uint8_t>& data) {
        Crc32 crc;
        std::vector<uint8_t> type_bytes(type.begin(), type.end());
        crc.update(type_bytes);
        if (!data.empty()) {
            crc.update(data);
        }
        return crc.get_value();
    }
};
std::vector<uint32_t> Crc32::table;


// C#のChunkクラスと派生クラス群をC++で再実装
class Chunk {
public:
    std::string Type;
    std::vector<uint8_t> Data;

    Chunk(const std::string& type) : Type(type) {}
    virtual ~Chunk() = default;

    void WriteTo(std::ostream& stream) {
        BuildData(); // 派生クラスのプロパティからDataを構築
        PngUtil::WriteUInt32BigEndian(stream, static_cast<uint32_t>(Data.size()));
        stream.write(Type.c_str(), 4);
        stream.write(reinterpret_cast<const char*>(Data.data()), Data.size());
        uint32_t crc = Crc32::calculate(Type, Data);
        PngUtil::WriteUInt32BigEndian(stream, crc);
    }
protected:
    // 派生クラスで実装。プロパティからDataベクターを更新する。
    virtual void BuildData() {}
};

class IhdrChunk : public Chunk {
public:
    IhdrData Info;
    IhdrChunk(const std::vector<uint8_t>& data) : Chunk("IHDR") {
        Data = data;
        memcpy(&Info, data.data(), sizeof(IhdrData));
        // エンディアン変換が必要
        Info.Width = PngUtil::SwapEndian(Info.Width);
        Info.Height = PngUtil::SwapEndian(Info.Height);
    }
protected:
    void BuildData() override {
        // InfoからDataを再構築
        Data.resize(sizeof(IhdrData));
        IhdrData swappedInfo = Info;
        swappedInfo.Width = PngUtil::SwapEndian(Info.Width);
        swappedInfo.Height = PngUtil::SwapEndian(Info.Height);
        memcpy(Data.data(), &swappedInfo, sizeof(IhdrData));
    }
};

class FcTlChunk : public Chunk {
public:
    FcTlData Info;
    FcTlChunk(const std::vector<uint8_t>& data) : Chunk("fcTL") {
        Data = data;
        std::stringstream ss(std::string(data.begin(), data.end()));
        Info.SequenceNumber = PngUtil::ReadUInt32BigEndian(ss);
        Info.Width = PngUtil::ReadUInt32BigEndian(ss);
        Info.Height = PngUtil::ReadUInt32BigEndian(ss);
        Info.XOffset = PngUtil::ReadUInt32BigEndian(ss);
        Info.YOffset = PngUtil::ReadUInt32BigEndian(ss);
        Info.DelayNum = PngUtil::ReadUInt16BigEndian(ss);
        Info.DelayDen = PngUtil::ReadUInt16BigEndian(ss);
        ss.read(reinterpret_cast<char*>(&Info.DisposeOp), 1);
        ss.read(reinterpret_cast<char*>(&Info.BlendOp), 1);
    }
};

// 他のチャンククラスも同様に定義...

class PngFile {
public:
    std::vector<std::shared_ptr<Chunk>> Chunks;
};

// --- エクスポート関数の実装 ---

CPNGKIT_API int PngFile_ReadFromFile(const char* filePath, HPNG* pngHandle) {
    if (!filePath || !pngHandle) return ERROR_INVALID_ARGUMENT;

    std::ifstream file(filePath, std::ios::binary);
    if (!file.is_open()) return ERROR_FILE_IO;

    // PNGシグネチャの確認
    char signature[8];
    file.read(signature, 8);
    const char png_sig[] = { (char)0x89, 'P', 'N', 'G', '\r', '\n', (char)0x1A, '\n' };
    if (memcmp(signature, png_sig, 8) != 0) {
        return ERROR_INVALID_SIGNATURE;
    }

    auto pngFile = new PngFile();

    try {
        while (file.peek() != EOF) {
            uint32_t length = PngUtil::ReadUInt32BigEndian(file);
            std::string type(4, '\0');
            file.read(&type[0], 4);
            std::vector<uint8_t> data(length);
            if (length > 0) {
                file.read(reinterpret_cast<char*>(data.data()), length);
            }
            uint32_t crc_read = PngUtil::ReadUInt32BigEndian(file);

            if (Crc32::calculate(type, data) != crc_read) {
                delete pngFile;
                return ERROR_CRC_MISMATCH;
            }

            if (type == "IHDR") {
                pngFile->Chunks.push_back(std::make_shared<IhdrChunk>(data));
            } else if (type == "fcTL") {
                pngFile->Chunks.push_back(std::make_shared<FcTlChunk>(data));
            }
            // 他のチャンクも同様にファクトリパターンで生成
            else {
                auto chunk = std::make_shared<Chunk>(type);
                chunk->Data = data;
                pngFile->Chunks.push_back(chunk);
            }

            if (type == "IEND") break;
        }
    } catch (...) {
        delete pngFile;
        return ERROR_UNKNOWN;
    }

    *pngHandle = pngFile;
    return SUCCESS;
}

CPNGKIT_API void PngFile_Release(HPNG pngHandle) {
    if (pngHandle) {
        delete static_cast<PngFile*>(pngHandle);
    }
}

CPNGKIT_API int PngFile_GetIhdr(HPNG pngHandle, IhdrData* ihdr) {
    if (!pngHandle || !ihdr) return ERROR_NULL_POINTER;
    auto pngFile = static_cast<PngFile*>(pngHandle);

    for (const auto& chunk : pngFile->Chunks) {
        if (chunk->Type == "IHDR") {
            auto ihdrChunk = std::dynamic_pointer_cast<IhdrChunk>(chunk);
            if (ihdrChunk) {
                *ihdr = ihdrChunk->Info;
                return SUCCESS;
            }
        }
    }
    return ERROR_UNKNOWN; // IHDRが見つからない
}

CPNGKIT_API int PngFile_WriteToFile(HPNG pngHandle, const char* filePath) {
    if (!pngHandle || !filePath) return ERROR_NULL_POINTER;
    auto pngFile = static_cast<PngFile*>(pngHandle);

    std::ofstream file(filePath, std::ios::binngary);
    if (!file.is_open()) return ERROR_FILE_IO;

    // PNGシグネチャ書き込み
    const char png_sig[] = { (char)0x89, 'P', 'N', 'G', '\r', '\n', (char)0x1A, '\n' };
    file.write(png_sig, 8);

    // 各チャンクを書き込み
    for (const auto& chunk : pngFile->Chunks) {
        chunk->WriteTo(file);
    }

    return SUCCESS;
}

// 他のエクスポート関数の実装も同様に続ける...
// PngFile_IsAnimated, GetAnimationInfo, GetFrameInfo, GetExifData, GetCICPData など




    static void Sample()
    {
        string inputPath = "path/to/your/image.png";
        string outputPath = "path/to/your/output.png";

        try
        {
            // usingステートメントで自動的にリソース解放
            using (var png = CPngKit.FromFile(inputPath))
            {
                var ihdr = png.GetIhdr();
                Console.WriteLine($"Image dimensions: {ihdr.Width} x {ihdr.Height}");

                // ここで他の情報(APNG, HDR, Exif)を取得するメソッドを呼び出す

                // 読み込んだデータをそのまま別名で保存
                png.Save(outputPath);
                Console.WriteLine($"File saved to {outputPath}");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"An error occurred: {ex.Message}");
        }
    }



using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;

namespace PngKit
{

    // ---------------------------------------------------------------------------

    // PNGファイルの仕様が20年以上ぶりにバージョンアップ、HDR・アニメーションPNG・Exifをサポート。
    // https://gigazine.net/news/20250626-png-update/

    // ポータブル ネットワーク グラフィックス (PNG) 仕様 (第 3 版)
    // https://www.w3.org/TR/png-3/

    // ---------------------------------------------------------------------------
    /// <summary>
    /// PNGファイルの読み書きに関する補助機能を提供する静的クラス。
    /// 
    /// ・PNG仕様で定められているビッグエンディアン形式の数値を、
    ///  .NET環境(通常リトルエンディアン)で正しく扱うための変換機能を持つ。
    /// </summary>
    public static class CPngHelper
    {

        // ---------------------------------------------------------------------------
        // フィールド

        /// <summary>
        /// PNGファイルの先頭8バイトのシグネチャ。
        /// 
        /// ・このバイト列でファイルがPNG形式であることを識別する。 { 137, 80, 78, 71, 13, 10, 26, 10 }
        /// </summary>
        public static readonly byte[] PngSignature = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };

        // ---------------------------------------------------------------------------
        /// <summary>
        /// BinaryReaderから4バイトを読み込み、
        /// ビッグエンディアン形式として解釈して
        /// 符号なし32ビット整数(uint)を返す。
        /// </summary>
        /// <param name="br_">読み込み元のBinaryReader。</param>
        /// <returns>エンディアン変換されたuint値。</returns>
        public static uint ReadUInt32BigEndian(BinaryReader br_)
        {

            var data = br_.ReadBytes(4);

            // .NETが動作する多くの環境はリトルエンディアンのため、バイトオーダーを逆転させる必要がある。
            
            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(data);
            }
            
            return BitConverter.ToUInt32(data, 0);
        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// BinaryReaderから2バイトを読み込み、
        /// ビッグエンディアン形式として解釈して
        /// 符号なし16ビット整数(ushort)を返す。
        /// </summary>
        /// <param name="br_">読み込み元のBinaryReader。</param>
        /// <returns>エンディアン変換されたushort値。</returns>
        public static ushort ReadUInt16BigEndian(BinaryReader br_)
        {
            var data = br_.ReadBytes(2);

            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(data);
            }
            
            return BitConverter.ToUInt16(data, 0);
        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 符号なし32ビット整数(uint)を
        /// ビッグエンディアン形式の4バイトとして
        /// BinaryWriterに書き込む。
        /// </summary>
        /// <param name="bw_">書き込み先のBinaryWriter。</param>
        /// <param name="value_">書き込むuint値。</param>
        public static void WriteUInt32BigEndian(BinaryWriter bw_, uint value_)
        {
            
            var data = BitConverter.GetBytes(value_);

            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(data);
            }
            
            bw_.Write(data);
        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 符号なし16ビット整数(ushort)を
        /// ビッグエンディアン形式の2バイトとして
        /// BinaryWriterに書き込む。
        /// </summary>
        /// <param name="bw_">書き込み先のBinaryWriter。</param>
        /// <param name="value_">書き込むushort値。</param>
        public static void WriteUInt16BigEndian(BinaryWriter bw_, ushort value_)
        {

            var data = BitConverter.GetBytes(value_);
        
            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(data);
            }
            
            bw_.Write(data);
        }

        // ---------------------------------------------------------------------------
    }

    // ---------------------------------------------------------------------------
    /// <summary>
    /// PNGチャンクの基底クラス。
    /// 
    /// ・チャンクは、「長さ」「タイプ」「データ」「CRC」の4つの部分から構成される。
    /// </summary>
    public abstract class CChunkBase
    {

        // ---------------------------------------------------------------------------
        // プロパティ

        /// <summary>
        /// チャンクタイプ。4文字のASCII文字列(例: "IHDR", "IDAT")。
        /// </summary>
        public string Type { get; }

        /// <summary>
        /// チャンクのデータ本体を保持するバイト配列。
        /// </summary>
        public byte[] Data { get; protected set; }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// コンストラクタ
        /// 
        /// ・データ部が空のチャンクを生成する。
        /// </summary>
        /// <param name="type_">チャンクタイプ。</param>
        protected CChunkBase(string type_)
        {
            Type = type_;
            Data = Array.Empty<byte>();
        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// コンストラクタ
        /// 
        /// ・読み込んだデータからチャンクを生成する。
        /// </summary>
        /// <param name="type_">チャンクタイプ。</param>
        /// <param name="data_">チャンクのデータ部。</param>
        protected CChunkBase(string type_, byte[] data_)
        {
            
            Type = type_;
            Data = data_;

            // 派生クラスのParseDataを呼び出し、データをプロパティに分解する。
            
            using (var reader = new BinaryReader(new MemoryStream(data_)))
            {
                ParseData(reader);
            }
        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// チャンクのデータ部(バイト配列)を解析し、具体的なプロパティに値を設定する。 ( ※ 派生クラスで実装する。)
        /// </summary>
        /// <param name="br_">データ部を読み込むためのBinaryReader。</param>
        protected abstract void ParseData(BinaryReader br_);

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 各プロパティの値を、チャンクのデータ部(バイト配列)として構築する。( ※ 派生クラスで実装する。)
        /// </summary>
        /// <param name="bw_">データ部を書き込むためのBinaryWriter。</param>
        protected abstract void WriteData(BinaryWriter bw_);

        // ---------------------------------------------------------------------------
        /// <summary>
        /// このチャンク全体(長さ、タイプ、データ、CRC)を書き込む。
        /// </summary>
        /// <param name="bw_">書き込み先のBinaryWriter。</param>
        public void WriteTo(BinaryWriter bw_)
        {

            // 派生クラスのプロパティから最新のデータバイト配列を構築する。
            
            using (var ms = new MemoryStream())
            using (var dataWriter = new BinaryWriter(ms))
            {
                WriteData(dataWriter);
                Data = ms.ToArray();
            }

            var type_bytes = Encoding.ASCII.GetBytes(Type);

            // 1. 長さ (データ部のバイト数)
            CPngHelper.WriteUInt32BigEndian(bw_, (uint)Data.Length);
            
            // 2. チャンクタイプ (4バイト)
            bw_.Write(type_bytes);
            
            // 3. データ
            bw_.Write(Data);
            
            // 4. CRC (タイプとデータを元に計算)
            uint crc = CCrc32.Calculate(type_bytes, Data);
            
            CPngHelper.WriteUInt32BigEndian(bw_, crc);
        }

        // ---------------------------------------------------------------------------
    }

    // ---------------------------------------------------------------------------
    /// <summary>
    /// IHDR (Image Header) チャンクのクラス。
    /// 
    /// ・PNGファイルの基本的な情報を格納する。
    /// ・必須かつ最初のチャンク。
    /// </summary>
    public class CIhdrChunk : CChunkBase
    {

        // ---------------------------------------------------------------------------
        // プロパティ

        #region "プロパティ"

        /// <summary>
        /// 画像の幅(ピクセル単位)。
        /// </summary>
        public uint Width { get; set; }

        /// <summary>
        /// 画像の高さ(ピクセル単位)。
        /// </summary>
        public uint Height { get; set; }

        /// <summary>
        /// ビット深度。1サンプルあたりのビット数。許可される値は 1, 2, 4, 8, 16。
        /// </summary>
        public byte BitDepth { get; set; }

        /// <summary>
        /// カラータイプ。画像の表現方法を示す。
        /// 0: グレースケール, 2: トゥルーカラー(RGB), 3: インデックスカラー, 4: グレースケール+アルファ, 6: トゥルーカラー+アルファ
        /// </summary>
        public byte ColorType { get; set; }

        /// <summary>
        /// 圧縮方式。常に0(Deflate圧縮)でなければならない。
        /// </summary>
        public byte CompressionMethod { get; set; }

        /// <summary>
        /// フィルター方式。常に0(適応フィルタリング)でなければならない。
        /// </summary>
        public byte FilterMethod { get; set; }

        /// <summary>
        /// インターレース方式。0: なし, 1: Adam7インターレース。
        /// </summary>
        public byte InterlaceMethod { get; set; }
        
        #endregion

        // ---------------------------------------------------------------------------
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="data_"></param>
        public CIhdrChunk(byte[] data_) : base("IHDR", data_) 
        {

        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 
        /// </summary>
        /// <param name="br_"></param>
        protected override void ParseData(BinaryReader br_)
        {
            Width = CPngHelper.ReadUInt32BigEndian(br_);
            Height = CPngHelper.ReadUInt32BigEndian(br_);

            BitDepth = br_.ReadByte();

            ColorType = br_.ReadByte();

            CompressionMethod = br_.ReadByte();
            FilterMethod = br_.ReadByte();
            InterlaceMethod = br_.ReadByte();
        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 
        /// </summary>
        /// <param name="bw_"></param>
        protected override void WriteData(BinaryWriter bw_)
        {
            CPngHelper.WriteUInt32BigEndian(bw_, Width);
            CPngHelper.WriteUInt32BigEndian(bw_, Height);

            bw_.Write(BitDepth);
            bw_.Write(ColorType);

            bw_.Write(CompressionMethod);
            bw_.Write(FilterMethod);
            bw_.Write(InterlaceMethod);
        }

        // ---------------------------------------------------------------------------
    }

    // ---------------------------------------------------------------------------
    /// <summary>
    /// IDAT (Image Data) チャンクのクラス。
    /// 
    /// ・圧縮された画像データを格納する。
    /// ・複数存在することがある。
    /// </summary>
    public class CIdatChunk : CChunkBase
    {

        // ---------------------------------------------------------------------------
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="data_"></param>
        public CIdatChunk(byte[] data_) : base("IDAT", data_) 
        {

        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 
        /// </summary>
        /// <param name="br_"></param>
        protected override void ParseData(BinaryReader br_)
        {
            /* データはそのまま保持 */ 
        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 
        /// </summary>
        /// <param name="bw_"></param>
        protected override void WriteData(BinaryWriter bw_) 
        {
            bw_.Write(Data); 
        }

        // ---------------------------------------------------------------------------
    }

    // ---------------------------------------------------------------------------
    /// <summary>
    /// IEND (Image End) チャンクのクラス。
    /// 
    /// ・PNGデータストリームの終端を示す。
    /// ・データ部は常に空。
    /// </summary>
    public class CIendChunk : CChunkBase
    {
        // ---------------------------------------------------------------------------
        /// <summary>
        /// コンストラクタ
        /// </summary>
        public CIendChunk() : base("IEND")
        {

        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="data_"></param>
        public CIendChunk(byte[] data_) : base("IEND", data_)
        {
        
        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 
        /// </summary>
        /// <param name="br_"></param>
        protected override void ParseData(BinaryReader br_) 
        {

        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 
        /// </summary>
        /// <param name="bw_"></param>
        protected override void WriteData(BinaryWriter bw_) 
        {

        }

        // ---------------------------------------------------------------------------
    }

    // ---------------------------------------------------------------------------
    // APNG (Animated PNG) 関連チャンク
    // ---------------------------------------------------------------------------

    /// <summary>
    /// acTL (Animation Control) チャンクのクラス。
    /// 
    /// ・アニメーションPNGであることを示し、全体のフレーム数とループ回数を定義する。
    /// </summary>
    public class CAcTlChunk : CChunkBase
    {

        // ---------------------------------------------------------------------------
        // プロパティ

        /// <summary>
        /// アニメーションの総フレーム数。
        /// </summary>
        public uint NumFrames { get; set; }
        
        /// <summary>
        /// アニメーションの再生回数。0は無限ループを示す。
        /// </summary>
        public uint NumPlays { get; set; }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="data_"></param>
        public CAcTlChunk(byte[] data_) : base("acTL", data_) 
        {

        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 
        /// </summary>
        /// <param name="br_"></param>
        protected override void ParseData(BinaryReader br_)
        {
            NumFrames = CPngHelper.ReadUInt32BigEndian(br_);
            NumPlays = CPngHelper.ReadUInt32BigEndian(br_);
        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 
        /// </summary>
        /// <param name="bw_"></param>
        protected override void WriteData(BinaryWriter bw_)
        {
            CPngHelper.WriteUInt32BigEndian(bw_, NumFrames);
            CPngHelper.WriteUInt32BigEndian(bw_, NumPlays);
        }

        // ---------------------------------------------------------------------------
    }

    // ---------------------------------------------------------------------------
    /// <summary>
    /// fcTL (Frame Control) チャンク。各アニメーションフレームの制御情報を格納する。
    /// </summary>
    public class CFcTlChunk : CChunkBase
    {
        // ---------------------------------------------------------------------------
        // プロパティ

        #region "プロパティ"

        /// <summary>
        /// このフレームのシーケンス番号(0から始まる)。
        /// </summary>
        public uint SequenceNumber { get; set; }
      
        /// <summary>
        /// このフレームの幅。
        /// </summary>
        public uint Width { get; set; }
        
        /// <summary>
        /// このフレームの高さ。
        /// </summary>
        public uint Height { get; set; }
        
        /// <summary>
        /// このフレームを描画するX座標のオフセット。
        /// </summary>
        public uint XOffset { get; set; }
        
        /// <summary>
        /// このフレームを描画するY座標のオフセット。
        /// </summary>
        public uint YOffset { get; set; }
        
        /// <summary>
        /// このフレームを表示する時間(分子)。
        /// </summary>
        public ushort DelayNum { get; set; }
        
        /// <summary>
        /// このフレームを表示する時間(分母)。
        /// 
        /// ・DelayNum/DelayDen秒。分母が0の場合は100として扱う。
        /// </summary>
        public ushort DelayDen { get; set; }
        
        /// <summary>
        /// 次のフレームを描画する前の描画領域の扱い方。
        /// 
        ///  0: 何もしない, 1: 背景色でクリア, 2: 前のフレームの状態に戻す。
        /// </summary>
        public byte DisposeOp { get; set; }
        
        /// <summary>
        /// 現在の描画バッファへの合成方法。
        /// 
        ///  0: 上書き (Source), 1: アルファブレンド (Over)。
        /// </summary>
        public byte BlendOp { get; set; }

        #endregion

        // ---------------------------------------------------------------------------
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="data_"></param>
        public CFcTlChunk(byte[] data_) : base("fcTL", data_) 
        {

        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 
        /// </summary>
        /// <param name="br_"></param>
        protected override void ParseData(BinaryReader br_)
        {

            SequenceNumber = CPngHelper.ReadUInt32BigEndian(br_);

            Width = CPngHelper.ReadUInt32BigEndian(br_);
            Height = CPngHelper.ReadUInt32BigEndian(br_);

            XOffset = CPngHelper.ReadUInt32BigEndian(br_);
            YOffset = CPngHelper.ReadUInt32BigEndian(br_);

            DelayNum = CPngHelper.ReadUInt16BigEndian(br_);
            DelayDen = CPngHelper.ReadUInt16BigEndian(br_);

            DisposeOp = br_.ReadByte();
            BlendOp = br_.ReadByte();
        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 
        /// </summary>
        /// <param name="bw_"></param>
        protected override void WriteData(BinaryWriter bw_)
        {
            CPngHelper.WriteUInt32BigEndian(bw_, SequenceNumber);

            CPngHelper.WriteUInt32BigEndian(bw_, Width);
            CPngHelper.WriteUInt32BigEndian(bw_, Height);

            CPngHelper.WriteUInt32BigEndian(bw_, XOffset);
            CPngHelper.WriteUInt32BigEndian(bw_, YOffset);

            CPngHelper.WriteUInt16BigEndian(bw_, DelayNum);
            CPngHelper.WriteUInt16BigEndian(bw_, DelayDen);

            bw_.Write(DisposeOp);
            bw_.Write(BlendOp);
        }

        // ---------------------------------------------------------------------------
    }

    // ---------------------------------------------------------------------------
    /// <summary>
    /// fdAT (Frame Data) チャンクのクラス。
    /// 
    /// ・アニメーションフレームの画像データを格納する。
    /// ・IDATと似ているが、先頭にシーケンス番号を持つ。
    /// </summary>
    public class CFdAtChunk : CChunkBase
    {

        // ---------------------------------------------------------------------------
        // プロパティ

        /// <summary>
        /// このデータチャンクのシーケンス番号。
        /// </summary>
        public uint SequenceNumber { get; set; }

        /// <summary>
        /// 圧縮されたフレームの画像データ。
        /// </summary>
        public byte[] FrameData { get; set; }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="data_"></param>
        public CFdAtChunk(byte[] data_) : base("fdAT", data_) 
        {

        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 
        /// </summary>
        /// <param name="br_"></param>
        protected override void ParseData(BinaryReader br_)
        {
            SequenceNumber = CPngHelper.ReadUInt32BigEndian(br_);

            FrameData = br_.ReadBytes((int)(br_.BaseStream.Length - br_.BaseStream.Position));
        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 
        /// </summary>
        /// <param name="bw_"></param>
        protected override void WriteData(BinaryWriter bw_)
        {
            CPngHelper.WriteUInt32BigEndian(bw_, SequenceNumber);
            
            bw_.Write(FrameData);
        }

        // ---------------------------------------------------------------------------
    }

    // ---------------------------------------------------------------------------
    // HDR (High Dynamic Range) 関連チャンク
    // ---------------------------------------------------------------------------

    /// <summary>
    /// cICP (Coding-independent Code Points) チャンクのクラス。
    /// 
    /// ・HDRのカラースペース、伝達関数などをITU-T H.273で定義されたコードポイントで指定する。
    /// </summary>
    public class CCICPChunk : CChunkBase
    {

        // ---------------------------------------------------------------------------
        // プロパティ

        /// <summary>
        /// 色域(プライマリ)を定義するコードポイント。 例: 9 (BT.2020), 1 (BT.709)。
        /// </summary>
        public byte ColorPrimaries { get; set; }
       
        /// <summary>
        /// 伝達関数(ガンマ)を定義するコードポイント。 例: 16 (PQ), 18 (HLG)。
        /// </summary>
        public byte TransferFunction { get; set; }
        
        /// <summary>
        /// RGBからYCrCbへの変換マトリックス係数。
        /// 
        /// ・RGB画像の場合は常に0。
        /// </summary>
        public byte MatrixCoefficients { get; set; }
       
        /// <summary>
        /// ビデオ信号のレンジを示すフラグ。
        /// 
        ///  1: フルレンジ, 0: ナローレンジ。
        /// </summary>
        public byte VideoFullRangeFlag { get; set; }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="data_"></param>
        public CCICPChunk(byte[] data_) : base("cICP", data_) 
        {

        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 
        /// </summary>
        /// <param name="br_"></param>
        protected override void ParseData(BinaryReader br_)
        {
            ColorPrimaries = br_.ReadByte();
            TransferFunction = br_.ReadByte();
            MatrixCoefficients = br_.ReadByte();
            VideoFullRangeFlag = br_.ReadByte();
        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 
        /// </summary>
        /// <param name="bw_"></param>
        protected override void WriteData(BinaryWriter bw_)
        {
            bw_.Write(ColorPrimaries);
            bw_.Write(TransferFunction);
            bw_.Write(MatrixCoefficients);
            bw_.Write(VideoFullRangeFlag);
        }

        // ---------------------------------------------------------------------------
    }

    // ---------------------------------------------------------------------------
    /// <summary>
    /// mDCV (Mastering Display Color Volume) チャンク。
    /// 
    /// ・コンテンツ制作時に使用されたマスタリングディスプレイの特性を格納する。
    /// </summary>
    public class CMDVCChunk : CChunkBase
    {
        // ---------------------------------------------------------------------------
        // プロパティ

        #region "プロパティ"

        /// <summary>プライマリRのx色度座標。</summary>
        public ushort PrimaryRx { get; set; }

        /// <summary>プライマリRのy色度座標。</summary>
        public ushort PrimaryRy { get; set; }

        /// <summary>プライマリGのx色度座標。</summary>
        public ushort PrimaryGx { get; set; }

        /// <summary>プライマリGのy色度座標。</summary>
        public ushort PrimaryGy { get; set; }

        /// <summary>プライマリBのx色度座標。</summary>
        public ushort PrimaryBx { get; set; }

        /// <summary>プライマリBのy色度座標。</summary>
        public ushort PrimaryBy { get; set; }

        /// <summary>ホワイトポイントのx色度座標。</summary>
        public ushort WhitePointX { get; set; }

        /// <summary>ホワイトポイントのy色度座標。</summary>
        public ushort WhitePointY { get; set; }

        /// <summary>マスタリングディスプレイの最大輝度 (cd/m^2)。</summary>
        public uint MaxLuminance { get; set; }

        /// <summary>マスタリングディスプレイの最小輝度 (cd/m^2)。</summary>
        public uint MinLuminance { get; set; }

        #endregion

        // ---------------------------------------------------------------------------
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="data"></param>
        public CMDVCChunk(byte[] data) : base("mDCV", data) 
        {

        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 
        /// </summary>
        /// <param name="br_"></param>
        protected override void ParseData(BinaryReader br_)
        {
            PrimaryRx = CPngHelper.ReadUInt16BigEndian(br_);
            PrimaryRy = CPngHelper.ReadUInt16BigEndian(br_);
            PrimaryGx = CPngHelper.ReadUInt16BigEndian(br_);
            PrimaryGy = CPngHelper.ReadUInt16BigEndian(br_);
            PrimaryBx = CPngHelper.ReadUInt16BigEndian(br_);
            PrimaryBy = CPngHelper.ReadUInt16BigEndian(br_);
            WhitePointX = CPngHelper.ReadUInt16BigEndian(br_);
            WhitePointY = CPngHelper.ReadUInt16BigEndian(br_);
            MaxLuminance = CPngHelper.ReadUInt32BigEndian(br_);
            MinLuminance = CPngHelper.ReadUInt32BigEndian(br_);
        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 
        /// </summary>
        /// <param name="bw_"></param>
        protected override void WriteData(BinaryWriter bw_)
        {
            CPngHelper.WriteUInt16BigEndian(bw_, PrimaryRx);
            CPngHelper.WriteUInt16BigEndian(bw_, PrimaryRy);
            CPngHelper.WriteUInt16BigEndian(bw_, PrimaryGx);
            CPngHelper.WriteUInt16BigEndian(bw_, PrimaryGy);
            CPngHelper.WriteUInt16BigEndian(bw_, PrimaryBx);
            CPngHelper.WriteUInt16BigEndian(bw_, PrimaryBy);
            CPngHelper.WriteUInt16BigEndian(bw_, WhitePointX);
            CPngHelper.WriteUInt16BigEndian(bw_, WhitePointY);
            CPngHelper.WriteUInt32BigEndian(bw_, MaxLuminance);
            CPngHelper.WriteUInt32BigEndian(bw_, MinLuminance);
        }

        // ---------------------------------------------------------------------------
    }

    // ---------------------------------------------------------------------------
    /// <summary>
    /// cLLI (Content Light Level Information) チャンクのクラス。
    ///  
    /// ・コンテンツ自体の最大輝度情報を格納する。
    /// </summary>
    public class CCLLIChunk : CChunkBase
    {
        // ---------------------------------------------------------------------------
        // プロパティ

        /// <summary>
        /// 最大コンテンツ輝度レベル (MaxCLL)。画像内の単一ピクセルの最大輝度 (cd/m^2)。
        /// </summary>
        public uint MaxCLL { get; set; }
        
        /// <summary>
        /// 最大フレーム平均輝度レベル (MaxFALL)。アニメーション全体のフレーム平均輝度の最大値 (cd/m^2)。
        /// </summary>
        public uint MaxFALL { get; set; }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="data"></param>
        public CCLLIChunk(byte[] data) : base("cLLI", data) 
        {

        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 
        /// </summary>
        /// <param name="br_"></param>
        protected override void ParseData(BinaryReader br_)
        {
            MaxCLL = CPngHelper.ReadUInt32BigEndian(br_);
            MaxFALL = CPngHelper.ReadUInt32BigEndian(br_);
        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 
        /// </summary>
        /// <param name="bw_"></param>
        protected override void WriteData(BinaryWriter bw_)
        {
            CPngHelper.WriteUInt32BigEndian(bw_, MaxCLL);
            CPngHelper.WriteUInt32BigEndian(bw_, MaxFALL);
        }

        // ---------------------------------------------------------------------------
    }

    // ---------------------------------------------------------------------------
    /// <summary>
    /// eXIf (Exchangeable Image File) チャンクのクラス。
    /// 
    /// ・デジタルカメラの写真情報などを格納する。
    /// ・データ自体はTIFFフォーマット。
    /// </summary>
    public class CExifChunk : CChunkBase
    {

        // ---------------------------------------------------------------------------
        /// <summary>
        /// コンストラクタ
        /// 
        /// ・ExifデータはTIFFフォーマットなので、ここではバイト配列として保持する。
        /// ・詳細な解析には外部のExifパーサーライブラリの利用を推奨する。
        /// </summary>
        public CExifChunk(byte[] data_) : base("eXIf", data_)
        {

        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 
        /// </summary>
        /// <param name="br_"></param>
        protected override void ParseData(BinaryReader br_) 
        {
            /* Dataをそのまま利用 */ 
        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 
        /// </summary>
        /// <param name="bw_"></param>
        protected override void WriteData(BinaryWriter bw_) 
        {
            bw_.Write(Data);
        }

        // ---------------------------------------------------------------------------
    }

    // ---------------------------------------------------------------------------
    /// <summary>
    /// アニメーションフレームのクラス。
    /// 
    /// ・フレームの制御情報 (fcTL) と、そのフレームの画像データ (IDATまたはfdAT) を保持する。
    /// </summary>
    public class CAnimationFrame
    {

        // ---------------------------------------------------------------------------
        // プロパティ

        /// <summary>
        /// このフレームの制御情報 (寸法、遅延時間など)。
        /// </summary>
        public CFcTlChunk FrameControl { get; set; }
        
        /// <summary>
        /// このフレームの画像データを含むチャンクのリスト。
        /// 
        /// ・最初のフレームはIDAT、以降はfdATチャンクとなる。
        /// </summary>
        public List<CChunkBase> FrameDataChunks { get; } = new List<CChunkBase>();

        // ---------------------------------------------------------------------------
        /// <summary>
        /// このフレームの圧縮された画像データを結合し、Deflate展開して生のピクセルデータを返す。
        /// </summary>
        /// <returns>フィルタ適用済みの生のピクセルデータ。</returns>
        public byte[] GetImageData()
        {

            using (var compressedStream = new MemoryStream())
            {
                foreach (var chunk in FrameDataChunks)
                {
                    // フレームデータはCFdAtChunkかCIdatChunkのどちらか
                    byte[] data = chunk is CFdAtChunk fdAt ? fdAt.FrameData : chunk.Data;
            
                    compressedStream.Write(data, 0, data.Length);
                }

                compressedStream.Position = 0;

                using (var resultStream = new MemoryStream())
                {
                    // ZlibStreamはzlibヘッダ(最初の2バイト)を期待するため、それをスキップする。
                    if (compressedStream.Length > 2)
                    {
                        compressedStream.Seek(2, SeekOrigin.Begin);

                        using (var zlibStream = new DeflateStream(compressedStream, CompressionMode.Decompress, true)) // leaveOpen: true
                        {
                            zlibStream.CopyTo(resultStream);
                        }
                    }

                    return resultStream.ToArray();
                }
            }
        }

        // ---------------------------------------------------------------------------
    }

    // ---------------------------------------------------------------------------
    /// <summary>
    /// PNGファイル全体を表現するクラス。
    /// 
    /// ・チャンクのリストとしてファイルを管理する。
    /// </summary>
    public class CPngFile
    {

        // ---------------------------------------------------------------------------
        // プロパティ

        /// <summary>
        /// ファイルに含まれる全てのチャンクを順序通りに保持するリスト。
        /// </summary>
        public List<CChunkBase> Chunks { get; } = new List<CChunkBase>();

        /// <summary>
        /// acTLチャンクが存在する場合にtrueを返す。
        /// 
        /// ・アニメーションPNGかどうかを判定する。
        /// </summary>
        public bool IsAnimated => Chunks.Any(c => c is CAcTlChunk);

        // ---------------------------------------------------------------------------
        /// <summary>
        /// APNGの場合、アニメーションフレームのシーケンスを返す。
        /// </summary>
        /// <returns>CAnimationFrameのIEnumerable。</returns>
        public IEnumerable<CAnimationFrame> GetAnimationFrames()
        {

            if (IsAnimated == false) yield break;

            CAnimationFrame currentFrame = null;

            bool isFirstFrameData = true;

            foreach (var chunk in Chunks)
            {
                if (chunk is CFcTlChunk fcTl)
                {
                    // 前のフレームが完了していれば、それを返す。
                    if (currentFrame != null)
                    {
                        yield return currentFrame;
                    }
                    // 新しいフレームを開始する。
                    currentFrame = new CAnimationFrame { FrameControl = fcTl };
                }
                else if (chunk is CIdatChunk idat && currentFrame != null && isFirstFrameData)
                {
                    // 最初のフレームのデータ。 (IDAT)
                    currentFrame.FrameDataChunks.Add(idat);
                }
                else if (chunk is CFdAtChunk fdAt && currentFrame != null)
                {
                    isFirstFrameData = false; // fdATが見つかったら、もうIDATはフレームデータとして扱わない。

                    currentFrame.FrameDataChunks.Add(fdAt);
                }
            }

            // 最後のフレームを返す。

            if (currentFrame != null)
            {
                yield return currentFrame;
            }
        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// ストリームからPNGファイルを読み込む。
        /// </summary>
        public static CPngFile Read(Stream stream) => new CPngReader(stream).Read();

        // ---------------------------------------------------------------------------
        /// <summary>
        /// PNGファイルの内容をストリームに書き込む。
        /// </summary>
        public void Write(Stream stream) => new CPngWriter(stream).Write(this);

        // ---------------------------------------------------------------------------
    }

    // ---------------------------------------------------------------------------
    /// <summary>
    /// PNGファイルをストリームから読み込み、CPngFileオブジェクトを構築するクラス。
    /// </summary>
    public class CPngReader
    {

        // ---------------------------------------------------------------------------
        // フィールド

        private readonly BinaryReader br = null;

        // ---------------------------------------------------------------------------
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="stream_"></param>
        public CPngReader(Stream stream_)
        {
            br = new BinaryReader(stream_, Encoding.ASCII, true);
        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        /// <exception cref="InvalidDataException"></exception>
        public CPngFile Read()
        {
            
            var png_file = new CPngFile();

            var signature = br.ReadBytes(8);

            if (!signature.SequenceEqual(CPngHelper.PngSignature))
            {
                throw new InvalidDataException("有効な PNG ファイルではありません。署名が無効です。");
            }

            while (br.BaseStream.Position < br.BaseStream.Length)
            {
                var length = CPngHelper.ReadUInt32BigEndian(br);
                var typeBytes = br.ReadBytes(4);
                var type = Encoding.ASCII.GetString(typeBytes);
                var data = br.ReadBytes((int)length);
                var crc = CPngHelper.ReadUInt32BigEndian(br);

                if (CCrc32.Calculate(typeBytes, data) != crc)
                {
                    throw new InvalidDataException($"CRC エラー。 {type} chunk.");
                }

                CChunkBase chunk = null;

                switch (type)
                {
                    case "IHDR": chunk = new CIhdrChunk(data); break;
                    case "IDAT": chunk = new CIdatChunk(data); break;
                    case "IEND": chunk = new CIendChunk(data); break;
                    case "acTL": chunk = new CAcTlChunk(data); break;
                    case "fcTL": chunk = new CFcTlChunk(data); break;
                    case "fdAT": chunk = new CFdAtChunk(data); break;
                    case "cICP": chunk = new CCICPChunk(data); break;
                    case "mDCV": chunk = new CMDVCChunk(data); break;
                    case "cLLI": chunk = new CCLLIChunk(data); break;
                    case "eXIf": chunk = new CExifChunk(data); break;
                
                    // 他の既知のチャンクもここに追加できる
                    default:
                        chunk = new CGenericChunk(type, data);
                        break;
                }

                png_file.Chunks.Add(chunk);

                if (type == "IEND") break;
            }

            return png_file;
        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 未知またはこのライブラリで未実装のチャンクを、データを損なわずに保持するためのクラス。
        /// </summary>
        private class CGenericChunk : CChunkBase
        {
            // ---------------------------------------------------------------------------
            /// <summary>
            /// コンストラクタ
            /// </summary>
            /// <param name="type_"></param>
            /// <param name="data_"></param>
            public CGenericChunk(string type_, byte[] data_) : base(type_, data_) 
            {
            
            }

            // ---------------------------------------------------------------------------
            /// <summary>
            /// 
            /// </summary>
            /// <param name="br_"></param>
            protected override void ParseData(BinaryReader br_) 
            {
                /* 何もせず、Dataをそのまま保持 */ 
            }

            // ---------------------------------------------------------------------------
            /// <summary>
            /// 
            /// </summary>
            /// <param name="bw_"></param>
            protected override void WriteData(BinaryWriter bw_) 
            {
                bw_.Write(Data);
            }

            // ---------------------------------------------------------------------------
        }

        // ---------------------------------------------------------------------------
    }

    // ---------------------------------------------------------------------------
    /// <summary>
    /// CPngFileオブジェクトをストリームに書き出すクラス。
    /// </summary>
    public class CPngWriter
    {
        // ---------------------------------------------------------------------------
        // フィールド

        private readonly BinaryWriter bw = null;

        // ---------------------------------------------------------------------------
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="stream_"></param>
        public CPngWriter(Stream stream_)
        {
            bw = new BinaryWriter(stream_, Encoding.ASCII, true);
        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 
        /// </summary>
        /// <param name="png_file_"></param>
        public void Write(CPngFile png_file_)
        {

            bw.Write(CPngHelper.PngSignature);

            foreach (var chunk in png_file_.Chunks)
            {
                chunk.WriteTo(bw);
            }
        }

        // ---------------------------------------------------------------------------
    }

    // ---------------------------------------------------------------------------
    /// <summary>
    /// PNG仕様で定義されているCRC-32チェックサムを計算するクラス。
    /// </summary>
    public class CCrc32
    {
        // ---------------------------------------------------------------------------
        // フィールド

        private static readonly uint[] table = null;
        
        private uint crc = 0xFFFFFFFF; // 初期値は全て1。

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 静的コンストラクタ。
        /// 
        /// ・CRC計算用のテーブルを初回アクセス時に一度だけ生成する。
        /// </summary>
        static CCrc32()
        {

            table = new uint[256];

            const uint poly = 0xEDB88320; // PNGで使用されるCRC-32の反転多項式。

            for (uint i = 0; i < 256; i++)
            {
                uint c = i;
            
                for (int k = 0; k < 8; k++)
                {
                    if ((c & 1) != 0)
                    {
                        c = poly ^ (c >> 1);
                    }
                    else
                    {
                        c = c >> 1;
                    }
                }
                
                table[i] = c;
            }
        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// CRCの計算を、バイト配列で更新する。
        /// </summary>
        public void Update(byte[] buffer_, int offset_, int length_)
        {
            for (int i = 0; i < length_; i++)
            {
                crc = table[(crc ^ buffer_[offset_ + i]) & 0xFF] ^ (crc >> 8);
            }
        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 最終的なCRC値を返す。
        /// </summary>
        /// <returns>計算されたCRC-32値。</returns>
        public uint GetValue()
        {

            // 仕様に従い、最後にビットを反転する。(1の補数をとる)

            return crc ^ 0xFFFFFFFF;
        }

        // ---------------------------------------------------------------------------
        /// <summary>
        /// 指定されたチャンクタイプとデータから、直接、CRC値を計算する。
        /// </summary>
        public static uint Calculate(byte[] chunk_type_, byte[] data_)
        {

            if (chunk_type_ == null || chunk_type_.Length != 4)
            {
                throw new ArgumentException("チャンク タイプは 4 バイトである必要があります。", nameof(chunk_type_));
            }

            var crcInstance = new CCrc32();

            // チャンクタイプと、データの両方で、CRCを計算する。

            crcInstance.Update(chunk_type_, 0, 4);
            
            if (data_ != null && data_.Length > 0)
            {
                crcInstance.Update(data_, 0, data_.Length);
            }
            
            return crcInstance.GetValue();
        }

        // ---------------------------------------------------------------------------
    }

    // ---------------------------------------------------------------------------

} 


いいなと思ったら応援しよう!

コメント

ログイン または 会員登録 するとコメントできます。
note会員1000万人突破記念 1000万ポイントみんなで山分け祭 エントリー7/8(火)まで
PNG第3版に対応する便利クラス|古井和雄
word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word

mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1