Merge pull request #826 from kugel-/doxygen-fixes2
[geany-mirror.git] / scintilla / src / XPM.cxx
blob20e6c8bfcb474c36ce2ad63078bf1647e453ca2c
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 <stdlib.h>
9 #include <string.h>
11 #include <stdexcept>
12 #include <vector>
13 #include <map>
15 #include "Platform.h"
17 #include "XPM.h"
19 #ifdef SCI_NAMESPACE
20 using namespace Scintilla;
21 #endif
23 static const char *NextField(const char *s) {
24 // In case there are leading spaces in the string
25 while (*s && *s == ' ') {
26 s++;
28 while (*s && *s != ' ') {
29 s++;
31 while (*s && *s == ' ') {
32 s++;
34 return s;
37 // Data lines in XPM can be terminated either with NUL or "
38 static size_t MeasureLength(const char *s) {
39 size_t i = 0;
40 while (s[i] && (s[i] != '\"'))
41 i++;
42 return 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) {
57 Init(textForm);
60 XPM::XPM(const char *const *linesForm) {
61 Init(linesForm);
64 XPM::~XPM() {
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()) {
74 Init(&linesForm[0]);
76 } else {
77 // It is really in line form
78 Init(reinterpret_cast<const char * const *>(textForm));
82 void XPM::Init(const char *const *linesForm) {
83 height = 1;
84 width = 1;
85 nColours = 1;
86 pixels.clear();
87 codeTransparent = ' ';
88 if (!linesForm)
89 return;
91 std::fill(colourCodeTable, colourCodeTable+256, 0);
92 const char *line0 = linesForm[0];
93 width = atoi(line0);
94 line0 = NextField(line0);
95 height = atoi(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
102 return;
105 for (int c=0; c<nColours; c++) {
106 const char *colourDef = linesForm[c+1];
107 int code = static_cast<unsigned char>(colourDef[0]);
108 colourDef += 4;
109 ColourDesired colour(0xff, 0xff, 0xff);
110 if (*colourDef == '#') {
111 colour.Set(colourDef);
112 } else {
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()) {
128 return;
130 // Centre the pixmap
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++) {
134 int prevCode = 0;
135 int xStartRun = 0;
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);
140 xStartRun = x;
141 prevCode = code;
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)) {
150 colour = 0;
151 transparent = true;
152 return;
154 int code = pixels[y * width + x];
155 transparent = code == codeTransparent;
156 if (transparent) {
157 colour = 0;
158 } else {
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;
166 int countQuotes = 0;
167 int strings=1;
168 int j=0;
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;
174 // Skip width
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);
188 countQuotes++;
191 if (textForm[j] == '\0' || countQuotes / 2 > strings) {
192 // Malformed XPM! Height + number of colors too high or too low
193 linesForm.clear();
195 return linesForm;
198 RGBAImage::RGBAImage(int width_, int height_, float scale_, const unsigned char *pixels_) :
199 height(height_), width(width_), scale(scale_) {
200 if (pixels_) {
201 pixelBytes.assign(pixels_, pixels_ + CountBytes());
202 } else {
203 pixelBytes.resize(CountBytes());
207 RGBAImage::RGBAImage(const XPM &xpm) {
208 height = xpm.GetHeight();
209 width = xpm.GetWidth();
210 scale = 1;
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;
235 // RGBA
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() {
246 Clear();
249 /// Remove all images.
250 void RGBAImageSet::Clear() {
251 for (ImageMap::iterator it=images.begin(); it != images.end(); ++it) {
252 delete it->second;
253 it->second = 0;
255 images.clear();
256 height = -1;
257 width = -1;
260 /// Add an image.
261 void RGBAImageSet::Add(int ident, RGBAImage *image) {
262 ImageMap::iterator it=images.find(ident);
263 if (it == images.end()) {
264 images[ident] = image;
265 } else {
266 delete it->second;
267 it->second = image;
269 height = -1;
270 width = -1;
273 /// Get image by id.
274 RGBAImage *RGBAImageSet::Get(int ident) {
275 ImageMap::iterator it = images.find(ident);
276 if (it != images.end()) {
277 return it->second;
279 return NULL;
282 /// Give the largest height of the set.
283 int RGBAImageSet::GetHeight() const {
284 if (height < 0) {
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 {
296 if (width < 0) {
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;