PNG (Portable Network Graphics) file: C++98/STL parsing library

NOTICE: Many of the documentation comments (or docstrings) in this file were copied from or derived from the Portable Network Graphics (PNG) Specification (Third Edition). Copyright © 1996-2025 World Wide Web Consortium. https://www.w3.org/copyright/software-license-2023/

The full text of the license for the original W3C PNG specification is provided below:

Software and Document license - 2023 version

This work is being provided by the copyright holders under the following license.

License

By obtaining and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions.

Permission to copy, modify, and distribute this work, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the work or portions thereof, including modifications:

  • The full text of this NOTICE in a location viewable to users of the redistributed or derivative work.
  • Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, the W3C software and document short notice should be included.
  • Notice of any changes or modifications, through a copyright statement on the new code or document such as "This software or document includes material copied from or derived from [title and URI of the W3C document]. Copyright © [$year-of-document] World Wide Web Consortium. https://www.w3.org/copyright/software-license-2023/"

Disclaimers

THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.

COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT.

The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the work without specific, written prior permission. Title to copyright in this work will at all times remain with copyright holders.


Test files for APNG can be found at the following locations:

This page hosts a formal specification of PNG (Portable Network Graphics) file using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Usage

Runtime library

All C++98/STL code generated by Kaitai Struct depends on the Kaitai Struct runtime library for C++/STL. You must add this dependency to your project before you can parse or serialize any data.

For C++, the easiest way is to clone the runtime library sources and build them along with your project.

Code

Using Kaitai Struct in C++/STL usually consists of 3 steps.

  1. We need to create an STL input stream (std::istream). One can open local file for that, or use existing std::string or char* buffer.
    #include <fstream>
    
    std::ifstream is("path/to/local/file.png", std::ifstream::binary);
    
    #include <sstream>
    
    std::istringstream is(str);
    
    #include <sstream>
    
    const char buf[] = { ... };
    std::string str(buf, sizeof buf);
    std::istringstream is(str);
    
  2. We need to wrap our input stream into Kaitai stream:
    #include "kaitai/kaitaistream.h"
    
    kaitai::kstream ks(&is);
    
  3. And finally, we can invoke the parsing:
    png_t data(&ks);
    

After that, one can get various attributes from the structure by invoking getter methods like:

data.magic() // => get magic

C++98/STL source code to parse PNG (Portable Network Graphics) file

png.h

#ifndef PNG_H_
#define PNG_H_

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

class png_t;

#include "kaitai/kaitaistruct.h"
#include <stdint.h>
#include "exif.h"
#include "icc_4.h"
#include <set>
#include <vector>

#if KAITAI_STRUCT_VERSION < 11000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.11 or later is required"
#endif

/**
 * NOTICE: Many of the documentation comments (or docstrings) in this file were
 * copied from or derived from the [Portable Network Graphics (PNG) Specification
 * (Third Edition)](https://www.w3.org/TR/2025/REC-png-3-20250624/).
 * Copyright © 1996-2025 [World Wide Web Consortium](https://www.w3.org/).
 * <https://www.w3.org/copyright/software-license-2023/>
 * 
 * The full text of the license for the original W3C PNG specification is
 * provided below:
 * 
 * > ## Software and Document license - 2023 version
 * >
 * > This work is being provided by the copyright holders under the following
 * > license.
 * >
 * > ### License
 * >
 * > By obtaining and/or copying this work, you (the licensee) agree that you
 * > have read, understood, and will comply with the following terms and
 * > conditions.
 * >
 * > Permission to copy, modify, and distribute this work, with or without
 * > modification, for any purpose and without fee or royalty is hereby granted,
 * > provided that you include the following on ALL copies of the work or
 * > portions thereof, including modifications:
 * >
 * > * The full text of this NOTICE in a location viewable to users of the
 * >   redistributed or derivative work.
 * > * Any pre-existing intellectual property disclaimers, notices, or terms and
 * >   conditions. If none exist, the [W3C software and document short
 * >   notice](https://www.w3.org/Consortium/Legal/2023/copyright-software-short-notice.html)
 * >   should be included.
 * > * Notice of any changes or modifications, through a copyright statement on
 * >   the new code or document such as "This software or document includes
 * >   material copied from or derived from [title and URI of the W3C document].
 * >   Copyright © [$year-of-document] [World Wide Web
 * >   Consortium](https://www.w3.org/).
 * >   <https://www.w3.org/copyright/software-license-2023/>"
 * >
 * > ### Disclaimers
 * >
 * > THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS
 * > OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES
 * > OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF
 * > THE SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS,
 * > COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
 * >
 * > COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR
 * > CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT.
 * >
 * > The name and trademarks of copyright holders may NOT be used in advertising
 * > or publicity pertaining to the work without specific, written prior
 * > permission. Title to copyright in this work will at all times remain with
 * > copyright holders.
 * 
 * ---
 * 
 * Test files for APNG can be found at the following locations:
 * 
 *   * <https://philip.html5.org/tests/apng/tests.html>
 *   * <http://littlesvr.ca/apng/>
 */

class png_t : public kaitai::kstruct {

public:
    class adobe_fireworks_chunk_t;
    class animation_control_chunk_t;
    class atch_chunk_t;
    class bkgd_chunk_t;
    class bkgd_greyscale_t;
    class bkgd_indexed_t;
    class bkgd_truecolor_t;
    class chrm_chromaticity_t;
    class chrm_chunk_t;
    class chunk_t;
    class cicp_chunk_t;
    class clli_chunk_t;
    class compressed_text_t;
    class compressed_text_chunk_t;
    class evernote_skmf_chunk_t;
    class evernote_skrf_chunk_t;
    class exif_chunk_t;
    class frame_control_chunk_t;
    class frame_data_chunk_t;
    class gama_chunk_t;
    class hist_chunk_t;
    class iccp_chunk_t;
    class ihdr_chunk_t;
    class international_text_t;
    class international_text_chunk_t;
    class mdcv_chromaticity_t;
    class mdcv_chunk_t;
    class phys_chunk_t;
    class plte_chunk_t;
    class rgb_t;
    class sbit_chunk_t;
    class sbit_greyscale_t;
    class sbit_truecolor_t;
    class splt_chunk_t;
    class splt_entry_t;
    class srgb_chunk_t;
    class text_chunk_t;
    class time_chunk_t;
    class trns_chunk_t;
    class trns_greyscale_color_t;
    class trns_truecolor_color_t;

    enum blend_op_values_t {
        BLEND_OP_VALUES_SOURCE = 0,
        BLEND_OP_VALUES_OVER = 1
    };
    static bool _is_defined_blend_op_values_t(blend_op_values_t v);

private:
    static const std::set<blend_op_values_t> _values_blend_op_values_t;
    static std::set<blend_op_values_t> _build_values_blend_op_values_t();

public:

    enum color_type_t {
        COLOR_TYPE_GREYSCALE = 0,
        COLOR_TYPE_TRUECOLOR = 2,
        COLOR_TYPE_INDEXED = 3,
        COLOR_TYPE_GREYSCALE_ALPHA = 4,
        COLOR_TYPE_TRUECOLOR_ALPHA = 6
    };
    static bool _is_defined_color_type_t(color_type_t v);

private:
    static const std::set<color_type_t> _values_color_type_t;
    static std::set<color_type_t> _build_values_color_type_t();

public:

    enum compression_methods_t {
        COMPRESSION_METHODS_ZLIB = 0
    };
    static bool _is_defined_compression_methods_t(compression_methods_t v);

private:
    static const std::set<compression_methods_t> _values_compression_methods_t;
    static std::set<compression_methods_t> _build_values_compression_methods_t();

public:

    enum dispose_op_values_t {
        DISPOSE_OP_VALUES_NONE = 0,
        DISPOSE_OP_VALUES_BACKGROUND = 1,
        DISPOSE_OP_VALUES_PREVIOUS = 2
    };
    static bool _is_defined_dispose_op_values_t(dispose_op_values_t v);

private:
    static const std::set<dispose_op_values_t> _values_dispose_op_values_t;
    static std::set<dispose_op_values_t> _build_values_dispose_op_values_t();

public:

    enum filter_method_t {
        FILTER_METHOD_BASE = 0
    };
    static bool _is_defined_filter_method_t(filter_method_t v);

private:
    static const std::set<filter_method_t> _values_filter_method_t;
    static std::set<filter_method_t> _build_values_filter_method_t();

public:

    enum interlace_method_t {
        INTERLACE_METHOD_NONE = 0,
        INTERLACE_METHOD_ADAM7 = 1
    };
    static bool _is_defined_interlace_method_t(interlace_method_t v);

private:
    static const std::set<interlace_method_t> _values_interlace_method_t;
    static std::set<interlace_method_t> _build_values_interlace_method_t();

public:

    enum phys_unit_t {
        PHYS_UNIT_UNKNOWN = 0,
        PHYS_UNIT_METER = 1
    };
    static bool _is_defined_phys_unit_t(phys_unit_t v);

private:
    static const std::set<phys_unit_t> _values_phys_unit_t;
    static std::set<phys_unit_t> _build_values_phys_unit_t();

public:

    png_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, png_t* p__root = 0);

private:
    void _read();
    void _clean_up();

