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.
16
using namespace Scintilla;
19
static const char *NextField(const char *s) {
20
// In case there are leading spaces in the string
21
while (*s && *s == ' ') {
24
while (*s && *s != ' ') {
27
while (*s && *s == ' ') {
33
// Data lines in XPM can be terminated either with NUL or "
34
static size_t MeasureLength(const char *s) {
36
while (s[i] && (s[i] != '\"'))
41
ColourAllocated XPM::ColourFromCode(int ch) {
42
return colourCodeTable[ch]->allocated;
44
for (int i=0; i<nColours; i++) {
46
return colours[i].allocated;
49
return colours[0].allocated;
53
void XPM::FillRun(Surface *surface, int code, int startX, int y, int x) {
54
if ((code != codeTransparent) && (startX != x)) {
55
PRectangle rc(startX, y, x, y+1);
56
surface->FillRectangle(rc, ColourFromCode(code));
60
XPM::XPM(const char *textForm) :
61
data(0), codes(0), colours(0), lines(0) {
65
XPM::XPM(const char * const *linesForm) :
66
data(0), codes(0), colours(0), lines(0) {
74
void XPM::Init(const char *textForm) {
76
// Test done is two parts to avoid possibility of overstepping the memory
77
// if memcmp implemented strangely. Must be 4 bytes at least at destination.
78
if ((0 == memcmp(textForm, "/* X", 4)) && (0 == memcmp(textForm, "/* XPM */", 9))) {
79
// Build the lines form out of the text form
80
const char **linesForm = LinesFormFromTextForm(textForm);
86
// It is really in line form
87
Init(reinterpret_cast<const char * const *>(textForm));
91
void XPM::Init(const char * const *linesForm) {
97
codeTransparent = ' ';
104
const char *line0 = linesForm[0];
106
line0 = NextField(line0);
107
height = atoi(line0);
108
line0 = NextField(line0);
109
nColours = atoi(line0);
110
line0 = NextField(line0);
111
if (atoi(line0) != 1) {
112
// Only one char per pixel is supported
115
codes = new char[nColours];
116
colours = new ColourPair[nColours];
118
int strings = 1+height+nColours;
119
lines = new char *[strings];
120
size_t allocation = 0;
121
for (int i=0; i<strings; i++) {
122
allocation += MeasureLength(linesForm[i]) + 1;
124
data = new char[allocation];
125
char *nextBit = data;
126
for (int j=0; j<strings; j++) {
128
size_t len = MeasureLength(linesForm[j]);
129
memcpy(nextBit, linesForm[j], len);
134
for (int code=0; code<256; code++) {
135
colourCodeTable[code] = 0;
138
for (int c=0; c<nColours; c++) {
139
const char *colourDef = linesForm[c+1];
140
codes[c] = colourDef[0];
142
if (*colourDef == '#') {
143
colours[c].desired.Set(colourDef);
145
colours[c].desired = ColourDesired(0xff, 0xff, 0xff);
146
codeTransparent = codes[c];
148
colourCodeTable[static_cast<unsigned char>(codes[c])] = &(colours[c]);
163
void XPM::RefreshColourPalette(Palette &pal, bool want) {
164
if (!data || !codes || !colours || !lines) {
167
for (int i=0; i<nColours; i++) {
168
pal.WantFind(colours[i], want);
172
void XPM::CopyDesiredColours() {
173
if (!data || !codes || !colours || !lines) {
176
for (int i=0; i<nColours; i++) {
181
void XPM::Draw(Surface *surface, PRectangle &rc) {
182
if (!data || !codes || !colours || !lines) {
186
int startY = rc.top + (rc.Height() - height) / 2;
187
int startX = rc.left + (rc.Width() - width) / 2;
188
for (int y=0;y<height;y++) {
191
for (int x=0; x<width; x++) {
192
int code = lines[y+nColours+1][x];
193
if (code != prevCode) {
194
FillRun(surface, prevCode, startX + xStartRun, startY + y, startX + x);
199
FillRun(surface, prevCode, startX + xStartRun, startY + y, startX + width);
203
const char **XPM::LinesFormFromTextForm(const char *textForm) {
204
// Build the lines form out of the text form
205
const char **linesForm = 0;
209
for (; countQuotes < (2*strings) && textForm[j] != '\0'; j++) {
210
if (textForm[j] == '\"') {
211
if (countQuotes == 0) {
212
// First field: width, height, number of colors, chars per pixel
213
const char *line0 = textForm + j + 1;
215
line0 = NextField(line0);
216
// Add 1 line for each pixel of height
217
strings += atoi(line0);
218
line0 = NextField(line0);
219
// Add 1 line for each colour
220
strings += atoi(line0);
221
linesForm = new const char *[strings];
222
if (linesForm == 0) {
223
break; // Memory error!
226
if (countQuotes / 2 >= strings) {
227
break; // Bad height or number of colors!
229
if ((countQuotes & 1) == 0) {
230
linesForm[countQuotes / 2] = textForm + j + 1;
235
if (textForm[j] == '\0' || countQuotes / 2 > strings) {
236
// Malformed XPM! Height + number of colors too high or too low
243
// In future, may want to minimize search time by sorting and using a binary search.
245
XPMSet::XPMSet() : set(0), len(0), maximum(0), height(-1), width(-1) {
252
void XPMSet::Clear() {
253
for (int i = 0; i < len; i++) {
264
void XPMSet::Add(int id, const char *textForm) {
265
// Invalidate cached dimensions
269
// Replace if this id already present
270
for (int i = 0; i < len; i++) {
271
if (set[i]->GetId() == id) {
272
set[i]->Init(textForm);
273
set[i]->CopyDesiredColours();
278
// Not present, so add to end
279
XPM *pxpm = new XPM(textForm);
282
pxpm->CopyDesiredColours();
283
if (len == maximum) {
285
XPM **setNew = new XPM *[maximum];
286
for (int i = 0; i < len; i++) {
297
XPM *XPMSet::Get(int id) {
298
for (int i = 0; i < len; i++) {
299
if (set[i]->GetId() == id) {
306
int XPMSet::GetHeight() {
308
for (int i = 0; i < len; i++) {
309
if (height < set[i]->GetHeight()) {
310
height = set[i]->GetHeight();
314
return (height > 0) ? height : 0;
317
int XPMSet::GetWidth() {
319
for (int i = 0; i < len; i++) {
320
if (width < set[i]->GetWidth()) {
321
width = set[i]->GetWidth();
325
return (width > 0) ? width : 0;