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.
21 using namespace Scintilla
;
24 static const char *NextField(const char *s
) {
25 // In case there are leading spaces in the string
29 while (*s
&& *s
!= ' ') {
38 // Data lines in XPM can be terminated either with NUL or "
39 static size_t MeasureLength(const char *s
) {
41 while (s
[i
] && (s
[i
] != '\"'))
46 ColourDesired
XPM::ColourFromCode(int ch
) const {
47 return colourCodeTable
[ch
];
50 void XPM::FillRun(Surface
*surface
, int code
, int startX
, int y
, int x
) const {
51 if ((code
!= codeTransparent
) && (startX
!= x
)) {
52 PRectangle rc
= PRectangle::FromInts(startX
, y
, x
, y
+ 1);
53 surface
->FillRectangle(rc
, ColourFromCode(code
));
57 XPM::XPM(const char *textForm
) {
61 XPM::XPM(const char *const *linesForm
) {
68 void XPM::Init(const char *textForm
) {
69 // Test done is two parts to avoid possibility of overstepping the memory
70 // if memcmp implemented strangely. Must be 4 bytes at least at destination.
71 if ((0 == memcmp(textForm
, "/* X", 4)) && (0 == memcmp(textForm
, "/* XPM */", 9))) {
72 // Build the lines form out of the text form
73 std::vector
<const char *> linesForm
= LinesFormFromTextForm(textForm
);
74 if (!linesForm
.empty()) {
78 // It is really in line form
79 Init(reinterpret_cast<const char * const *>(textForm
));
83 void XPM::Init(const char *const *linesForm
) {
88 codeTransparent
= ' ';
92 std::fill(colourCodeTable
, colourCodeTable
+256, 0);
93 const char *line0
= linesForm
[0];
95 line0
= NextField(line0
);
97 pixels
.resize(width
*height
);
98 line0
= NextField(line0
);
99 nColours
= atoi(line0
);
100 line0
= NextField(line0
);
101 if (atoi(line0
) != 1) {
102 // Only one char per pixel is supported
106 for (int c
=0; c
<nColours
; c
++) {
107 const char *colourDef
= linesForm
[c
+1];
108 int code
= static_cast<unsigned char>(colourDef
[0]);
110 ColourDesired
colour(0xff, 0xff, 0xff);
111 if (*colourDef
== '#') {
112 colour
.Set(colourDef
);
114 codeTransparent
= static_cast<char>(code
);
116 colourCodeTable
[code
] = colour
;
119 for (int y
=0; y
<height
; y
++) {
120 const char *lform
= linesForm
[y
+nColours
+1];
121 const size_t len
= MeasureLength(lform
);
122 for (size_t x
= 0; x
<len
; x
++)
123 pixels
[y
* width
+ x
] = static_cast<unsigned char>(lform
[x
]);
127 void XPM::Draw(Surface
*surface
, const PRectangle
&rc
) {
128 if (pixels
.empty()) {
132 const int startY
= static_cast<int>(rc
.top
+ (rc
.Height() - height
) / 2);
133 const int startX
= static_cast<int>(rc
.left
+ (rc
.Width() - width
) / 2);
134 for (int y
=0; y
<height
; y
++) {
137 for (int x
=0; x
<width
; x
++) {
138 const int code
= pixels
[y
* width
+ x
];
139 if (code
!= prevCode
) {
140 FillRun(surface
, prevCode
, startX
+ xStartRun
, startY
+ y
, startX
+ x
);
145 FillRun(surface
, prevCode
, startX
+ xStartRun
, startY
+ y
, startX
+ width
);
149 void XPM::PixelAt(int x
, int y
, ColourDesired
&colour
, bool &transparent
) const {
150 if (pixels
.empty() || (x
<0) || (x
>= width
) || (y
<0) || (y
>= height
)) {
155 int code
= pixels
[y
* width
+ x
];
156 transparent
= code
== codeTransparent
;
160 colour
= ColourFromCode(code
).AsLong();
164 std::vector
<const char *> XPM::LinesFormFromTextForm(const char *textForm
) {
165 // Build the lines form out of the text form
166 std::vector
<const char *> linesForm
;
170 for (; countQuotes
< (2*strings
) && textForm
[j
] != '\0'; j
++) {
171 if (textForm
[j
] == '\"') {
172 if (countQuotes
== 0) {
173 // First field: width, height, number of colors, chars per pixel
174 const char *line0
= textForm
+ j
+ 1;
176 line0
= NextField(line0
);
177 // Add 1 line for each pixel of height
178 strings
+= atoi(line0
);
179 line0
= NextField(line0
);
180 // Add 1 line for each colour
181 strings
+= atoi(line0
);
183 if (countQuotes
/ 2 >= strings
) {
184 break; // Bad height or number of colors!
186 if ((countQuotes
& 1) == 0) {
187 linesForm
.push_back(textForm
+ j
+ 1);
192 if (textForm
[j
] == '\0' || countQuotes
/ 2 > strings
) {
193 // Malformed XPM! Height + number of colors too high or too low
199 RGBAImage::RGBAImage(int width_
, int height_
, float scale_
, const unsigned char *pixels_
) :
200 height(height_
), width(width_
), scale(scale_
) {
202 pixelBytes
.assign(pixels_
, pixels_
+ CountBytes());
204 pixelBytes
.resize(CountBytes());
208 RGBAImage::RGBAImage(const XPM
&xpm
) {
209 height
= xpm
.GetHeight();
210 width
= xpm
.GetWidth();
212 pixelBytes
.resize(CountBytes());
213 for (int y
=0; y
<height
; y
++) {
214 for (int x
=0; x
<width
; x
++) {
215 ColourDesired colour
;
216 bool transparent
= false;
217 xpm
.PixelAt(x
, y
, colour
, transparent
);
218 SetPixel(x
, y
, colour
, transparent
? 0 : 255);
223 RGBAImage::~RGBAImage() {
226 int RGBAImage::CountBytes() const {
227 return width
* height
* 4;
230 const unsigned char *RGBAImage::Pixels() const {
231 return &pixelBytes
[0];
234 void RGBAImage::SetPixel(int x
, int y
, ColourDesired colour
, int alpha
) {
235 unsigned char *pixel
= &pixelBytes
[0] + (y
*width
+x
) * 4;
237 pixel
[0] = static_cast<unsigned char>(colour
.GetRed());
238 pixel
[1] = static_cast<unsigned char>(colour
.GetGreen());
239 pixel
[2] = static_cast<unsigned char>(colour
.GetBlue());
240 pixel
[3] = static_cast<unsigned char>(alpha
);
243 RGBAImageSet::RGBAImageSet() : height(-1), width(-1) {
246 RGBAImageSet::~RGBAImageSet() {
250 /// Remove all images.
251 void RGBAImageSet::Clear() {
258 void RGBAImageSet::Add(int ident
, RGBAImage
*image
) {
259 ImageMap::iterator it
=images
.find(ident
);
260 if (it
== images
.end()) {
261 images
[ident
] = std::unique_ptr
<RGBAImage
>(image
);
263 it
->second
.reset(image
);
270 RGBAImage
*RGBAImageSet::Get(int ident
) {
271 ImageMap::iterator it
= images
.find(ident
);
272 if (it
!= images
.end()) {
273 return it
->second
.get();
278 /// Give the largest height of the set.
279 int RGBAImageSet::GetHeight() const {
281 for (const std::pair
<const int, std::unique_ptr
<RGBAImage
>> &image
: images
) {
282 if (height
< image
.second
->GetHeight()) {
283 height
= image
.second
->GetHeight();
287 return (height
> 0) ? height
: 0;
290 /// Give the largest width of the set.
291 int RGBAImageSet::GetWidth() const {
293 for (const std::pair
<const int, std::unique_ptr
<RGBAImage
>> &image
: images
) {
294 if (width
< image
.second
->GetWidth()) {
295 width
= image
.second
->GetWidth();
299 return (width
> 0) ? width
: 0;