PageRenderTime 49ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/contrib/scintilla/src/XPM.cxx

https://gitlab.com/Smileyt/KomodoEdit
C++ | 303 lines | 244 code | 33 blank | 26 comment | 76 complexity | 814a8ffab7e2098c48944451bebe24ce MD5 | raw file
  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.
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <vector>
  10. #include <map>
  11. #include "Platform.h"
  12. #include "XPM.h"
  13. #ifdef SCI_NAMESPACE
  14. using namespace Scintilla;
  15. #endif
  16. static const char *NextField(const char *s) {
  17. // In case there are leading spaces in the string
  18. while (*s && *s == ' ') {
  19. s++;
  20. }
  21. while (*s && *s != ' ') {
  22. s++;
  23. }
  24. while (*s && *s == ' ') {
  25. s++;
  26. }
  27. return s;
  28. }
  29. // Data lines in XPM can be terminated either with NUL or "
  30. static size_t MeasureLength(const char *s) {
  31. size_t i = 0;
  32. while (s[i] && (s[i] != '\"'))
  33. i++;
  34. return i;
  35. }
  36. ColourDesired XPM::ColourFromCode(int ch) const {
  37. return colourCodeTable[ch];
  38. }
  39. void XPM::FillRun(Surface *surface, int code, int startX, int y, int x) {
  40. if ((code != codeTransparent) && (startX != x)) {
  41. PRectangle rc = PRectangle::FromInts(startX, y, x, y + 1);
  42. surface->FillRectangle(rc, ColourFromCode(code));
  43. }
  44. }
  45. XPM::XPM(const char *textForm) {
  46. Init(textForm);
  47. }
  48. XPM::XPM(const char *const *linesForm) {
  49. Init(linesForm);
  50. }
  51. XPM::~XPM() {
  52. }
  53. void XPM::Init(const char *textForm) {
  54. // Test done is two parts to avoid possibility of overstepping the memory
  55. // if memcmp implemented strangely. Must be 4 bytes at least at destination.
  56. if ((0 == memcmp(textForm, "/* X", 4)) && (0 == memcmp(textForm, "/* XPM */", 9))) {
  57. // Build the lines form out of the text form
  58. std::vector<const char *> linesForm = LinesFormFromTextForm(textForm);
  59. if (!linesForm.empty()) {
  60. Init(&linesForm[0]);
  61. }
  62. } else {
  63. // It is really in line form
  64. Init(reinterpret_cast<const char * const *>(textForm));
  65. }
  66. }
  67. void XPM::Init(const char *const *linesForm) {
  68. height = 1;
  69. width = 1;
  70. nColours = 1;
  71. pixels.clear();
  72. codeTransparent = ' ';
  73. if (!linesForm)
  74. return;
  75. std::fill(colourCodeTable, colourCodeTable+256, 0);
  76. const char *line0 = linesForm[0];
  77. width = atoi(line0);
  78. line0 = NextField(line0);
  79. height = atoi(line0);
  80. pixels.resize(width*height);
  81. line0 = NextField(line0);
  82. nColours = atoi(line0);
  83. line0 = NextField(line0);
  84. if (atoi(line0) != 1) {
  85. // Only one char per pixel is supported
  86. return;
  87. }
  88. for (int c=0; c<nColours; c++) {
  89. const char *colourDef = linesForm[c+1];
  90. int code = static_cast<unsigned char>(colourDef[0]);
  91. colourDef += 4;
  92. ColourDesired colour(0xff, 0xff, 0xff);
  93. if (*colourDef == '#') {
  94. colour.Set(colourDef);
  95. } else {
  96. codeTransparent = static_cast<char>(code);
  97. }
  98. colourCodeTable[code] = colour;
  99. }
  100. for (int y=0; y<height; y++) {
  101. const char *lform = linesForm[y+nColours+1];
  102. size_t len = MeasureLength(lform);
  103. for (size_t x = 0; x<len; x++)
  104. pixels[y * width + x] = static_cast<unsigned char>(lform[x]);
  105. }
  106. }
  107. void XPM::Draw(Surface *surface, PRectangle &rc) {
  108. if (pixels.empty()) {
  109. return;
  110. }
  111. // Centre the pixmap
  112. int startY = static_cast<int>(rc.top + (rc.Height() - height) / 2);
  113. int startX = static_cast<int>(rc.left + (rc.Width() - width) / 2);
  114. for (int y=0; y<height; y++) {
  115. int prevCode = 0;
  116. int xStartRun = 0;
  117. for (int x=0; x<width; x++) {
  118. int code = pixels[y * width + x];
  119. if (code != prevCode) {
  120. FillRun(surface, prevCode, startX + xStartRun, startY + y, startX + x);
  121. xStartRun = x;
  122. prevCode = code;
  123. }
  124. }
  125. FillRun(surface, prevCode, startX + xStartRun, startY + y, startX + width);
  126. }
  127. }
  128. void XPM::PixelAt(int x, int y, ColourDesired &colour, bool &transparent) const {
  129. if (pixels.empty() || (x<0) || (x >= width) || (y<0) || (y >= height)) {
  130. colour = 0;
  131. transparent = true;
  132. return;
  133. }
  134. int code = pixels[y * width + x];
  135. transparent = code == codeTransparent;
  136. if (transparent) {
  137. colour = 0;
  138. } else {
  139. colour = ColourFromCode(code).AsLong();
  140. }
  141. }
  142. std::vector<const char *> XPM::LinesFormFromTextForm(const char *textForm) {
  143. // Build the lines form out of the text form
  144. std::vector<const char *> linesForm;
  145. int countQuotes = 0;
  146. int strings=1;
  147. int j=0;
  148. for (; countQuotes < (2*strings) && textForm[j] != '\0'; j++) {
  149. if (textForm[j] == '\"') {
  150. if (countQuotes == 0) {
  151. // First field: width, height, number of colors, chars per pixel
  152. const char *line0 = textForm + j + 1;
  153. // Skip width
  154. line0 = NextField(line0);
  155. // Add 1 line for each pixel of height
  156. strings += atoi(line0);
  157. line0 = NextField(line0);
  158. // Add 1 line for each colour
  159. strings += atoi(line0);
  160. }
  161. if (countQuotes / 2 >= strings) {
  162. break; // Bad height or number of colors!
  163. }
  164. if ((countQuotes & 1) == 0) {
  165. linesForm.push_back(textForm + j + 1);
  166. }
  167. countQuotes++;
  168. }
  169. }
  170. if (textForm[j] == '\0' || countQuotes / 2 > strings) {
  171. // Malformed XPM! Height + number of colors too high or too low
  172. linesForm.clear();
  173. }
  174. return linesForm;
  175. }
  176. RGBAImage::RGBAImage(int width_, int height_, float scale_, const unsigned char *pixels_) :
  177. height(height_), width(width_), scale(scale_) {
  178. if (pixels_) {
  179. pixelBytes.assign(pixels_, pixels_ + CountBytes());
  180. } else {
  181. pixelBytes.resize(CountBytes());
  182. }
  183. }
  184. RGBAImage::RGBAImage(const XPM &xpm) {
  185. height = xpm.GetHeight();
  186. width = xpm.GetWidth();
  187. scale = 1;
  188. pixelBytes.resize(CountBytes());
  189. for (int y=0; y<height; y++) {
  190. for (int x=0; x<width; x++) {
  191. ColourDesired colour;
  192. bool transparent = false;
  193. xpm.PixelAt(x, y, colour, transparent);
  194. SetPixel(x, y, colour, transparent ? 0 : 255);
  195. }
  196. }
  197. }
  198. RGBAImage::~RGBAImage() {
  199. }
  200. int RGBAImage::CountBytes() const {
  201. return width * height * 4;
  202. }
  203. const unsigned char *RGBAImage::Pixels() const {
  204. return &pixelBytes[0];
  205. }
  206. void RGBAImage::SetPixel(int x, int y, ColourDesired colour, int alpha) {
  207. unsigned char *pixel = &pixelBytes[0] + (y*width+x) * 4;
  208. // RGBA
  209. pixel[0] = static_cast<unsigned char>(colour.GetRed());
  210. pixel[1] = static_cast<unsigned char>(colour.GetGreen());
  211. pixel[2] = static_cast<unsigned char>(colour.GetBlue());
  212. pixel[3] = static_cast<unsigned char>(alpha);
  213. }
  214. RGBAImageSet::RGBAImageSet() : height(-1), width(-1) {
  215. }
  216. RGBAImageSet::~RGBAImageSet() {
  217. Clear();
  218. }
  219. /// Remove all images.
  220. void RGBAImageSet::Clear() {
  221. for (ImageMap::iterator it=images.begin(); it != images.end(); ++it) {
  222. delete it->second;
  223. it->second = 0;
  224. }
  225. images.clear();
  226. height = -1;
  227. width = -1;
  228. }
  229. /// Add an image.
  230. void RGBAImageSet::Add(int ident, RGBAImage *image) {
  231. ImageMap::iterator it=images.find(ident);
  232. if (it == images.end()) {
  233. images[ident] = image;
  234. } else {
  235. delete it->second;
  236. it->second = image;
  237. }
  238. height = -1;
  239. width = -1;
  240. }
  241. /// Get image by id.
  242. RGBAImage *RGBAImageSet::Get(int ident) {
  243. ImageMap::iterator it = images.find(ident);
  244. if (it != images.end()) {
  245. return it->second;
  246. }
  247. return NULL;
  248. }
  249. /// Give the largest height of the set.
  250. int RGBAImageSet::GetHeight() const {
  251. if (height < 0) {
  252. for (ImageMap::const_iterator it=images.begin(); it != images.end(); ++it) {
  253. if (height < it->second->GetHeight()) {
  254. height = it->second->GetHeight();
  255. }
  256. }
  257. }
  258. return (height > 0) ? height : 0;
  259. }
  260. /// Give the largest width of the set.
  261. int RGBAImageSet::GetWidth() const {
  262. if (width < 0) {
  263. for (ImageMap::const_iterator it=images.begin(); it != images.end(); ++it) {
  264. if (width < it->second->GetWidth()) {
  265. width = it->second->GetWidth();
  266. }
  267. }
  268. }
  269. return (width > 0) ? width : 0;
  270. }