Update Scintilla to 4.0.4
[TortoiseGit.git] / ext / scintilla / src / XPM.cxx
blob006afc47100bbcc28172fc4d8db69d0cd8d11d10
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 <cstdlib>
9 #include <cstring>
11 #include <stdexcept>
12 #include <vector>
13 #include <map>
14 #include <algorithm>
15 #include <iterator>
16 #include <memory>
18 #include "Platform.h"
20 #include "XPM.h"
22 using namespace Scintilla;
24 static const char *NextField(const char *s) {
25 // In case there are leading spaces in the string
26 while (*s == ' ') {
27 s++;
29 while (*s && *s != ' ') {
30 s++;
32 while (*s == ' ') {
33 s++;
35 return s;
38 // Data lines in XPM can be terminated either with NUL or "
39 static size_t MeasureLength(const char *s) {
40 size_t i = 0;
41 while (s[i] && (s[i] != '\"'))
42 i++;
43 return 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 const PRectangle rc = PRectangle::FromInts(startX, y, x, y + 1);
53 surface->FillRectangle(rc, ColourFromCode(code));
57 XPM::XPM(const char *textForm) {
58 Init(textForm);
61 XPM::XPM(const char *const *linesForm) {
62 Init(linesForm);
65 XPM::~XPM() {
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()) {
75 Init(&linesForm[0]);
77 } else {
78 // It is really in line form
79 Init(reinterpret_cast<const char * const *>(textForm));
83 void XPM::Init(const char *const *linesForm) {
84 height = 1;
85 width = 1;
86 nColours = 1;
87 pixels.clear();
88 codeTransparent = ' ';
89 if (!linesForm)
90 return;
92 std::fill(colourCodeTable, std::end(colourCodeTable), 0);
93 const char *line0 = linesForm[0];
94 width = atoi(line0);
95 line0 = NextField(line0);
96 height = atoi(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
103 return;
106 for (int c=0; c<nColours; c++) {
107 const char *colourDef = linesForm[c+1];
108 int code = static_cast<unsigned char>(colourDef[0]);
109 colourDef += 4;
110 ColourDesired colour(0xff, 0xff, 0xff);
111 if (*colourDef == '#') {
112 colour.Set(colourDef);
113 } else {
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()) {
129 return;
131 // Centre the pixmap
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++) {
135 int prevCode = 0;
136 int xStartRun = 0;
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);
141 xStartRun = x;
142 prevCode = code;
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)) {
151 colour = 0;
152 transparent = true;
153 return;
155 int code = pixels[y * width + x];
156 transparent = code == codeTransparent;
157 if (transparent) {
158 colour = 0;
159 } else {
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;
167 int countQuotes = 0;
168 int strings=1;
169 int j=0;
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;
175 // Skip width
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);
189 countQuotes++;
192 if (textForm[j] == '\0' || countQuotes / 2 > strings) {
193 // Malformed XPM! Height + number of colors too high or too low
194 linesForm.clear();
196 return linesForm;
199 RGBAImage::RGBAImage(int width_, int height_, float scale_, const unsigned char *pixels_) :
200 height(height_), width(width_), scale(scale_) {
201 if (pixels_) {
202 pixelBytes.assign(pixels_, pixels_ + CountBytes());
203 } else {
204 pixelBytes.resize(CountBytes());
208 RGBAImage::RGBAImage(const XPM &xpm) {
209 height = xpm.GetHeight();
210 width = xpm.GetWidth();
211 scale = 1;
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;
236 // RGBA
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() {
247 Clear();
250 /// Remove all images.
251 void RGBAImageSet::Clear() {
252 images.clear();
253 height = -1;
254 width = -1;
257 /// Add an image.
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);
262 } else {
263 it->second.reset(image);
265 height = -1;
266 width = -1;
269 /// Get image by id.
270 RGBAImage *RGBAImageSet::Get(int ident) {
271 ImageMap::iterator it = images.find(ident);
272 if (it != images.end()) {
273 return it->second.get();
275 return nullptr;
278 /// Give the largest height of the set.
279 int RGBAImageSet::GetHeight() const {
280 if (height < 0) {
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 {
292 if (width < 0) {
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;