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: */