/** * @module CPixmap * @author Guenther Neuwirth (0626638), Manuel Mausz (0728348) * @brief Implementation of CFile handling Windows Bitmaps. * @date 27.04.2009 */ #include #include #include #include #include #include #include #ifdef DEBUG # include #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) { /* * XPM * static char * [] = { * * * * * }; */ string line, tmp; stringstream istr; std::vector list; m_fileheader._XPMEXT = false; m_fileheader._HOTSPOT = false; /* get pixelformat instance first */ m_pixelformat = NULL; set::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) { /* *[] */ 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 -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(list[0]); m_fileheader.height = lexical_cast(list[1]); m_fileheader.nColor = lexical_cast(list[2]); m_fileheader.nChar = lexical_cast(list[3]); if (list.size() > 4) { if (list.size() >= 6) { m_fileheader._HOTSPOT = true; m_fileheader.xHotspot = lexical_cast(list[4]); m_fileheader.yHotspot = lexical_cast(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 -table */ string character; /* map[id][colortype] = color */ map > colors; /* map[id] = indices */ map 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::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::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: */