1 // Scintilla source code edit control
\r
3 ** Define a class that holds data in the X Pixmap (XPM) format.
\r
5 // Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
\r
6 // The License.txt file describes the conditions under which this software may be distributed.
\r
11 #include "Platform.h"
\r
15 #ifdef SCI_NAMESPACE
\r
16 using namespace Scintilla;
\r
19 static const char *NextField(const char *s) {
\r
20 // In case there are leading spaces in the string
\r
21 while (*s && *s == ' ') {
\r
24 while (*s && *s != ' ') {
\r
27 while (*s && *s == ' ') {
\r
33 // Data lines in XPM can be terminated either with NUL or "
\r
34 static size_t MeasureLength(const char *s) {
\r
36 while (s[i] && (s[i] != '\"'))
\r
41 ColourAllocated XPM::ColourFromCode(int ch) {
\r
42 return colourCodeTable[ch]->allocated;
\r
44 for (int i=0; i<nColours; i++) {
\r
45 if (codes[i] == ch) {
\r
46 return colours[i].allocated;
\r
49 return colours[0].allocated;
\r
53 void XPM::FillRun(Surface *surface, int code, int startX, int y, int x) {
\r
54 if ((code != codeTransparent) && (startX != x)) {
\r
55 PRectangle rc(startX, y, x, y+1);
\r
56 surface->FillRectangle(rc, ColourFromCode(code));
\r
60 XPM::XPM(const char *textForm) :
\r
61 data(0), codes(0), colours(0), lines(0) {
\r
65 XPM::XPM(const char * const *linesForm) :
\r
66 data(0), codes(0), colours(0), lines(0) {
\r
74 void XPM::Init(const char *textForm) {
\r
76 // Test done is two parts to avoid possibility of overstepping the memory
\r
77 // if memcmp implemented strangely. Must be 4 bytes at least at destination.
\r
78 if ((0 == memcmp(textForm, "/* X", 4)) && (0 == memcmp(textForm, "/* XPM */", 9))) {
\r
79 // Build the lines form out of the text form
\r
80 const char **linesForm = LinesFormFromTextForm(textForm);
\r
81 if (linesForm != 0) {
\r
86 // It is really in line form
\r
87 Init(reinterpret_cast<const char * const *>(textForm));
\r
91 void XPM::Init(const char * const *linesForm) {
\r
97 codeTransparent = ' ';
\r
104 const char *line0 = linesForm[0];
\r
105 width = atoi(line0);
\r
106 line0 = NextField(line0);
\r
107 height = atoi(line0);
\r
108 line0 = NextField(line0);
\r
109 nColours = atoi(line0);
\r
110 line0 = NextField(line0);
\r
111 if (atoi(line0) != 1) {
\r
112 // Only one char per pixel is supported
\r
115 codes = new char[nColours];
\r
116 colours = new ColourPair[nColours];
\r
118 int strings = 1+height+nColours;
\r
119 lines = new char *[strings];
\r
120 size_t allocation = 0;
\r
121 for (int i=0; i<strings; i++) {
\r
122 allocation += MeasureLength(linesForm[i]) + 1;
\r
124 data = new char[allocation];
\r
125 char *nextBit = data;
\r
126 for (int j=0; j<strings; j++) {
\r
127 lines[j] = nextBit;
\r
128 size_t len = MeasureLength(linesForm[j]);
\r
129 memcpy(nextBit, linesForm[j], len);
\r
134 for (int code=0; code<256; code++) {
\r
135 colourCodeTable[code] = 0;
\r
138 for (int c=0; c<nColours; c++) {
\r
139 const char *colourDef = linesForm[c+1];
\r
140 codes[c] = colourDef[0];
\r
142 if (*colourDef == '#') {
\r
143 colours[c].desired.Set(colourDef);
\r
145 colours[c].desired = ColourDesired(0xff, 0xff, 0xff);
\r
146 codeTransparent = codes[c];
\r
148 colourCodeTable[static_cast<unsigned char>(codes[c])] = &(colours[c]);
\r
152 void XPM::Clear() {
\r
163 void XPM::RefreshColourPalette(Palette &pal, bool want) {
\r
164 if (!data || !codes || !colours || !lines) {
\r
167 for (int i=0; i<nColours; i++) {
\r
168 pal.WantFind(colours[i], want);
\r
172 void XPM::CopyDesiredColours() {
\r
173 if (!data || !codes || !colours || !lines) {
\r
176 for (int i=0; i<nColours; i++) {
\r
181 void XPM::Draw(Surface *surface, PRectangle &rc) {
\r
182 if (!data || !codes || !colours || !lines) {
\r
185 // Centre the pixmap
\r
186 int startY = rc.top + (rc.Height() - height) / 2;
\r
187 int startX = rc.left + (rc.Width() - width) / 2;
\r
188 for (int y=0;y<height;y++) {
\r
191 for (int x=0; x<width; x++) {
\r
192 int code = lines[y+nColours+1][x];
\r
193 if (code != prevCode) {
\r
194 FillRun(surface, prevCode, startX + xStartRun, startY + y, startX + x);
\r
199 FillRun(surface, prevCode, startX + xStartRun, startY + y, startX + width);
\r
203 const char **XPM::LinesFormFromTextForm(const char *textForm) {
\r
204 // Build the lines form out of the text form
\r
205 const char **linesForm = 0;
\r
206 int countQuotes = 0;
\r
209 for (; countQuotes < (2*strings) && textForm[j] != '\0'; j++) {
\r
210 if (textForm[j] == '\"') {
\r
211 if (countQuotes == 0) {
\r
212 // First field: width, height, number of colors, chars per pixel
\r
213 const char *line0 = textForm + j + 1;
\r
215 line0 = NextField(line0);
\r
216 // Add 1 line for each pixel of height
\r
217 strings += atoi(line0);
\r
218 line0 = NextField(line0);
\r
219 // Add 1 line for each colour
\r
220 strings += atoi(line0);
\r
221 linesForm = new const char *[strings];
\r
222 if (linesForm == 0) {
\r
223 break; // Memory error!
\r
226 if (countQuotes / 2 >= strings) {
\r
227 break; // Bad height or number of colors!
\r
229 if ((countQuotes & 1) == 0) {
\r
230 linesForm[countQuotes / 2] = textForm + j + 1;
\r
235 if (textForm[j] == '\0' || countQuotes / 2 > strings) {
\r
236 // Malformed XPM! Height + number of colors too high or too low
\r
237 delete []linesForm;
\r
243 // In future, may want to minimize search time by sorting and using a binary search.
\r
245 XPMSet::XPMSet() : set(0), len(0), maximum(0), height(-1), width(-1) {
\r
248 XPMSet::~XPMSet() {
\r
252 void XPMSet::Clear() {
\r
253 for (int i = 0; i < len; i++) {
\r
264 void XPMSet::Add(int id, const char *textForm) {
\r
265 // Invalidate cached dimensions
\r
269 // Replace if this id already present
\r
270 for (int i = 0; i < len; i++) {
\r
271 if (set[i]->GetId() == id) {
\r
272 set[i]->Init(textForm);
\r
273 set[i]->CopyDesiredColours();
\r
278 // Not present, so add to end
\r
279 XPM *pxpm = new XPM(textForm);
\r
282 pxpm->CopyDesiredColours();
\r
283 if (len == maximum) {
\r
285 XPM **setNew = new XPM *[maximum];
\r
286 for (int i = 0; i < len; i++) {
\r
287 setNew[i] = set[i];
\r
297 XPM *XPMSet::Get(int id) {
\r
298 for (int i = 0; i < len; i++) {
\r
299 if (set[i]->GetId() == id) {
\r
306 int XPMSet::GetHeight() {
\r
308 for (int i = 0; i < len; i++) {
\r
309 if (height < set[i]->GetHeight()) {
\r
310 height = set[i]->GetHeight();
\r
314 return (height > 0) ? height : 0;
\r
317 int XPMSet::GetWidth() {
\r
319 for (int i = 0; i < len; i++) {
\r
320 if (width < set[i]->GetWidth()) {
\r
321 width = set[i]->GetWidth();
\r
325 return (width > 0) ? width : 0;
\r