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.
20 using namespace Scintilla
;
23 static const char *NextField(const char *s
) {
24 // In case there are leading spaces in the string
25 while (*s
&& *s
== ' ') {
28 while (*s
&& *s
!= ' ') {
31 while (*s
&& *s
== ' ') {
37 // Data lines in XPM can be terminated either with NUL or "
38 static size_t MeasureLength(const char *s
) {
40 while (s
[i
] && (s
[i
] != '\"'))
45 ColourDesired
XPM::ColourFromCode(int ch
) const {
46 return colourCodeTable
[ch
];
49 void XPM::FillRun(Surface
*surface
, int code
, int startX
, int y
, int x
) {
50 if ((code
!= codeTransparent
) && (startX
!= x
)) {
51 PRectangle rc
= PRectangle::FromInts(startX
, y
, x
, y
+ 1);
52 surface
->FillRectangle(rc
, ColourFromCode(code
));
56 XPM::XPM(const char *textForm
) {
60 XPM::XPM(const char *const *linesForm
) {
67 void XPM::Init(const char *textForm
) {
68 // Test done is two parts to avoid possibility of overstepping the memory
69 // if memcmp implemented strangely. Must be 4 bytes at least at destination.
70 if ((0 == memcmp(textForm
, "/* X", 4)) && (0 == memcmp(textForm
, "/* XPM */", 9))) {
71 // Build the lines form out of the text form
72 std::vector
<const char *> linesForm
= LinesFormFromTextForm(textForm
);
73 if (!linesForm
.empty()) {
77 // It is really in line form
78 Init(reinterpret_cast<const char * const *>(textForm
));
82 void XPM::Init(const char *const *linesForm
) {
87 codeTransparent
= ' ';
91 std::fill(colourCodeTable
, colourCodeTable
+256, 0);
92 const char *line0
= linesForm
[0];
94 line0
= NextField(line0
);
96 pixels
.resize(width
*height
);
97 line0
= NextField(line0
);
98 nColours
= atoi(line0
);
99 line0
= NextField(line0
);
100 if (atoi(line0
) != 1) {
101 // Only one char per pixel is supported
105 for (int c
=0; c
<nColours
; c
++) {
106 const char *colourDef
= linesForm
[c
+1];
107 int code
= static_cast<unsigned char>(colourDef
[0]);
109 ColourDesired
colour(0xff, 0xff, 0xff);
110 if (*colourDef
== '#') {
111 colour
.Set(colourDef
);
113 codeTransparent
= static_cast<char>(code
);
115 colourCodeTable
[code
] = colour
;
118 for (int y
=0; y
<height
; y
++) {
119 const char *lform
= linesForm
[y
+nColours
+1];
120 size_t len
= MeasureLength(lform
);
121 for (size_t x
= 0; x
<len
; x
++)
122 pixels
[y
* width
+ x
] = static_cast<unsigned char>(lform
[x
]);
126 void XPM::Draw(Surface
*surface
, PRectangle
&rc
) {
127 if (pixels
.empty()) {
131 int startY
= static_cast<int>(rc
.top
+ (rc
.Height() - height
) / 2);
132 int startX
= static_cast<int>(rc
.left
+ (rc
.Width() - width
) / 2);
133 for (int y
=0; y
<height
; y
++) {
136 for (int x
=0; x
<width
; x
++) {
137 int code
= pixels
[y
* width
+ x
];
138 if (code
!= prevCode
) {
139 FillRun(surface
, prevCode
, startX
+ xStartRun
, startY
+ y
, startX
+ x
);
144 FillRun(surface
, prevCode
, startX
+ xStartRun
, startY
+ y
, startX
+ width
);
148 void XPM::PixelAt(int x
, int y
, ColourDesired
&colour
, bool &transparent
) const {
149 if (pixels
.empty() || (x
<0) || (x
>= width
) || (y
<0) || (y
>= height
)) {
154 int code
= pixels
[y
* width
+ x
];
155 transparent
= code
== codeTransparent
;
159 colour
= ColourFromCode(code
).AsLong();
163 std::vector
<const char *> XPM::LinesFormFromTextForm(const char *textForm
) {
164 // Build the lines form out of the text form
165 std::vector
<const char *> linesForm
;
169 for (; countQuotes
< (2*strings
) && textForm
[j
] != '\0'; j
++) {
170 if (textForm
[j
] == '\"') {
171 if (countQuotes
== 0) {
172 // First field: width, height, number of colors, chars per pixel
173 const char *line0
= textForm
+ j
+ 1;
175 line0
= NextField(line0
);
176 // Add 1 line for each pixel of height
177 strings
+= atoi(line0
);
178 line0
= NextField(line0
);
179 // Add 1 line for each colour
180 strings
+= atoi(line0
);
182 if (countQuotes
/ 2 >= strings
) {
183 break; // Bad height or number of colors!
185 if ((countQuotes
& 1) == 0) {
186 linesForm
.push_back(textForm
+ j
+ 1);
191 if (textForm
[j
] == '\0' || countQuotes
/ 2 > strings
) {
192 // Malformed XPM! Height + number of colors too high or too low
198 RGBAImage::RGBAImage(int width_
, int height_
, float scale_
, const unsigned char *pixels_
) :
199 height(height_
), width(width_
), scale(scale_
) {
201 pixelBytes
.assign(pixels_
, pixels_
+ CountBytes());
203 pixelBytes
.resize(CountBytes());
207 RGBAImage::RGBAImage(const XPM
&xpm
) {
208 height
= xpm
.GetHeight();
209 width
= xpm
.GetWidth();
211 pixelBytes
.resize(CountBytes());
212 for (int y
=0; y
<height
; y
++) {
213 for (int x
=0; x
<width
; x
++) {
214 ColourDesired colour
;
215 bool transparent
= false;
216 xpm
.PixelAt(x
, y
, colour
, transparent
);
217 SetPixel(x
, y
, colour
, transparent
? 0 : 255);
222 RGBAImage::~RGBAImage() {
225 int RGBAImage::CountBytes() const {
226 return width
* height
* 4;
229 const unsigned char *RGBAImage::Pixels() const {
230 return &pixelBytes
[0];
233 void RGBAImage::SetPixel(int x
, int y
, ColourDesired colour
, int alpha
) {
234 unsigned char *pixel
= &pixelBytes
[0] + (y
*width
+x
) * 4;
236 pixel
[0] = static_cast<unsigned char>(colour
.GetRed());
237 pixel
[1] = static_cast<unsigned char>(colour
.GetGreen());
238 pixel
[2] = static_cast<unsigned char>(colour
.GetBlue());
239 pixel
[3] = static_cast<unsigned char>(alpha
);
242 RGBAImageSet::RGBAImageSet() : height(-1), width(-1) {
245 RGBAImageSet::~RGBAImageSet() {
249 /// Remove all images.
250 void RGBAImageSet::Clear() {
251 for (ImageMap::iterator it
=images
.begin(); it
!= images
.end(); ++it
) {
261 void RGBAImageSet::Add(int ident
, RGBAImage
*image
) {
262 ImageMap::iterator it
=images
.find(ident
);
263 if (it
== images
.end()) {
264 images
[ident
] = image
;
274 RGBAImage
*RGBAImageSet::Get(int ident
) {
275 ImageMap::iterator it
= images
.find(ident
);
276 if (it
!= images
.end()) {
282 /// Give the largest height of the set.
283 int RGBAImageSet::GetHeight() const {
285 for (ImageMap::const_iterator it
=images
.begin(); it
!= images
.end(); ++it
) {
286 if (height
< it
->second
->GetHeight()) {
287 height
= it
->second
->GetHeight();
291 return (height
> 0) ? height
: 0;
294 /// Give the largest width of the set.
295 int RGBAImageSet::GetWidth() const {
297 for (ImageMap::const_iterator it
=images
.begin(); it
!= images
.end(); ++it
) {
298 if (width
< it
->second
->GetWidth()) {
299 width
= it
->second
->GetWidth();
303 return (width
> 0) ? width
: 0;