Fixed issue #2059: "Go to line" CLI argument support for diff command
[TortoiseGit.git] / ext / scintilla / src / XPM.cxx
blobeeadce9b2a438f7ea773666bac19285368eec005
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 <vector>
12 #include <map>
14 #include "Platform.h"
16 #include "XPM.h"
18 #ifdef SCI_NAMESPACE
19 using namespace Scintilla;
20 #endif
22 static const char *NextField(const char *s) {
23 // In case there are leading spaces in the string
24 while (*s && *s == ' ') {
25 s++;
27 while (*s && *s != ' ') {
28 s++;
30 while (*s && *s == ' ') {
31 s++;
33 return s;
36 // Data lines in XPM can be terminated either with NUL or "
37 static size_t MeasureLength(const char *s) {
38 size_t i = 0;
39 while (s[i] && (s[i] != '\"'))
40 i++;
41 return i;
44 ColourDesired XPM::ColourFromCode(int ch) const {
45 return colourCodeTable[ch];
48 void XPM::FillRun(Surface *surface, int code, int startX, int y, int x) {
49 if ((code != codeTransparent) && (startX != x)) {
50 PRectangle rc(startX, y, x, y+1);
51 surface->FillRectangle(rc, ColourFromCode(code));
55 XPM::XPM(const char *textForm) {
56 Init(textForm);
59 XPM::XPM(const char *const *linesForm) {
60 Init(linesForm);
63 XPM::~XPM() {
64 Clear();
67 void XPM::Init(const char *textForm) {
68 Clear();
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 Clear();
85 height = 1;
86 width = 1;
87 nColours = 1;
88 pixels.clear();
89 codeTransparent = ' ';
90 if (!linesForm)
91 return;
93 std::fill(colourCodeTable, colourCodeTable+256, 0);
94 const char *line0 = linesForm[0];
95 width = atoi(line0);
96 line0 = NextField(line0);
97 height = atoi(line0);
98 pixels.resize(width*height);
99 line0 = NextField(line0);
100 nColours = atoi(line0);
101 line0 = NextField(line0);
102 if (atoi(line0) != 1) {
103 // Only one char per pixel is supported
104 return;
107 for (int c=0; c<nColours; c++) {
108 const char *colourDef = linesForm[c+1];
109 int code = static_cast<unsigned char>(colourDef[0]);
110 colourDef += 4;
111 ColourDesired colour(0xff, 0xff, 0xff);
112 if (*colourDef == '#') {
113 colour.Set(colourDef);
114 } else {
115 codeTransparent = code;
117 colourCodeTable[code] = colour;
120 for (int y=0; y<height; y++) {
121 const char *lform = linesForm[y+nColours+1];
122 size_t len = MeasureLength(lform);
123 for (size_t x = 0; x<len; x++)
124 pixels[y * width + x] = static_cast<unsigned char>(lform[x]);
128 void XPM::Clear() {
131 void XPM::Draw(Surface *surface, PRectangle &rc) {
132 if (pixels.empty()) {
133 return;
135 // Centre the pixmap
136 int startY = rc.top + (rc.Height() - height) / 2;
137 int startX = rc.left + (rc.Width() - width) / 2;
138 for (int y=0; y<height; y++) {
139 int prevCode = 0;
140 int xStartRun = 0;
141 for (int x=0; x<width; x++) {
142 int code = pixels[y * width + x];
143 if (code != prevCode) {
144 FillRun(surface, prevCode, startX + xStartRun, startY + y, startX + x);
145 xStartRun = x;
146 prevCode = code;
149 FillRun(surface, prevCode, startX + xStartRun, startY + y, startX + width);
153 void XPM::PixelAt(int x, int y, ColourDesired &colour, bool &transparent) const {
154 if (pixels.empty() || (x<0) || (x >= width) || (y<0) || (y >= height)) {
155 colour = 0;
156 transparent = true;
157 return;
159 int code = pixels[y * width + x];
160 transparent = code == codeTransparent;
161 if (transparent) {
162 colour = 0;
163 } else {
164 colour = ColourFromCode(code).AsLong();
168 std::vector<const char *> XPM::LinesFormFromTextForm(const char *textForm) {
169 // Build the lines form out of the text form
170 std::vector<const char *> linesForm;
171 int countQuotes = 0;
172 int strings=1;
173 int j=0;
174 for (; countQuotes < (2*strings) && textForm[j] != '\0'; j++) {
175 if (textForm[j] == '\"') {
176 if (countQuotes == 0) {
177 // First field: width, height, number of colors, chars per pixel
178 const char *line0 = textForm + j + 1;
179 // Skip width
180 line0 = NextField(line0);
181 // Add 1 line for each pixel of height
182 strings += atoi(line0);
183 line0 = NextField(line0);
184 // Add 1 line for each colour
185 strings += atoi(line0);
187 if (countQuotes / 2 >= strings) {
188 break; // Bad height or number of colors!
190 if ((countQuotes & 1) == 0) {
191 linesForm.push_back(textForm + j + 1);
193 countQuotes++;
196 if (textForm[j] == '\0' || countQuotes / 2 > strings) {
197 // Malformed XPM! Height + number of colors too high or too low
198 linesForm.clear();
200 return linesForm;
203 RGBAImage::RGBAImage(int width_, int height_, float scale_, const unsigned char *pixels_) :
204 height(height_), width(width_), scale(scale_) {
205 if (pixels_) {
206 pixelBytes.assign(pixels_, pixels_ + CountBytes());
207 } else {
208 pixelBytes.resize(CountBytes());
212 RGBAImage::RGBAImage(const XPM &xpm) {
213 height = xpm.GetHeight();
214 width = xpm.GetWidth();
215 scale = 1;
216 pixelBytes.resize(CountBytes());
217 for (int y=0; y<height; y++) {
218 for (int x=0; x<width; x++) {
219 ColourDesired colour;
220 bool transparent = false;
221 xpm.PixelAt(x, y, colour, transparent);
222 SetPixel(x, y, colour, transparent ? 0 : 255);
227 RGBAImage::~RGBAImage() {
230 int RGBAImage::CountBytes() const {
231 return width * height * 4;
234 const unsigned char *RGBAImage::Pixels() const {
235 return &pixelBytes[0];
238 void RGBAImage::SetPixel(int x, int y, ColourDesired colour, int alpha) {
239 unsigned char *pixel = &pixelBytes[0] + (y*width+x) * 4;
240 // RGBA
241 pixel[0] = static_cast<unsigned char>(colour.GetRed());
242 pixel[1] = static_cast<unsigned char>(colour.GetGreen());
243 pixel[2] = static_cast<unsigned char>(colour.GetBlue());
244 pixel[3] = static_cast<unsigned char>(alpha);
247 RGBAImageSet::RGBAImageSet() : height(-1), width(-1){
250 RGBAImageSet::~RGBAImageSet() {
251 Clear();
254 /// Remove all images.
255 void RGBAImageSet::Clear() {
256 for (ImageMap::iterator it=images.begin(); it != images.end(); ++it) {
257 delete it->second;
258 it->second = 0;
260 images.clear();
261 height = -1;
262 width = -1;
265 /// Add an image.
266 void RGBAImageSet::Add(int ident, RGBAImage *image) {
267 ImageMap::iterator it=images.find(ident);
268 if (it == images.end()) {
269 images[ident] = image;
270 } else {
271 delete it->second;
272 it->second = image;
274 height = -1;
275 width = -1;
278 /// Get image by id.
279 RGBAImage *RGBAImageSet::Get(int ident) {
280 ImageMap::iterator it = images.find(ident);
281 if (it != images.end()) {
282 return it->second;
284 return NULL;
287 /// Give the largest height of the set.
288 int RGBAImageSet::GetHeight() const {
289 if (height < 0) {
290 for (ImageMap::const_iterator it=images.begin(); it != images.end(); ++it) {
291 if (height < it->second->GetHeight()) {
292 height = it->second->GetHeight();
296 return (height > 0) ? height : 0;
299 /// Give the largest width of the set.
300 int RGBAImageSet::GetWidth() const {
301 if (width < 0) {
302 for (ImageMap::const_iterator it=images.begin(); it != images.end(); ++it) {
303 if (width < it->second->GetWidth()) {
304 width = it->second->GetWidth();
308 return (width > 0) ? width : 0;