]> git.saurik.com Git - wxWidgets.git/blob - contrib/src/stc/scintilla/src/XPM.cxx
36e7994f46c4dd9b562523c48df0db82de9f5363
[wxWidgets.git] / contrib / src / stc / scintilla / src / XPM.cxx
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
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 "
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
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
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
56 XPM::XPM(const char *textForm) :
57 data(0), codes(0), colours(0), lines(0) {
58 Init(textForm);
59 }
60
61 XPM::XPM(const char * const *linesForm) :
62 data(0), codes(0), colours(0), lines(0) {
63 Init(linesForm);
64 }
65
66 XPM::~XPM() {
67 Clear();
68 }
69
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
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
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
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
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
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
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
241 XPMSet::XPMSet() : set(0), len(0), maximum(0), height(-1), width(-1) {
242 }
243
244 XPMSet::~XPMSet() {
245 Clear();
246 }
247
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
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
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
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
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 }