1 // Scintilla source code edit control
2 /** @file XPM.cxx
3  ** Define a class that holds data in the X Pixmap (XPM) format.
4  **/
5 // Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
7 
8 #include <string.h>
9 #include <stdlib.h>
10 
11 #include "Platform.h"
12 
13 #include "XPM.h"
14 
NextField(const char * s)15 static const char *NextField(const char *s) {
16 	// In case there are leading spaces in the string
17 	while (*s && *s == ' ') {
18 		s++;
19 	}
20 	while (*s && *s != ' ') {
21 		s++;
22 	}
23 	while (*s && *s == ' ') {
24 		s++;
25 	}
26 	return s;
27 }
28 
29 // Data lines in XPM can be terminated either with NUL or "
MeasureLength(const char * s)30 static size_t MeasureLength(const char *s) {
31 	size_t i = 0;
32 	while (s[i] && (s[i] != '\"'))
33 		i++;
34 	return i;
35 }
36 
ColourFromCode(int ch)37 ColourAllocated XPM::ColourFromCode(int ch) {
38 	return colourCodeTable[ch]->allocated;
39 #ifdef SLOW
40 	for (int i=0; i<nColours; i++) {
41 		if (codes[i] == ch) {
42 			return colours[i].allocated;
43 		}
44 	}
45 	return colours[0].allocated;
46 #endif
47 }
48 
FillRun(Surface * surface,int code,int startX,int y,int x)49 void XPM::FillRun(Surface *surface, int code, int startX, int y, int x) {
50 	if ((code != codeTransparent) && (startX != x)) {
51 		PRectangle rc(startX, y, x, y+1);
52 		surface->FillRectangle(rc, ColourFromCode(code));
53 	}
54 }
55 
XPM(const char * textForm)56 XPM::XPM(const char *textForm) :
57 	data(0), codes(0), colours(0), lines(0) {
58 	Init(textForm);
59 }
60 
XPM(const char * const * linesForm)61 XPM::XPM(const char * const *linesForm) :
62 	data(0), codes(0), colours(0), lines(0) {
63 	Init(linesForm);
64 }
65 
~XPM()66 XPM::~XPM() {
67 	Clear();
68 }
69 
Init(const char * textForm)70 void XPM::Init(const char *textForm) {
71 	Clear();
72 	// Test done is two parts to avoid possibility of overstepping the memory
73 	// if memcmp implemented strangely. Must be 4 bytes at least at destination.
74 	if ((0 == memcmp(textForm, "/* X", 4)) && (0 == memcmp(textForm, "/* XPM */", 9))) {
75 		// Build the lines form out of the text form
76 		const char **linesForm = LinesFormFromTextForm(textForm);
77 		if (linesForm != 0) {
78 			Init(linesForm);
79 			delete []linesForm;
80 		}
81 	} else {
82 		// It is really in line form
83 		Init(reinterpret_cast<const char * const *>(textForm));
84 	}
85 }
86 
Init(const char * const * linesForm)87 void XPM::Init(const char * const *linesForm) {
88 	Clear();
89 	height = 1;
90 	width = 1;
91 	nColours = 1;
92 	data = NULL;
93 	codeTransparent = ' ';
94 	codes = NULL;
95 	colours = NULL;
96 	lines = NULL;
97 	if (!linesForm)
98 		return;
99 
100 	const char *line0 = linesForm[0];
101 	width = atoi(line0);
102 	line0 = NextField(line0);
103 	height = atoi(line0);
104 	line0 = NextField(line0);
105 	nColours = atoi(line0);
106 	line0 = NextField(line0);
107 	if (atoi(line0) != 1) {
108 		// Only one char per pixel is supported
109 		return;
110 	}
111 	codes = new char[nColours];
112 	colours = new ColourPair[nColours];
113 
114 	int strings = 1+height+nColours;
115 	lines = new char *[strings];
116 	size_t allocation = 0;
117 	for (int i=0; i<strings; i++) {
118 		allocation += MeasureLength(linesForm[i]) + 1;
119 	}
120 	data = new char[allocation];
121 	char *nextBit = data;
122 	for (int j=0; j<strings; j++) {
123 		lines[j] = nextBit;
124 		size_t len = MeasureLength(linesForm[j]);
125 		memcpy(nextBit, linesForm[j], len);
126 		nextBit += len;
127 		*nextBit++ = '\0';
128 	}
129 
130 	for (int code=0; code<256; code++) {
131 		colourCodeTable[code] = 0;
132 	}
133 
134 	for (int c=0; c<nColours; c++) {
135 		const char *colourDef = linesForm[c+1];
136 		codes[c] = colourDef[0];
137 		colourDef += 4;
138 		if (*colourDef == '#') {
139 			colours[c].desired.Set(colourDef);
140 		} else {
141 			colours[c].desired = ColourDesired(0xff, 0xff, 0xff);
142 			codeTransparent = codes[c];
143 		}
144 		colourCodeTable[static_cast<unsigned char>(codes[c])] = &(colours[c]);
145 	}
146 }
147 
Clear()148 void XPM::Clear() {
149 	delete []data;
150 	data = 0;
151 	delete []codes;
152 	codes = 0;
153 	delete []colours;
154 	colours = 0;
155 	delete []lines;
156 	lines = 0;
157 }
158 
RefreshColourPalette(Palette & pal,bool want)159 void XPM::RefreshColourPalette(Palette &pal, bool want) {
160 	if (!data || !codes || !colours || !lines) {
161 		return;
162 	}
163 	for (int i=0; i<nColours; i++) {
164 		pal.WantFind(colours[i], want);
165 	}
166 }
167 
CopyDesiredColours()168 void XPM::CopyDesiredColours() {
169 	if (!data || !codes || !colours || !lines) {
170 		return;
171 	}
172 	for (int i=0; i<nColours; i++) {
173 		colours[i].Copy();
174 	}
175 }
176 
Draw(Surface * surface,PRectangle & rc)177 void XPM::Draw(Surface *surface, PRectangle &rc) {
178 	if (!data || !codes || !colours || !lines) {
179 		return;
180 	}
181 	// Centre the pixmap
182 	int startY = rc.top + (rc.Height() - height) / 2;
183 	int startX = rc.left + (rc.Width() - width) / 2;
184 	for (int y=0;y<height;y++) {
185 		int prevCode = 0;
186 		int xStartRun = 0;
187 		for (int x=0; x<width; x++) {
188 			int code = lines[y+nColours+1][x];
189 			if (code != prevCode) {
190 				FillRun(surface, prevCode, startX + xStartRun, startY + y, startX + x);
191 				xStartRun = x;
192 				prevCode = code;
193 			}
194 		}
195 		FillRun(surface, prevCode, startX + xStartRun, startY + y, startX + width);
196 	}
197 }
198 
LinesFormFromTextForm(const char * textForm)199 const char **XPM::LinesFormFromTextForm(const char *textForm) {
200 	// Build the lines form out of the text form
201 	const char **linesForm = 0;
202 	int countQuotes = 0;
203 	int strings=1;
204 	int j=0;
205 	for (; countQuotes < (2*strings) && textForm[j] != '\0'; j++) {
206 		if (textForm[j] == '\"') {
207 			if (countQuotes == 0) {
208 				// First field: width, height, number of colors, chars per pixel
209 				const char *line0 = textForm + j + 1;
210 				// Skip width
211 				line0 = NextField(line0);
212 				// Add 1 line for each pixel of height
213 				strings += atoi(line0);
214 				line0 = NextField(line0);
215 				// Add 1 line for each colour
216 				strings += atoi(line0);
217 				linesForm = new const char *[strings];
218 				if (linesForm == 0) {
219 					break;	// Memory error!
220 				}
221 			}
222 			if (countQuotes / 2 >= strings) {
223 				break;	// Bad height or number of colors!
224 			}
225 			if ((countQuotes & 1) == 0) {
226 				linesForm[countQuotes / 2] = textForm + j + 1;
227 			}
228 			countQuotes++;
229 		}
230 	}
231 	if (textForm[j] == '\0' || countQuotes / 2 > strings) {
232 		// Malformed XPM! Height + number of colors too high or too low
233 		delete []linesForm;
234 		linesForm = 0;
235 	}
236 	return linesForm;
237 }
238 
239 // In future, may want to minimize search time by sorting and using a binary search.
240 
XPMSet()241 XPMSet::XPMSet() : set(0), len(0), maximum(0), height(-1), width(-1) {
242 }
243 
~XPMSet()244 XPMSet::~XPMSet() {
245 	Clear();
246 }
247 
Clear()248 void XPMSet::Clear() {
249 	for (int i = 0; i < len; i++) {
250 		delete set[i];
251 	}
252 	delete []set;
253 	set = 0;
254 	len = 0;
255 	maximum = 0;
256 	height = -1;
257 	width = -1;
258 }
259 
Add(int id,const char * textForm)260 void XPMSet::Add(int id, const char *textForm) {
261 	// Invalidate cached dimensions
262 	height = -1;
263 	width = -1;
264 
265 	// Replace if this id already present
266 	for (int i = 0; i < len; i++) {
267 		if (set[i]->GetId() == id) {
268 			set[i]->Init(textForm);
269 			set[i]->CopyDesiredColours();
270 			return;
271 		}
272 	}
273 
274 	// Not present, so add to end
275 	XPM *pxpm = new XPM(textForm);
276 	if (pxpm) {
277 		pxpm->SetId(id);
278 		pxpm->CopyDesiredColours();
279 		if (len == maximum) {
280 			maximum += 64;
281 			XPM **setNew = new XPM *[maximum];
282 			for (int i = 0; i < len; i++) {
283 				setNew[i] = set[i];
284 			}
285 			delete []set;
286 			set = setNew;
287 		}
288 		set[len] = pxpm;
289 		len++;
290 	}
291 }
292 
Get(int id)293 XPM *XPMSet::Get(int id) {
294 	for (int i = 0; i < len; i++) {
295 		if (set[i]->GetId() == id) {
296 			return set[i];
297 		}
298 	}
299 	return 0;
300 }
301 
GetHeight()302 int XPMSet::GetHeight() {
303 	if (height < 0) {
304 		for (int i = 0; i < len; i++) {
305 			if (height < set[i]->GetHeight()) {
306 				height = set[i]->GetHeight();
307 			}
308 		}
309 	}
310 	return (height > 0) ? height : 0;
311 }
312 
GetWidth()313 int XPMSet::GetWidth() {
314 	if (width < 0) {
315 		for (int i = 0; i < len; i++) {
316 			if (width < set[i]->GetWidth()) {
317 				width = set[i]->GetWidth();
318 			}
319 		}
320 	}
321 	return (width > 0) ? width : 0;
322 }
323