1 // Scintilla source code edit control
3 ** Define a class that holds data in the X Pixmap (XPM) format.
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.
19 using namespace Scintilla
;
22 static const char *NextField(const char *s
) {
23 // In case there are leading spaces in the string
24 while (*s
&& *s
== ' ') {
27 while (*s
&& *s
!= ' ') {
30 while (*s
&& *s
== ' ') {
36 // Data lines in XPM can be terminated either with NUL or "
37 static size_t MeasureLength(const char *s
) {
39 while (s
[i
] && (s
[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) {
64 XPM::XPM(const char *const *linesForm
) :
65 data(0), codes(0), colours(0), lines(0) {
73 void XPM::Init(const char *textForm
) {
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
);
85 // It is really in line form
86 Init(reinterpret_cast<const char * const *>(textForm
));
90 void XPM::Init(const char *const *linesForm
) {
96 codeTransparent
= ' ';
103 const char *line0
= linesForm
[0];
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
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
++) {
127 size_t len
= MeasureLength(linesForm
[j
]);
128 memcpy(nextBit
, linesForm
[j
], len
);
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];
141 if (*colourDef
== '#') {
142 colours
[c
].Set(colourDef
);
144 colours
[c
] = ColourDesired(0xff, 0xff, 0xff);
145 codeTransparent
= codes
[c
];
147 colourCodeTable
[static_cast<unsigned char>(codes
[c
])] = &(colours
[c
]);
162 void XPM::Draw(Surface
*surface
, PRectangle
&rc
) {
163 if (!data
|| !codes
|| !colours
|| !lines
) {
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
++) {
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
);
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
)) {
190 int code
= lines
[y
+nColours
+1][x
];
191 transparent
= code
== codeTransparent
;
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;
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;
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;
231 if (textForm
[j
] == '\0' || countQuotes
/ 2 > strings
) {
232 // Malformed XPM! Height + number of colors too high or too low
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) {
248 void XPMSet::Clear() {
249 for (int i
= 0; i
< len
; i
++) {
260 void XPMSet::Add(int ident
, const char *textForm
) {
261 // Invalidate cached dimensions
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
);
273 // Not present, so add to end
274 XPM
*pxpm
= new XPM(textForm
);
277 if (len
== maximum
) {
279 XPM
**setNew
= new XPM
*[maximum
];
280 for (int i
= 0; i
< len
; i
++) {
291 XPM
*XPMSet::Get(int ident
) {
292 for (int i
= 0; i
< len
; i
++) {
293 if (set
[i
]->GetId() == ident
) {
300 int XPMSet::GetHeight() {
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() {
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_
) {
325 pixelBytes
.assign(pixels_
, pixels_
+ CountBytes());
327 pixelBytes
.resize(CountBytes());
331 RGBAImage::RGBAImage(const XPM
&xpm
) {
332 height
= xpm
.GetHeight();
333 width
= xpm
.GetWidth();
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;
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() {
373 /// Remove all images.
374 void RGBAImageSet::Clear() {
375 for (ImageMap::iterator it
=images
.begin(); it
!= images
.end(); ++it
) {
385 void RGBAImageSet::Add(int ident
, RGBAImage
*image
) {
386 ImageMap::iterator it
=images
.find(ident
);
387 if (it
== images
.end()) {
388 images
[ident
] = image
;
398 RGBAImage
*RGBAImageSet::Get(int ident
) {
399 ImageMap::iterator it
= images
.find(ident
);
400 if (it
!= images
.end()) {
406 /// Give the largest height of the set.
407 int RGBAImageSet::GetHeight() const {
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 {
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;