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