Download | Plain Text | Line Numbers
/**
* @module CPixmap
* @author Guenther Neuwirth (0626638), Manuel Mausz (0728348)
* @brief Implementation of CFile handling Windows Bitmaps.
* @date 27.04.2009
*/
#include <sstream>
#include <iomanip>
#include <vector>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <assert.h>
#ifdef DEBUG
# include <iostream>
#endif
#include "cpixmap.h"
#include "cpixelformat_indexed8.h"
using namespace std;
using namespace boost;
CPixmap::CPixmap()
: m_imagename("")
{
m_types.insert("XPM");
/* add our handlers */
m_handlers.insert(new CPixelFormat_Indexed8(this));
}
/*----------------------------------------------------------------------------*/
std::string CPixmap::getLine(std::ifstream& in, bool ignore_comments)
{
string line("");
while(!in.eof() && in.good())
{
getline(in, line);
trim(line);
/* ignore simple ansi c comments. only one-liners */
if (ignore_comments && line.find_first_of("/*") == 0)
continue;
if (!line.empty())
break;
}
return line;
}
/*----------------------------------------------------------------------------*/
std::string CPixmap::getCArrayLine(std::ifstream& in)
{
string line = getLine(in, true);
if (line.empty())
return line;
/* this stuff isn't correct too, but we are no c-parser anyway */
if (line[0] != '"')
throw FileError("Pixmap array has invalid c-syntax.");
if (line[line.length() - 1] != ',' && line[line.length() - 1] != '}')
throw FileError("Pixmap array has invalid c-syntax.");
size_t end = line.find_last_of("\"");
if (end == string::npos)
throw FileError("Pixmap array has invalid c-syntax.");
return line.substr(1, end - 1);
}
/*----------------------------------------------------------------------------*/
void CPixmap::read(std::ifstream& in)
{
/*
* <C-Comment> XPM <C-Comment>
* static char * <pixmap_name>[] = {
* <Values>
* <Colors>
* <Pixels>
* <Extensions>
* };
*/
string line, tmp;
stringstream istr;
std::vector<std::string> list;
m_fileheader._XPMEXT = false;
m_fileheader._HOTSPOT = false;
/* get pixelformat instance first */
m_pixelformat = NULL;
set<CPixelFormat *>::iterator it;
for (it = m_handlers.begin(); it != m_handlers.end(); it++)
{
/* we only have one! */
m_pixelformat = *it;
break;
}
if (m_pixelformat == NULL)
throw FileError("Pixmap color mode is not supported.");
/* first line has to be PIXMAP_IDENTIFIER */
line = getLine(in, false);
if (line != PIXMAP_IDENTIFIER)
throw FileError("Pixmap has no identifier.");
/* second line is a c array. we don't do much syntax checking */
line = getLine(in);
istr.str(line);
while(m_imagename.empty() && !istr.eof() && istr.good())
{
size_t end;
istr >> tmp;
if ((end = tmp.find_first_of("[]")) != string::npos)
{
/* <xxx>*<imagename>[]<yyy> */
size_t start = tmp.find_first_of('*');
start = (start == string::npos) ? 0 : start + 1;
m_imagename = tmp.substr(start, end - start);
}
}
if (m_imagename.empty())
throw FileError("Pixmap has no imagename.");
/* additional: check "{" exists */
assert(!line.empty());
if (line[line.length() - 1] != '{')
throw FileError("Pixmap array has no opening bracket.");
/* parse <Values>-section */
line = getCArrayLine(in);
if (line.empty())
throw FileError("Pixmap has no Values-section.");
algorithm::split(list, line, is_any_of(" \t"));
if (list.size() < 4)
throw FileError("Pixmap has invalid Values-section.");
try
{
m_fileheader.width = lexical_cast<uint32_t>(list[0]);
m_fileheader.height = lexical_cast<uint32_t>(list[1]);
m_fileheader.nColor = lexical_cast<uint32_t>(list[2]);
m_fileheader.nChar = lexical_cast<uint32_t>(list[3]);
if (list.size() > 4)
{
if (list.size() >= 6)
{
m_fileheader._HOTSPOT = true;
m_fileheader.xHotspot = lexical_cast<uint32_t>(list[4]);
m_fileheader.yHotspot = lexical_cast<uint32_t>(list[5]);
}
if (list.size() != 6)
{
if (list[list.size() - 1] != "XPMEXT")
throw FileError("Unknown parameter count in Values-Section.");
else
m_fileheader._XPMEXT = true;
}
}
}
catch(bad_lexical_cast& ex)
{
throw FileError("Value of Values-section is invalid: " + string(ex.what()));
}
/* parse <Colors>-table */
string character;
/* map[id][colortype] = color */
map<string, map<string, CPixelFormat::RGBPIXEL *> > colors;
/* map[id] = indices */
map<string, uint32_t> colornr;
uint32_t index = 0;
for(uint32_t i = 0; i < m_fileheader.nColor; i++)
{
line = getCArrayLine(in);
if (line.empty())
throw FileError("Pixmap has missing colortable-entry.");
algorithm::split(list, line, is_any_of(" \t"));
if (list.size() < 3)
throw FileError("Pixmap colortable-entry is invalid.");
/* read pixel character */
character = list[0];
if (character.length() != m_fileheader.nChar)
throw FileError("Pixmap colorcharacter is invalid.");
if (colors.find(character) != colors.end())
throw FileError("Duplicate colorcharacter found.");
/* read colors */
if ((list.size() - 1) % 2 != 0)
throw FileError("Pixmap color entrys are invalid.");
for(uint32_t j = 1; j < list.size(); j = j + 2)
{
/* we only support hex-color notations */
if (list[j + 1].length() != 7)
throw FileError("Pixmap color value is invalid.");
if (list[j + 1].at(0) != '#')
throw FileError("Pixmap color table value is not hexadecimal.");
/* we only support c-colors! - remove only if you free the pixels */
if (list[j] != "c")
continue;
CPixelFormat::RGBPIXEL *pixel = new CPixelFormat::RGBPIXEL;
pixel->red = strtoul(list[j + 1].substr(1, 2).c_str(), NULL, 16);
pixel->green = strtoul(list[j + 1].substr(3, 2).c_str(), NULL, 16);
pixel->blue = strtoul(list[j + 1].substr(5, 2).c_str(), NULL, 16);
colors[ character ][ list[j] ] = pixel;
}
/* we only support c-colors! */
if (colors[ character ].find("c") == colors[ character ].end())
throw FileError("Pixmap color entry has missing c-value.");
/* add pixel to colortable */
colornr[ character ] = index;
m_colortable[ index ] = colors[ character ]["c"];
index++;
}
/* read pixel data */
if (getPixelDataSize() > 0)
{
if (m_pixeldata != NULL)
delete[] m_pixeldata;
m_pixeldata = new uint8_t[getPixelDataSize()];
for (uint32_t y = 0; y < getHeight(); y++)
{
line = getCArrayLine(in);
if (line.empty())
throw FileError("Pixmap has no pixel data.");
if (line.length() != getWidth())
throw FileError("Pixmap pixeldata width is larger than header width.");
/* convert color identifier to our own identifiers */
for(uint32_t x = 0; x < getWidth(); x++)
{
character = line.substr(x * m_fileheader.nChar, m_fileheader.nChar);
assert(!character.empty());
if (colornr.find(character) == colornr.end())
throw FileError("Pixel has no reference in colortable.");
uint32_t offset = y * getWidth() + x;
/* boundary check */
if (offset * sizeof(uint32_t) + sizeof(uint32_t) > getPixelDataSize())
throw FileError("Pixel position is out of range.");
*((uint32_t *)m_pixeldata + offset) = colornr[ character ];
}
}
}
/* get extension */
if (m_fileheader._XPMEXT)
getline(in, m_fileheader.extension, '}');
if (!in.good())
throw FileError("Pixmap array isn't closed properly.");
/* set rowsize */
m_rowsize = sizeof(uint32_t) * getWidth();
}
/*----------------------------------------------------------------------------*/
const std::string CPixmap::getXPMColorID(unsigned int index, unsigned int length)
{
static const char code[] = PIXMAP_COLORCHARS;
assert(strlen(code) > 0);
assert(length > 0);
string str("");
for(unsigned int i = length - 1; i > 0; i--)
{
str += code[index % strlen(code)];
index /= strlen(code);
}
str += code[index];
assert(!str.empty());
return str;
}
/*----------------------------------------------------------------------------*/
void CPixmap::write(std::ofstream& out)
{
m_fileheader.nColor = m_colortable.size();
m_fileheader.nChar = m_fileheader.nColor / strlen(PIXMAP_COLORCHARS) + 1;
/* header comment */
out << PIXMAP_IDENTIFIER << endl;
/* variables*/
assert(!m_imagename.empty());
out << "static char * " << m_imagename << "[] = {" << endl;
out << "\"" << m_fileheader.width << " " << m_fileheader.height
<< " " << m_fileheader.nColor << " " << m_fileheader.nChar;
/* optional values */
if (m_fileheader._HOTSPOT)
out << " " << m_fileheader.xHotspot << " " << m_fileheader.yHotspot;
if (m_fileheader._XPMEXT)
out << " " << "XPMEXT";
out << "\"," << endl;
/* color table */
map<uint32_t, CPixelFormat::RGBPIXEL *>::iterator it;
for (it = m_colortable.begin(); it != m_colortable.end(); it++)
{
out << "\"" << getXPMColorID((*it).first, m_fileheader.nChar);
/* we only support c-colors! */
out << "\tc #";
out << setfill('0') << setw(2) << hex << uppercase << (*it).second->red
<< setfill('0') << setw(2) << hex << uppercase << (*it).second->green
<< setfill('0') << setw(2) << hex << uppercase << (*it).second->blue;
out << "\"," << endl;
}
/* pixel data */
for (uint32_t y = 0; y < getHeight(); y++)
{
out << "\"";
for(uint32_t x = 0; x < getWidth(); x++)
{
uint32_t offset = y * getWidth() + x;
/* boundary check */
if (offset * sizeof(uint32_t) + sizeof(uint32_t) > getPixelDataSize())
throw FileError("Pixel position is out of range.");
uint32_t color = *((uint32_t *)m_pixeldata + offset);
if ((it = m_colortable.find(color)) == m_colortable.end())
throw FileError("Pixel has no reference in colortable.");
out << getXPMColorID((*it).first, m_fileheader.nChar);
}
out << "\"," << endl;
}
/* extension */
if (m_fileheader._XPMEXT)
out << m_fileheader.extension;
out <<"};";
}
/*----------------------------------------------------------------------------*/
#ifdef DEBUG
void CPixmap::dump(std::ostream& out)
{
/* values*/
cout << "[XPM Header Values]" << endl
<< "width=" << m_fileheader.width << endl
<< "height=" << m_fileheader.height << endl
<< "nColor=" << m_fileheader.nColor << endl
<< "nChar=" << m_fileheader.nChar << endl
<< "Hotspot=" << m_fileheader.xHotspot << endl
<< "yHotspot=" << m_fileheader.yHotspot << endl
<< "_HOTSPOT=" << m_fileheader._HOTSPOT << endl
<< "_XPMEXT=" << m_fileheader._XPMEXT << endl
<< "extension=" << m_fileheader.extension << endl
<< endl;
/* colors*/
map<uint32_t, CPixelFormat::RGBPIXEL *>::iterator it;
cout << "[Color Table]" << endl;
for (it = m_colortable.begin(); it != m_colortable.end(); it++)
{
out << (*it).first << ": "
<< setfill('0') << setw(3) << (*it).second->red << " "
<< setfill('0') << setw(3) << (*it).second->green << " "
<< setfill('0') << setw(3) << (*it).second->blue << " "
<< endl;
}
}
#endif
/* vim: set et sw=2 ts=2: */