savepcx.hpp

Back to Pcx image files

pastel/gfx/image_file/pcx/

#ifndef PASTELGFX_SAVEPCX_HPP
#define PASTELGFX_SAVEPCX_HPP

#include "pastel/gfx/image_file/pcx/savepcx.h"
#include "pastel/gfx/image_file/pcx/pcxheader.h"
#include "pastel/gfx/color/color_palette.h"
#include "pastel/gfx/color/coloradapter.h"
#include "pastel/gfx/color/color_tools.h"
#include "pastel/gfx/color/color_palette.h"
#include "pastel/gfx/color/coloradapter.h"

#include "pastel/sys/adaptedarray.h"
#include "pastel/sys/binaryfile.h"
#include "pastel/sys/tuple.h"
#include "pastel/sys/view/view_tools.h"
#include "pastel/sys/view/arrayview.h"
#include "pastel/sys/vector.h"

#include <algorithm>
#include <string>

namespace Pastel
{

    namespace Pcx_
    {

        inline void saveScanline(BinaryFile& file,
            const std::vector<uint8>& scanline,
            integer width)
        {
            integer xPos = 0;
            while(xPos < width)
            {
                uint8 aData = scanline[xPos];
                ++xPos;

                integer repetition = 1;
                while (xPos < width && scanline[xPos] == aData)
                {
                    ++xPos;
                    ++repetition;
                }

                if (((aData & 0xC0) == 0xC0) ||
                    repetition > 1)
                {
                    while (repetition > 63)
                    {
                        file << (char)(0xC0 | 63);
                        file << aData;
                        repetition -= 63;
                    }
                    if (repetition > 0)
                    {
                        file << (char)(0xC0 | repetition);
                        file << aData;
                    }
                }
                else
                {
                    file << aData;
                }
            }
        }

    }

    inline bool saveBinaryPcx(
        const ConstAbstractArray<2, bool>& image,
        const std::string& fileName)
    {
        // TODO: This does not work with empty images!

        /*
       std::cout << "Saving a binary image to file "
           << fileName
           << " in pcx format."
           << std::endl;
       */

        if (fileName.empty())
        {
            std::cout << "No filename given. Aborting..."
                << std::endl;

            return false;
        }

        integer width = image.extent().x();
        integer height = image.extent().y();

        integer bytesPerScanlinePerPlane =
            roundUpToEven((width + 7) / 8);

        PcxHeader pcxHeader;

        pcxHeader.manufacturer_ = 10;
        pcxHeader.version_ = 5;
        pcxHeader.encoding_ = 1;
        pcxHeader.bitsPerPixel_ = 1;
        pcxHeader.leftMargin_ = 0;
        pcxHeader.upperMargin_ = 0;
        pcxHeader.rightMargin_ = (int16)(width - 1);
        pcxHeader.lowerMargin_ = (int16)(height - 1);
        pcxHeader.hss_ = 72;
        pcxHeader.vss_ = 72;
        pcxHeader.colorPlanes_ = 1;
        pcxHeader.bytesPerScanlinePerPlane_ =
            (uint16)bytesPerScanlinePerPlane;
        pcxHeader.paletteInfo_ = 1;

        BinaryFile file(fileName, false, true);

        // Define the palette.

        pcxHeader.palette16_[0 * 3 + 0] = 0;
        pcxHeader.palette16_[0 * 3 + 1] = 0;
        pcxHeader.palette16_[0 * 3 + 2] = 0;

        pcxHeader.palette16_[1 * 3 + 0] = 255;
        pcxHeader.palette16_[1 * 3 + 1] = 255;
        pcxHeader.palette16_[1 * 3 + 2] = 255;

        for (integer i = 2;i < 16;++i)
        {
            pcxHeader.palette16_[i * 3 + 0] = 0;
            pcxHeader.palette16_[i * 3 + 1] = 0;
            pcxHeader.palette16_[i * 3 + 2] = 0;
        }

        pcxHeader.write(file);

        integer bytesPerScanline =
            (integer)pcxHeader.bytesPerScanlinePerPlane_ * (integer)pcxHeader.colorPlanes_;

        std::vector<uint8> scanline(pcxHeader.bytesPerScanlinePerPlane_, 0);
        for (integer yPos = height - 1;yPos >= 0;--yPos)
        {
            for (integer xPos = 0;xPos < width;xPos += 8)
            {
                uint8 data = 0;
                integer i = 0;
                for (i = 0;i < 8 && xPos + i < width;++i)
                {
                    data <<= 1;
                    if (image(Vector2i(xPos + i, yPos)))
                    {
                        data += 1;
                    }
                }
                data <<= 8 - i;

                scanline[xPos >> 3] = data;
            }

            Pcx_::saveScanline(file, scanline, bytesPerScanline);
        }

        file.close();

        //std::cout << "Pcx image saving complete." << std::endl;

        return true;
    }

