#ifndef PASTELGFX_LOADPCX_HPP
#define PASTELGFX_LOADPCX_HPP
#include "pastel/gfx/image_file/pcx/loadpcx.h"
#include "pastel/sys/array.h"
#include "pastel/sys/abstractarray.h"
#include "pastel/sys/adaptedarray.h"
#include "pastel/sys/ensure.h"
#include "pastel/sys/endian.h"
#include "pastel/sys/binaryfile.h"
#include "pastel/gfx/image_file/pcx/pcxheader.h"
#include "pastel/gfx/color/color_tools.h"
#include "pastel/gfx/color/coloradapter.h"
#include "pastel/gfx/color/color_palette.h"
#include <memory>
namespace Pastel
{
namespace Pcx_
{
inline bool loadHeader(
const std::string& fileName,
BinaryFile& file,
PcxHeader& pcxHeader,
std::vector<Color>& palette)
{
if (fileName.empty())
{
std::cout << "No fileName given, aborting..."
<< std::endl;
return false;
}
file.open(fileName.c_str(), true, false);
if (!file.isOpen())
{
std::cout << "File " << fileName
<< " could not be opened for reading. Aborting..."
<< std::endl;
return false;
}
// Read the pcx header.
pcxHeader.read(file);
// Make sure that the encoding
// uses run-length encoding
// (there is no other known encoding
// technique for pcx's).
if (pcxHeader.encoding_ != 1)
{
std::cout << "Unsupported encoding type. Is "
<< (integer)pcxHeader.encoding_
<< ", must be 1. Aborting..."
<< std::endl;
return false;
}
// Make sure the bits per pixel
// is in legal range.
if (pcxHeader.bitsPerPixel_ != 1 &&
pcxHeader.bitsPerPixel_ != 2 &&
pcxHeader.bitsPerPixel_ != 4 &&
pcxHeader.bitsPerPixel_ != 8)
{
std::cout << "Unsupported number of bits per pixel. Is "
<< (integer)pcxHeader.bitsPerPixel_
<< ", must be 1, 2, 4, or 8. Aborting..."
<< std::endl;
return false;
}
// Make sure the number of color planes
// is in legal range.
if (pcxHeader.colorPlanes_ != 1 &&
pcxHeader.colorPlanes_ != 3)
{
std::cout << "Unsupported number of color planes. Is "
<< (integer)pcxHeader.colorPlanes_ << ", must be 1 or 3. Aborting..."
<< std::endl;
return false;
}
if (pcxHeader.colorPlanes_ == 3 && pcxHeader.bitsPerPixel_ != 8)
{
std::cout << "Unsupported color planes - bits per pixel combination: "
<< "3 color planes, " << (integer)pcxHeader.bitsPerPixel_
<< " bits per pixel. Aborting..."
<< std::endl;
return false;
}
// Read the possible vga palette.
integer colors = (integer)1 << pcxHeader.bitsPerPixel_;
palette.resize(colors);
if (pcxHeader.bitsPerPixel_ == 1)
{
// Black and white palette for binary
// images.
palette[0].set(0);
palette[1].set(1);
}
else if (
pcxHeader.bitsPerPixel_ == 2 ||
pcxHeader.bitsPerPixel_ == 4)
{
// For 2- and 4-bit images, use
// the setExtent of the 16 color palette.
integer toCopy = std::min(colors, (integer)16);
for (integer i = 0;i < toCopy;++i)
{
palette[i] =
Color(
pcxHeader.palette16_[i * 3 + 0],
pcxHeader.palette16_[i * 3 + 1],
pcxHeader.palette16_[i * 3 + 2]) / 255;
}
}
else
{
// For 8-bit images, check if there
// is the extended vga palette available.
bool foundPalette = false;
if (pcxHeader.version_ >= 5)
{
file.setOffset(-769);
uint8 paletteCode = 0;
file >> paletteCode;
if (paletteCode == 12)
{
foundPalette = true;
uint8 red = 0;
uint8 green = 0;
uint8 blue = 0;
for (integer i = 0;i < colors;++i)
{
file >> red >> green >> blue;
palette[i] =
Color(red, green, blue) / 255;
}
}
}
if (!foundPalette)
{
// If there is no palette,
// use the default vga palette.
vgaPalette(palette);
}
}
file.setOffset(128);
return true;
}
inline void loadScanline(
BinaryFile& file,
integer amount,
std::vector<uint8>& scanline)
{
ASSERT1(amount >= 0, amount);
ASSERT2(scanline.size() >= amount, scanline.size(), amount);
integer bytesDecoded = 0;
while (bytesDecoded < amount)
{
uint8 aData = 0;
file >> aData;
// If the two most signifact bits are set,
// we have run-length encoding.
if ((aData & 0xC0) == 0xC0)
{
// The 6 lower bits is
// the repetition count
// Extract repetition count
// It can also be 0.
const integer repetition = aData & 0x3F;
// The next byte represents the
// data that is to be repeated.
uint8 bData = 0;
file >> bData;
for (integer i = 0;i < repetition;++i)
{
scanline[bytesDecoded] = bData;
++bytesDecoded;
}
}
else
{
// This is just normal data.
scanline[bytesDecoded] = aData;
++bytesDecoded;
}
}
}
}
inline bool loadIndexedPcx(
const std::string& fileName,
AbstractArray<2, uint8>& image,
std::vector<Color>* colorPalette)
{
BinaryFile file;
PcxHeader pcxHeader;
std::vector<Color> palette;
if (!Pcx_::loadHeader(fileName, file, pcxHeader, palette))
{
return false;
}
if (pcxHeader.colorPlanes_ != 1)
{
std::cout << "An unsupported number of color planes for an indexed image. Is "
<< (integer)pcxHeader.colorPlanes_ << ", must be 1. Aborting..."
<< std::endl;
return false;
}
integer bytesPerScanline =
(integer)pcxHeader.bytesPerScanlinePerPlane_ * (integer)pcxHeader.colorPlanes_;
std::vector<uint8> scanline(bytesPerScanline, 0);
integer width = pcxHeader.width();
integer height = pcxHeader.height();
image.setExtent(Vector2i(width, height));
for (integer yPos = height - 1;yPos >= 0;--yPos)
{
Pcx_::loadScanline(file, bytesPerScanline, scanline);
switch(pcxHeader.bitsPerPixel_)
{
case 1:
{
integer bytesToRead = (pcxHeader.width() + 7) / 8;
integer xPos = 0;
for (integer i = 0;i < bytesToRead - 1;++i)
{
uint8 data = scanline[i];
image.write(Vector2i(xPos, yPos), (data >> 7) & 1);
++xPos;
image.write(Vector2i(xPos, yPos), (data >> 6) & 1);
++xPos;
image.write(Vector2i(xPos, yPos), (data >> 5) & 1);
++xPos;
image.write(Vector2i(xPos, yPos), (data >> 4) & 1);
++xPos;
image.write(Vector2i(xPos, yPos), (data >> 3) & 1);
++xPos;
image.write(Vector2i(xPos, yPos), (data >> 2) & 1);
++xPos;
image.write(Vector2i(xPos, yPos), (data >> 1) & 1);
++xPos;
image.write(Vector2i(xPos, yPos), (data >> 0) & 1);
++xPos;
}
uint8 data = scanline[bytesToRead - 1];
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), (data >> 7) & 1);
++xPos;
}
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), (data >> 6) & 1);
++xPos;
}
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), (data >> 5) & 1);
++xPos;
}
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), (data >> 4) & 1);
++xPos;
}
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), (data >> 3) & 1);
++xPos;
}
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), (data >> 2) & 1);
++xPos;
}
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), (data >> 1) & 1);
++xPos;
}
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), (data >> 0) & 1);
++xPos;
}
}
break;
case 2:
{
integer bytesToRead = (pcxHeader.width() + 3) / 4;
integer xPos = 0;
for (integer i = 0;i < bytesToRead - 1;++i)
{
uint8 data = scanline[i];
image.write(Vector2i(xPos, yPos), (data >> 6) & 3);
++xPos;
image.write(Vector2i(xPos, yPos), (data >> 4) & 3);
++xPos;
image.write(Vector2i(xPos, yPos), (data >> 2) & 3);
++xPos;
image.write(Vector2i(xPos, yPos), (data >> 0) & 3);
++xPos;
}
uint8 data = scanline[bytesToRead - 1];
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), (data >> 6) & 3);
++xPos;
}
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), (data >> 4) & 3);
++xPos;
}
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), (data >> 2) & 3);
++xPos;
}
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), (data >> 0) & 3);
++xPos;
}
}
break;
case 4:
{
integer bytesToRead = (pcxHeader.width() + 1) / 2;
integer xPos = 0;
for (integer i = 0;i < bytesToRead - 1;++i)
{
uint8 data = scanline[i];
image.write(Vector2i(xPos, yPos), (data >> 4) & 15);
++xPos;
image.write(Vector2i(xPos, yPos), (data >> 0) & 15);
++xPos;
}
uint8 data = scanline[bytesToRead - 1];
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), (data >> 4) & 15);
++xPos;
}
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), (data >> 0) & 15);
++xPos;
}
}
break;
case 8:
{
integer bytesToRead = pcxHeader.width();
integer xPos = 0;
for (integer i = 0;i < bytesToRead;++i)
{
uint8 data = scanline[i];
image.write(Vector2i(xPos, yPos), data);
++xPos;
}
}
break;
};
}
file.close();
if (colorPalette)
{
palette.swap(*colorPalette);
}
return true;
}
inline bool loadPcx(
const std::string& fileName,
AbstractArray<2, Color>& image)
{
BinaryFile file;
PcxHeader pcxHeader;
std::vector<Color> palette;
if (!Pcx_::loadHeader(fileName, file, pcxHeader, palette))
{
return false;
}
integer bytesPerScanline =
(integer)pcxHeader.bytesPerScanlinePerPlane_ * (integer)pcxHeader.colorPlanes_;
std::vector<uint8> scanline(bytesPerScanline, 0);
integer width = pcxHeader.width();
integer height = pcxHeader.height();
image.setExtent(Vector<integer, 2>(width, height));
for (integer yPos = height - 1;yPos >= 0;--yPos)
{
Pcx_::loadScanline(file, bytesPerScanline, scanline);
// Copy scanline to the image
if (pcxHeader.colorPlanes_ == 1)
{
switch(pcxHeader.bitsPerPixel_)
{
case 1:
{
integer bytesToRead = (width + 7) / 8;
integer xPos = 0;
for (integer i = 0;i < bytesToRead - 1;++i)
{
uint8 data = scanline[i];
image.write(Vector2i(xPos, yPos), palette[(data >> 7) & 1]);
++xPos;
image.write(Vector2i(xPos, yPos), palette[(data >> 6) & 1]);
++xPos;
image.write(Vector2i(xPos, yPos), palette[(data >> 5) & 1]);
++xPos;
image.write(Vector2i(xPos, yPos), palette[(data >> 4) & 1]);
++xPos;
image.write(Vector2i(xPos, yPos), palette[(data >> 3) & 1]);
++xPos;
image.write(Vector2i(xPos, yPos), palette[(data >> 2) & 1]);
++xPos;
image.write(Vector2i(xPos, yPos), palette[(data >> 1) & 1]);
++xPos;
image.write(Vector2i(xPos, yPos), palette[(data >> 0) & 1]);
++xPos;
}
uint8 data = scanline[bytesToRead - 1];
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), palette[(data >> 7) & 1]);
++xPos;
}
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), palette[(data >> 6) & 1]);
++xPos;
}
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), palette[(data >> 5) & 1]);
++xPos;
}
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), palette[(data >> 4) & 1]);
++xPos;
}
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), palette[(data >> 3) & 1]);
++xPos;
}
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), palette[(data >> 2) & 1]);
++xPos;
}
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), palette[(data >> 1) & 1]);
++xPos;
}
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), palette[(data >> 0) & 1]);
++xPos;
}
}
break;
case 2:
{
integer bytesToRead = (width + 3) / 4;
integer xPos = 0;
for (integer i = 0;i < bytesToRead - 1;++i)
{
uint8 data = scanline[i];
image.write(Vector2i(xPos, yPos), palette[(data >> 6) & 3]);
++xPos;
image.write(Vector2i(xPos, yPos), palette[(data >> 4) & 3]);
++xPos;
image.write(Vector2i(xPos, yPos), palette[(data >> 2) & 3]);
++xPos;
image.write(Vector2i(xPos, yPos), palette[(data >> 0) & 3]);
++xPos;
}
uint8 data = scanline[bytesToRead - 1];
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), palette[(data >> 6) & 3]);
++xPos;
}
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), palette[(data >> 4) & 3]);
++xPos;
}
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), palette[(data >> 2) & 3]);
++xPos;
}
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), palette[(data >> 0) & 3]);
++xPos;
}
}
break;
case 4:
{
integer bytesToRead = (width + 1) / 2;
integer xPos = 0;
for (integer i = 0;i < bytesToRead - 1;++i)
{
uint8 data = scanline[i];
image.write(Vector2i(xPos, yPos), palette[(data >> 4) & 15]);
++xPos;
image.write(Vector2i(xPos, yPos), palette[(data >> 0) & 15]);
++xPos;
}
uint8 data = scanline[bytesToRead - 1];
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), palette[(data >> 4) & 15]);
++xPos;
}
if (xPos < width)
{
image.write(Vector2i(xPos, yPos), palette[(data >> 0) & 15]);
++xPos;
}
}
break;
case 8:
{
integer bytesToRead = width;
integer xPos = 0;
for (integer i = 0;i < bytesToRead;++i)
{
uint8 data = scanline[i];
image.write(Vector2i(xPos, yPos), palette[data]);
++xPos;
}
}
break;
};
}
else if (pcxHeader.colorPlanes_ == 3)
{
uint8 *scanlineRed = &scanline[0];
uint8 *scanlineGreen =
&scanline[pcxHeader.bytesPerScanlinePerPlane_];
uint8 *scanlineBlue =
&scanline[pcxHeader.bytesPerScanlinePerPlane_ * 2];
for (integer xPos = 0;xPos < width;++xPos)
{
image.write(Vector2i(xPos, yPos),
Color(scanlineRed[xPos],
scanlineGreen[xPos],
scanlineBlue[xPos]) / 255);
}
}
}
file.close();
return true;
}
inline bool loadIndexedPcx(
const std::string& fileName,
Array<bool, 2>& image,
std::vector<Color>* colorPalette)
{
Integer_To_Bool<uint8> adapter;
return loadIndexedPcx(fileName, image, adapter, colorPalette);
}
inline bool loadIndexedPcx(
const std::string& fileName,
Array<uint8, 2>& image,
std::vector<Color>* colorPalette)
{
IdentityAdapter<uint8> adapter;
return loadIndexedPcx(fileName, image, adapter, colorPalette);
}
inline bool loadPcx(
const std::string& fileName,
Array<Color, 2>& image)
{
IdentityAdapter<Color> adapter;
return loadPcx(fileName, image, adapter);
}
inline bool loadPcx(
const std::string& fileName,
Array<real32>& image)
{
Luma_Color_Adapter adapter;
return loadPcx(fileName, image, adapter);
}
inline bool loadPcx(
const std::string& fileName,
Array<ByteColor, 2>& image)
{
ByteColor_To_Color adapter;
return loadPcx(fileName, image, adapter);
}
inline bool loadPcx(
const std::string& fileName,
Array<uint32, 2>& image)
{
Integer_To_Color<uint32, 8, 8, 8> adapter;
return loadPcx(fileName, image, adapter);
}
inline bool loadPcx(
const std::string& fileName,
Array<uint16, 2>& image)
{
Integer_To_Color<uint16, 5, 6, 5> adapter;
return loadPcx(fileName, image, adapter);
}
}
namespace Pastel
{
template <typename Type, typename Adapter>
bool loadPcx(
const std::string& fileName,
Array<Type, 2>& image,
const Adapter& adapter)
{
AdaptedArray<2, Color, Type, Adapter> abstractImage(
image, adapter);
return loadPcx(fileName, abstractImage);
}
template <typename Type, typename Adapter>
bool loadIndexedPcx(
const std::string& fileName,
Array<Type, 2>& image,
const Adapter& adapter,
std::vector<Color>* colorPalette)
{
AdaptedArray<2, uint8, Type, Adapter> abstractImage(
image, adapter);
return loadIndexedPcx(fileName, abstractImage, colorPalette);
}
}
#endif