2009-04-24 23:35:41 +00:00
|
|
|
// Scintilla source code edit control
|
|
|
|
/** @file XPM.cxx
|
|
|
|
** Define a class that holds data in the X Pixmap (XPM) format.
|
|
|
|
**/
|
|
|
|
// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
|
|
|
|
// The License.txt file describes the conditions under which this software may be distributed.
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include "Platform.h"
|
|
|
|
|
|
|
|
#include "XPM.h"
|
|
|
|
|
|
|
|
#ifdef SCI_NAMESPACE
|
|
|
|
using namespace Scintilla;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static const char *NextField(const char *s) {
|
|
|
|
// In case there are leading spaces in the string
|
|
|
|
while (*s && *s == ' ') {
|
|
|
|
s++;
|
|
|
|
}
|
|
|
|
while (*s && *s != ' ') {
|
|
|
|
s++;
|
|
|
|
}
|
|
|
|
while (*s && *s == ' ') {
|
|
|
|
s++;
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Data lines in XPM can be terminated either with NUL or "
|
|
|
|
static size_t MeasureLength(const char *s) {
|
|
|
|
size_t i = 0;
|
|
|
|
while (s[i] && (s[i] != '\"'))
|
|
|
|
i++;
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2010-07-12 22:19:51 +00:00
|
|
|
ColourAllocated XPM::ColourFromCode(int ch) const {
|
2009-04-24 23:35:41 +00:00
|
|
|
return colourCodeTable[ch]->allocated;
|
|
|
|
#ifdef SLOW
|
|
|
|
for (int i=0; i<nColours; i++) {
|
|
|
|
if (codes[i] == ch) {
|
|
|
|
return colours[i].allocated;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return colours[0].allocated;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void XPM::FillRun(Surface *surface, int code, int startX, int y, int x) {
|
|
|
|
if ((code != codeTransparent) && (startX != x)) {
|
|
|
|
PRectangle rc(startX, y, x, y+1);
|
|
|
|
surface->FillRectangle(rc, ColourFromCode(code));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
XPM::XPM(const char *textForm) :
|
|
|
|
data(0), codes(0), colours(0), lines(0) {
|
|
|
|
Init(textForm);
|
|
|
|
}
|
|
|
|
|
2010-07-12 22:19:51 +00:00
|
|
|
XPM::XPM(const char *const *linesForm) :
|
2009-04-24 23:35:41 +00:00
|
|
|
data(0), codes(0), colours(0), lines(0) {
|
|
|
|
Init(linesForm);
|
|
|
|
}
|
|
|
|
|
|
|
|
XPM::~XPM() {
|
|
|
|
Clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void XPM::Init(const char *textForm) {
|
|
|
|
Clear();
|
|
|
|
// Test done is two parts to avoid possibility of overstepping the memory
|
|
|
|
// if memcmp implemented strangely. Must be 4 bytes at least at destination.
|
|
|
|
if ((0 == memcmp(textForm, "/* X", 4)) && (0 == memcmp(textForm, "/* XPM */", 9))) {
|
|
|
|
// Build the lines form out of the text form
|
|
|
|
const char **linesForm = LinesFormFromTextForm(textForm);
|
|
|
|
if (linesForm != 0) {
|
|
|
|
Init(linesForm);
|
|
|
|
delete []linesForm;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// It is really in line form
|
|
|
|
Init(reinterpret_cast<const char * const *>(textForm));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-12 22:19:51 +00:00
|
|
|
void XPM::Init(const char *const *linesForm) {
|
2009-04-24 23:35:41 +00:00
|
|
|
Clear();
|
|
|
|
height = 1;
|
|
|
|
width = 1;
|
|
|
|
nColours = 1;
|
|
|
|
data = NULL;
|
|
|
|
codeTransparent = ' ';
|
|
|
|
codes = NULL;
|
|
|
|
colours = NULL;
|
|
|
|
lines = NULL;
|
|
|
|
if (!linesForm)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const char *line0 = linesForm[0];
|
|
|
|
width = atoi(line0);
|
|
|
|
line0 = NextField(line0);
|
|
|
|
height = atoi(line0);
|
|
|
|
line0 = NextField(line0);
|
|
|
|
nColours = atoi(line0);
|
|
|
|
line0 = NextField(line0);
|
|
|
|
if (atoi(line0) != 1) {
|
|
|
|
// Only one char per pixel is supported
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
codes = new char[nColours];
|
|
|
|
colours = new ColourPair[nColours];
|
|
|
|
|
|
|
|
int strings = 1+height+nColours;
|
|
|
|
lines = new char *[strings];
|
|
|
|
size_t allocation = 0;
|
|
|
|
for (int i=0; i<strings; i++) {
|
|
|
|
allocation += MeasureLength(linesForm[i]) + 1;
|
|
|
|
}
|
|
|
|
data = new char[allocation];
|
|
|
|
char *nextBit = data;
|
|
|
|
for (int j=0; j<strings; j++) {
|
|
|
|
lines[j] = nextBit;
|
|
|
|
size_t len = MeasureLength(linesForm[j]);
|
|
|
|
memcpy(nextBit, linesForm[j], len);
|
|
|
|
nextBit += len;
|
|
|
|
*nextBit++ = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int code=0; code<256; code++) {
|
|
|
|
colourCodeTable[code] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int c=0; c<nColours; c++) {
|
|
|
|
const char *colourDef = linesForm[c+1];
|
|
|
|
codes[c] = colourDef[0];
|
|
|
|
colourDef += 4;
|
|
|
|
if (*colourDef == '#') {
|
|
|
|
colours[c].desired.Set(colourDef);
|
|
|
|
} else {
|
|
|
|
colours[c].desired = ColourDesired(0xff, 0xff, 0xff);
|
|
|
|
codeTransparent = codes[c];
|
|
|
|
}
|
|
|
|
colourCodeTable[static_cast<unsigned char>(codes[c])] = &(colours[c]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void XPM::Clear() {
|
|
|
|
delete []data;
|
|
|
|
data = 0;
|
|
|
|
delete []codes;
|
|
|
|
codes = 0;
|
|
|
|
delete []colours;
|
|
|
|
colours = 0;
|
|
|
|
delete []lines;
|
|
|
|
lines = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void XPM::RefreshColourPalette(Palette &pal, bool want) {
|
|
|
|
if (!data || !codes || !colours || !lines) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (int i=0; i<nColours; i++) {
|
|
|
|
pal.WantFind(colours[i], want);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void XPM::CopyDesiredColours() {
|
|
|
|
if (!data || !codes || !colours || !lines) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (int i=0; i<nColours; i++) {
|
|
|
|
colours[i].Copy();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void XPM::Draw(Surface *surface, PRectangle &rc) {
|
|
|
|
if (!data || !codes || !colours || !lines) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Centre the pixmap
|
|
|
|
int startY = rc.top + (rc.Height() - height) / 2;
|
|
|
|
int startX = rc.left + (rc.Width() - width) / 2;
|
2010-07-12 22:19:51 +00:00
|
|
|
for (int y=0; y<height; y++) {
|
2009-04-24 23:35:41 +00:00
|
|
|
int prevCode = 0;
|
|
|
|
int xStartRun = 0;
|
|
|
|
for (int x=0; x<width; x++) {
|
|
|
|
int code = lines[y+nColours+1][x];
|
|
|
|
if (code != prevCode) {
|
|
|
|
FillRun(surface, prevCode, startX + xStartRun, startY + y, startX + x);
|
|
|
|
xStartRun = x;
|
|
|
|
prevCode = code;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FillRun(surface, prevCode, startX + xStartRun, startY + y, startX + width);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const char **XPM::LinesFormFromTextForm(const char *textForm) {
|
|
|
|
// Build the lines form out of the text form
|
|
|
|
const char **linesForm = 0;
|
|
|
|
int countQuotes = 0;
|
|
|
|
int strings=1;
|
|
|
|
int j=0;
|
|
|
|
for (; countQuotes < (2*strings) && textForm[j] != '\0'; j++) {
|
|
|
|
if (textForm[j] == '\"') {
|
|
|
|
if (countQuotes == 0) {
|
|
|
|
// First field: width, height, number of colors, chars per pixel
|
|
|
|
const char *line0 = textForm + j + 1;
|
|
|
|
// Skip width
|
|
|
|
line0 = NextField(line0);
|
|
|
|
// Add 1 line for each pixel of height
|
|
|
|
strings += atoi(line0);
|
|
|
|
line0 = NextField(line0);
|
|
|
|
// Add 1 line for each colour
|
|
|
|
strings += atoi(line0);
|
|
|
|
linesForm = new const char *[strings];
|
|
|
|
if (linesForm == 0) {
|
|
|
|
break; // Memory error!
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (countQuotes / 2 >= strings) {
|
|
|
|
break; // Bad height or number of colors!
|
|
|
|
}
|
|
|
|
if ((countQuotes & 1) == 0) {
|
|
|
|
linesForm[countQuotes / 2] = textForm + j + 1;
|
|
|
|
}
|
|
|
|
countQuotes++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (textForm[j] == '\0' || countQuotes / 2 > strings) {
|
|
|
|
// Malformed XPM! Height + number of colors too high or too low
|
|
|
|
delete []linesForm;
|
|
|
|
linesForm = 0;
|
|
|
|
}
|
|
|
|
return linesForm;
|
|
|
|
}
|
|
|
|
|
|
|
|
// In future, may want to minimize search time by sorting and using a binary search.
|
|
|
|
|
|
|
|
XPMSet::XPMSet() : set(0), len(0), maximum(0), height(-1), width(-1) {
|
|
|
|
}
|
|
|
|
|
|
|
|
XPMSet::~XPMSet() {
|
|
|
|
Clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void XPMSet::Clear() {
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
|
delete set[i];
|
|
|
|
}
|
|
|
|
delete []set;
|
|
|
|
set = 0;
|
|
|
|
len = 0;
|
|
|
|
maximum = 0;
|
|
|
|
height = -1;
|
|
|
|
width = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void XPMSet::Add(int id, const char *textForm) {
|
|
|
|
// Invalidate cached dimensions
|
|
|
|
height = -1;
|
|
|
|
width = -1;
|
|
|
|
|
|
|
|
// Replace if this id already present
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
|
if (set[i]->GetId() == id) {
|
|
|
|
set[i]->Init(textForm);
|
|
|
|
set[i]->CopyDesiredColours();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Not present, so add to end
|
|
|
|
XPM *pxpm = new XPM(textForm);
|
|
|
|
if (pxpm) {
|
|
|
|
pxpm->SetId(id);
|
|
|
|
pxpm->CopyDesiredColours();
|
|
|
|
if (len == maximum) {
|
|
|
|
maximum += 64;
|
|
|
|
XPM **setNew = new XPM *[maximum];
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
|
setNew[i] = set[i];
|
|
|
|
}
|
|
|
|
delete []set;
|
|
|
|
set = setNew;
|
|
|
|
}
|
|
|
|
set[len] = pxpm;
|
|
|
|
len++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
XPM *XPMSet::Get(int id) {
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
|
if (set[i]->GetId() == id) {
|
|
|
|
return set[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int XPMSet::GetHeight() {
|
|
|
|
if (height < 0) {
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
|
if (height < set[i]->GetHeight()) {
|
|
|
|
height = set[i]->GetHeight();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (height > 0) ? height : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int XPMSet::GetWidth() {
|
|
|
|
if (width < 0) {
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
|
if (width < set[i]->GetWidth()) {
|
|
|
|
width = set[i]->GetWidth();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (width > 0) ? width : 0;
|
|
|
|
}
|