    inline bool saveBinaryPcx(
        const Array<bool, 2>& image,
        const std::string& fileName)
    {
        IdentityAdapter<bool> adapter;
        return saveBinaryPcx(image, fileName, adapter);
    }

    inline bool saveIndexedPcx(
        const ConstAbstractArray<2, uint8>& image,
        const std::vector<Color>& colorPalette,
        const std::string& fileName)
    {
        // TODO: This does not work with empty images!

        /*
       std::cout << "Saving an indexed image to file "
           << fileName << " in pcx format."
           << std::endl;
       */

        if (fileName.empty())
        {
            std::cout << "No filename given. Aborting..."
                << std::endl;

            return false;
        }

        integer width = image.extent().x();
        integer height = image.extent().y();

        PcxHeader pcxHeader;

        pcxHeader.manufacturer_ = 10;
        pcxHeader.version_ = 5;
        pcxHeader.encoding_ = 1;
        pcxHeader.bitsPerPixel_ = 8;
        pcxHeader.leftMargin_ = 0;
        pcxHeader.upperMargin_ = 0;
        pcxHeader.rightMargin_ = (int16)(width - 1);
        pcxHeader.lowerMargin_ = (int16)(height - 1);
        pcxHeader.hss_ = 72;
        pcxHeader.vss_ = 72;
        pcxHeader.colorPlanes_ = 1;
        pcxHeader.bytesPerScanlinePerPlane_ =
            odd(width) ?
            (int16)(width + 1) : (int16)width;
        pcxHeader.paletteInfo_ = 1;

        integer colors = std::min((integer)16, (integer)colorPalette.size());

        for (integer i = 0;i < colors;++i)
        {
            Color color = fitColor(colorPalette[i]);

            pcxHeader.palette16_[i * 3 + 0] = quantizeUnsigned(color[0], 256);
            pcxHeader.palette16_[i * 3 + 1] = quantizeUnsigned(color[1], 256);
            pcxHeader.palette16_[i * 3 + 2] = quantizeUnsigned(color[2], 256);
        }

        for (integer i = colors;i < 16;++i)
        {
            pcxHeader.palette16_[i * 3 + 0] = 0;
            pcxHeader.palette16_[i * 3 + 1] = 0;
            pcxHeader.palette16_[i * 3 + 2] = 0;
        }

        BinaryFile file(fileName, false, true);

        pcxHeader.write(file);

        integer bytesPerScanline =
            (integer)pcxHeader.bytesPerScanlinePerPlane_ * (integer)pcxHeader.colorPlanes_;

        std::vector<uint8> scanline(pcxHeader.bytesPerScanlinePerPlane_, 0);
        for (integer yPos = height - 1;yPos >= 0;--yPos)
        {
            for (integer xPos = 0;xPos < width;++xPos)
            {
                scanline[xPos] = image(Vector2i(xPos, yPos));
            }

            Pcx_::saveScanline(file, scanline, bytesPerScanline);
        }

        file << (char)0xC;

        // There can be more than 256 colors in the palette,
        // so write at most 256 colors.

        integer colorsToWrite = std::min((integer)colorPalette.size(), (integer)256);

        for (integer i = 0;i < colorsToWrite;++i)
        {
            Color color = fitColor(colorPalette[i]);

            uint8 red = quantizeUnsigned(color[0], 256);
            uint8 green = quantizeUnsigned(color[1], 256);
            uint8 blue = quantizeUnsigned(color[2], 256);

            file << red << green << blue;
        }

        // If the palette does not define all 256 colors,
        // define the rest as (0, 0, 0).

        for (integer i = colorsToWrite;i < 256;++i)
        {
            file << (uint8)0;
            file << (uint8)0;
            file << (uint8)0;
        }

        file.close();

        //std::cout << "Pcx image saving complete." << std::endl;

        return true;
    }

    inline bool saveIndexedPcx(
        const Array<uint8, 2>& image,
        const std::vector<Color>& palette,
        const std::string& fileName)
    {
        IdentityAdapter<uint8> adapter;
        return saveIndexedPcx(image, palette, fileName, adapter);
    }