public:
    ~png_t();

    /**
     * \sa https://stackoverflow.com/questions/4242402/the-fireworks-png-format-any-insight-any-libs/51683285#51683285 Source
     */

    class adobe_fireworks_chunk_t : public kaitai::kstruct {

    public:

        adobe_fireworks_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~adobe_fireworks_chunk_t();

    private:
        std::string m_preview_data;
        png_t* m__root;
        png_t::chunk_t* m__parent;
        std::string m__raw_preview_data;

    public:
        std::string preview_data() const { return m_preview_data; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
        std::string _raw_preview_data() const { return m__raw_preview_data; }
    };

    /**
     * \sa https://www.w3.org/TR/png/#acTL-chunk Source
     */

    class animation_control_chunk_t : public kaitai::kstruct {

    public:

        animation_control_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~animation_control_chunk_t();

    private:
        uint32_t m_num_frames;
        uint32_t m_num_plays;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:

        /**
         * Number of frames, must be equal to the number of `fcTL` chunks (i.e.
         * `frame_control_chunk` objects)
         */
        uint32_t num_frames() const { return m_num_frames; }

        /**
         * Number of times to loop, 0 indicates infinite looping.
         */
        uint32_t num_plays() const { return m_num_plays; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://github.com/skeeto/scratch/tree/58470254f4a95cdf7a53888e405c851c21eb2cae/pngattach Source
     * \sa https://nullprogram.com/blog/2021/12/31/ A new protocol and tool for PNG file attachments
     */

    class atch_chunk_t : public kaitai::kstruct {

    public:

        enum compression_attach_methods_t {
            COMPRESSION_ATTACH_METHODS_NONE = 0,
            COMPRESSION_ATTACH_METHODS_ZLIB = 1
        };
        static bool _is_defined_compression_attach_methods_t(compression_attach_methods_t v);

    private:
        static const std::set<compression_attach_methods_t> _values_compression_attach_methods_t;
        static std::set<compression_attach_methods_t> _build_values_compression_attach_methods_t();

    public:

        atch_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~atch_chunk_t();

    private:
        bool f_data;
        std::string m_data;

    public:
        std::string data();

    private:
        std::string m_file_name;
        compression_attach_methods_t m_compression;
        std::string m_data_plain;
        bool n_data_plain;

    public:
        bool _is_null_data_plain() { data_plain(); return n_data_plain; };

    private:
        std::string m_data_zlib;
        bool n_data_zlib;

    public:
        bool _is_null_data_zlib() { data_zlib(); return n_data_zlib; };

    private:
        png_t* m__root;
        png_t::chunk_t* m__parent;
        std::string m__raw_data_zlib;
        bool n__raw_data_zlib;

    public:
        bool _is_null__raw_data_zlib() { _raw_data_zlib(); return n__raw_data_zlib; };

    private:

    public:

        /**
         * From the [official
         * specification](https://github.com/skeeto/scratch/tree/58470254f4a95cdf7a53888e405c851c21eb2cae/pngattach#atch-chunk-specification):
         * 
         * > The name can be any length that fits in the chunk, and should be
         * > encoded with UTF-8. It's up to each implementation to determine how
         * > to appropriately interpret the bytestring for the local system.
         * 
         * > The name must be at least one byte long, not counting the null
         * > terminator. It cannot begin with a period (`0x2e`), nor contain
         * > control bytes (anything less than `0x20`), nor slash (`0x2f`), nor
         * > backslash (`0x5c`), i.e. no directory hierarchies.
         * 
         * As of Kaitai Struct 0.11, we cannot easily check whether a string
         * contains certain characters, so we only enforce that the file name is
         * not empty and that it doesn't start with a period.
         */
        std::string file_name() const { return m_file_name; }
        compression_attach_methods_t compression() const { return m_compression; }
        std::string data_plain() const { return m_data_plain; }
        std::string data_zlib() const { return m_data_zlib; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
        std::string _raw_data_zlib() const { return m__raw_data_zlib; }
    };

    /**
     * Background chunk stores default background color to display this
     * image against. Contents depend on `color_type` of the image.
     * \sa https://www.w3.org/TR/png/#11bKGD Source
     */

    class bkgd_chunk_t : public kaitai::kstruct {

    public:

        bkgd_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~bkgd_chunk_t();

    private:
        kaitai::kstruct* m_bkgd;
        bool n_bkgd;

    public:
        bool _is_null_bkgd() { bkgd(); return n_bkgd; };

    private:
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:
        kaitai::kstruct* bkgd() const { return m_bkgd; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * Background chunk for greyscale images.
     */

    class bkgd_greyscale_t : public kaitai::kstruct {

    public:

        bkgd_greyscale_t(kaitai::kstream* p__io, png_t::bkgd_chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~bkgd_greyscale_t();

    private:
        uint16_t m_value;
        png_t* m__root;
        png_t::bkgd_chunk_t* m__parent;

    public:
        uint16_t value() const { return m_value; }
        png_t* _root() const { return m__root; }
        png_t::bkgd_chunk_t* _parent() const { return m__parent; }
    };

    /**
     * Background chunk for images with indexed palette.
     */

    class bkgd_indexed_t : public kaitai::kstruct {

    public:

        bkgd_indexed_t(kaitai::kstream* p__io, png_t::bkgd_chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~bkgd_indexed_t();

    private:
        uint8_t m_palette_index;
        png_t* m__root;
        png_t::bkgd_chunk_t* m__parent;

    public:
        uint8_t palette_index() const { return m_palette_index; }
        png_t* _root() const { return m__root; }
        png_t::bkgd_chunk_t* _parent() const { return m__parent; }
    };

    /**
     * Background chunk for truecolor images.
     */

    class bkgd_truecolor_t : public kaitai::kstruct {

    public:

        bkgd_truecolor_t(kaitai::kstream* p__io, png_t::bkgd_chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~bkgd_truecolor_t();

    private:
        uint16_t m_red;
        uint16_t m_green;
        uint16_t m_blue;
        png_t* m__root;
        png_t::bkgd_chunk_t* m__parent;

    public:
        uint16_t red() const { return m_red; }
        uint16_t green() const { return m_green; }
        uint16_t blue() const { return m_blue; }
        png_t* _root() const { return m__root; }
        png_t::bkgd_chunk_t* _parent() const { return m__parent; }
    };

    class chrm_chromaticity_t : public kaitai::kstruct {

    public:

        chrm_chromaticity_t(kaitai::kstream* p__io, png_t::chrm_chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~chrm_chromaticity_t();

    private:
        bool f_x;
        double m_x;

    public:
        double x();

    private:
        bool f_y;
        double m_y;

    public:
        double y();

    private:
        uint32_t m_x_int;
        uint32_t m_y_int;
        png_t* m__root;
        png_t::chrm_chunk_t* m__parent;

    public:
        uint32_t x_int() const { return m_x_int; }
        uint32_t y_int() const { return m_y_int; }
        png_t* _root() const { return m__root; }
        png_t::chrm_chunk_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://www.w3.org/TR/png/#11cHRM Source
     */

    class chrm_chunk_t : public kaitai::kstruct {

    public:

        chrm_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~chrm_chunk_t();

    private:
        chrm_chromaticity_t* m_white_point;
        chrm_chromaticity_t* m_red;
        chrm_chromaticity_t* m_green;
        chrm_chromaticity_t* m_blue;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:
        chrm_chromaticity_t* white_point() const { return m_white_point; }
        chrm_chromaticity_t* red() const { return m_red; }
        chrm_chromaticity_t* green() const { return m_green; }
        chrm_chromaticity_t* blue() const { return m_blue; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    class chunk_t : public kaitai::kstruct {

    public:

        chunk_t(kaitai::kstream* p__io, png_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~chunk_t();

    private:
        bool f_is_ancillary;
        bool m_is_ancillary;

    public:

        /**
         * false = critical chunk, true = ancillary chunk
         */
        bool is_ancillary();

    private:
        bool f_is_private;
        bool m_is_private;

    public:

        /**
         * false = public chunk (defined by the W3C), true = private chunk (can
         * be defined by anyone)
         */
        bool is_private();

    private:
        bool f_is_safe_to_copy;
        bool m_is_safe_to_copy;

    public:

        /**
         * Defines whether the chunk may be copied if the image data (i.e.
         * pixels) is modified. This tells PNG editors how to handle unknown
         * chunks - see section [14.2 Behavior of PNG
         * editors](https://www.w3.org/TR/2025/REC-png-3-20250624/#14Ordering) in
         * the official specification.
         */
        bool is_safe_to_copy();

    private:
        bool f_reserved_bit;
        bool m_reserved_bit;

    public:

        /**
         * Should be `false`, i.e. all chunk types should have uppercase third
         * letters (the lowercase third letter is reserved for possible future
         * extensions to the PNG standard)
         */
        bool reserved_bit();

    private:
        bool f_type;
        std::string m_type;

    public:
        std::string type();

    private:
        uint32_t m_len;
        std::string m_type_raw;
        kaitai::kstruct* m_body;
        bool n_body;

    public:
        bool _is_null_body() { body(); return n_body; };

    private:
        uint32_t m_crc;
        png_t* m__root;
        png_t* m__parent;
        std::string m__raw_body;
        kaitai::kstream* m__io__raw_body;

    public:
        uint32_t len() const { return m_len; }

        /**
         * Each byte of a chunk type is restricted to the hexadecimal values
         * 0x41..0x5a and 0x61..0x7a, i.e. uppercase and lowercase ASCII letters
         * (`A-Z` and `a-z`).
         * \sa https://www.w3.org/TR/2025/REC-png-3-20250624/#table51 Source
         */
        std::string type_raw() const { return m_type_raw; }
        kaitai::kstruct* body() const { return m_body; }
        uint32_t crc() const { return m_crc; }
        png_t* _root() const { return m__root; }
        png_t* _parent() const { return m__parent; }
        std::string _raw_body() const { return m__raw_body; }
        kaitai::kstream* _io__raw_body() const { return m__io__raw_body; }
    };

    /**
     * \sa https://www.w3.org/TR/png/#cICP-chunk Source
     * \sa https://w3c.github.io/png/Implementation_Report_3e/#cicp Source
     */

    class cicp_chunk_t : public kaitai::kstruct {

    public:

        cicp_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~cicp_chunk_t();

    private:
        uint8_t m_color_primaries;
        uint8_t m_transfer_function;
        uint8_t m_matrix_coefficients;
        uint8_t m_video_full_range_flag;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:

        /**
         * values above 22 are reserved, see
         * <https://github.com/pnggroup/pngcheck/blob/bd33ad6490269df07cac81e5305f4ebf56c2b637/pngcheck.c#L3322-L3325>
         */
        uint8_t color_primaries() const { return m_color_primaries; }

        /**
         * values above 18 are reserved, see
         * <https://github.com/pnggroup/pngcheck/blob/bd33ad6490269df07cac81e5305f4ebf56c2b637/pngcheck.c#L3326-L3329>
         */
        uint8_t transfer_function() const { return m_transfer_function; }

        /**
         * From the [official
         * specification](https://www.w3.org/TR/2025/REC-png-3-20250624/#cICP-chunk):
         * 
         * > RGB is currently the only supported color model in PNG, and as such
         * > `Matrix Coefficients` shall be set to `0`.
         */
        uint8_t matrix_coefficients() const { return m_matrix_coefficients; }

        /**
         * From the [official
         * specification](https://www.w3.org/TR/2025/REC-png-3-20250624/#cICP-chunk):
         * 
         * > If `Video Full Range Flag` value is `1`, then the image is a
         * > full-range image. Typically, images in the RGB color representation
         * > are stored in the full-range signal quantization, therefore the vast
         * > majority of computer graphics and web images, including those used
         * > in traditional PNG workflows, are full-range images.
         * 
         * > If `Video Full Range Flag` value is `0`, then the image is a
         * > narrow-range image.
         */
        uint8_t video_full_range_flag() const { return m_video_full_range_flag; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://www.w3.org/TR/png/#cLLI-chunk Source
     * \sa https://w3c.github.io/png/Implementation_Report_3e/#light Source
     */

    class clli_chunk_t : public kaitai::kstruct {

    public:

        clli_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~clli_chunk_t();

    private:
        bool f_max_content_light_level;
        double m_max_content_light_level;

    public:

        /**
         * Maximum Content Light Level (MaxCLL), in cd/m^2
         */
        double max_content_light_level();

    private:
        bool f_max_frame_average_light_level;
        double m_max_frame_average_light_level;

    public:

        /**
         * Maximum Frame Average Light Level (MaxFALL), in cd/m^2
         */
        double max_frame_average_light_level();

    private:
        uint32_t m_max_content_light_level_int;
        uint32_t m_max_frame_average_light_level_int;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:
        uint32_t max_content_light_level_int() const { return m_max_content_light_level_int; }
        uint32_t max_frame_average_light_level_int() const { return m_max_frame_average_light_level_int; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    class compressed_text_t : public kaitai::kstruct {

    public:

        compressed_text_t(kaitai::kstream* p__io, png_t::compressed_text_chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~compressed_text_t();

    private:
        std::string m_value;
        png_t* m__root;
        png_t::compressed_text_chunk_t* m__parent;

    public:

        /**
         * Text string (the "value" of this key-value pair).
         * 
         * Although it is not null-terminated (unlike the keyword), it must not
         * contain a zero byte (U+0000 NULL character). A newline should be
         * represented by a single U+000A LINE FEED (LF) character (aka `\n`).
         * The remaining control characters (U+0001..U+0009, U+000B..0+001F,
         * U+007F..U+009F) are discouraged.
         */
        std::string value() const { return m_value; }
        png_t* _root() const { return m__root; }
        png_t::compressed_text_chunk_t* _parent() const { return m__parent; }
    };

    /**
     * Compressed textual data (`zTXt`) chunk effectively allows you to store
     * key-value string pairs in the PNG container, compressing the "value" part
     * (which can be quite lengthy) with zlib compression.
     * 
     * The `zTXt` and `tEXt` chunks are semantically equivalent, but the `zTXt`
     * chunk is recommended for storing large blocks of text.
     * \sa https://www.w3.org/TR/png/#11zTXt Source
     */

    class compressed_text_chunk_t : public kaitai::kstruct {

    public:

        compressed_text_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~compressed_text_chunk_t();

    private:
        std::string m_keyword;
        compression_methods_t m_compression_method;
        compressed_text_t* m_text;
        png_t* m__root;
        png_t::chunk_t* m__parent;
        std::string m__raw_text;
        kaitai::kstream* m__io__raw_text;
        std::string m__raw__raw_text;

    public:

        /**
         * Indicates the type of information represented by the text string.
         * 
         * Keywords must consist exclusively of printable ISO-8859-1 (Latin-1)
         * characters and spaces; that is, only code points 0x20-0x7E and
         * 0xA1-0xFF are allowed. To reduce the chances for human misreading of a
         * keyword, leading spaces, trailing spaces, and consecutive spaces are
         * not permitted.
         * \sa https://www.w3.org/TR/2025/REC-png-3-20250624/#11keywords Source
         */
        std::string keyword() const { return m_keyword; }
        compression_methods_t compression_method() const { return m_compression_method; }
        compressed_text_t* text() const { return m_text; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
        std::string _raw_text() const { return m__raw_text; }
        kaitai::kstream* _io__raw_text() const { return m__io__raw_text; }
        std::string _raw__raw_text() const { return m__raw__raw_text; }
    };

    /**
     * \sa https://web.archive.org/web/20210302212148/https://discussion.evernote.com/forums/topic/88532-how-to-extract-annotation-information-from-annotated-evernoteskitch-images/#comment-451501 Source
     */

    class evernote_skmf_chunk_t : public kaitai::kstruct {

    public:

        evernote_skmf_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~evernote_skmf_chunk_t();

    private:
        std::string m_json;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:

        /**
         * JSON document with information about editable annotations (text,
         * lines, paths, etc.) in Evernote/Skitch.
         * 
         * It refers to the original image stored in the `skRf` chunk (which
         * usually follows immediately after `skMf`) via the
         * `.children[0].children[0].uri` JSON property. This has the format
         * `"skitch+uuid:///$UUID"`, where `$UUID` is a random UUIDv4 value that
         * matches the `uuid` field in `evernote_skrf_chunk` (i.e. in the first
         * 16 bytes of the `skRf` chunk).
         */
        std::string json() const { return m_json; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://web.archive.org/web/20210302212148/https://discussion.evernote.com/forums/topic/88532-how-to-extract-annotation-information-from-annotated-evernoteskitch-images/#comment-451501 Source
     */

    class evernote_skrf_chunk_t : public kaitai::kstruct {

    public:

        evernote_skrf_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~evernote_skrf_chunk_t();

    private:
        std::string m_uuid;
        std::string m_orig_img;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:

        /**
         * Random UUIDv4 value used to identify the image. It is referenced by
         * the `skMf` chunk - see the documentation for the `json` field in
         * `evernote_skmf_chunk`.
         */
        std::string uuid() const { return m_uuid; }

        /**
         * The original source image without annotations. It's usually a PNG
         * image as well, but it can also be a JPEG or possibly other formats.
         */
        std::string orig_img() const { return m_orig_img; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * Exchangeable Image File (Exif) Profile (`eXIf`) chunk.
     * 
     * Only one `eXIf` chunk is allowed in a PNG datastream.
     * 
     * The `eXIf` chunk contains metadata concerning the original image data. If
     * the image has been edited subsequent to creation of the Exif profile, this
     * data might no longer apply to the PNG image data.
     * \sa https://www.w3.org/TR/png/#eXIf Source
     */

    class exif_chunk_t : public kaitai::kstruct {

    public:

        exif_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~exif_chunk_t();

    private:
        exif_t* m_exif;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:
        exif_t* exif() const { return m_exif; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://www.w3.org/TR/png/#fcTL-chunk Source
     */

    class frame_control_chunk_t : public kaitai::kstruct {

    public:

        frame_control_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~frame_control_chunk_t();

    private:
        bool f_delay;
        double m_delay;

    public:

        /**
         * Time to display this frame, in seconds
         */
        double delay();

    private:
        uint32_t m_sequence_number;
        uint32_t m_width;
        uint32_t m_height;
        uint32_t m_x_offset;
        uint32_t m_y_offset;
        uint16_t m_delay_num;
        uint16_t m_delay_den;
        dispose_op_values_t m_dispose_op;
        blend_op_values_t m_blend_op;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:

        /**
         * Sequence number of the animation chunk, starting from 0.
         * 
         * The `fcTL` and `fdAT` chunks have a 4-byte sequence number. Both chunk
         * types share the sequence. The purpose of this number is to detect (and
         * optionally correct) sequence errors in an Animated PNG, since the PNG
         * specification does not impose ordering restrictions on ancillary
         * chunks (which means that a PNG editor is technically allowed to
         * reorder them arbitrarily, see [14.2 Behavior of PNG
         * editors](https://www.w3.org/TR/png/#14Ordering) in the spec).
         * 
         * The first `fcTL` chunk must contain sequence number 0, and the
         * sequence numbers in the remaining `fcTL` and `fdAT` chunks must be in
         * ascending order, with no gaps or duplicates.
         */
        uint32_t sequence_number() const { return m_sequence_number; }

        /**
         * Width of the following frame
         */
        uint32_t width() const { return m_width; }

        /**
         * Height of the following frame
         */
        uint32_t height() const { return m_height; }

        /**
         * X position at which to render the following frame
         */
        uint32_t x_offset() const { return m_x_offset; }

        /**
         * Y position at which to render the following frame
         */
        uint32_t y_offset() const { return m_y_offset; }

        /**
         * Frame delay fraction numerator
         */
        uint16_t delay_num() const { return m_delay_num; }

        /**
         * Frame delay fraction denominator
         */
        uint16_t delay_den() const { return m_delay_den; }

        /**
         * Type of frame area disposal to be done after rendering this frame
         */
        dispose_op_values_t dispose_op() const { return m_dispose_op; }

        /**
         * Type of frame area rendering for this frame
         */
        blend_op_values_t blend_op() const { return m_blend_op; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://www.w3.org/TR/png/#fdAT-chunk Source
     */

    class frame_data_chunk_t : public kaitai::kstruct {

    public:

        frame_data_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~frame_data_chunk_t();

    private:
        uint32_t m_sequence_number;
        std::string m_frame_data;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:

        /**
         * Sequence number of the animation chunk, starting from 0.
         * 
         * The `fcTL` and `fdAT` chunks have a 4-byte sequence number. Both chunk
         * types share the sequence. The purpose of this number is to detect (and
         * optionally correct) sequence errors in an Animated PNG, since the PNG
         * specification does not impose ordering restrictions on ancillary
         * chunks (which means that a PNG editor is technically allowed to
         * reorder them arbitrarily, see [14.2 Behavior of PNG
         * editors](https://www.w3.org/TR/png/#14Ordering) in the spec).
         * 
         * The first `fcTL` chunk must contain sequence number 0, and the
         * sequence numbers in the remaining `fcTL` and `fdAT` chunks must be in
         * ascending order, with no gaps or duplicates.
         */
        uint32_t sequence_number() const { return m_sequence_number; }

        /**
         * Frame data for the frame. At least one `fdAT` chunk is required for
         * each frame, except for the first frame, if that frame is represented
         * by an `IDAT` chunk. The compressed datastream for each frame is the
         * concatenation of the contents of the data fields of all the `fdAT`
         * chunks within a frame.
         */
        std::string frame_data() const { return m_frame_data; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://www.w3.org/TR/png/#11gAMA Source
     */

    class gama_chunk_t : public kaitai::kstruct {

    public:

        gama_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~gama_chunk_t();

    private:
        bool f_gamma;
        double m_gamma;

    public:

        /**
         * Image gamma, typically 0.45455 = 1/2.2
         */
        double gamma();

    private:
        bool f_inv_gamma;
        double m_inv_gamma;

    public:

        /**
         * Inverse of the image gamma (1 / gamma), typically 2.2 (not considering
         * rounding)
         */
        double inv_gamma();

    private:
        uint32_t m_gamma_int;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:

        /**
         * Image gamma multiplied by 100000 (a gamma value of 1/2.2 is stored as
         * 45455)
         */
        uint32_t gamma_int() const { return m_gamma_int; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * Image histogram (`hIST`) chunk gives the approximate usage frequency of
     * each color in the palette. A histogram chunk can appear only when a `PLTE`
     * chunk appears.
     * \sa https://www.w3.org/TR/png/#11hIST Source
     */

    class hist_chunk_t : public kaitai::kstruct {

    public:

        hist_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~hist_chunk_t();

    private:
        std::vector<uint16_t>* m_usage_freqs;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:

        /**
         * Usage frequencies of each color in the palette.
         * 
         * There must be exactly one entry for each entry in the `PLTE` chunk. Each
         * entry is proportional to the fraction of pixels in the image that have
         * that palette index; the exact scale factor is chosen by the encoder.
         * 
         * Histogram entries are approximate, with the exception that a zero
         * entry specifies that the corresponding palette entry is not used at
         * all in the image. A histogram entry must be nonzero if there are any
         * pixels of that color.
         */
        std::vector<uint16_t>* usage_freqs() const { return m_usage_freqs; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * Embedded ICC profile (`iCCP`) chunk.
     * 
     * If the `iCCP` chunk is present, the image samples conform to the color
     * space represented by the embedded ICC profile as defined by the
     * International Color Consortium.
     * 
     * This chunk is ignored unless it is the [highest-precedence color
     * chunk](https://www.w3.org/TR/png/#color-chunk-precendence) understood by
     * the decoder. Unless a `cICP` chunk exists, a PNG datastream should contain
     * at most one embedded profile, whether specified explicitly with an `iCCP`
     * or implicitly with an `sRGB` chunk.
     * 
     * It is recommended that the `sRGB` and `iCCP` chunks do not appear
     * simultaneously in a PNG datastream.
     * \sa https://www.w3.org/TR/png/#11iCCP Source
     */

    class iccp_chunk_t : public kaitai::kstruct {

    public:

        iccp_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~iccp_chunk_t();

    private:
        std::string m_profile_name;
        compression_methods_t m_compression_method;
        icc_4_t* m_profile;
        png_t* m__root;
        png_t::chunk_t* m__parent;
        std::string m__raw_profile;
        kaitai::kstream* m__io__raw_profile;
        std::string m__raw__raw_profile;

    public:

        /**
         * Any convenient name for referring to the profile. It is
         * case-sensitive.
         * 
         * Profile names must contain only printable ISO-8859-1 (Latin-1)
         * characters and spaces; that is, only code points 0x20-0x7E and
         * 0xA1-0xFF are allowed. Leading, trailing, and consecutive spaces are
         * not permitted.
         */
        std::string profile_name() const { return m_profile_name; }
        compression_methods_t compression_method() const { return m_compression_method; }

        /**
         * Embedded ICC profile.
         * 
         * The color space of the ICC profile must be:
         * 
         * * an RGB color space for color images (color types
         *   `color_type::truecolor` = 2, `color_type::indexed` = 3, and
         *   `color_type::truecolor_alpha` = 6), or
         * * a greyscale color space for greyscale images (color types
         *   `color_type::greyscale` = 0 and `color_type::greyscale_alpha` = 4).
         * 
         * Note that the imported `icc_4.ksy` spec currently in use here supports
         * only the ICC.1 v4 specification (as the name suggests), not ICC.1 v2.
         * This means that PNG files with an embedded v2 profile (for example
         * https://github.com/web-platform-tests/wpt/blob/495d9d7716298588ff49d6e701bf27c5134bde06/css/css-color/support/swap-990000-iCCP.png)
         * will fail to parse.
         * 
         * TODO: extend `icc_4.ksy` to support both v4 and v2 profiles, rename it
         * to `icc.ksy`, and use it here.
         */
        icc_4_t* profile() const { return m_profile; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
        std::string _raw_profile() const { return m__raw_profile; }
        kaitai::kstream* _io__raw_profile() const { return m__io__raw_profile; }
        std::string _raw__raw_profile() const { return m__raw__raw_profile; }
    };

    /**
     * \sa https://www.w3.org/TR/png/#11IHDR Source
     */

    class ihdr_chunk_t : public kaitai::kstruct {

    public:

        ihdr_chunk_t(kaitai::kstream* p__io, png_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~ihdr_chunk_t();

    private:
        uint32_t m_width;
        uint32_t m_height;
        uint8_t m_bit_depth;
        color_type_t m_color_type;
        compression_methods_t m_compression_method;
        filter_method_t m_filter_method;
        interlace_method_t m_interlace_method;
        png_t* m__root;
        png_t* m__parent;

    public:
        uint32_t width() const { return m_width; }
        uint32_t height() const { return m_height; }
        uint8_t bit_depth() const { return m_bit_depth; }
        color_type_t color_type() const { return m_color_type; }
        compression_methods_t compression_method() const { return m_compression_method; }
        filter_method_t filter_method() const { return m_filter_method; }
        interlace_method_t interlace_method() const { return m_interlace_method; }
        png_t* _root() const { return m__root; }
        png_t* _parent() const { return m__parent; }
    };

    class international_text_t : public kaitai::kstruct {

    public:

        international_text_t(kaitai::kstream* p__io, png_t::international_text_chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~international_text_t();

    private:
        std::string m_value;
        png_t* m__root;
        png_t::international_text_chunk_t* m__parent;

    public:

        /**
         * Text string (the "value" of this key-value pair), written in language
         * specified in `_parent.language_tag`.
         * 
         * Although it is not null-terminated (unlike other textual data in the
         * `iTXt` chunk), it must not contain a zero byte
         * (U+0000 NULL character). A newline should be represented by a single
         * U+000A LINE FEED (LF) character (aka `\n`). The remaining control
         * characters (U+0001..U+0009, U+000B..0+001F, U+007F..U+009F) are
         * discouraged.
         */
        std::string value() const { return m_value; }
        png_t* _root() const { return m__root; }
        png_t::international_text_chunk_t* _parent() const { return m__parent; }
    };

    /**
     * International textual data (`iTXt`) chunk effectively allows you to store
     * key-value string pairs in the PNG container.
     * 
     * The "key" part (`keyword`) is restricted to printable ISO-8859-1 (Latin-1)
     * characters and spaces. The translated keyword and the "value" part
     * (`text`) are stored in UTF-8 and thus can store text in any language -
     * this language can be indicated via the language tag (`language_tag`).
     * \sa https://www.w3.org/TR/png/#11iTXt Source
     */

    class international_text_chunk_t : public kaitai::kstruct {

    public:

        international_text_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~international_text_chunk_t();

    private:
        bool f_text;
        std::string m_text;

    public:

        /**
         * Text string (the "value" of this key-value pair), written in language
         * specified in `language_tag`.
         * 
         * Although it is not null-terminated (unlike other textual data in the
         * `iTXt` chunk), it must not contain a zero byte
         * (U+0000 NULL character). A newline should be represented by a single
         * U+000A LINE FEED (LF) character (aka `\n`). The remaining control
         * characters (U+0001..U+0009, U+000B..0+001F, U+007F..U+009F) are
         * discouraged.
         */
        std::string text();

    private:
        std::string m_keyword;
        uint8_t m_compression_flag;
        compression_methods_t m_compression_method;
        std::string m_language_tag;
        std::string m_translated_keyword;
        international_text_t* m_text_plain;
        bool n_text_plain;

    public:
        bool _is_null_text_plain() { text_plain(); return n_text_plain; };

    private:
        international_text_t* m_text_zlib;
        bool n_text_zlib;

    public:
        bool _is_null_text_zlib() { text_zlib(); return n_text_zlib; };

    private:
        png_t* m__root;
        png_t::chunk_t* m__parent;
        std::string m__raw_text_plain;
        bool n__raw_text_plain;

    public:
        bool _is_null__raw_text_plain() { _raw_text_plain(); return n__raw_text_plain; };

    private:
        kaitai::kstream* m__io__raw_text_plain;
        std::string m__raw_text_zlib;
        bool n__raw_text_zlib;

    public:
        bool _is_null__raw_text_zlib() { _raw_text_zlib(); return n__raw_text_zlib; };

    private:
        kaitai::kstream* m__io__raw_text_zlib;
        std::string m__raw__raw_text_zlib;
        bool n__raw__raw_text_zlib;

    public:
        bool _is_null__raw__raw_text_zlib() { _raw__raw_text_zlib(); return n__raw__raw_text_zlib; };

    private:

    public:

        /**
         * Indicates the type of information represented by the text string.
         * 
         * Keywords must consist exclusively of printable ISO-8859-1 (Latin-1)
         * characters and spaces; that is, only code points 0x20-0x7E and
         * 0xA1-0xFF are allowed. To reduce the chances for human misreading of a
         * keyword, leading spaces, trailing spaces, and consecutive spaces are
         * not permitted.
         * \sa https://www.w3.org/TR/2025/REC-png-3-20250624/#11keywords Source
         */
        std::string keyword() const { return m_keyword; }

        /**
         * 0 = text is uncompressed, 1 = text is compressed with a
         * method specified in `compression_method`.
         */
        uint8_t compression_flag() const { return m_compression_flag; }
        compression_methods_t compression_method() const { return m_compression_method; }

        /**
         * Human language used in the `translated_keyword` and `text` fields.
         * 
         * From the [official
         * specification](https://www.w3.org/TR/2025/REC-png-3-20250624/#11iTXt):
         * 
         * > The language tag is a well-formed language tag defined by [RFC 5646:
         * > BCP 47: Tags for Identifying
         * > Languages](https://www.rfc-editor.org/info/rfc5646/). Unlike the
         * > keyword, the language tag is case-insensitive. Subtags must appear
         * > in the [IANA language subtag
         * > registry](https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry).
         * > If the language tag is empty, the language is unspecified. Examples
         * > of language tags include: `en`, `en-GB`, `es-419`, `zh-Hans`,
         * > `zh-Hans-CN`, `tlh-Cyrl-AQ`, `ar-AE-u-nu-latn`, and `x-private`.
         */
        std::string language_tag() const { return m_language_tag; }

        /**
         * The keyword (`keyword`) translated into the language specified in
         * `language_tag`.
         * 
         * It must not contain a zero byte (U+0000 NULL character). Line breaks
         * should not appear. The remaining control characters (U+0001..U+0009,
         * U+000B..0+001F, U+007F..U+009F) are discouraged.
         */
        std::string translated_keyword() const { return m_translated_keyword; }
        international_text_t* text_plain() const { return m_text_plain; }
        international_text_t* text_zlib() const { return m_text_zlib; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
        std::string _raw_text_plain() const { return m__raw_text_plain; }
        kaitai::kstream* _io__raw_text_plain() const { return m__io__raw_text_plain; }
        std::string _raw_text_zlib() const { return m__raw_text_zlib; }
        kaitai::kstream* _io__raw_text_zlib() const { return m__io__raw_text_zlib; }
        std::string _raw__raw_text_zlib() const { return m__raw__raw_text_zlib; }
    };

    class mdcv_chromaticity_t : public kaitai::kstruct {

    public:

        mdcv_chromaticity_t(kaitai::kstream* p__io, png_t::mdcv_chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~mdcv_chromaticity_t();

    private:
        bool f_x;
        double m_x;

    public:
        double x();

    private:
        bool f_y;
        double m_y;

    public:
        double y();

    private:
        uint16_t m_x_int;
        uint16_t m_y_int;
        png_t* m__root;
        png_t::mdcv_chunk_t* m__parent;

    public:
        uint16_t x_int() const { return m_x_int; }
        uint16_t y_int() const { return m_y_int; }
        png_t* _root() const { return m__root; }
        png_t::mdcv_chunk_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://www.w3.org/TR/png/#mDCV-chunk Source
     * \sa https://w3c.github.io/png/Implementation_Report_3e/#mastering Source
     */

    class mdcv_chunk_t : public kaitai::kstruct {

    public:

        mdcv_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~mdcv_chunk_t();

    private:
        bool f_max_luminance;
        double m_max_luminance;

    public:

        /**
         * Maximum luminance in cd/m^2
         */
        double max_luminance();

    private:
        bool f_min_luminance;
        double m_min_luminance;

    public:

        /**
         * Minimum luminance in cd/m^2
         */
        double min_luminance();

    private:
        mdcv_chromaticity_t* m_red;
        mdcv_chromaticity_t* m_green;
        mdcv_chromaticity_t* m_blue;
        mdcv_chromaticity_t* m_white_point;
        uint32_t m_max_luminance_int;
        uint32_t m_min_luminance_int;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:
        mdcv_chromaticity_t* red() const { return m_red; }
        mdcv_chromaticity_t* green() const { return m_green; }
        mdcv_chromaticity_t* blue() const { return m_blue; }
        mdcv_chromaticity_t* white_point() const { return m_white_point; }
        uint32_t max_luminance_int() const { return m_max_luminance_int; }
        uint32_t min_luminance_int() const { return m_min_luminance_int; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * Physical pixel dimensions (`pHYs`) chunk specifies the intended physical
     * size of the pixels (in meters) or pixel aspect ratio for display of the
     * image.
     * \sa https://www.w3.org/TR/png/#11pHYs Source
     */

    class phys_chunk_t : public kaitai::kstruct {

    public:

        phys_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~phys_chunk_t();

    private:
        bool f_dots_per_inch_x;
        double m_dots_per_inch_x;
        bool n_dots_per_inch_x;

    public:
        bool _is_null_dots_per_inch_x() { dots_per_inch_x(); return n_dots_per_inch_x; };

    private:

    public:

        /**
         * Horizontal resolution (DPI)
         */
        double dots_per_inch_x();

    private:
        bool f_dots_per_inch_y;
        double m_dots_per_inch_y;
        bool n_dots_per_inch_y;

    public:
        bool _is_null_dots_per_inch_y() { dots_per_inch_y(); return n_dots_per_inch_y; };

    private:

    public:

        /**
         * Vertical resolution (DPI)
         */
        double dots_per_inch_y();

    private:
        uint32_t m_pixels_per_unit_x;
        uint32_t m_pixels_per_unit_y;
        phys_unit_t m_unit;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:

        /**
         * Number of pixels per physical unit (typically, 1 meter) by X
         * axis.
         */
        uint32_t pixels_per_unit_x() const { return m_pixels_per_unit_x; }

        /**
         * Number of pixels per physical unit (typically, 1 meter) by Y
         * axis.
         */
        uint32_t pixels_per_unit_y() const { return m_pixels_per_unit_y; }
        phys_unit_t unit() const { return m_unit; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://www.w3.org/TR/png/#11PLTE Source
     */

    class plte_chunk_t : public kaitai::kstruct {

    public:

        plte_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~plte_chunk_t();

    private:
        std::vector<rgb_t*>* m_entries;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:
        std::vector<rgb_t*>* entries() const { return m_entries; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    class rgb_t : public kaitai::kstruct {

    public:

        rgb_t(kaitai::kstream* p__io, png_t::plte_chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~rgb_t();

    private:
        uint8_t m_r;
        uint8_t m_g;
        uint8_t m_b;
        png_t* m__root;
        png_t::plte_chunk_t* m__parent;

    public:
        uint8_t r() const { return m_r; }
        uint8_t g() const { return m_g; }
        uint8_t b() const { return m_b; }
        png_t* _root() const { return m__root; }
        png_t::plte_chunk_t* _parent() const { return m__parent; }
    };

    /**
     * Significant bits (`sBIT`) chunk stores the original number of significant
     * bits of the sample values (which can be less than or equal to the sample
     * depth). This allows PNG decoders to recover the original data losslessly
     * even if the data had a sample depth not directly supported by PNG.
     * \sa https://www.w3.org/TR/png/#11sBIT Source
     */

    class sbit_chunk_t : public kaitai::kstruct {

    public:

        sbit_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~sbit_chunk_t();

    private:
        bool f_sample_depth;
        uint8_t m_sample_depth;

    public:
        uint8_t sample_depth();

    private:
        kaitai::kstruct* m_significant_bits;
        bool n_significant_bits;

    public:
        bool _is_null_significant_bits() { significant_bits(); return n_significant_bits; };

    private:
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:
        kaitai::kstruct* significant_bits() const { return m_significant_bits; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    class sbit_greyscale_t : public kaitai::kstruct {

    public:

        sbit_greyscale_t(bool p_has_alpha, kaitai::kstream* p__io, png_t::sbit_chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~sbit_greyscale_t();

    private:
        uint8_t m_grey;
        uint8_t m_alpha;
        bool n_alpha;

    public:
        bool _is_null_alpha() { alpha(); return n_alpha; };

    private:
        bool m_has_alpha;
        png_t* m__root;
        png_t::sbit_chunk_t* m__parent;

    public:
        uint8_t grey() const { return m_grey; }
        uint8_t alpha() const { return m_alpha; }
        bool has_alpha() const { return m_has_alpha; }
        png_t* _root() const { return m__root; }
        png_t::sbit_chunk_t* _parent() const { return m__parent; }
    };

    class sbit_truecolor_t : public kaitai::kstruct {

    public:

        sbit_truecolor_t(bool p_has_alpha, kaitai::kstream* p__io, png_t::sbit_chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~sbit_truecolor_t();

    private:
        uint8_t m_red;
        uint8_t m_green;
        uint8_t m_blue;
        uint8_t m_alpha;
        bool n_alpha;

    public:
        bool _is_null_alpha() { alpha(); return n_alpha; };

    private:
        bool m_has_alpha;
        png_t* m__root;
        png_t::sbit_chunk_t* m__parent;

    public:
        uint8_t red() const { return m_red; }
        uint8_t green() const { return m_green; }
        uint8_t blue() const { return m_blue; }
        uint8_t alpha() const { return m_alpha; }
        bool has_alpha() const { return m_has_alpha; }
        png_t* _root() const { return m__root; }
        png_t::sbit_chunk_t* _parent() const { return m__parent; }
    };

    /**
     * Suggested palette (`sPLT`) chunk.
     * 
     * Multiple `sPLT` chunks are permitted, but each must have a different
     * palette name.
     * \sa https://www.w3.org/TR/png/#11sPLT Source
     * \sa https://www.w3.org/TR/png/#12Suggested-palettes Source
     */

    class splt_chunk_t : public kaitai::kstruct {

    public:

        splt_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~splt_chunk_t();

    private:
        std::string m_palette_name;
        uint8_t m_sample_depth;
        std::vector<splt_entry_t*>* m_entries;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:

        /**
         * Any convenient name for referring to the palette. It is
         * case-sensitive. The palette name may aid the choice of the appropriate
         * suggested palette when more than one appears in a PNG datastream.
         * 
         * Palette names must contain only printable ISO-8859-1 (Latin-1)
         * characters and spaces; that is, only code points 0x20-0x7E and
         * 0xA1-0xFF are allowed. Leading, trailing, and consecutive spaces are
         * not permitted.
         */
        std::string palette_name() const { return m_palette_name; }
        uint8_t sample_depth() const { return m_sample_depth; }

        /**
         * There may be any number of entries. Entries must appear "in decreasing
         * order of frequency" (note: strictly speaking, I think the W3C
         * specification actually meant "non-increasing"). There is no
         * requirement that the entries all be used by the image, nor that they
         * all be different.
         * 
         * The color samples are not premultiplied by alpha, nor are they
         * precomposited against any background.
         * 
         * Entries in `sPLT` use the same gamma value and chromaticity values as
         * the PNG image, but may fall outside the range of values used in the
         * color space of the PNG image; for example, in a greyscale PNG image,
         * each `sPLT` entry would typically have equal red, green, and blue
         * values, but this is not required. Similarly, `sPLT` entries can have
         * non-opaque alpha values even when the PNG image does not use
         * transparency.
         */
        std::vector<splt_entry_t*>* entries() const { return m_entries; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    class splt_entry_t : public kaitai::kstruct {

    public:

        splt_entry_t(kaitai::kstream* p__io, png_t::splt_chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~splt_entry_t();

    private:
        uint16_t m_red;
        uint16_t m_green;
        uint16_t m_blue;
        uint16_t m_alpha;
        uint16_t m_freq;
        png_t* m__root;
        png_t::splt_chunk_t* m__parent;

    public:
        uint16_t red() const { return m_red; }
        uint16_t green() const { return m_green; }
        uint16_t blue() const { return m_blue; }

        /**
         * An alpha value of 0 means fully transparent. An alpha value of 255
         * (when `_parent.sample_depth` is 8) or 65535 (when
         * `_parent.sample_depth` is 16) means fully opaque.
         */
        uint16_t alpha() const { return m_alpha; }

        /**
         * Each frequency value is proportional to the fraction of the pixels in
         * the image for which that palette entry is the closest match in RGBA
         * space, before the image has been composited against any background.
         * 
         * The exact scale factor is chosen by the PNG encoder; it is recommended
         * that the resulting range of individual values reasonably fills the
         * range 0 to 65535.
         * 
         * Zero is a valid frequency meaning that the color is "least important"
         * or that it is rarely, if ever, used. When all the frequencies are
         * zero, they are meaningless, that is to say, nothing may be inferred
         * about the actual frequencies with which the colors appear in the PNG
         * image.
         */
        uint16_t freq() const { return m_freq; }
        png_t* _root() const { return m__root; }
        png_t::splt_chunk_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://www.w3.org/TR/png/#11sRGB Source
     */

    class srgb_chunk_t : public kaitai::kstruct {

    public:

        enum intent_t {
            INTENT_PERCEPTUAL = 0,
            INTENT_RELATIVE_COLORIMETRIC = 1,
            INTENT_SATURATION = 2,
            INTENT_ABSOLUTE_COLORIMETRIC = 3
        };
        static bool _is_defined_intent_t(intent_t v);

    private:
        static const std::set<intent_t> _values_intent_t;
        static std::set<intent_t> _build_values_intent_t();

    public:

        srgb_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~srgb_chunk_t();

    private:
        intent_t m_render_intent;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:
        intent_t render_intent() const { return m_render_intent; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * Textual data (`tEXt`) chunk effectively allows you to store key-value
     * string pairs in the PNG container.
     * 
     * Both the "key" (`keyword`) and "value" (`text`) parts are restricted to
     * printable ISO-8859-1 (Latin-1) characters and ASCII spaces, with the
     * exception that `text` can also contain newlines (U+000A LINE FEED (LF)
     * characters) and U+00A0 NON-BREAKING SPACE characters.
     * \sa https://www.w3.org/TR/png/#11tEXt Source
     */

    class text_chunk_t : public kaitai::kstruct {

    public:

        text_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~text_chunk_t();

    private:
        std::string m_keyword;
        std::string m_text;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:

        /**
         * Indicates the type of information represented by the text string.
         * 
         * Keywords must consist exclusively of printable ISO-8859-1 (Latin-1)
         * characters and spaces; that is, only code points 0x20-0x7E and
         * 0xA1-0xFF are allowed. To reduce the chances for human misreading of a
         * keyword, leading spaces, trailing spaces, and consecutive spaces are
         * not permitted.
         * \sa https://www.w3.org/TR/2025/REC-png-3-20250624/#11keywords Source
         */
        std::string keyword() const { return m_keyword; }

        /**
         * Text string (the "value" of this key-value pair).
         * 
         * Although it is not null-terminated (unlike the keyword), it must not
         * contain a zero byte (U+0000 NULL character). A newline should be
         * represented by a single U+000A LINE FEED (LF) character (aka `\n`).
         * The remaining control characters (U+0001..U+0009, U+000B..0+001F,
         * U+007F..U+009F) are discouraged.
         */
        std::string text() const { return m_text; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * Time chunk stores time stamp of last modification of this image,
     * up to 1 second precision in UTC timezone.
     * \sa https://www.w3.org/TR/png/#11tIME Source
     */

    class time_chunk_t : public kaitai::kstruct {

    public:

        time_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~time_chunk_t();

    private:
        uint16_t m_year;
        uint8_t m_month;
        uint8_t m_day;
        uint8_t m_hour;
        uint8_t m_minute;
        uint8_t m_second;
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:
        uint16_t year() const { return m_year; }
        uint8_t month() const { return m_month; }
        uint8_t day() const { return m_day; }
        uint8_t hour() const { return m_hour; }
        uint8_t minute() const { return m_minute; }
        uint8_t second() const { return m_second; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    /**
     * Transparency (`tRNS`) chunk specifies either alpha values that are
     * associated with palette entries (for indexed-color images) or a single
     * transparent color (for greyscale and truecolor images).
     * 
     * A `tRNS` chunk must not appear for color types
     * `color_type::greyscale_alpha` = 4 and `color_type::truecolor_alpha` = 6,
     * since a full alpha channel is already present in those cases.
     * \sa https://www.w3.org/TR/png/#11tRNS Source
     */

    class trns_chunk_t : public kaitai::kstruct {

    public:

        trns_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~trns_chunk_t();

    private:
        bool f_sample_mask;
        int32_t m_sample_mask;

    public:
        int32_t sample_mask();

    private:
        std::vector<uint8_t>* m_palette_alphas;
        bool n_palette_alphas;

    public:
        bool _is_null_palette_alphas() { palette_alphas(); return n_palette_alphas; };

    private:
        kaitai::kstruct* m_transparent_color;
        bool n_transparent_color;

    public:
        bool _is_null_transparent_color() { transparent_color(); return n_transparent_color; };

    private:
        png_t* m__root;
        png_t::chunk_t* m__parent;

    public:

        /**
         * Alpha values associated with palette entries in the `PLTE` chunk.
         * 
         * Each entry indicates that pixels of the corresponding palette index
         * shall be treated as having the specified alpha value. Alpha values
         * have the same interpretation as in an 8-bit full alpha channel: 0 is
         * fully transparent, 255 is fully opaque, regardless of image bit depth.
         * 
         * The `tRNS` chunk must not contain more alpha values than there are
         * palette entries, but it may contain fewer values than there are
         * palette entries. In this case, the alpha value for all remaining
         * palette entries is assumed to be 255. If all palette indices are
         * opaque, the `tRNS` chunk may be omitted.
         */
        std::vector<uint8_t>* palette_alphas() const { return m_palette_alphas; }

        /**
         * Pixels of the specified grey sample value or RGB sample values are
         * treated as transparent (equivalent to alpha value 0); all other pixels
         * are to be treated as fully opaque (alpha value `2^{bitdepth} - 1`).
         * 
         * If the image bit depth is less than 16, the least significant bits of
         * these sample values are used. Encoders should set the other bits to 0,
         * and decoders must mask the other bits to 0 before the value is used.
         * 
         * Note: in this Kaitai Struct implementation, the bitmask used to
         * implement this masking is stored in the value instance `sample_mask`.
         */
        kaitai::kstruct* transparent_color() const { return m_transparent_color; }
        png_t* _root() const { return m__root; }
        png_t::chunk_t* _parent() const { return m__parent; }
    };

    class trns_greyscale_color_t : public kaitai::kstruct {

    public:

        trns_greyscale_color_t(kaitai::kstream* p__io, png_t::trns_chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~trns_greyscale_color_t();

    private:
        bool f_grey;
        int32_t m_grey;

    public:
        int32_t grey();

    private:
        uint16_t m_grey_raw;
        png_t* m__root;
        png_t::trns_chunk_t* m__parent;

    public:
        uint16_t grey_raw() const { return m_grey_raw; }
        png_t* _root() const { return m__root; }
        png_t::trns_chunk_t* _parent() const { return m__parent; }
    };

    class trns_truecolor_color_t : public kaitai::kstruct {

    public:

        trns_truecolor_color_t(kaitai::kstream* p__io, png_t::trns_chunk_t* p__parent = 0, png_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~trns_truecolor_color_t();

    private:
        bool f_blue;
        int32_t m_blue;

    public:
        int32_t blue();

    private:
        bool f_green;
        int32_t m_green;

    public:
        int32_t green();

    private:
        bool f_red;
        int32_t m_red;

    public:
        int32_t red();

    private:
        uint16_t m_red_raw;
        uint16_t m_green_raw;
        uint16_t m_blue_raw;
        png_t* m__root;
        png_t::trns_chunk_t* m__parent;

    public:
        uint16_t red_raw() const { return m_red_raw; }
        uint16_t green_raw() const { return m_green_raw; }
        uint16_t blue_raw() const { return m_blue_raw; }
        png_t* _root() const { return m__root; }
        png_t::trns_chunk_t* _parent() const { return m__parent; }
    };

private:
    std::string m_magic;
    uint32_t m_ihdr_len;
    std::string m_ihdr_type;
    ihdr_chunk_t* m_ihdr;
    uint32_t m_ihdr_crc;
    std::vector<chunk_t*>* m_chunks;
    png_t* m__root;
    kaitai::kstruct* m__parent;

public:
    std::string magic() const { return m_magic; }
    uint32_t ihdr_len() const { return m_ihdr_len; }
    std::string ihdr_type() const { return m_ihdr_type; }
    ihdr_chunk_t* ihdr() const { return m_ihdr; }
    uint32_t ihdr_crc() const { return m_ihdr_crc; }
    std::vector<chunk_t*>* chunks() const { return m_chunks; }
    png_t* _root() const { return m__root; }
    kaitai::kstruct* _parent() const { return m__parent; }
};

#endif  // PNG_H_

png.cpp

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#include "png.h"
#include "kaitai/exceptions.h"
std::set<png_t::blend_op_values_t> png_t::_build_values_blend_op_values_t() {
    std::set<png_t::blend_op_values_t> _t;
    _t.insert(png_t::BLEND_OP_VALUES_SOURCE);
    _t.insert(png_t::BLEND_OP_VALUES_OVER);
    return _t;
}
const std::set<png_t::blend_op_values_t> png_t::_values_blend_op_values_t = png_t::_build_values_blend_op_values_t();
bool png_t::_is_defined_blend_op_values_t(png_t::blend_op_values_t v) {
    return png_t::_values_blend_op_values_t.find(v) != png_t::_values_blend_op_values_t.end();
}
std::set<png_t::color_type_t> png_t::_build_values_color_type_t() {
    std::set<png_t::color_type_t> _t;
    _t.insert(png_t::COLOR_TYPE_GREYSCALE);
    _t.insert(png_t::COLOR_TYPE_TRUECOLOR);
    _t.insert(png_t::COLOR_TYPE_INDEXED);
    _t.insert(png_t::COLOR_TYPE_GREYSCALE_ALPHA);
    _t.insert(png_t::COLOR_TYPE_TRUECOLOR_ALPHA);
    return _t;
}
const std::set<png_t::color_type_t> png_t::_values_color_type_t = png_t::_build_values_color_type_t();
bool png_t::_is_defined_color_type_t(png_t::color_type_t v) {
    return png_t::_values_color_type_t.find(v) != png_t::_values_color_type_t.end();
}
std::set<png_t::compression_methods_t> png_t::_build_values_compression_methods_t() {
    std::set<png_t::compression_methods_t> _t;
    _t.insert(png_t::COMPRESSION_METHODS_ZLIB);
    return _t;
}
const std::set<png_t::compression_methods_t> png_t::_values_compression_methods_t = png_t::_build_values_compression_methods_t();
bool png_t::_is_defined_compression_methods_t(png_t::compression_methods_t v) {
    return png_t::_values_compression_methods_t.find(v) != png_t::_values_compression_methods_t.end();
}
std::set<png_t::dispose_op_values_t> png_t::_build_values_dispose_op_values_t() {
    std::set<png_t::dispose_op_values_t> _t;
    _t.insert(png_t::DISPOSE_OP_VALUES_NONE);
    _t.insert(png_t::DISPOSE_OP_VALUES_BACKGROUND);
    _t.insert(png_t::DISPOSE_OP_VALUES_PREVIOUS);
    return _t;
}
const std::set<png_t::dispose_op_values_t> png_t::_values_dispose_op_values_t = png_t::_build_values_dispose_op_values_t();
bool png_t::_is_defined_dispose_op_values_t(png_t::dispose_op_values_t v) {
    return png_t::_values_dispose_op_values_t.find(v) != png_t::_values_dispose_op_values_t.end();
}
std::set<png_t::filter_method_t> png_t::_build_values_filter_method_t() {
    std::set<png_t::filter_method_t> _t;
    _t.insert(png_t::FILTER_METHOD_BASE);
    return _t;
}
const std::set<png_t::filter_method_t> png_t::_values_filter_method_t = png_t::_build_values_filter_method_t();
bool png_t::_is_defined_filter_method_t(png_t::filter_method_t v) {
    return png_t::_values_filter_method_t.find(v) != png_t::_values_filter_method_t.end();
}
std::set<png_t::interlace_method_t> png_t::_build_values_interlace_method_t() {
    std::set<png_t::interlace_method_t> _t;
    _t.insert(png_t::INTERLACE_METHOD_NONE);
    _t.insert(png_t::INTERLACE_METHOD_ADAM7);
    return _t;
}
const std::set<png_t::interlace_method_t> png_t::_values_interlace_method_t = png_t::_build_values_interlace_method_t();
bool png_t::_is_defined_interlace_method_t(png_t::interlace_method_t v) {
    return png_t::_values_interlace_method_t.find(v) != png_t::_values_interlace_method_t.end();
}
std::set<png_t::phys_unit_t> png_t::_build_values_phys_unit_t() {
    std::set<png_t::phys_unit_t> _t;
    _t.insert(png_t::PHYS_UNIT_UNKNOWN);
    _t.insert(png_t::PHYS_UNIT_METER);
    return _t;
}
const std::set<png_t::phys_unit_t> png_t::_values_phys_unit_t = png_t::_build_values_phys_unit_t();
bool png_t::_is_defined_phys_unit_t(png_t::phys_unit_t v) {
    return png_t::_values_phys_unit_t.find(v) != png_t::_values_phys_unit_t.end();
}

png_t::png_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root ? p__root : this;
    m_ihdr = 0;
    m_chunks = 0;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::_read() {
    m_magic = m__io->read_bytes(8);
    if (!(m_magic == std::string("\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8), m_magic, m__io, std::string("/seq/0"));
    }
    m_ihdr_len = m__io->read_u4be();
    if (!(m_ihdr_len == 13)) {
        throw kaitai::validation_not_equal_error<uint32_t>(13, m_ihdr_len, m__io, std::string("/seq/1"));
    }
    m_ihdr_type = m__io->read_bytes(4);
    if (!(m_ihdr_type == std::string("\x49\x48\x44\x52", 4))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\x49\x48\x44\x52", 4), m_ihdr_type, m__io, std::string("/seq/2"));
    }
    m_ihdr = new ihdr_chunk_t(m__io, this, m__root);
    m_ihdr_crc = m__io->read_u4be();
    m_chunks = new std::vector<chunk_t*>();
    {
        int i = 0;
        chunk_t* _;
        do {
            _ = new chunk_t(m__io, this, m__root);
            m_chunks->push_back(_);
            i++;
        } while (!( ((_->type() == std::string("IEND")) || (_io()->is_eof())) ));
    }
}

png_t::~png_t() {
    _clean_up();
}

void png_t::_clean_up() {
    if (m_ihdr) {
        delete m_ihdr; m_ihdr = 0;
    }
    if (m_chunks) {
        for (std::vector<chunk_t*>::iterator it = m_chunks->begin(); it != m_chunks->end(); ++it) {
            delete *it;
        }
        delete m_chunks; m_chunks = 0;
    }
}

png_t::adobe_fireworks_chunk_t::adobe_fireworks_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::adobe_fireworks_chunk_t::_read() {
    m__raw_preview_data = m__io->read_bytes_full();
    m_preview_data = kaitai::kstream::process_zlib(m__raw_preview_data);
}

png_t::adobe_fireworks_chunk_t::~adobe_fireworks_chunk_t() {
    _clean_up();
}

void png_t::adobe_fireworks_chunk_t::_clean_up() {
}

png_t::animation_control_chunk_t::animation_control_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::animation_control_chunk_t::_read() {
    m_num_frames = m__io->read_u4be();
    m_num_plays = m__io->read_u4be();
}

png_t::animation_control_chunk_t::~animation_control_chunk_t() {
    _clean_up();
}

void png_t::animation_control_chunk_t::_clean_up() {
}
std::set<png_t::atch_chunk_t::compression_attach_methods_t> png_t::atch_chunk_t::_build_values_compression_attach_methods_t() {
    std::set<png_t::atch_chunk_t::compression_attach_methods_t> _t;
    _t.insert(png_t::atch_chunk_t::COMPRESSION_ATTACH_METHODS_NONE);
    _t.insert(png_t::atch_chunk_t::COMPRESSION_ATTACH_METHODS_ZLIB);
    return _t;
}
const std::set<png_t::atch_chunk_t::compression_attach_methods_t> png_t::atch_chunk_t::_values_compression_attach_methods_t = png_t::atch_chunk_t::_build_values_compression_attach_methods_t();
bool png_t::atch_chunk_t::_is_defined_compression_attach_methods_t(png_t::atch_chunk_t::compression_attach_methods_t v) {
    return png_t::atch_chunk_t::_values_compression_attach_methods_t.find(v) != png_t::atch_chunk_t::_values_compression_attach_methods_t.end();
}

png_t::atch_chunk_t::atch_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    f_data = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::atch_chunk_t::_read() {
    m_file_name = kaitai::kstream::bytes_to_str(m__io->read_bytes_term(0, false, true, true), "UTF-8");
    {
        std::string _ = m_file_name;
        if (!( ((_.length() != 0) && (_.substr(0, 1 - 0) != std::string("."))) )) {
            throw kaitai::validation_expr_error<std::string>(m_file_name, m__io, std::string("/types/atch_chunk/seq/0"));
        }
    }
    m_compression = static_cast<png_t::atch_chunk_t::compression_attach_methods_t>(m__io->read_u1());
    if (!png_t::atch_chunk_t::_is_defined_compression_attach_methods_t(m_compression)) {
        throw kaitai::validation_not_in_enum_error<png_t::atch_chunk_t::compression_attach_methods_t>(m_compression, m__io, std::string("/types/atch_chunk/seq/1"));
    }
    n_data_plain = true;
    if (compression() == png_t::atch_chunk_t::COMPRESSION_ATTACH_METHODS_NONE) {
        n_data_plain = false;
        m_data_plain = m__io->read_bytes_full();
    }
    n_data_zlib = true;
    if (compression() == png_t::atch_chunk_t::COMPRESSION_ATTACH_METHODS_ZLIB) {
        n_data_zlib = false;
        m__raw_data_zlib = m__io->read_bytes_full();
        m_data_zlib = kaitai::kstream::process_zlib(m__raw_data_zlib);
    }
}

png_t::atch_chunk_t::~atch_chunk_t() {
    _clean_up();
}

void png_t::atch_chunk_t::_clean_up() {
    if (!n_data_plain) {
    }
    if (!n_data_zlib) {
    }
}

std::string png_t::atch_chunk_t::data() {
    if (f_data)
        return m_data;
    f_data = true;
    m_data = ((compression() == png_t::atch_chunk_t::COMPRESSION_ATTACH_METHODS_NONE) ? (data_plain()) : (data_zlib()));
    return m_data;
}

png_t::bkgd_chunk_t::bkgd_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::bkgd_chunk_t::_read() {
    n_bkgd = true;
    switch (_root()->ihdr()->color_type()) {
    case png_t::COLOR_TYPE_GREYSCALE: {
        n_bkgd = false;
        m_bkgd = new bkgd_greyscale_t(m__io, this, m__root);
        break;
    }
    case png_t::COLOR_TYPE_GREYSCALE_ALPHA: {
        n_bkgd = false;
        m_bkgd = new bkgd_greyscale_t(m__io, this, m__root);
        break;
    }
    case png_t::COLOR_TYPE_INDEXED: {
        n_bkgd = false;
        m_bkgd = new bkgd_indexed_t(m__io, this, m__root);
        break;
    }
    case png_t::COLOR_TYPE_TRUECOLOR: {
        n_bkgd = false;
        m_bkgd = new bkgd_truecolor_t(m__io, this, m__root);
        break;
    }
    case png_t::COLOR_TYPE_TRUECOLOR_ALPHA: {
        n_bkgd = false;
        m_bkgd = new bkgd_truecolor_t(m__io, this, m__root);
        break;
    }
    }
}

png_t::bkgd_chunk_t::~bkgd_chunk_t() {
    _clean_up();
}

void png_t::bkgd_chunk_t::_clean_up() {
    if (!n_bkgd) {
        if (m_bkgd) {
            delete m_bkgd; m_bkgd = 0;
        }
    }
}

png_t::bkgd_greyscale_t::bkgd_greyscale_t(kaitai::kstream* p__io, png_t::bkgd_chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::bkgd_greyscale_t::_read() {
    m_value = m__io->read_u2be();
}

png_t::bkgd_greyscale_t::~bkgd_greyscale_t() {
    _clean_up();
}

void png_t::bkgd_greyscale_t::_clean_up() {
}

png_t::bkgd_indexed_t::bkgd_indexed_t(kaitai::kstream* p__io, png_t::bkgd_chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::bkgd_indexed_t::_read() {
    m_palette_index = m__io->read_u1();
}

png_t::bkgd_indexed_t::~bkgd_indexed_t() {
    _clean_up();
}

void png_t::bkgd_indexed_t::_clean_up() {
}

png_t::bkgd_truecolor_t::bkgd_truecolor_t(kaitai::kstream* p__io, png_t::bkgd_chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::bkgd_truecolor_t::_read() {
    m_red = m__io->read_u2be();
    m_green = m__io->read_u2be();
    m_blue = m__io->read_u2be();
}

png_t::bkgd_truecolor_t::~bkgd_truecolor_t() {
    _clean_up();
}

void png_t::bkgd_truecolor_t::_clean_up() {
}

png_t::chrm_chromaticity_t::chrm_chromaticity_t(kaitai::kstream* p__io, png_t::chrm_chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    f_x = false;
    f_y = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::chrm_chromaticity_t::_read() {
    m_x_int = m__io->read_u4be();
    m_y_int = m__io->read_u4be();
}

png_t::chrm_chromaticity_t::~chrm_chromaticity_t() {
    _clean_up();
}

void png_t::chrm_chromaticity_t::_clean_up() {
}

double png_t::chrm_chromaticity_t::x() {
    if (f_x)
        return m_x;
    f_x = true;
    m_x = x_int() / 100000.0;
    return m_x;
}

double png_t::chrm_chromaticity_t::y() {
    if (f_y)
        return m_y;
    f_y = true;
    m_y = y_int() / 100000.0;
    return m_y;
}

png_t::chrm_chunk_t::chrm_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_white_point = 0;
    m_red = 0;
    m_green = 0;
    m_blue = 0;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::chrm_chunk_t::_read() {
    m_white_point = new chrm_chromaticity_t(m__io, this, m__root);
    m_red = new chrm_chromaticity_t(m__io, this, m__root);
    m_green = new chrm_chromaticity_t(m__io, this, m__root);
    m_blue = new chrm_chromaticity_t(m__io, this, m__root);
}

png_t::chrm_chunk_t::~chrm_chunk_t() {
    _clean_up();
}

void png_t::chrm_chunk_t::_clean_up() {
    if (m_white_point) {
        delete m_white_point; m_white_point = 0;
    }
    if (m_red) {
        delete m_red; m_red = 0;
    }
    if (m_green) {
        delete m_green; m_green = 0;
    }
    if (m_blue) {
        delete m_blue; m_blue = 0;
    }
}

png_t::chunk_t::chunk_t(kaitai::kstream* p__io, png_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m__io__raw_body = 0;
    f_is_ancillary = false;
    f_is_private = false;
    f_is_safe_to_copy = false;
    f_reserved_bit = false;
    f_type = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::chunk_t::_read() {
    m_len = m__io->read_u4be();
    m_type_raw = m__io->read_bytes(4);
    {
        std::string _ = m_type_raw;
        if (!( (( (( ((_.at(0) >= 65) && (_.at(0) <= 90)) ) || ( ((_.at(0) >= 97) && (_.at(0) <= 122)) )) ) && ( (( ((_.at(1) >= 65) && (_.at(1) <= 90)) ) || ( ((_.at(1) >= 97) && (_.at(1) <= 122)) )) ) && ( (( ((_.at(2) >= 65) && (_.at(2) <= 90)) ) || ( ((_.at(2) >= 97) && (_.at(2) <= 122)) )) ) && ( (( ((_.at(3) >= 65) && (_.at(3) <= 90)) ) || ( ((_.at(3) >= 97) && (_.at(3) <= 122)) )) )) )) {
            throw kaitai::validation_expr_error<std::string>(m_type_raw, m__io, std::string("/types/chunk/seq/1"));
        }
    }
    n_body = true;
    {
        std::string on = type();
        if (on == std::string("PLTE")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new plte_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("acTL")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new animation_control_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("atCh")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new atch_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("bKGD")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new bkgd_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("cHRM")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new chrm_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("cICP")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new cicp_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("cLLI")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new clli_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("eXIf")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new exif_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("fcTL")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new frame_control_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("fdAT")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new frame_data_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("gAMA")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new gama_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("hIST")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new hist_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("iCCP")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new iccp_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("iTXt")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new international_text_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("mDCV")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new mdcv_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("mkBS")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new adobe_fireworks_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("mkTS")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new adobe_fireworks_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("pHYs")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new phys_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("prVW")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new adobe_fireworks_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("sBIT")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new sbit_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("sPLT")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new splt_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("sRGB")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new srgb_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("skMf")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new evernote_skmf_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("skRf")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new evernote_skrf_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("tEXt")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new text_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("tIME")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new time_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("tRNS")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new trns_chunk_t(m__io__raw_body, this, m__root);
        }
        else if (on == std::string("zTXt")) {
            n_body = false;
            m__raw_body = m__io->read_bytes(len());
            m__io__raw_body = new kaitai::kstream(m__raw_body);
            m_body = new compressed_text_chunk_t(m__io__raw_body, this, m__root);
        }
        else {
            m__raw_body = m__io->read_bytes(len());
        }
    }
    m_crc = m__io->read_u4be();
}

png_t::chunk_t::~chunk_t() {
    _clean_up();
}

void png_t::chunk_t::_clean_up() {
    if (!n_body) {
        if (m__io__raw_body) {
            delete m__io__raw_body; m__io__raw_body = 0;
        }
        if (m_body) {
            delete m_body; m_body = 0;
        }
    }
}

bool png_t::chunk_t::is_ancillary() {
    if (f_is_ancillary)
        return m_is_ancillary;
    f_is_ancillary = true;
    m_is_ancillary = (type_raw().at(0) & 32) != 0;
    return m_is_ancillary;
}

bool png_t::chunk_t::is_private() {
    if (f_is_private)
        return m_is_private;
    f_is_private = true;
    m_is_private = (type_raw().at(1) & 32) != 0;
    return m_is_private;
}

bool png_t::chunk_t::is_safe_to_copy() {
    if (f_is_safe_to_copy)
        return m_is_safe_to_copy;
    f_is_safe_to_copy = true;
    m_is_safe_to_copy = (type_raw().at(3) & 32) != 0;
    return m_is_safe_to_copy;
}

bool png_t::chunk_t::reserved_bit() {
    if (f_reserved_bit)
        return m_reserved_bit;
    f_reserved_bit = true;
    m_reserved_bit = (type_raw().at(2) & 32) != 0;
    return m_reserved_bit;
}

std::string png_t::chunk_t::type() {
    if (f_type)
        return m_type;
    f_type = true;
    m_type = kaitai::kstream::bytes_to_str(type_raw(), "ASCII");
    return m_type;
}

png_t::cicp_chunk_t::cicp_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::cicp_chunk_t::_read() {
    m_color_primaries = m__io->read_u1();
    m_transfer_function = m__io->read_u1();
    m_matrix_coefficients = m__io->read_u1();
    if (!(m_matrix_coefficients == 0)) {
        throw kaitai::validation_not_equal_error<uint8_t>(0, m_matrix_coefficients, m__io, std::string("/types/cicp_chunk/seq/2"));
    }
    m_video_full_range_flag = m__io->read_u1();
    if (!( ((m_video_full_range_flag == 0) || (m_video_full_range_flag == 1)) )) {
        throw kaitai::validation_not_any_of_error<uint8_t>(m_video_full_range_flag, m__io, std::string("/types/cicp_chunk/seq/3"));
    }
}

png_t::cicp_chunk_t::~cicp_chunk_t() {
    _clean_up();
}

void png_t::cicp_chunk_t::_clean_up() {
}

png_t::clli_chunk_t::clli_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    f_max_content_light_level = false;
    f_max_frame_average_light_level = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::clli_chunk_t::_read() {
    m_max_content_light_level_int = m__io->read_u4be();
    m_max_frame_average_light_level_int = m__io->read_u4be();
}

png_t::clli_chunk_t::~clli_chunk_t() {
    _clean_up();
}

void png_t::clli_chunk_t::_clean_up() {
}

double png_t::clli_chunk_t::max_content_light_level() {
    if (f_max_content_light_level)
        return m_max_content_light_level;
    f_max_content_light_level = true;
    m_max_content_light_level = max_content_light_level_int() * 0.0001;
    return m_max_content_light_level;
}

double png_t::clli_chunk_t::max_frame_average_light_level() {
    if (f_max_frame_average_light_level)
        return m_max_frame_average_light_level;
    f_max_frame_average_light_level = true;
    m_max_frame_average_light_level = max_frame_average_light_level_int() * 0.0001;
    return m_max_frame_average_light_level;
}

png_t::compressed_text_t::compressed_text_t(kaitai::kstream* p__io, png_t::compressed_text_chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::compressed_text_t::_read() {
    m_value = kaitai::kstream::bytes_to_str(m__io->read_bytes_full(), "ISO-8859-1");
}

png_t::compressed_text_t::~compressed_text_t() {
    _clean_up();
}

void png_t::compressed_text_t::_clean_up() {
}

png_t::compressed_text_chunk_t::compressed_text_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_text = 0;
    m__io__raw_text = 0;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::compressed_text_chunk_t::_read() {
    m_keyword = kaitai::kstream::bytes_to_str(m__io->read_bytes_term(0, false, true, true), "ISO-8859-1");
    m_compression_method = static_cast<png_t::compression_methods_t>(m__io->read_u1());
    if (!(m_compression_method == png_t::COMPRESSION_METHODS_ZLIB)) {
        throw kaitai::validation_not_equal_error<png_t::compression_methods_t>(png_t::COMPRESSION_METHODS_ZLIB, m_compression_method, m__io, std::string("/types/compressed_text_chunk/seq/1"));
    }
    m__raw__raw_text = m__io->read_bytes_full();
    m__raw_text = kaitai::kstream::process_zlib(m__raw__raw_text);
    m__io__raw_text = new kaitai::kstream(m__raw_text);
    m_text = new compressed_text_t(m__io__raw_text, this, m__root);
}

png_t::compressed_text_chunk_t::~compressed_text_chunk_t() {
    _clean_up();
}

void png_t::compressed_text_chunk_t::_clean_up() {
    if (m__io__raw_text) {
        delete m__io__raw_text; m__io__raw_text = 0;
    }
    if (m_text) {
        delete m_text; m_text = 0;
    }
}

png_t::evernote_skmf_chunk_t::evernote_skmf_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::evernote_skmf_chunk_t::_read() {
    m_json = kaitai::kstream::bytes_to_str(m__io->read_bytes_full(), "UTF-8");
}

png_t::evernote_skmf_chunk_t::~evernote_skmf_chunk_t() {
    _clean_up();
}

void png_t::evernote_skmf_chunk_t::_clean_up() {
}

png_t::evernote_skrf_chunk_t::evernote_skrf_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::evernote_skrf_chunk_t::_read() {
    m_uuid = m__io->read_bytes(16);
    m_orig_img = m__io->read_bytes_full();
}

png_t::evernote_skrf_chunk_t::~evernote_skrf_chunk_t() {
    _clean_up();
}

void png_t::evernote_skrf_chunk_t::_clean_up() {
}

png_t::exif_chunk_t::exif_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_exif = 0;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::exif_chunk_t::_read() {
    m_exif = new exif_t(m__io);
}

png_t::exif_chunk_t::~exif_chunk_t() {
    _clean_up();
}

void png_t::exif_chunk_t::_clean_up() {
    if (m_exif) {
        delete m_exif; m_exif = 0;
    }
}

png_t::frame_control_chunk_t::frame_control_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    f_delay = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::frame_control_chunk_t::_read() {
    m_sequence_number = m__io->read_u4be();
    m_width = m__io->read_u4be();
    if (!(m_width >= 1)) {
        throw kaitai::validation_less_than_error<uint32_t>(1, m_width, m__io, std::string("/types/frame_control_chunk/seq/1"));
    }
    if (!(m_width <= _root()->ihdr()->width())) {
        throw kaitai::validation_greater_than_error<uint32_t>(_root()->ihdr()->width(), m_width, m__io, std::string("/types/frame_control_chunk/seq/1"));
    }
    m_height = m__io->read_u4be();
    if (!(m_height >= 1)) {
        throw kaitai::validation_less_than_error<uint32_t>(1, m_height, m__io, std::string("/types/frame_control_chunk/seq/2"));
    }
    if (!(m_height <= _root()->ihdr()->height())) {
        throw kaitai::validation_greater_than_error<uint32_t>(_root()->ihdr()->height(), m_height, m__io, std::string("/types/frame_control_chunk/seq/2"));
    }
    m_x_offset = m__io->read_u4be();
    if (!(m_x_offset <= _root()->ihdr()->width() - width())) {
        throw kaitai::validation_greater_than_error<uint32_t>(_root()->ihdr()->width() - width(), m_x_offset, m__io, std::string("/types/frame_control_chunk/seq/3"));
    }
    m_y_offset = m__io->read_u4be();
    if (!(m_y_offset <= _root()->ihdr()->height() - height())) {
        throw kaitai::validation_greater_than_error<uint32_t>(_root()->ihdr()->height() - height(), m_y_offset, m__io, std::string("/types/frame_control_chunk/seq/4"));
    }
    m_delay_num = m__io->read_u2be();
    m_delay_den = m__io->read_u2be();
    m_dispose_op = static_cast<png_t::dispose_op_values_t>(m__io->read_u1());
    if (!png_t::_is_defined_dispose_op_values_t(m_dispose_op)) {
        throw kaitai::validation_not_in_enum_error<png_t::dispose_op_values_t>(m_dispose_op, m__io, std::string("/types/frame_control_chunk/seq/7"));
    }
    m_blend_op = static_cast<png_t::blend_op_values_t>(m__io->read_u1());
    if (!png_t::_is_defined_blend_op_values_t(m_blend_op)) {
        throw kaitai::validation_not_in_enum_error<png_t::blend_op_values_t>(m_blend_op, m__io, std::string("/types/frame_control_chunk/seq/8"));
    }
}

png_t::frame_control_chunk_t::~frame_control_chunk_t() {
    _clean_up();
}

void png_t::frame_control_chunk_t::_clean_up() {
}

double png_t::frame_control_chunk_t::delay() {
    if (f_delay)
        return m_delay;
    f_delay = true;
    m_delay = delay_num() / ((delay_den() == 0) ? (100.0) : (delay_den()));
    return m_delay;
}

png_t::frame_data_chunk_t::frame_data_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::frame_data_chunk_t::_read() {
    m_sequence_number = m__io->read_u4be();
    m_frame_data = m__io->read_bytes_full();
}

png_t::frame_data_chunk_t::~frame_data_chunk_t() {
    _clean_up();
}

void png_t::frame_data_chunk_t::_clean_up() {
}

png_t::gama_chunk_t::gama_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    f_gamma = false;
    f_inv_gamma = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::gama_chunk_t::_read() {
    m_gamma_int = m__io->read_u4be();
    {
        uint32_t _ = m_gamma_int;
        if (!(_ != 0)) {
            throw kaitai::validation_expr_error<uint32_t>(m_gamma_int, m__io, std::string("/types/gama_chunk/seq/0"));
        }
    }
}

png_t::gama_chunk_t::~gama_chunk_t() {
    _clean_up();
}

void png_t::gama_chunk_t::_clean_up() {
}

double png_t::gama_chunk_t::gamma() {
    if (f_gamma)
        return m_gamma;
    f_gamma = true;
    m_gamma = gamma_int() / 100000.0;
    return m_gamma;
}

double png_t::gama_chunk_t::inv_gamma() {
    if (f_inv_gamma)
        return m_inv_gamma;
    f_inv_gamma = true;
    m_inv_gamma = 100000.0 / gamma_int();
    return m_inv_gamma;
}

png_t::hist_chunk_t::hist_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_usage_freqs = 0;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::hist_chunk_t::_read() {
    m_usage_freqs = new std::vector<uint16_t>();
    {
        int i = 0;
        while (!m__io->is_eof()) {
            m_usage_freqs->push_back(m__io->read_u2be());
            i++;
        }
    }
}

png_t::hist_chunk_t::~hist_chunk_t() {
    _clean_up();
}

void png_t::hist_chunk_t::_clean_up() {
    if (m_usage_freqs) {
        delete m_usage_freqs; m_usage_freqs = 0;
    }
}

png_t::iccp_chunk_t::iccp_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_profile = 0;
    m__io__raw_profile = 0;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::iccp_chunk_t::_read() {
    m_profile_name = kaitai::kstream::bytes_to_str(m__io->read_bytes_term(0, false, true, true), "ISO-8859-1");
    m_compression_method = static_cast<png_t::compression_methods_t>(m__io->read_u1());
    if (!(m_compression_method == png_t::COMPRESSION_METHODS_ZLIB)) {
        throw kaitai::validation_not_equal_error<png_t::compression_methods_t>(png_t::COMPRESSION_METHODS_ZLIB, m_compression_method, m__io, std::string("/types/iccp_chunk/seq/1"));
    }
    m__raw__raw_profile = m__io->read_bytes_full();
    m__raw_profile = kaitai::kstream::process_zlib(m__raw__raw_profile);
    m__io__raw_profile = new kaitai::kstream(m__raw_profile);
    m_profile = new icc_4_t(m__io__raw_profile);
}

png_t::iccp_chunk_t::~iccp_chunk_t() {
    _clean_up();
}

void png_t::iccp_chunk_t::_clean_up() {
    if (m__io__raw_profile) {
        delete m__io__raw_profile; m__io__raw_profile = 0;
    }
    if (m_profile) {
        delete m_profile; m_profile = 0;
    }
}

png_t::ihdr_chunk_t::ihdr_chunk_t(kaitai::kstream* p__io, png_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::ihdr_chunk_t::_read() {
    m_width = m__io->read_u4be();
    if (!(m_width >= 1)) {
        throw kaitai::validation_less_than_error<uint32_t>(1, m_width, m__io, std::string("/types/ihdr_chunk/seq/0"));
    }
    m_height = m__io->read_u4be();
    if (!(m_height >= 1)) {
        throw kaitai::validation_less_than_error<uint32_t>(1, m_height, m__io, std::string("/types/ihdr_chunk/seq/1"));
    }
    m_bit_depth = m__io->read_u1();
    if (!( ((m_bit_depth == 1) || (m_bit_depth == 2) || (m_bit_depth == 4) || (m_bit_depth == 8) || (m_bit_depth == 16)) )) {
        throw kaitai::validation_not_any_of_error<uint8_t>(m_bit_depth, m__io, std::string("/types/ihdr_chunk/seq/2"));
    }
    m_color_type = static_cast<png_t::color_type_t>(m__io->read_u1());
    if (!png_t::_is_defined_color_type_t(m_color_type)) {
        throw kaitai::validation_not_in_enum_error<png_t::color_type_t>(m_color_type, m__io, std::string("/types/ihdr_chunk/seq/3"));
    }
    m_compression_method = static_cast<png_t::compression_methods_t>(m__io->read_u1());
    if (!png_t::_is_defined_compression_methods_t(m_compression_method)) {
        throw kaitai::validation_not_in_enum_error<png_t::compression_methods_t>(m_compression_method, m__io, std::string("/types/ihdr_chunk/seq/4"));
    }
    m_filter_method = static_cast<png_t::filter_method_t>(m__io->read_u1());
    if (!png_t::_is_defined_filter_method_t(m_filter_method)) {
        throw kaitai::validation_not_in_enum_error<png_t::filter_method_t>(m_filter_method, m__io, std::string("/types/ihdr_chunk/seq/5"));
    }
    m_interlace_method = static_cast<png_t::interlace_method_t>(m__io->read_u1());
    if (!png_t::_is_defined_interlace_method_t(m_interlace_method)) {
        throw kaitai::validation_not_in_enum_error<png_t::interlace_method_t>(m_interlace_method, m__io, std::string("/types/ihdr_chunk/seq/6"));
    }
}

png_t::ihdr_chunk_t::~ihdr_chunk_t() {
    _clean_up();
}

void png_t::ihdr_chunk_t::_clean_up() {
}

png_t::international_text_t::international_text_t(kaitai::kstream* p__io, png_t::international_text_chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::international_text_t::_read() {
    m_value = kaitai::kstream::bytes_to_str(m__io->read_bytes_full(), "UTF-8");
}

png_t::international_text_t::~international_text_t() {
    _clean_up();
}

void png_t::international_text_t::_clean_up() {
}

png_t::international_text_chunk_t::international_text_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_text_plain = 0;
    m__io__raw_text_plain = 0;
    m_text_zlib = 0;
    m__io__raw_text_zlib = 0;
    f_text = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::international_text_chunk_t::_read() {
    m_keyword = kaitai::kstream::bytes_to_str(m__io->read_bytes_term(0, false, true, true), "ISO-8859-1");
    m_compression_flag = m__io->read_u1();
    if (!( ((m_compression_flag == 0) || (m_compression_flag == 1)) )) {
        throw kaitai::validation_not_any_of_error<uint8_t>(m_compression_flag, m__io, std::string("/types/international_text_chunk/seq/1"));
    }
    m_compression_method = static_cast<png_t::compression_methods_t>(m__io->read_u1());
    if (!(m_compression_method == ((compression_flag() == 1) ? (png_t::COMPRESSION_METHODS_ZLIB) : (compression_method())))) {
        throw kaitai::validation_not_equal_error<png_t::compression_methods_t>(((compression_flag() == 1) ? (png_t::COMPRESSION_METHODS_ZLIB) : (compression_method())), m_compression_method, m__io, std::string("/types/international_text_chunk/seq/2"));
    }
    m_language_tag = kaitai::kstream::bytes_to_str(m__io->read_bytes_term(0, false, true, true), "ASCII");
    m_translated_keyword = kaitai::kstream::bytes_to_str(m__io->read_bytes_term(0, false, true, true), "UTF-8");
    n_text_plain = true;
    if (compression_flag() == 0) {
        n_text_plain = false;
        m__raw_text_plain = m__io->read_bytes_full();
        m__io__raw_text_plain = new kaitai::kstream(m__raw_text_plain);
        m_text_plain = new international_text_t(m__io__raw_text_plain, this, m__root);
    }
    n_text_zlib = true;
    if (compression_flag() == 1) {
        n_text_zlib = false;
        m__raw__raw_text_zlib = m__io->read_bytes_full();
        m__raw_text_zlib = kaitai::kstream::process_zlib(m__raw__raw_text_zlib);
        m__io__raw_text_zlib = new kaitai::kstream(m__raw_text_zlib);
        m_text_zlib = new international_text_t(m__io__raw_text_zlib, this, m__root);
    }
}

png_t::international_text_chunk_t::~international_text_chunk_t() {
    _clean_up();
}

void png_t::international_text_chunk_t::_clean_up() {
    if (!n_text_plain) {
        if (m__io__raw_text_plain) {
            delete m__io__raw_text_plain; m__io__raw_text_plain = 0;
        }
        if (m_text_plain) {
            delete m_text_plain; m_text_plain = 0;
        }
    }
    if (!n_text_zlib) {
        if (m__io__raw_text_zlib) {
            delete m__io__raw_text_zlib; m__io__raw_text_zlib = 0;
        }
        if (m_text_zlib) {
            delete m_text_zlib; m_text_zlib = 0;
        }
    }
}

std::string png_t::international_text_chunk_t::text() {
    if (f_text)
        return m_text;
    f_text = true;
    m_text = ((compression_flag() == 0) ? (text_plain()) : (text_zlib()))->value();
    return m_text;
}

png_t::mdcv_chromaticity_t::mdcv_chromaticity_t(kaitai::kstream* p__io, png_t::mdcv_chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    f_x = false;
    f_y = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::mdcv_chromaticity_t::_read() {
    m_x_int = m__io->read_u2be();
    m_y_int = m__io->read_u2be();
}

png_t::mdcv_chromaticity_t::~mdcv_chromaticity_t() {
    _clean_up();
}

void png_t::mdcv_chromaticity_t::_clean_up() {
}

double png_t::mdcv_chromaticity_t::x() {
    if (f_x)
        return m_x;
    f_x = true;
    m_x = x_int() * 0.00002;
    return m_x;
}

double png_t::mdcv_chromaticity_t::y() {
    if (f_y)
        return m_y;
    f_y = true;
    m_y = y_int() * 0.00002;
    return m_y;
}

png_t::mdcv_chunk_t::mdcv_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_red = 0;
    m_green = 0;
    m_blue = 0;
    m_white_point = 0;
    f_max_luminance = false;
    f_min_luminance = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::mdcv_chunk_t::_read() {
    m_red = new mdcv_chromaticity_t(m__io, this, m__root);
    m_green = new mdcv_chromaticity_t(m__io, this, m__root);
    m_blue = new mdcv_chromaticity_t(m__io, this, m__root);
    m_white_point = new mdcv_chromaticity_t(m__io, this, m__root);
    m_max_luminance_int = m__io->read_u4be();
    m_min_luminance_int = m__io->read_u4be();
}

png_t::mdcv_chunk_t::~mdcv_chunk_t() {
    _clean_up();
}

void png_t::mdcv_chunk_t::_clean_up() {
    if (m_red) {
        delete m_red; m_red = 0;
    }
    if (m_green) {
        delete m_green; m_green = 0;
    }
    if (m_blue) {
        delete m_blue; m_blue = 0;
    }
    if (m_white_point) {
        delete m_white_point; m_white_point = 0;
    }
}

double png_t::mdcv_chunk_t::max_luminance() {
    if (f_max_luminance)
        return m_max_luminance;
    f_max_luminance = true;
    m_max_luminance = max_luminance_int() * 0.0001;
    return m_max_luminance;
}

double png_t::mdcv_chunk_t::min_luminance() {
    if (f_min_luminance)
        return m_min_luminance;
    f_min_luminance = true;
    m_min_luminance = min_luminance_int() * 0.0001;
    return m_min_luminance;
}

png_t::phys_chunk_t::phys_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    f_dots_per_inch_x = false;
    f_dots_per_inch_y = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::phys_chunk_t::_read() {
    m_pixels_per_unit_x = m__io->read_u4be();
    m_pixels_per_unit_y = m__io->read_u4be();
    m_unit = static_cast<png_t::phys_unit_t>(m__io->read_u1());
    if (!png_t::_is_defined_phys_unit_t(m_unit)) {
        throw kaitai::validation_not_in_enum_error<png_t::phys_unit_t>(m_unit, m__io, std::string("/types/phys_chunk/seq/2"));
    }
}

png_t::phys_chunk_t::~phys_chunk_t() {
    _clean_up();
}

void png_t::phys_chunk_t::_clean_up() {
}

double png_t::phys_chunk_t::dots_per_inch_x() {
    if (f_dots_per_inch_x)
        return m_dots_per_inch_x;
    f_dots_per_inch_x = true;
    n_dots_per_inch_x = true;
    if (unit() == png_t::PHYS_UNIT_METER) {
        n_dots_per_inch_x = false;
        m_dots_per_inch_x = pixels_per_unit_x() * 0.0254;
    }
    return m_dots_per_inch_x;
}

double png_t::phys_chunk_t::dots_per_inch_y() {
    if (f_dots_per_inch_y)
        return m_dots_per_inch_y;
    f_dots_per_inch_y = true;
    n_dots_per_inch_y = true;
    if (unit() == png_t::PHYS_UNIT_METER) {
        n_dots_per_inch_y = false;
        m_dots_per_inch_y = pixels_per_unit_y() * 0.0254;
    }
    return m_dots_per_inch_y;
}

png_t::plte_chunk_t::plte_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_entries = 0;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::plte_chunk_t::_read() {
    m_entries = new std::vector<rgb_t*>();
    {
        int i = 0;
        while (!m__io->is_eof()) {
            m_entries->push_back(new rgb_t(m__io, this, m__root));
            i++;
        }
    }
}

png_t::plte_chunk_t::~plte_chunk_t() {
    _clean_up();
}

void png_t::plte_chunk_t::_clean_up() {
    if (m_entries) {
        for (std::vector<rgb_t*>::iterator it = m_entries->begin(); it != m_entries->end(); ++it) {
            delete *it;
        }
        delete m_entries; m_entries = 0;
    }
}

png_t::rgb_t::rgb_t(kaitai::kstream* p__io, png_t::plte_chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::rgb_t::_read() {
    m_r = m__io->read_u1();
    m_g = m__io->read_u1();
    m_b = m__io->read_u1();
}

png_t::rgb_t::~rgb_t() {
    _clean_up();
}

void png_t::rgb_t::_clean_up() {
}

png_t::sbit_chunk_t::sbit_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    f_sample_depth = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::sbit_chunk_t::_read() {
    n_significant_bits = true;
    switch (_root()->ihdr()->color_type()) {
    case png_t::COLOR_TYPE_GREYSCALE: {
        n_significant_bits = false;
        m_significant_bits = new sbit_greyscale_t(false, m__io, this, m__root);
        break;
    }
    case png_t::COLOR_TYPE_GREYSCALE_ALPHA: {
        n_significant_bits = false;
        m_significant_bits = new sbit_greyscale_t(true, m__io, this, m__root);
        break;
    }
    case png_t::COLOR_TYPE_INDEXED: {
        n_significant_bits = false;
        m_significant_bits = new sbit_truecolor_t(false, m__io, this, m__root);
        break;
    }
    case png_t::COLOR_TYPE_TRUECOLOR: {
        n_significant_bits = false;
        m_significant_bits = new sbit_truecolor_t(false, m__io, this, m__root);
        break;
    }
    case png_t::COLOR_TYPE_TRUECOLOR_ALPHA: {
        n_significant_bits = false;
        m_significant_bits = new sbit_truecolor_t(true, m__io, this, m__root);
        break;
    }
    }
}

png_t::sbit_chunk_t::~sbit_chunk_t() {
    _clean_up();
}

void png_t::sbit_chunk_t::_clean_up() {
    if (!n_significant_bits) {
        if (m_significant_bits) {
            delete m_significant_bits; m_significant_bits = 0;
        }
    }
}

uint8_t png_t::sbit_chunk_t::sample_depth() {
    if (f_sample_depth)
        return m_sample_depth;
    f_sample_depth = true;
    m_sample_depth = ((_root()->ihdr()->color_type() == png_t::COLOR_TYPE_INDEXED) ? (8) : (_root()->ihdr()->bit_depth()));
    return m_sample_depth;
}

png_t::sbit_greyscale_t::sbit_greyscale_t(bool p_has_alpha, kaitai::kstream* p__io, png_t::sbit_chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_has_alpha = p_has_alpha;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::sbit_greyscale_t::_read() {
    m_grey = m__io->read_u1();
    if (!(m_grey >= 1)) {
        throw kaitai::validation_less_than_error<uint8_t>(1, m_grey, m__io, std::string("/types/sbit_greyscale/seq/0"));
    }
    if (!(m_grey <= _parent()->sample_depth())) {
        throw kaitai::validation_greater_than_error<uint8_t>(_parent()->sample_depth(), m_grey, m__io, std::string("/types/sbit_greyscale/seq/0"));
    }
    n_alpha = true;
    if (has_alpha()) {
        n_alpha = false;
        m_alpha = m__io->read_u1();
        if (!(m_alpha >= 1)) {
            throw kaitai::validation_less_than_error<uint8_t>(1, m_alpha, m__io, std::string("/types/sbit_greyscale/seq/1"));
        }
        if (!(m_alpha <= _parent()->sample_depth())) {
            throw kaitai::validation_greater_than_error<uint8_t>(_parent()->sample_depth(), m_alpha, m__io, std::string("/types/sbit_greyscale/seq/1"));
        }
    }
}

png_t::sbit_greyscale_t::~sbit_greyscale_t() {
    _clean_up();
}

void png_t::sbit_greyscale_t::_clean_up() {
    if (!n_alpha) {
    }
}

png_t::sbit_truecolor_t::sbit_truecolor_t(bool p_has_alpha, kaitai::kstream* p__io, png_t::sbit_chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_has_alpha = p_has_alpha;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::sbit_truecolor_t::_read() {
    m_red = m__io->read_u1();
    if (!(m_red >= 1)) {
        throw kaitai::validation_less_than_error<uint8_t>(1, m_red, m__io, std::string("/types/sbit_truecolor/seq/0"));
    }
    if (!(m_red <= _parent()->sample_depth())) {
        throw kaitai::validation_greater_than_error<uint8_t>(_parent()->sample_depth(), m_red, m__io, std::string("/types/sbit_truecolor/seq/0"));
    }
    m_green = m__io->read_u1();
    if (!(m_green >= 1)) {
        throw kaitai::validation_less_than_error<uint8_t>(1, m_green, m__io, std::string("/types/sbit_truecolor/seq/1"));
    }
    if (!(m_green <= _parent()->sample_depth())) {
        throw kaitai::validation_greater_than_error<uint8_t>(_parent()->sample_depth(), m_green, m__io, std::string("/types/sbit_truecolor/seq/1"));
    }
    m_blue = m__io->read_u1();
    if (!(m_blue >= 1)) {
        throw kaitai::validation_less_than_error<uint8_t>(1, m_blue, m__io, std::string("/types/sbit_truecolor/seq/2"));
    }
    if (!(m_blue <= _parent()->sample_depth())) {
        throw kaitai::validation_greater_than_error<uint8_t>(_parent()->sample_depth(), m_blue, m__io, std::string("/types/sbit_truecolor/seq/2"));
    }
    n_alpha = true;
    if (has_alpha()) {
        n_alpha = false;
        m_alpha = m__io->read_u1();
        if (!(m_alpha >= 1)) {
            throw kaitai::validation_less_than_error<uint8_t>(1, m_alpha, m__io, std::string("/types/sbit_truecolor/seq/3"));
        }
        if (!(m_alpha <= _parent()->sample_depth())) {
            throw kaitai::validation_greater_than_error<uint8_t>(_parent()->sample_depth(), m_alpha, m__io, std::string("/types/sbit_truecolor/seq/3"));
        }
    }
}

png_t::sbit_truecolor_t::~sbit_truecolor_t() {
    _clean_up();
}

void png_t::sbit_truecolor_t::_clean_up() {
    if (!n_alpha) {
    }
}

png_t::splt_chunk_t::splt_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_entries = 0;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::splt_chunk_t::_read() {
    m_palette_name = kaitai::kstream::bytes_to_str(m__io->read_bytes_term(0, false, true, true), "ISO-8859-1");
    m_sample_depth = m__io->read_u1();
    if (!( ((m_sample_depth == 8) || (m_sample_depth == 16)) )) {
        throw kaitai::validation_not_any_of_error<uint8_t>(m_sample_depth, m__io, std::string("/types/splt_chunk/seq/1"));
    }
    m_entries = new std::vector<splt_entry_t*>();
    {
        int i = 0;
        while (!m__io->is_eof()) {
            m_entries->push_back(new splt_entry_t(m__io, this, m__root));
            i++;
        }
    }
}

png_t::splt_chunk_t::~splt_chunk_t() {
    _clean_up();
}

void png_t::splt_chunk_t::_clean_up() {
    if (m_entries) {
        for (std::vector<splt_entry_t*>::iterator it = m_entries->begin(); it != m_entries->end(); ++it) {
            delete *it;
        }
        delete m_entries; m_entries = 0;
    }
}

png_t::splt_entry_t::splt_entry_t(kaitai::kstream* p__io, png_t::splt_chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::splt_entry_t::_read() {
    switch (_parent()->sample_depth()) {
    case 8: {
        m_red = m__io->read_u1();
        break;
    }
    default: {
        m_red = m__io->read_u2be();
        break;
    }
    }
    switch (_parent()->sample_depth()) {
    case 8: {
        m_green = m__io->read_u1();
        break;
    }
    default: {
        m_green = m__io->read_u2be();
        break;
    }
    }
    switch (_parent()->sample_depth()) {
    case 8: {
        m_blue = m__io->read_u1();
        break;
    }
    default: {
        m_blue = m__io->read_u2be();
        break;
    }
    }
    switch (_parent()->sample_depth()) {
    case 8: {
        m_alpha = m__io->read_u1();
        break;
    }
    default: {
        m_alpha = m__io->read_u2be();
        break;
    }
    }
    m_freq = m__io->read_u2be();
}

png_t::splt_entry_t::~splt_entry_t() {
    _clean_up();
}

void png_t::splt_entry_t::_clean_up() {
}
std::set<png_t::srgb_chunk_t::intent_t> png_t::srgb_chunk_t::_build_values_intent_t() {
    std::set<png_t::srgb_chunk_t::intent_t> _t;
    _t.insert(png_t::srgb_chunk_t::INTENT_PERCEPTUAL);
    _t.insert(png_t::srgb_chunk_t::INTENT_RELATIVE_COLORIMETRIC);
    _t.insert(png_t::srgb_chunk_t::INTENT_SATURATION);
    _t.insert(png_t::srgb_chunk_t::INTENT_ABSOLUTE_COLORIMETRIC);
    return _t;
}
const std::set<png_t::srgb_chunk_t::intent_t> png_t::srgb_chunk_t::_values_intent_t = png_t::srgb_chunk_t::_build_values_intent_t();
bool png_t::srgb_chunk_t::_is_defined_intent_t(png_t::srgb_chunk_t::intent_t v) {
    return png_t::srgb_chunk_t::_values_intent_t.find(v) != png_t::srgb_chunk_t::_values_intent_t.end();
}

png_t::srgb_chunk_t::srgb_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::srgb_chunk_t::_read() {
    m_render_intent = static_cast<png_t::srgb_chunk_t::intent_t>(m__io->read_u1());
    if (!png_t::srgb_chunk_t::_is_defined_intent_t(m_render_intent)) {
        throw kaitai::validation_not_in_enum_error<png_t::srgb_chunk_t::intent_t>(m_render_intent, m__io, std::string("/types/srgb_chunk/seq/0"));
    }
}

png_t::srgb_chunk_t::~srgb_chunk_t() {
    _clean_up();
}

void png_t::srgb_chunk_t::_clean_up() {
}

png_t::text_chunk_t::text_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::text_chunk_t::_read() {
    m_keyword = kaitai::kstream::bytes_to_str(m__io->read_bytes_term(0, false, true, true), "ISO-8859-1");
    m_text = kaitai::kstream::bytes_to_str(m__io->read_bytes_full(), "ISO-8859-1");
}

png_t::text_chunk_t::~text_chunk_t() {
    _clean_up();
}

void png_t::text_chunk_t::_clean_up() {
}

png_t::time_chunk_t::time_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::time_chunk_t::_read() {
    m_year = m__io->read_u2be();
    m_month = m__io->read_u1();
    m_day = m__io->read_u1();
    m_hour = m__io->read_u1();
    m_minute = m__io->read_u1();
    m_second = m__io->read_u1();
}

png_t::time_chunk_t::~time_chunk_t() {
    _clean_up();
}

void png_t::time_chunk_t::_clean_up() {
}

png_t::trns_chunk_t::trns_chunk_t(kaitai::kstream* p__io, png_t::chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_palette_alphas = 0;
    f_sample_mask = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::trns_chunk_t::_read() {
    n_palette_alphas = true;
    if (_root()->ihdr()->color_type() == png_t::COLOR_TYPE_INDEXED) {
        n_palette_alphas = false;
        m_palette_alphas = new std::vector<uint8_t>();
        {
            int i = 0;
            while (!m__io->is_eof()) {
                m_palette_alphas->push_back(m__io->read_u1());
                i++;
            }
        }
    }
    n_transparent_color = true;
    switch (_root()->ihdr()->color_type()) {
    case png_t::COLOR_TYPE_GREYSCALE: {
        n_transparent_color = false;
        m_transparent_color = new trns_greyscale_color_t(m__io, this, m__root);
        break;
    }
    case png_t::COLOR_TYPE_TRUECOLOR: {
        n_transparent_color = false;
        m_transparent_color = new trns_truecolor_color_t(m__io, this, m__root);
        break;
    }
    }
}

png_t::trns_chunk_t::~trns_chunk_t() {
    _clean_up();
}

void png_t::trns_chunk_t::_clean_up() {
    if (!n_palette_alphas) {
        if (m_palette_alphas) {
            delete m_palette_alphas; m_palette_alphas = 0;
        }
    }
    if (!n_transparent_color) {
        if (m_transparent_color) {
            delete m_transparent_color; m_transparent_color = 0;
        }
    }
}

int32_t png_t::trns_chunk_t::sample_mask() {
    if (f_sample_mask)
        return m_sample_mask;
    f_sample_mask = true;
    m_sample_mask = (1 << _root()->ihdr()->bit_depth()) - 1;
    return m_sample_mask;
}

png_t::trns_greyscale_color_t::trns_greyscale_color_t(kaitai::kstream* p__io, png_t::trns_chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    f_grey = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::trns_greyscale_color_t::_read() {
    m_grey_raw = m__io->read_u2be();
}

png_t::trns_greyscale_color_t::~trns_greyscale_color_t() {
    _clean_up();
}

void png_t::trns_greyscale_color_t::_clean_up() {
}

int32_t png_t::trns_greyscale_color_t::grey() {
    if (f_grey)
        return m_grey;
    f_grey = true;
    m_grey = grey_raw() & _parent()->sample_mask();
    return m_grey;
}

png_t::trns_truecolor_color_t::trns_truecolor_color_t(kaitai::kstream* p__io, png_t::trns_chunk_t* p__parent, png_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    f_blue = false;
    f_green = false;
    f_red = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void png_t::trns_truecolor_color_t::_read() {
    m_red_raw = m__io->read_u2be();
    m_green_raw = m__io->read_u2be();
    m_blue_raw = m__io->read_u2be();
}

png_t::trns_truecolor_color_t::~trns_truecolor_color_t() {
    _clean_up();
}

void png_t::trns_truecolor_color_t::_clean_up() {
}

int32_t png_t::trns_truecolor_color_t::blue() {
    if (f_blue)
        return m_blue;
    f_blue = true;
    m_blue = blue_raw() & _parent()->sample_mask();
    return m_blue;
}

int32_t png_t::trns_truecolor_color_t::green() {
    if (f_green)
        return m_green;
    f_green = true;
    m_green = green_raw() & _parent()->sample_mask();
    return m_green;
}

int32_t png_t::trns_truecolor_color_t::red() {
    if (f_red)
        return m_red;
    f_red = true;
    m_red = red_raw() & _parent()->sample_mask();
    return m_red;
}