diff options
Diffstat (limited to 'src/XPM.cpp')
-rw-r--r-- | src/XPM.cpp | 348 |
1 files changed, 348 insertions, 0 deletions
diff --git a/src/XPM.cpp b/src/XPM.cpp new file mode 100644 index 0000000..b8e27f4 --- /dev/null +++ b/src/XPM.cpp @@ -0,0 +1,348 @@ +// 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 <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#include <string.h> +#include <stdlib.h> + +#include "Platform.h" + +#include "XPM.h" + +#if defined(PLAT_QT) + +XPM::XPM(const char *textForm) +{ + qpm = *reinterpret_cast<const QPixmap *>(textForm); +} + +XPM::XPM(const char * const *linesForm) +{ + qpm = *reinterpret_cast<const QPixmap *>(linesForm); +} + +void XPM::RefreshColourPalette(Palette &pal, bool want) +{ + // Nothing to do. +} + +void XPM::Draw(Surface *surface, PRectangle &rc) +{ + surface -> DrawXPM(rc,this); +} + +#else + +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; +} + +ColourAllocated XPM::ColourFromCode(int ch) { + return colourCodeTable[ch]->allocated; +#ifdef SLOW + for (int i=0; i<nColours; i++) { + if (codes[i] == ch) { + return colours[i].allocated; + } + } + return colours[0].allocated; +#endif +} + +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<const char * const *>(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 ColourPair[nColours]; + + int strings = 1+height+nColours; + lines = new char *[strings]; + size_t allocation = 0; + for (int i=0; i<strings; i++) { + allocation += MeasureLength(linesForm[i]) + 1; + } + data = new char[allocation]; + char *nextBit = data; + for (int j=0; j<strings; j++) { + lines[j] = nextBit; + size_t len = MeasureLength(linesForm[j]); + memcpy(nextBit, linesForm[j], len); + nextBit += len; + *nextBit++ = '\0'; + } + + for (int code=0; code<256; code++) { + colourCodeTable[code] = 0; + } + + for (int c=0; c<nColours; c++) { + const char *colourDef = linesForm[c+1]; + codes[c] = colourDef[0]; + colourDef += 4; + if (*colourDef == '#') { + colours[c].desired.Set(colourDef); + } else { + colours[c].desired = ColourDesired(0xff, 0xff, 0xff); + codeTransparent = codes[c]; + } + colourCodeTable[static_cast<unsigned char>(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::RefreshColourPalette(Palette &pal, bool want) { + if (!data || !codes || !colours || !lines) { + return; + } + for (int i=0; i<nColours; i++) { + pal.WantFind(colours[i], want); + } +} + +void XPM::CopyDesiredColours() { + if (!data || !codes || !colours || !lines) { + return; + } + for (int i=0; i<nColours; i++) { + colours[i].Copy(); + } +} + +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<height;y++) { + int prevCode = 0; + int xStartRun = 0; + for (int x=0; x<width; x++) { + int code = lines[y+nColours+1][x]; + if (code != prevCode) { + FillRun(surface, prevCode, startX + xStartRun, startY + y, startX + x); + xStartRun = x; + prevCode = code; + } + } + FillRun(surface, prevCode, startX + xStartRun, startY + y, startX + width); + } +} + +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 id, 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() == id) { + set[i]->Init(textForm); + set[i]->CopyDesiredColours(); + return; + } + } + + // Not present, so add to end + XPM *pxpm = new XPM(textForm); + if (pxpm) { + pxpm->SetId(id); + pxpm->CopyDesiredColours(); + 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 id) { + for (int i = 0; i < len; i++) { + if (set[i]->GetId() == id) { + 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; +} + +#endif |