    inline bool savePcx(
        const ConstAbstractArray<2, Color>& image,
        const std::string& fileName)
    {
        // TODO: This does not work with empty images!

        /*
       std::cout << "Saving an rgb image to file "
           << fileName
           << " in pcx format."
           << std::endl;
       */

        if (fileName.empty())
        {
            std::cout << "No filename given. Aborting..."
                << std::endl;

            return false;
        }

        integer width = image.extent().x();
        integer height = image.extent().y();

        PcxHeader pcxHeader;

        pcxHeader.manufacturer_ = 10;
        pcxHeader.version_ = 5;
        pcxHeader.encoding_ = 1;
        pcxHeader.bitsPerPixel_ = 8;
        pcxHeader.leftMargin_ = 0;
        pcxHeader.upperMargin_ = 0;
        pcxHeader.rightMargin_ = (int16)(width - 1);
        pcxHeader.lowerMargin_ = (int16)(height - 1);
        pcxHeader.hss_ = 72;
        pcxHeader.vss_ = 72;
        pcxHeader.colorPlanes_ = 3;
        pcxHeader.bytesPerScanlinePerPlane_ =
            odd(width) ?
            (int16)(width + 1) : (int16)width;
        pcxHeader.paletteInfo_ = 1;

        BinaryFile file(fileName, false, true);

        pcxHeader.write(file);

        integer bytesPerScanline =
            (integer)pcxHeader.bytesPerScanlinePerPlane_ * (integer)pcxHeader.colorPlanes_;

        std::vector<uint8> scanline(bytesPerScanline, 0);
        for (integer yPos = height - 1;yPos >= 0;--yPos)
        {
            for (integer xPos = 0;xPos < width;++xPos)
            {
                Color color = image(Vector2i(xPos, yPos));

                scanline[xPos] =
                    quantizeUnsigned(color[0], 256);
                scanline[xPos + pcxHeader.bytesPerScanlinePerPlane_] =
                    quantizeUnsigned(color[1], 256);

                scanline[xPos + pcxHeader.bytesPerScanlinePerPlane_ * 2] =
                    quantizeUnsigned(color[2], 256);
            }

            Pcx_::saveScanline(file, scanline, bytesPerScanline);
        }

        file.close();

        //std::cout << "Pcx image saving complete." << std::endl;

        return true;
    }

    inline bool savePcx(
        const Array<uint32, 2>& image,
        const std::string& fileName)
    {
        Integer_To_Color<uint32, 8, 8, 8> adapter;
        return savePcx(image, fileName, adapter);
    }

    inline bool savePcx(
        const Array<uint16, 2>& image,
        const std::string& fileName)
    {
        Integer_To_Color<uint16, 5, 6, 5> adapter;
        return savePcx(image, fileName, adapter);
    }

    inline bool savePcx(
        const Array<Color, 2>& image,
        const std::string& fileName)
    {
        IdentityAdapter<Color> adapter;
        return savePcx(image, fileName, adapter);
    }

    inline bool savePcx(
        const Array<ByteColor, 2>& image,
        const std::string& fileName)
    {
        ByteColor_To_Color adapter;
        return savePcx(image, fileName, adapter);
    }

}

namespace Pastel
{

    template <typename Type, typename Adapter>
    bool saveBinaryPcx(
        const Array<Type, 2>& image,
        const std::string& fileName,
        const Adapter& adapter)
    {
        ConstAdaptedArray<2, bool, Type, Adapter> adaptedImage(
            image, adapter);

        return saveBinaryPcx(adaptedImage, fileName);
    }

    template <typename Type, typename Adapter>
    bool saveIndexedPcx(
        const Array<Type, 2>& image,
        const std::vector<Color>& palette,
        const std::string& fileName,
        const Adapter& adapter)
    {
        ConstAdaptedArray<2, uint8, Type, Adapter> adaptedImage(
            image, adapter);

        return saveIndexedPcx(adaptedImage, palette, fileName);
    }

    template <typename Type, typename Adapter>
    bool savePcx(
        const Array<Type, 2>& image,
        const std::string& fileName,
        const Adapter& adapter)
    {
        ConstAdaptedArray<2, Color, Type, Adapter> adaptedImage(
            image, adapter);

        return savePcx(adaptedImage, fileName);
    }

    template <typename Real>
    bool saveGrayscalePcx(
        const Array<Real>& image,
        const std::string& fileName,
        bool maximizeContrast)
    {
        Real maxValue = image.empty() ? 1 :

            std::max(*std::max_element(image.cbegin(), image.cend()), (Real)0);

        ReverseAdapter<Integer_To_Real<Real, uint8> > adapter(
            Integer_To_Real<Real, uint8>(256, maxValue));

        real32 paletteMaxValue = maximizeContrast ? 
            1 : maxValue;

        std::vector<Color> palette;
        grayscalePalette(palette, 256, paletteMaxValue);

        return saveIndexedPcx(image, palette, fileName, adapter);
    }

    template <typename Real>
    bool saveRainbowPcx(
        const Array<Real>& image,
        const std::string& fileName,
        bool maximizeContrast)
    {
        Real maxValue =
            std::max(*std::max_element(image.begin(), image.end()), (Real)0);

        ReverseAdapter<Integer_To_Real<Real, uint8> > adapter(
            Integer_To_Real<Real, uint8>(256, maxValue));

        std::vector<Color> palette;
        smoothFalseColorPalette(palette, 256);

        return saveIndexedPcx(image, palette, fileName, adapter);
    }

}

#endif