    // Copyright (C) 2008  Davis E. King (davis@dlib.net), Nils Labugt
    // License: Boost Software License   See LICENSE.txt for the full license.
    #ifndef DLIB_PNG_IMPORT
    #define DLIB_PNG_IMPORT

    #include <memory>
    #include <cstring>
    #include <functional>
    #include <istream>
    #include <fstream>

    #include "png_loader_abstract.h"
    #include "image_loader.h"
    #include "../pixel.h"
    #include "../type_traits.h"
    #include "../test_for_odr_violations.h"
    #include "../dir_nav.h"

    namespace dlib
    {

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

        class png_loader : noncopyable
        {
        public:

            png_loader( std::istream& in );
            png_loader( const char* filename );
            png_loader( const std::string& filename );
            png_loader( const dlib::file& f );
            png_loader( const unsigned char* image_buffer, std::size_t buffer_size );

            bool is_gray()              const;
            bool is_graya()             const;
            bool is_rgb()               const;
            bool is_rgba()              const;
            unsigned int bit_depth ()   const;
            long nr()                   const;
            long nc()                   const;

            template<class image_type>
            void get_image( image_type& img) const
            {
    #ifndef DLIB_PNG_SUPPORT
                /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                    You are getting this error because you are trying to use the png_loader
                    object but you haven't defined DLIB_PNG_SUPPORT.  You must do so to use
                    this object.   You must also make sure you set your build environment
                    to link against the libpng library.
                !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
                COMPILE_TIME_ASSERT(sizeof(T) == 0);
    #else
                using pixel_type = pixel_type_t<image_type>;
                auto t = make_image_view(img);

                t.set_size( height, width );

                const auto assign_gray = [&](const auto** lines)
                {
                    for ( int n = 0; n < height; ++n )
                        for ( int m = 0; m < width; ++m )
                            assign_pixel( t[n][m], lines[n][m]);
                };

                const auto assign_gray_alpha = [&](const auto** lines)
                {
                    for ( int n = 0; n < height; ++n )
                    {
                        for ( int m = 0; m < width; ++m )
                        {
                            if (!pixel_traits<pixel_type>::has_alpha)
                            {
                                assign_pixel(t[n][m], lines[n][m*2]);
                            }
                            else
                            {
                                rgb_alpha_pixel pix;
                                assign_pixel(pix,       lines[n][m*2]);
                                assign_pixel(pix.alpha, lines[n][m*2+1]);
                                assign_pixel(t[n][m], pix);
                            }
                        }
                    }
                };

                const auto assign_rgb = [&](const auto** lines)
                {
                    for ( int n = 0; n < height;++n )
                    {
                        for ( int m = 0; m < width;++m )
                        {
                            rgb_pixel p;
                            p.red   = static_cast<uint8>(lines[n][m*3]);
                            p.green = static_cast<uint8>(lines[n][m*3+1]);
                            p.blue  = static_cast<uint8>(lines[n][m*3+2]);
                            assign_pixel( t[n][m], p );
                        }
                    }
                };

                const auto assign_rgba = [&](const auto** lines)
                {
                    if (!pixel_traits<pixel_type>::has_alpha)
                        assign_all_pixels(t,0);

                    for ( int n = 0; n < height; ++n )
                    {
                        for ( int m = 0; m < width; ++m )
                        {
                            rgb_alpha_pixel p;
                            p.red   = static_cast<uint8>(lines[n][m*4]);
                            p.green = static_cast<uint8>(lines[n][m*4+1]);
                            p.blue  = static_cast<uint8>(lines[n][m*4+2]);
                            p.alpha = static_cast<uint8>(lines[n][m*4+3]);
                            assign_pixel( t[n][m], p );
                        }
                    }
                };

                const auto assign = [&](const auto** lines)
                {
                    if (is_gray())
                        assign_gray(lines);

                    else if (is_graya())
                        assign_gray_alpha(lines);

                    else if (is_rgb())
                        assign_rgb(lines);

                    else if (is_rgba())
                        assign_rgba(lines);
                };

                if (bit_depth_ == 8)
                    assign((const uint8_t**)(rows));

                else if (bit_depth_ == 16)
                    assign((const uint16_t**)(rows));
    #endif
            }

        private:
            void load(std::function<std::size_t(char*,std::size_t)> clb);
            void load(std::istream& in);

            int             height{0};
            int             width{0};
            int             bit_depth_{0};
            int             color_type{0};
            unsigned char** rows{nullptr};
            std::shared_ptr<void> finalizer;
        };

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

        template <class image_type>
        void load_png (
            image_type& img,
            std::istream& in
        )
        {
            png_loader(in).get_image(img);
        }

        template <class image_type>
        void load_png (
            image_type& img,
            const std::string& file_name
        )
        {
            png_loader(file_name).get_image(img);
        }

        template <
          class image_type,
          class Byte,
          std::enable_if_t<is_byte<Byte>::value, bool> = true
        >
        void load_png (
            image_type& img,
            const Byte* image_buffer,
            std::size_t buffer_size
        )
        {
            png_loader((const unsigned char*)image_buffer, buffer_size).get_image(img);
        }

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

    }

    #ifdef NO_MAKEFILE
    #include "png_loader.cpp"
    #endif

    #endif // DLIB_PNG_IMPORT