xref: /MusicPlayer2/scintilla/src/XPM.cxx (revision 8af74909132ed5e696cb05b6689ae4baf14c1c96)
1*8af74909SZhong Yang // Scintilla source code edit control
2*8af74909SZhong Yang /** @file XPM.cxx
3*8af74909SZhong Yang  ** Define a class that holds data in the X Pixmap (XPM) format.
4*8af74909SZhong Yang  **/
5*8af74909SZhong Yang // Copyright 1998-2003 by Neil Hodgson <[email protected]>
6*8af74909SZhong Yang // The License.txt file describes the conditions under which this software may be distributed.
7*8af74909SZhong Yang 
8*8af74909SZhong Yang #include <cstdlib>
9*8af74909SZhong Yang #include <cstring>
10*8af74909SZhong Yang 
11*8af74909SZhong Yang #include <stdexcept>
12*8af74909SZhong Yang #include <string_view>
13*8af74909SZhong Yang #include <vector>
14*8af74909SZhong Yang #include <map>
15*8af74909SZhong Yang #include <algorithm>
16*8af74909SZhong Yang #include <iterator>
17*8af74909SZhong Yang #include <memory>
18*8af74909SZhong Yang 
19*8af74909SZhong Yang #include "Platform.h"
20*8af74909SZhong Yang 
21*8af74909SZhong Yang #include "XPM.h"
22*8af74909SZhong Yang 
23*8af74909SZhong Yang using namespace Scintilla;
24*8af74909SZhong Yang 
25*8af74909SZhong Yang namespace {
26*8af74909SZhong Yang 
NextField(const char * s)27*8af74909SZhong Yang const char *NextField(const char *s) noexcept {
28*8af74909SZhong Yang 	// In case there are leading spaces in the string
29*8af74909SZhong Yang 	while (*s == ' ') {
30*8af74909SZhong Yang 		s++;
31*8af74909SZhong Yang 	}
32*8af74909SZhong Yang 	while (*s && *s != ' ') {
33*8af74909SZhong Yang 		s++;
34*8af74909SZhong Yang 	}
35*8af74909SZhong Yang 	while (*s == ' ') {
36*8af74909SZhong Yang 		s++;
37*8af74909SZhong Yang 	}
38*8af74909SZhong Yang 	return s;
39*8af74909SZhong Yang }
40*8af74909SZhong Yang 
41*8af74909SZhong Yang // Data lines in XPM can be terminated either with NUL or "
MeasureLength(const char * s)42*8af74909SZhong Yang size_t MeasureLength(const char *s) noexcept {
43*8af74909SZhong Yang 	size_t i = 0;
44*8af74909SZhong Yang 	while (s[i] && (s[i] != '\"'))
45*8af74909SZhong Yang 		i++;
46*8af74909SZhong Yang 	return i;
47*8af74909SZhong Yang }
48*8af74909SZhong Yang 
ValueOfHex(const char ch)49*8af74909SZhong Yang unsigned int ValueOfHex(const char ch) noexcept {
50*8af74909SZhong Yang 	if (ch >= '0' && ch <= '9')
51*8af74909SZhong Yang 		return ch - '0';
52*8af74909SZhong Yang 	else if (ch >= 'A' && ch <= 'F')
53*8af74909SZhong Yang 		return ch - 'A' + 10;
54*8af74909SZhong Yang 	else if (ch >= 'a' && ch <= 'f')
55*8af74909SZhong Yang 		return ch - 'a' + 10;
56*8af74909SZhong Yang 	else
57*8af74909SZhong Yang 		return 0;
58*8af74909SZhong Yang }
59*8af74909SZhong Yang 
ColourFromHex(const char * val)60*8af74909SZhong Yang ColourDesired ColourFromHex(const char *val) noexcept {
61*8af74909SZhong Yang 	const unsigned int r = ValueOfHex(val[0]) * 16 + ValueOfHex(val[1]);
62*8af74909SZhong Yang 	const unsigned int g = ValueOfHex(val[2]) * 16 + ValueOfHex(val[3]);
63*8af74909SZhong Yang 	const unsigned int b = ValueOfHex(val[4]) * 16 + ValueOfHex(val[5]);
64*8af74909SZhong Yang 	return ColourDesired(r, g, b);
65*8af74909SZhong Yang }
66*8af74909SZhong Yang 
67*8af74909SZhong Yang }
68*8af74909SZhong Yang 
69*8af74909SZhong Yang 
ColourFromCode(int ch) const70*8af74909SZhong Yang ColourDesired XPM::ColourFromCode(int ch) const noexcept {
71*8af74909SZhong Yang 	return colourCodeTable[ch];
72*8af74909SZhong Yang }
73*8af74909SZhong Yang 
FillRun(Surface * surface,int code,int startX,int y,int x) const74*8af74909SZhong Yang void XPM::FillRun(Surface *surface, int code, int startX, int y, int x) const {
75*8af74909SZhong Yang 	if ((code != codeTransparent) && (startX != x)) {
76*8af74909SZhong Yang 		const PRectangle rc = PRectangle::FromInts(startX, y, x, y + 1);
77*8af74909SZhong Yang 		surface->FillRectangle(rc, ColourFromCode(code));
78*8af74909SZhong Yang 	}
79*8af74909SZhong Yang }
80*8af74909SZhong Yang 
XPM(const char * textForm)81*8af74909SZhong Yang XPM::XPM(const char *textForm) {
82*8af74909SZhong Yang 	Init(textForm);
83*8af74909SZhong Yang }
84*8af74909SZhong Yang 
XPM(const char * const * linesForm)85*8af74909SZhong Yang XPM::XPM(const char *const *linesForm) {
86*8af74909SZhong Yang 	Init(linesForm);
87*8af74909SZhong Yang }
88*8af74909SZhong Yang 
~XPM()89*8af74909SZhong Yang XPM::~XPM() {
90*8af74909SZhong Yang }
91*8af74909SZhong Yang 
Init(const char * textForm)92*8af74909SZhong Yang void XPM::Init(const char *textForm) {
93*8af74909SZhong Yang 	// Test done is two parts to avoid possibility of overstepping the memory
94*8af74909SZhong Yang 	// if memcmp implemented strangely. Must be 4 bytes at least at destination.
95*8af74909SZhong Yang 	if ((0 == memcmp(textForm, "/* X", 4)) && (0 == memcmp(textForm, "/* XPM */", 9))) {
96*8af74909SZhong Yang 		// Build the lines form out of the text form
97*8af74909SZhong Yang 		std::vector<const char *> linesForm = LinesFormFromTextForm(textForm);
98*8af74909SZhong Yang 		if (!linesForm.empty()) {
99*8af74909SZhong Yang 			Init(&linesForm[0]);
100*8af74909SZhong Yang 		}
101*8af74909SZhong Yang 	} else {
102*8af74909SZhong Yang 		// It is really in line form
103*8af74909SZhong Yang 		Init(reinterpret_cast<const char * const *>(textForm));
104*8af74909SZhong Yang 	}
105*8af74909SZhong Yang }
106*8af74909SZhong Yang 
Init(const char * const * linesForm)107*8af74909SZhong Yang void XPM::Init(const char *const *linesForm) {
108*8af74909SZhong Yang 	height = 1;
109*8af74909SZhong Yang 	width = 1;
110*8af74909SZhong Yang 	nColours = 1;
111*8af74909SZhong Yang 	pixels.clear();
112*8af74909SZhong Yang 	codeTransparent = ' ';
113*8af74909SZhong Yang 	if (!linesForm)
114*8af74909SZhong Yang 		return;
115*8af74909SZhong Yang 
116*8af74909SZhong Yang 	std::fill(colourCodeTable, std::end(colourCodeTable), ColourDesired(0));
117*8af74909SZhong Yang 	const char *line0 = linesForm[0];
118*8af74909SZhong Yang 	width = atoi(line0);
119*8af74909SZhong Yang 	line0 = NextField(line0);
120*8af74909SZhong Yang 	height = atoi(line0);
121*8af74909SZhong Yang 	pixels.resize(width*height);
122*8af74909SZhong Yang 	line0 = NextField(line0);
123*8af74909SZhong Yang 	nColours = atoi(line0);
124*8af74909SZhong Yang 	line0 = NextField(line0);
125*8af74909SZhong Yang 	if (atoi(line0) != 1) {
126*8af74909SZhong Yang 		// Only one char per pixel is supported
127*8af74909SZhong Yang 		return;
128*8af74909SZhong Yang 	}
129*8af74909SZhong Yang 
130*8af74909SZhong Yang 	for (int c=0; c<nColours; c++) {
131*8af74909SZhong Yang 		const char *colourDef = linesForm[c+1];
132*8af74909SZhong Yang 		const char code = colourDef[0];
133*8af74909SZhong Yang 		colourDef += 4;
134*8af74909SZhong Yang 		ColourDesired colour(0xff, 0xff, 0xff);
135*8af74909SZhong Yang 		if (*colourDef == '#') {
136*8af74909SZhong Yang 			colour = ColourFromHex(colourDef+1);
137*8af74909SZhong Yang 		} else {
138*8af74909SZhong Yang 			codeTransparent = code;
139*8af74909SZhong Yang 		}
140*8af74909SZhong Yang 		colourCodeTable[static_cast<unsigned char>(code)] = colour;
141*8af74909SZhong Yang 	}
142*8af74909SZhong Yang 
143*8af74909SZhong Yang 	for (int y=0; y<height; y++) {
144*8af74909SZhong Yang 		const char *lform = linesForm[y+nColours+1];
145*8af74909SZhong Yang 		const size_t len = MeasureLength(lform);
146*8af74909SZhong Yang 		for (size_t x = 0; x<len; x++)
147*8af74909SZhong Yang 			pixels[y * width + x] = lform[x];
148*8af74909SZhong Yang 	}
149*8af74909SZhong Yang }
150*8af74909SZhong Yang 
Draw(Surface * surface,const PRectangle & rc)151*8af74909SZhong Yang void XPM::Draw(Surface *surface, const PRectangle &rc) {
152*8af74909SZhong Yang 	if (pixels.empty()) {
153*8af74909SZhong Yang 		return;
154*8af74909SZhong Yang 	}
155*8af74909SZhong Yang 	// Centre the pixmap
156*8af74909SZhong Yang 	const int startY = static_cast<int>(rc.top + (rc.Height() - height) / 2);
157*8af74909SZhong Yang 	const int startX = static_cast<int>(rc.left + (rc.Width() - width) / 2);
158*8af74909SZhong Yang 	for (int y=0; y<height; y++) {
159*8af74909SZhong Yang 		int prevCode = 0;
160*8af74909SZhong Yang 		int xStartRun = 0;
161*8af74909SZhong Yang 		for (int x=0; x<width; x++) {
162*8af74909SZhong Yang 			const int code = pixels[y * width + x];
163*8af74909SZhong Yang 			if (code != prevCode) {
164*8af74909SZhong Yang 				FillRun(surface, prevCode, startX + xStartRun, startY + y, startX + x);
165*8af74909SZhong Yang 				xStartRun = x;
166*8af74909SZhong Yang 				prevCode = code;
167*8af74909SZhong Yang 			}
168*8af74909SZhong Yang 		}
169*8af74909SZhong Yang 		FillRun(surface, prevCode, startX + xStartRun, startY + y, startX + width);
170*8af74909SZhong Yang 	}
171*8af74909SZhong Yang }
172*8af74909SZhong Yang 
PixelAt(int x,int y,ColourDesired & colour,bool & transparent) const173*8af74909SZhong Yang void XPM::PixelAt(int x, int y, ColourDesired &colour, bool &transparent) const noexcept {
174*8af74909SZhong Yang 	if (pixels.empty() || (x<0) || (x >= width) || (y<0) || (y >= height)) {
175*8af74909SZhong Yang 		colour = ColourDesired(0);
176*8af74909SZhong Yang 		transparent = true;
177*8af74909SZhong Yang 		return;
178*8af74909SZhong Yang 	}
179*8af74909SZhong Yang 	const int code = pixels[y * width + x];
180*8af74909SZhong Yang 	transparent = code == codeTransparent;
181*8af74909SZhong Yang 	if (transparent) {
182*8af74909SZhong Yang 		colour = ColourDesired(0);
183*8af74909SZhong Yang 	} else {
184*8af74909SZhong Yang 		colour = ColourFromCode(code);
185*8af74909SZhong Yang 	}
186*8af74909SZhong Yang }
187*8af74909SZhong Yang 
LinesFormFromTextForm(const char * textForm)188*8af74909SZhong Yang std::vector<const char *> XPM::LinesFormFromTextForm(const char *textForm) {
189*8af74909SZhong Yang 	// Build the lines form out of the text form
190*8af74909SZhong Yang 	std::vector<const char *> linesForm;
191*8af74909SZhong Yang 	int countQuotes = 0;
192*8af74909SZhong Yang 	int strings=1;
193*8af74909SZhong Yang 	int j=0;
194*8af74909SZhong Yang 	for (; countQuotes < (2*strings) && textForm[j] != '\0'; j++) {
195*8af74909SZhong Yang 		if (textForm[j] == '\"') {
196*8af74909SZhong Yang 			if (countQuotes == 0) {
197*8af74909SZhong Yang 				// First field: width, height, number of colours, chars per pixel
198*8af74909SZhong Yang 				const char *line0 = textForm + j + 1;
199*8af74909SZhong Yang 				// Skip width
200*8af74909SZhong Yang 				line0 = NextField(line0);
201*8af74909SZhong Yang 				// Add 1 line for each pixel of height
202*8af74909SZhong Yang 				strings += atoi(line0);
203*8af74909SZhong Yang 				line0 = NextField(line0);
204*8af74909SZhong Yang 				// Add 1 line for each colour
205*8af74909SZhong Yang 				strings += atoi(line0);
206*8af74909SZhong Yang 			}
207*8af74909SZhong Yang 			if (countQuotes / 2 >= strings) {
208*8af74909SZhong Yang 				break;	// Bad height or number of colours!
209*8af74909SZhong Yang 			}
210*8af74909SZhong Yang 			if ((countQuotes & 1) == 0) {
211*8af74909SZhong Yang 				linesForm.push_back(textForm + j + 1);
212*8af74909SZhong Yang 			}
213*8af74909SZhong Yang 			countQuotes++;
214*8af74909SZhong Yang 		}
215*8af74909SZhong Yang 	}
216*8af74909SZhong Yang 	if (textForm[j] == '\0' || countQuotes / 2 > strings) {
217*8af74909SZhong Yang 		// Malformed XPM! Height + number of colours too high or too low
218*8af74909SZhong Yang 		linesForm.clear();
219*8af74909SZhong Yang 	}
220*8af74909SZhong Yang 	return linesForm;
221*8af74909SZhong Yang }
222*8af74909SZhong Yang 
RGBAImage(int width_,int height_,float scale_,const unsigned char * pixels_)223*8af74909SZhong Yang RGBAImage::RGBAImage(int width_, int height_, float scale_, const unsigned char *pixels_) :
224*8af74909SZhong Yang 	height(height_), width(width_), scale(scale_) {
225*8af74909SZhong Yang 	if (pixels_) {
226*8af74909SZhong Yang 		pixelBytes.assign(pixels_, pixels_ + CountBytes());
227*8af74909SZhong Yang 	} else {
228*8af74909SZhong Yang 		pixelBytes.resize(CountBytes());
229*8af74909SZhong Yang 	}
230*8af74909SZhong Yang }
231*8af74909SZhong Yang 
RGBAImage(const XPM & xpm)232*8af74909SZhong Yang RGBAImage::RGBAImage(const XPM &xpm) {
233*8af74909SZhong Yang 	height = xpm.GetHeight();
234*8af74909SZhong Yang 	width = xpm.GetWidth();
235*8af74909SZhong Yang 	scale = 1;
236*8af74909SZhong Yang 	pixelBytes.resize(CountBytes());
237*8af74909SZhong Yang 	for (int y=0; y<height; y++) {
238*8af74909SZhong Yang 		for (int x=0; x<width; x++) {
239*8af74909SZhong Yang 			ColourDesired colour;
240*8af74909SZhong Yang 			bool transparent = false;
241*8af74909SZhong Yang 			xpm.PixelAt(x, y, colour, transparent);
242*8af74909SZhong Yang 			SetPixel(x, y, colour, transparent ? 0 : 255);
243*8af74909SZhong Yang 		}
244*8af74909SZhong Yang 	}
245*8af74909SZhong Yang }
246*8af74909SZhong Yang 
~RGBAImage()247*8af74909SZhong Yang RGBAImage::~RGBAImage() {
248*8af74909SZhong Yang }
249*8af74909SZhong Yang 
CountBytes() const250*8af74909SZhong Yang int RGBAImage::CountBytes() const noexcept {
251*8af74909SZhong Yang 	return width * height * 4;
252*8af74909SZhong Yang }
253*8af74909SZhong Yang 
Pixels() const254*8af74909SZhong Yang const unsigned char *RGBAImage::Pixels() const noexcept {
255*8af74909SZhong Yang 	return &pixelBytes[0];
256*8af74909SZhong Yang }
257*8af74909SZhong Yang 
SetPixel(int x,int y,ColourDesired colour,int alpha)258*8af74909SZhong Yang void RGBAImage::SetPixel(int x, int y, ColourDesired colour, int alpha) noexcept {
259*8af74909SZhong Yang 	unsigned char *pixel = &pixelBytes[0] + (y*width+x) * 4;
260*8af74909SZhong Yang 	// RGBA
261*8af74909SZhong Yang 	pixel[0] = colour.GetRed();
262*8af74909SZhong Yang 	pixel[1] = colour.GetGreen();
263*8af74909SZhong Yang 	pixel[2] = colour.GetBlue();
264*8af74909SZhong Yang 	pixel[3] = static_cast<unsigned char>(alpha);
265*8af74909SZhong Yang }
266*8af74909SZhong Yang 
267*8af74909SZhong Yang // Transform a block of pixels from RGBA to BGRA with premultiplied alpha.
268*8af74909SZhong Yang // Used for DrawRGBAImage on some platforms.
BGRAFromRGBA(unsigned char * pixelsBGRA,const unsigned char * pixelsRGBA,size_t count)269*8af74909SZhong Yang void RGBAImage::BGRAFromRGBA(unsigned char *pixelsBGRA, const unsigned char *pixelsRGBA, size_t count) noexcept {
270*8af74909SZhong Yang 	for (size_t i = 0; i < count; i++) {
271*8af74909SZhong Yang 		const unsigned char alpha = pixelsRGBA[3];
272*8af74909SZhong Yang 		// Input is RGBA, output is BGRA with premultiplied alpha
273*8af74909SZhong Yang 		pixelsBGRA[2] = pixelsRGBA[0] * alpha / 255;
274*8af74909SZhong Yang 		pixelsBGRA[1] = pixelsRGBA[1] * alpha / 255;
275*8af74909SZhong Yang 		pixelsBGRA[0] = pixelsRGBA[2] * alpha / 255;
276*8af74909SZhong Yang 		pixelsBGRA[3] = alpha;
277*8af74909SZhong Yang 		pixelsRGBA += bytesPerPixel;
278*8af74909SZhong Yang 		pixelsBGRA += bytesPerPixel;
279*8af74909SZhong Yang 	}
280*8af74909SZhong Yang }
281*8af74909SZhong Yang 
RGBAImageSet()282*8af74909SZhong Yang RGBAImageSet::RGBAImageSet() : height(-1), width(-1) {
283*8af74909SZhong Yang }
284*8af74909SZhong Yang 
~RGBAImageSet()285*8af74909SZhong Yang RGBAImageSet::~RGBAImageSet() {
286*8af74909SZhong Yang 	Clear();
287*8af74909SZhong Yang }
288*8af74909SZhong Yang 
289*8af74909SZhong Yang /// Remove all images.
Clear()290*8af74909SZhong Yang void RGBAImageSet::Clear() noexcept {
291*8af74909SZhong Yang 	images.clear();
292*8af74909SZhong Yang 	height = -1;
293*8af74909SZhong Yang 	width = -1;
294*8af74909SZhong Yang }
295*8af74909SZhong Yang 
296*8af74909SZhong Yang /// Add an image.
Add(int ident,RGBAImage * image)297*8af74909SZhong Yang void RGBAImageSet::Add(int ident, RGBAImage *image) {
298*8af74909SZhong Yang 	ImageMap::iterator it=images.find(ident);
299*8af74909SZhong Yang 	if (it == images.end()) {
300*8af74909SZhong Yang 		images[ident] = std::unique_ptr<RGBAImage>(image);
301*8af74909SZhong Yang 	} else {
302*8af74909SZhong Yang 		it->second.reset(image);
303*8af74909SZhong Yang 	}
304*8af74909SZhong Yang 	height = -1;
305*8af74909SZhong Yang 	width = -1;
306*8af74909SZhong Yang }
307*8af74909SZhong Yang 
308*8af74909SZhong Yang /// Get image by id.
Get(int ident)309*8af74909SZhong Yang RGBAImage *RGBAImageSet::Get(int ident) {
310*8af74909SZhong Yang 	ImageMap::iterator it = images.find(ident);
311*8af74909SZhong Yang 	if (it != images.end()) {
312*8af74909SZhong Yang 		return it->second.get();
313*8af74909SZhong Yang 	}
314*8af74909SZhong Yang 	return nullptr;
315*8af74909SZhong Yang }
316*8af74909SZhong Yang 
317*8af74909SZhong Yang /// Give the largest height of the set.
GetHeight() const318*8af74909SZhong Yang int RGBAImageSet::GetHeight() const {
319*8af74909SZhong Yang 	if (height < 0) {
320*8af74909SZhong Yang 		for (const std::pair<const int, std::unique_ptr<RGBAImage>> &image : images) {
321*8af74909SZhong Yang 			if (height < image.second->GetHeight()) {
322*8af74909SZhong Yang 				height = image.second->GetHeight();
323*8af74909SZhong Yang 			}
324*8af74909SZhong Yang 		}
325*8af74909SZhong Yang 	}
326*8af74909SZhong Yang 	return (height > 0) ? height : 0;
327*8af74909SZhong Yang }
328*8af74909SZhong Yang 
329*8af74909SZhong Yang /// Give the largest width of the set.
GetWidth() const330*8af74909SZhong Yang int RGBAImageSet::GetWidth() const {
331*8af74909SZhong Yang 	if (width < 0) {
332*8af74909SZhong Yang 		for (const std::pair<const int, std::unique_ptr<RGBAImage>> &image : images) {
333*8af74909SZhong Yang 			if (width < image.second->GetWidth()) {
334*8af74909SZhong Yang 				width = image.second->GetWidth();
335*8af74909SZhong Yang 			}
336*8af74909SZhong Yang 		}
337*8af74909SZhong Yang 	}
338*8af74909SZhong Yang 	return (width > 0) ? width : 0;
339*8af74909SZhong Yang }
340