Download | Plain Text | No Line Numbers


  1. /**
  2.  * @module CPixmap
  3.  * @author Guenther Neuwirth (0626638), Manuel Mausz (0728348)
  4.  * @brief Implementation of CFile handling Windows Bitmaps.
  5.  * @date 27.04.2009
  6.  */
  7.  
  8. #include <sstream>
  9. #include <iomanip>
  10. #include <vector>
  11. #include <boost/algorithm/string/split.hpp>
  12. #include <boost/algorithm/string.hpp>
  13. #include <boost/lexical_cast.hpp>
  14. #include <assert.h>
  15. #ifdef DEBUG
  16. # include <iostream>
  17. #endif
  18. #include "cpixmap.h"
  19. #include "cpixelformat_indexed8.h"
  20.  
  21. using namespace std;
  22. using namespace boost;
  23.  
  24. CPixmap::CPixmap()
  25. : m_imagename("")
  26. {
  27. m_types.insert("XPM");
  28.  
  29. /* add our handlers */
  30. m_handlers.insert(new CPixelFormat_Indexed8(this));
  31. }
  32.  
  33. /*----------------------------------------------------------------------------*/
  34.  
  35. std::string CPixmap::getLine(std::ifstream& in, bool ignore_comments)
  36. {
  37. string line("");
  38. while(!in.eof() && in.good())
  39. {
  40. getline(in, line);
  41. trim(line);
  42. /* ignore simple ansi c comments. only one-liners */
  43. if (ignore_comments && line.find_first_of("/*") == 0)
  44. continue;
  45. if (!line.empty())
  46. break;
  47. }
  48. return line;
  49. }
  50.  
  51. /*----------------------------------------------------------------------------*/
  52.  
  53. std::string CPixmap::getCArrayLine(std::ifstream& in)
  54. {
  55. string line = getLine(in, true);
  56. if (line.empty())
  57. return line;
  58.  
  59. /* this stuff isn't correct too, but we are no c-parser anyway */
  60. if (line[0] != '"')
  61. throw FileError("Pixmap array has invalid c-syntax.");
  62. if (line[line.length() - 1] != ',' && line[line.length() - 1] != '}')
  63. throw FileError("Pixmap array has invalid c-syntax.");
  64. size_t end = line.find_last_of("\"");
  65. if (end == string::npos)
  66. throw FileError("Pixmap array has invalid c-syntax.");
  67.  
  68. return line.substr(1, end - 1);
  69. }
  70.  
  71. /*----------------------------------------------------------------------------*/
  72.  
  73. void CPixmap::read(std::ifstream& in)
  74. {
  75. /*
  76.   * <C-Comment> XPM <C-Comment>
  77.   * static char * <pixmap_name>[] = {
  78.   * <Values>
  79.   * <Colors>
  80.   * <Pixels>
  81.   * <Extensions>
  82.   * };
  83.   */
  84.  
  85. string line, tmp;
  86. stringstream istr;
  87. std::vector<std::string> list;
  88. m_fileheader._XPMEXT = false;
  89. m_fileheader._HOTSPOT = false;
  90.  
  91. /* get pixelformat instance first */
  92. m_pixelformat = NULL;
  93. set<CPixelFormat *>::iterator it;
  94. for (it = m_handlers.begin(); it != m_handlers.end(); it++)
  95. {
  96. /* we only have one! */
  97. m_pixelformat = *it;
  98. break;
  99. }
  100. if (m_pixelformat == NULL)
  101. throw FileError("Pixmap color mode is not supported.");
  102.  
  103. /* first line has to be PIXMAP_IDENTIFIER */
  104. line = getLine(in, false);
  105. if (line != PIXMAP_IDENTIFIER)
  106. throw FileError("Pixmap has no identifier.");
  107.  
  108. /* second line is a c array. we don't do much syntax checking */
  109. line = getLine(in);
  110. istr.str(line);
  111. while(m_imagename.empty() && !istr.eof() && istr.good())
  112. {
  113. size_t end;
  114. istr >> tmp;
  115. if ((end = tmp.find_first_of("[]")) != string::npos)
  116. {
  117. /* <xxx>*<imagename>[]<yyy> */
  118. size_t start = tmp.find_first_of('*');
  119. start = (start == string::npos) ? 0 : start + 1;
  120. m_imagename = tmp.substr(start, end - start);
  121. }
  122. }
  123. if (m_imagename.empty())
  124. throw FileError("Pixmap has no imagename.");
  125.  
  126. /* additional: check "{" exists */
  127. assert(!line.empty());
  128. if (line[line.length() - 1] != '{')
  129. throw FileError("Pixmap array has no opening bracket.");
  130.  
  131. /* parse <Values>-section */
  132. line = getCArrayLine(in);
  133. if (line.empty())
  134. throw FileError("Pixmap has no Values-section.");
  135. algorithm::split(list, line, is_any_of(" \t"));
  136. if (list.size() < 4)
  137. throw FileError("Pixmap has invalid Values-section.");
  138. try
  139. {
  140. m_fileheader.width = lexical_cast<uint32_t>(list[0]);
  141. m_fileheader.height = lexical_cast<uint32_t>(list[1]);
  142. m_fileheader.nColor = lexical_cast<uint32_t>(list[2]);
  143. m_fileheader.nChar = lexical_cast<uint32_t>(list[3]);
  144.  
  145. if (list.size() > 4)
  146. {
  147. if (list.size() >= 6)
  148. {
  149. m_fileheader._HOTSPOT = true;
  150. m_fileheader.xHotspot = lexical_cast<uint32_t>(list[4]);
  151. m_fileheader.yHotspot = lexical_cast<uint32_t>(list[5]);
  152. }
  153. if (list.size() != 6)
  154. {
  155. if (list[list.size() - 1] != "XPMEXT")
  156. throw FileError("Unknown parameter count in Values-Section.");
  157. else
  158. m_fileheader._XPMEXT = true;
  159. }
  160. }
  161. }
  162. catch(bad_lexical_cast& ex)
  163. {
  164. throw FileError("Value of Values-section is invalid: " + string(ex.what()));
  165. }
  166.  
  167. /* parse <Colors>-table */
  168. string character;
  169. /* map[id][colortype] = color */
  170. map<string, map<string, CPixelFormat::RGBPIXEL *> > colors;
  171. /* map[id] = indices */
  172. map<string, uint32_t> colornr;
  173. uint32_t index = 0;
  174. for(uint32_t i = 0; i < m_fileheader.nColor; i++)
  175. {
  176. line = getCArrayLine(in);
  177. if (line.empty())
  178. throw FileError("Pixmap has missing colortable-entry.");
  179. algorithm::split(list, line, is_any_of(" \t"));
  180. if (list.size() < 3)
  181. throw FileError("Pixmap colortable-entry is invalid.");
  182.  
  183. /* read pixel character */
  184. character = list[0];
  185. if (character.length() != m_fileheader.nChar)
  186. throw FileError("Pixmap colorcharacter is invalid.");
  187. if (colors.find(character) != colors.end())
  188. throw FileError("Duplicate colorcharacter found.");
  189.  
  190. /* read colors */
  191. if ((list.size() - 1) % 2 != 0)
  192. throw FileError("Pixmap color entrys are invalid.");
  193. for(uint32_t j = 1; j < list.size(); j = j + 2)
  194. {
  195. /* we only support hex-color notations */
  196. if (list[j + 1].length() != 7)
  197. throw FileError("Pixmap color value is invalid.");
  198. if (list[j + 1].at(0) != '#')
  199. throw FileError("Pixmap color table value is not hexadecimal.");
  200.  
  201. /* we only support c-colors! - remove only if you free the pixels */
  202. if (list[j] != "c")
  203. continue;
  204.  
  205. CPixelFormat::RGBPIXEL *pixel = new CPixelFormat::RGBPIXEL;
  206. pixel->red = strtoul(list[j + 1].substr(1, 2).c_str(), NULL, 16);
  207. pixel->green = strtoul(list[j + 1].substr(3, 2).c_str(), NULL, 16);
  208. pixel->blue = strtoul(list[j + 1].substr(5, 2).c_str(), NULL, 16);
  209. colors[ character ][ list[j] ] = pixel;
  210. }
  211.  
  212. /* we only support c-colors! */
  213. if (colors[ character ].find("c") == colors[ character ].end())
  214. throw FileError("Pixmap color entry has missing c-value.");
  215.  
  216. /* add pixel to colortable */
  217. colornr[ character ] = index;
  218. m_colortable[ index ] = colors[ character ]["c"];
  219. index++;
  220. }
  221.  
  222. /* read pixel data */
  223. if (getPixelDataSize() > 0)
  224. {
  225. if (m_pixeldata != NULL)
  226. delete[] m_pixeldata;
  227. m_pixeldata = new uint8_t[getPixelDataSize()];
  228.  
  229. for (uint32_t y = 0; y < getHeight(); y++)
  230. {
  231. line = getCArrayLine(in);
  232. if (line.empty())
  233. throw FileError("Pixmap has no pixel data.");
  234. if (line.length() != getWidth())
  235. throw FileError("Pixmap pixeldata width is larger than header width.");
  236.  
  237. /* convert color identifier to our own identifiers */
  238. for(uint32_t x = 0; x < getWidth(); x++)
  239. {
  240. character = line.substr(x * m_fileheader.nChar, m_fileheader.nChar);
  241. assert(!character.empty());
  242. if (colornr.find(character) == colornr.end())
  243. throw FileError("Pixel has no reference in colortable.");
  244.  
  245. uint32_t offset = y * getWidth() + x;
  246.  
  247. /* boundary check */
  248. if (offset * sizeof(uint32_t) + sizeof(uint32_t) > getPixelDataSize())
  249. throw FileError("Pixel position is out of range.");
  250.  
  251. *((uint32_t *)m_pixeldata + offset) = colornr[ character ];
  252. }
  253. }
  254. }
  255.  
  256. /* get extension */
  257. if (m_fileheader._XPMEXT)
  258. getline(in, m_fileheader.extension, '}');
  259. if (!in.good())
  260. throw FileError("Pixmap array isn't closed properly.");
  261.  
  262. /* set rowsize */
  263. m_rowsize = sizeof(uint32_t) * getWidth();
  264. }
  265.  
  266. /*----------------------------------------------------------------------------*/
  267.  
  268. const std::string CPixmap::getXPMColorID(unsigned int index, unsigned int length)
  269. {
  270. static const char code[] = PIXMAP_COLORCHARS;
  271. assert(strlen(code) > 0);
  272. assert(length > 0);
  273. string str("");
  274. for(unsigned int i = length - 1; i > 0; i--)
  275. {
  276. str += code[index % strlen(code)];
  277. index /= strlen(code);
  278. }
  279. str += code[index];
  280. assert(!str.empty());
  281. return str;
  282. }
  283.  
  284. /*----------------------------------------------------------------------------*/
  285.  
  286. void CPixmap::write(std::ofstream& out)
  287. {
  288. m_fileheader.nColor = m_colortable.size();
  289. m_fileheader.nChar = m_fileheader.nColor / strlen(PIXMAP_COLORCHARS) + 1;
  290.  
  291. /* header comment */
  292. out << PIXMAP_IDENTIFIER << endl;
  293.  
  294. /* variables*/
  295. assert(!m_imagename.empty());
  296. out << "static char * " << m_imagename << "[] = {" << endl;
  297. out << "\"" << m_fileheader.width << " " << m_fileheader.height
  298. << " " << m_fileheader.nColor << " " << m_fileheader.nChar;
  299.  
  300. /* optional values */
  301. if (m_fileheader._HOTSPOT)
  302. out << " " << m_fileheader.xHotspot << " " << m_fileheader.yHotspot;
  303. if (m_fileheader._XPMEXT)
  304. out << " " << "XPMEXT";
  305. out << "\"," << endl;
  306.  
  307. /* color table */
  308. map<uint32_t, CPixelFormat::RGBPIXEL *>::iterator it;
  309. for (it = m_colortable.begin(); it != m_colortable.end(); it++)
  310. {
  311. out << "\"" << getXPMColorID((*it).first, m_fileheader.nChar);
  312. /* we only support c-colors! */
  313. out << "\tc #";
  314. out << setfill('0') << setw(2) << hex << uppercase << (*it).second->red
  315. << setfill('0') << setw(2) << hex << uppercase << (*it).second->green
  316. << setfill('0') << setw(2) << hex << uppercase << (*it).second->blue;
  317. out << "\"," << endl;
  318. }
  319.  
  320. /* pixel data */
  321. for (uint32_t y = 0; y < getHeight(); y++)
  322. {
  323. out << "\"";
  324. for(uint32_t x = 0; x < getWidth(); x++)
  325. {
  326. uint32_t offset = y * getWidth() + x;
  327.  
  328. /* boundary check */
  329. if (offset * sizeof(uint32_t) + sizeof(uint32_t) > getPixelDataSize())
  330. throw FileError("Pixel position is out of range.");
  331.  
  332. uint32_t color = *((uint32_t *)m_pixeldata + offset);
  333.  
  334. if ((it = m_colortable.find(color)) == m_colortable.end())
  335. throw FileError("Pixel has no reference in colortable.");
  336. out << getXPMColorID((*it).first, m_fileheader.nChar);
  337. }
  338. out << "\"," << endl;
  339. }
  340.  
  341. /* extension */
  342. if (m_fileheader._XPMEXT)
  343. out << m_fileheader.extension;
  344.  
  345. out <<"};";
  346. }
  347.  
  348. /*----------------------------------------------------------------------------*/
  349.  
  350. #ifdef DEBUG
  351. void CPixmap::dump(std::ostream& out)
  352. {
  353. /* values*/
  354. cout << "[XPM Header Values]" << endl
  355. << "width=" << m_fileheader.width << endl
  356. << "height=" << m_fileheader.height << endl
  357. << "nColor=" << m_fileheader.nColor << endl
  358. << "nChar=" << m_fileheader.nChar << endl
  359. << "Hotspot=" << m_fileheader.xHotspot << endl
  360. << "yHotspot=" << m_fileheader.yHotspot << endl
  361. << "_HOTSPOT=" << m_fileheader._HOTSPOT << endl
  362. << "_XPMEXT=" << m_fileheader._XPMEXT << endl
  363. << "extension=" << m_fileheader.extension << endl
  364. << endl;
  365.  
  366. /* colors*/
  367. map<uint32_t, CPixelFormat::RGBPIXEL *>::iterator it;
  368. cout << "[Color Table]" << endl;
  369. for (it = m_colortable.begin(); it != m_colortable.end(); it++)
  370. {
  371. out << (*it).first << ": "
  372. << setfill('0') << setw(3) << (*it).second->red << " "
  373. << setfill('0') << setw(3) << (*it).second->green << " "
  374. << setfill('0') << setw(3) << (*it).second->blue << " "
  375. << endl;
  376. }
  377. }
  378. #endif
  379.  
  380. /* vim: set et sw=2 ts=2: */
  381.