Upgraded to scintilla 3.2.3
[TortoiseGit.git] / ext / scintilla / src / XPM.cxx
blob02b2f329815b327a3788ab83b3ceb1ce23c0bdfe
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.
8 #include <string.h>
9 #include <stdlib.h>
11 #include <vector>
12 #include <map>
14 #include "Platform.h"
16 #include "XPM.h"
18 #ifdef SCI_NAMESPACE
19 using namespace Scintilla;
20 #endif
22 static const char *NextField(const char *s) {
23 // In case there are leading spaces in the string
24 while (*s && *s == ' ') {
25 s++;
27 while (*s && *s != ' ') {
28 s++;
30 while (*s && *s == ' ') {
31 s++;
33 return s;
36 // Data lines in XPM can be terminated either with NUL or "
37 static size_t MeasureLength(const char *s) {
38 size_t i = 0;
39 while (s[i] && (s[i] != '\"'))
40 i++;
41 return i;
44 ColourDesired XPM::ColourDesiredFromCode(int ch) const {
45 return *colourCodeTable[ch];
48 ColourDesired XPM::ColourFromCode(int ch) const {
49 return *colourCodeTable[ch];
52 void XPM::FillRun(Surface *surface, int code, int startX, int y, int x) {
53 if ((code != codeTransparent) && (startX != x)) {
54 PRectangle rc(startX, y, x, y+1);
55 surface->FillRectangle(rc, ColourFromCode(code));
59 XPM::XPM(const char *textForm) :
60 data(0), codes(0), colours(0), lines(0) {
61 Init(textForm);
64 XPM::XPM(const char *const *linesForm) :
65 data(0), codes(0), colours(0), lines(0) {
66 Init(linesForm);
69 XPM::~XPM() {
70 Clear();
73 void XPM::Init(const char *textForm) {
74 Clear();
75 // Test done is two parts to avoid possibility of overstepping the memory
76 // if memcmp implemented strangely. Must be 4 bytes at least at destination.
77 if ((0 == memcmp(textForm, "/* X", 4)) && (0 == memcmp(textForm, "/* XPM */", 9))) {
78 // Build the lines form out of the text form
79 const char **linesForm = LinesFormFromTextForm(textForm);
80 if (linesForm != 0) {
81 Init(linesForm);
82 delete []linesForm;
84 } else {
85 // It is really in line form
86 Init(reinterpret_cast<const char * const *>(textForm));
90 void XPM::Init(const char *const *linesForm) {
91 Clear();
92 height = 1;
93 width = 1;
94 nColours = 1;
95 data = NULL;
96 codeTransparent = ' ';
97 codes = NULL;
98 colours = NULL;
99 lines = NULL;
100 if (!linesForm)
101 return;
103 const char *line0 = linesForm[0];
104 width = atoi(line0);
105 line0 = NextField(line0);
106 height = atoi(line0);
107 line0 = NextField(line0);
108 nColours = atoi(line0);
109 line0 = NextField(line0);
110 if (atoi(line0) != 1) {
111 // Only one char per pixel is supported
112 return;
114 codes = new char[nColours];
115 colours = new ColourDesired[nColours];
117 int strings = 1+height+nColours;
118 lines = new char *[strings];
119 size_t allocation = 0;
120 for (int i=0; i<strings; i++) {
121 allocation += MeasureLength(linesForm[i]) + 1;
123 data = new char[allocation];
124 char *nextBit = data;
125 for (int j=0; j<strings; j++) {
126 lines[j] = nextBit;
127 size_t len = MeasureLength(linesForm[j]);
128 memcpy(nextBit, linesForm[j], len);
129 nextBit += len;
130 *nextBit++ = '\0';
133 for (int code=0; code<256; code++) {
134 colourCodeTable[code] = 0;
137 for (int c=0; c<nColours; c++) {
138 const char *colourDef = linesForm[c+1];
139 codes[c] = colourDef[0];
140 colourDef += 4;
141 if (*colourDef == '#') {
142 colours[c].Set(colourDef);
143 } else {
144 colours[c] = ColourDesired(0xff, 0xff, 0xff);
145 codeTransparent = codes[c];
147 colourCodeTable[static_cast<unsigned char>(codes[c])] = &(colours[c]);
151 void XPM::Clear() {
152 delete []data;
153 data = 0;
154 delete []codes;
155 codes = 0;
156 delete []colours;
157 colours = 0;
158 delete []lines;
159 lines = 0;
162 void XPM::Draw(Surface *surface, PRectangle &rc) {
163 if (!data || !codes || !colours || !lines) {
164 return;
166 // Centre the pixmap
167 int startY = rc.top + (rc.Height() - height) / 2;
168 int startX = rc.left + (rc.Width() - width) / 2;
169 for (int y=0; y<height; y++) {
170 int prevCode = 0;
171 int xStartRun = 0;
172 for (int x=0; x<width; x++) {
173 int code = lines[y+nColours+1][x];
174 if (code != prevCode) {
175 FillRun(surface, prevCode, startX + xStartRun, startY + y, startX + x);
176 xStartRun = x;
177 prevCode = code;
180 FillRun(surface, prevCode, startX + xStartRun, startY + y, startX + width);
184 void XPM::PixelAt(int x, int y, ColourDesired &colour, bool &transparent) const {
185 if (!data || !codes || !colours || !lines || (x<0) || (x >= width) || (y<0) || (y >= height)) {
186 colour = 0;
187 transparent = true;
188 return;
190 int code = lines[y+nColours+1][x];
191 transparent = code == codeTransparent;
192 if (transparent) {
193 colour = 0;
194 } else {
195 colour = ColourDesiredFromCode(code).AsLong();
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!
222 if (countQuotes / 2 >= strings) {
223 break; // Bad height or number of colors!
225 if ((countQuotes & 1) == 0) {
226 linesForm[countQuotes / 2] = textForm + j + 1;
228 countQuotes++;
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;
236 return linesForm;
239 // In future, may want to minimize search time by sorting and using a binary search.
241 XPMSet::XPMSet() : set(0), len(0), maximum(0), height(-1), width(-1) {
244 XPMSet::~XPMSet() {
245 Clear();
248 void XPMSet::Clear() {
249 for (int i = 0; i < len; i++) {
250 delete set[i];
252 delete []set;
253 set = 0;
254 len = 0;
255 maximum = 0;
256 height = -1;
257 width = -1;
260 void XPMSet::Add(int ident, const char *textForm) {
261 // Invalidate cached dimensions
262 height = -1;
263 width = -1;
265 // Replace if this id already present
266 for (int i = 0; i < len; i++) {
267 if (set[i]->GetId() == ident) {
268 set[i]->Init(textForm);
269 return;
273 // Not present, so add to end
274 XPM *pxpm = new XPM(textForm);
275 if (pxpm) {
276 pxpm->SetId(ident);
277 if (len == maximum) {
278 maximum += 64;
279 XPM **setNew = new XPM *[maximum];
280 for (int i = 0; i < len; i++) {
281 setNew[i] = set[i];
283 delete []set;
284 set = setNew;
286 set[len] = pxpm;
287 len++;
291 XPM *XPMSet::Get(int ident) {
292 for (int i = 0; i < len; i++) {
293 if (set[i]->GetId() == ident) {
294 return set[i];
297 return 0;
300 int XPMSet::GetHeight() {
301 if (height < 0) {
302 for (int i = 0; i < len; i++) {
303 if (height < set[i]->GetHeight()) {
304 height = set[i]->GetHeight();
308 return (height > 0) ? height : 0;
311 int XPMSet::GetWidth() {
312 if (width < 0) {
313 for (int i = 0; i < len; i++) {
314 if (width < set[i]->GetWidth()) {
315 width = set[i]->GetWidth();
319 return (width > 0) ? width : 0;
322 RGBAImage::RGBAImage(int width_, int height_, float scale_, const unsigned char *pixels_) :
323 height(height_), width(width_), scale(scale_) {
324 if (pixels_) {
325 pixelBytes.assign(pixels_, pixels_ + CountBytes());
326 } else {
327 pixelBytes.resize(CountBytes());
331 RGBAImage::RGBAImage(const XPM &xpm) {
332 height = xpm.GetHeight();
333 width = xpm.GetWidth();
334 scale = 1;
335 pixelBytes.resize(CountBytes());
336 for (int y=0; y<height; y++) {
337 for (int x=0; x<width; x++) {
338 ColourDesired colour;
339 bool transparent = false;
340 xpm.PixelAt(x, y, colour, transparent);
341 SetPixel(x, y, colour, transparent ? 0 : 255);
346 RGBAImage::~RGBAImage() {
349 int RGBAImage::CountBytes() const {
350 return width * height * 4;
353 const unsigned char *RGBAImage::Pixels() const {
354 return &pixelBytes[0];
357 void RGBAImage::SetPixel(int x, int y, ColourDesired colour, int alpha) {
358 unsigned char *pixel = &pixelBytes[0] + (y*width+x) * 4;
359 // RGBA
360 pixel[0] = static_cast<unsigned char>(colour.GetRed());
361 pixel[1] = static_cast<unsigned char>(colour.GetGreen());
362 pixel[2] = static_cast<unsigned char>(colour.GetBlue());
363 pixel[3] = static_cast<unsigned char>(alpha);
366 RGBAImageSet::RGBAImageSet() : height(-1), width(-1){
369 RGBAImageSet::~RGBAImageSet() {
370 Clear();
373 /// Remove all images.
374 void RGBAImageSet::Clear() {
375 for (ImageMap::iterator it=images.begin(); it != images.end(); ++it) {
376 delete it->second;
377 it->second = 0;
379 images.clear();
380 height = -1;
381 width = -1;
384 /// Add an image.
385 void RGBAImageSet::Add(int ident, RGBAImage *image) {
386 ImageMap::iterator it=images.find(ident);
387 if (it == images.end()) {
388 images[ident] = image;
389 } else {
390 delete it->second;
391 it->second = image;
393 height = -1;
394 width = -1;
397 /// Get image by id.
398 RGBAImage *RGBAImageSet::Get(int ident) {
399 ImageMap::iterator it = images.find(ident);
400 if (it != images.end()) {
401 return it->second;
403 return NULL;
406 /// Give the largest height of the set.
407 int RGBAImageSet::GetHeight() const {
408 if (height < 0) {
409 for (ImageMap::const_iterator it=images.begin(); it != images.end(); ++it) {
410 if (height < it->second->GetHeight()) {
411 height = it->second->GetHeight();
415 return (height > 0) ? height : 0;
418 /// Give the largest width of the set.
419 int RGBAImageSet::GetWidth() const {
420 if (width < 0) {
421 for (ImageMap::const_iterator it=images.begin(); it != images.end(); ++it) {
422 if (width < it->second->GetWidth()) {
423 width = it->second->GetWidth();
427 return (width > 0) ? width : 0;