// Scintilla source code edit control /** @file XPM.cxx ** Define a class that holds data in the X Pixmap (XPM) format. **/ // Copyright 1998-2003 by Neil Hodgson // The License.txt file describes the conditions under which this software may be distributed. #include #include #include #include #include "Platform.h" #include "XPM.h" #ifdef SCI_NAMESPACE using namespace Scintilla; #endif static const char *NextField(const char *s) { // In case there are leading spaces in the string while (*s && *s == ' ') { s++; } while (*s && *s != ' ') { s++; } while (*s && *s == ' ') { s++; } return s; } // Data lines in XPM can be terminated either with NUL or " static size_t MeasureLength(const char *s) { size_t i = 0; while (s[i] && (s[i] != '\"')) i++; return i; } ColourDesired XPM::ColourDesiredFromCode(int ch) const { return *colourCodeTable[ch]; } ColourDesired XPM::ColourFromCode(int ch) const { return *colourCodeTable[ch]; } void XPM::FillRun(Surface *surface, int code, int startX, int y, int x) { if ((code != codeTransparent) && (startX != x)) { PRectangle rc(startX, y, x, y+1); surface->FillRectangle(rc, ColourFromCode(code)); } } XPM::XPM(const char *textForm) : data(0), codes(0), colours(0), lines(0) { Init(textForm); } XPM::XPM(const char *const *linesForm) : data(0), codes(0), colours(0), lines(0) { Init(linesForm); } XPM::~XPM() { Clear(); } void XPM::Init(const char *textForm) { Clear(); // Test done is two parts to avoid possibility of overstepping the memory // if memcmp implemented strangely. Must be 4 bytes at least at destination. if ((0 == memcmp(textForm, "/* X", 4)) && (0 == memcmp(textForm, "/* XPM */", 9))) { // Build the lines form out of the text form const char **linesForm = LinesFormFromTextForm(textForm); if (linesForm != 0) { Init(linesForm); delete []linesForm; } } else { // It is really in line form Init(reinterpret_cast(textForm)); } } void XPM::Init(const char *const *linesForm) { Clear(); height = 1; width = 1; nColours = 1; data = NULL; codeTransparent = ' '; codes = NULL; colours = NULL; lines = NULL; if (!linesForm) return; const char *line0 = linesForm[0]; width = atoi(line0); line0 = NextField(line0); height = atoi(line0); line0 = NextField(line0); nColours = atoi(line0); line0 = NextField(line0); if (atoi(line0) != 1) { // Only one char per pixel is supported return; } codes = new char[nColours]; colours = new ColourDesired[nColours]; int strings = 1+height+nColours; lines = new char *[strings]; size_t allocation = 0; for (int i=0; i(codes[c])] = &(colours[c]); } } void XPM::Clear() { delete []data; data = 0; delete []codes; codes = 0; delete []colours; colours = 0; delete []lines; lines = 0; } void XPM::Draw(Surface *surface, PRectangle &rc) { if (!data || !codes || !colours || !lines) { return; } // Centre the pixmap int startY = rc.top + (rc.Height() - height) / 2; int startX = rc.left + (rc.Width() - width) / 2; for (int y=0; y= width) || (y<0) || (y >= height)) { colour = 0; transparent = true; return; } int code = lines[y+nColours+1][x]; transparent = code == codeTransparent; if (transparent) { colour = 0; } else { colour = ColourDesiredFromCode(code).AsLong(); } } const char **XPM::LinesFormFromTextForm(const char *textForm) { // Build the lines form out of the text form const char **linesForm = 0; int countQuotes = 0; int strings=1; int j=0; for (; countQuotes < (2*strings) && textForm[j] != '\0'; j++) { if (textForm[j] == '\"') { if (countQuotes == 0) { // First field: width, height, number of colors, chars per pixel const char *line0 = textForm + j + 1; // Skip width line0 = NextField(line0); // Add 1 line for each pixel of height strings += atoi(line0); line0 = NextField(line0); // Add 1 line for each colour strings += atoi(line0); linesForm = new const char *[strings]; if (linesForm == 0) { break; // Memory error! } } if (countQuotes / 2 >= strings) { break; // Bad height or number of colors! } if ((countQuotes & 1) == 0) { linesForm[countQuotes / 2] = textForm + j + 1; } countQuotes++; } } if (textForm[j] == '\0' || countQuotes / 2 > strings) { // Malformed XPM! Height + number of colors too high or too low delete []linesForm; linesForm = 0; } return linesForm; } // In future, may want to minimize search time by sorting and using a binary search. XPMSet::XPMSet() : set(0), len(0), maximum(0), height(-1), width(-1) { } XPMSet::~XPMSet() { Clear(); } void XPMSet::Clear() { for (int i = 0; i < len; i++) { delete set[i]; } delete []set; set = 0; len = 0; maximum = 0; height = -1; width = -1; } void XPMSet::Add(int ident, const char *textForm) { // Invalidate cached dimensions height = -1; width = -1; // Replace if this id already present for (int i = 0; i < len; i++) { if (set[i]->GetId() == ident) { set[i]->Init(textForm); return; } } // Not present, so add to end XPM *pxpm = new XPM(textForm); if (pxpm) { pxpm->SetId(ident); if (len == maximum) { maximum += 64; XPM **setNew = new XPM *[maximum]; for (int i = 0; i < len; i++) { setNew[i] = set[i]; } delete []set; set = setNew; } set[len] = pxpm; len++; } } XPM *XPMSet::Get(int ident) { for (int i = 0; i < len; i++) { if (set[i]->GetId() == ident) { return set[i]; } } return 0; } int XPMSet::GetHeight() { if (height < 0) { for (int i = 0; i < len; i++) { if (height < set[i]->GetHeight()) { height = set[i]->GetHeight(); } } } return (height > 0) ? height : 0; } int XPMSet::GetWidth() { if (width < 0) { for (int i = 0; i < len; i++) { if (width < set[i]->GetWidth()) { width = set[i]->GetWidth(); } } } return (width > 0) ? width : 0; } RGBAImage::RGBAImage(int width_, int height_, float scale_, const unsigned char *pixels_) : height(height_), width(width_), scale(scale_) { if (pixels_) { pixelBytes.assign(pixels_, pixels_ + CountBytes()); } else { pixelBytes.resize(CountBytes()); } } RGBAImage::RGBAImage(const XPM &xpm) { height = xpm.GetHeight(); width = xpm.GetWidth(); scale = 1; pixelBytes.resize(CountBytes()); for (int y=0; y(colour.GetRed()); pixel[1] = static_cast(colour.GetGreen()); pixel[2] = static_cast(colour.GetBlue()); pixel[3] = static_cast(alpha); } RGBAImageSet::RGBAImageSet() : height(-1), width(-1){ } RGBAImageSet::~RGBAImageSet() { Clear(); } /// Remove all images. void RGBAImageSet::Clear() { for (ImageMap::iterator it=images.begin(); it != images.end(); ++it) { delete it->second; it->second = 0; } images.clear(); height = -1; width = -1; } /// Add an image. void RGBAImageSet::Add(int ident, RGBAImage *image) { ImageMap::iterator it=images.find(ident); if (it == images.end()) { images[ident] = image; } else { delete it->second; it->second = image; } height = -1; width = -1; } /// Get image by id. RGBAImage *RGBAImageSet::Get(int ident) { ImageMap::iterator it = images.find(ident); if (it != images.end()) { return it->second; } return NULL; } /// Give the largest height of the set. int RGBAImageSet::GetHeight() const { if (height < 0) { for (ImageMap::const_iterator it=images.begin(); it != images.end(); ++it) { if (height < it->second->GetHeight()) { height = it->second->GetHeight(); } } } return (height > 0) ? height : 0; } /// Give the largest width of the set. int RGBAImageSet::GetWidth() const { if (width < 0) { for (ImageMap::const_iterator it=images.begin(); it != images.end(); ++it) { if (width < it->second->GetWidth()) { width = it->second->GetWidth(); } } } return (width > 0) ? width : 0; }