Compare commits

...

2 Commits

Author SHA1 Message Date
Kim Woelders 33ab27e323 XPM loader: Major overhaul
Simplifying things somewhat, IMO.
2024-03-22 19:46:19 +01:00
Kim Woelders 13d311561d XPM loader: Fix some color table parsing errors
Correct values could be overwritten.
2024-03-22 17:11:05 +01:00
1 changed files with 236 additions and 227 deletions

View File

@ -1,6 +1,8 @@
#include "config.h"
#include "Imlib2_Loader.h"
#include <stdbool.h>
#define DBG_PFX "LDR-xpm"
static const char *const _formats[] = { "xpm" };
@ -32,19 +34,19 @@ mm_getc(void)
static FILE *rgb_txt = NULL;
static void
xpm_parse_color(char *color, uint32_t *pixel)
static uint32_t
xpm_parse_color(const char *color)
{
char buf[4096];
int r, g, b;
char buf[256];
int a, r, g, b;
a = 0xff;
r = g = b = 0;
/* is a #ff00ff like color */
if (color[0] == '#')
{
int len;
char val[32];
len = strlen(color) - 1;
if (len < 96)
@ -53,17 +55,17 @@ xpm_parse_color(char *color, uint32_t *pixel)
len /= 3;
for (i = 0; i < len; i++)
val[i] = color[1 + i + (0 * len)];
val[i] = 0;
sscanf(val, "%x", &r);
buf[i] = color[1 + i + (0 * len)];
buf[i] = '\0';
sscanf(buf, "%x", &r);
for (i = 0; i < len; i++)
val[i] = color[1 + i + (1 * len)];
val[i] = 0;
sscanf(val, "%x", &g);
buf[i] = color[1 + i + (1 * len)];
buf[i] = '\0';
sscanf(buf, "%x", &g);
for (i = 0; i < len; i++)
val[i] = color[1 + i + (2 * len)];
val[i] = 0;
sscanf(val, "%x", &b);
buf[i] = color[1 + i + (2 * len)];
buf[i] = '\0';
sscanf(buf, "%x", &b);
if (len == 1)
{
r = (r << 4) | r;
@ -80,6 +82,12 @@ xpm_parse_color(char *color, uint32_t *pixel)
goto done;
}
if (!strcasecmp(color, "none"))
{
a = 0;
goto done;
}
/* look in rgb txt database */
if (!rgb_txt)
rgb_txt = fopen(PACKAGE_DATA_DIR "/rgb.txt", "r");
@ -89,12 +97,12 @@ xpm_parse_color(char *color, uint32_t *pixel)
goto done;
fseek(rgb_txt, 0, SEEK_SET);
while (fgets(buf, 4000, rgb_txt))
while (fgets(buf, sizeof(buf), rgb_txt))
{
if (buf[0] != '!')
{
int rr, gg, bb;
char name[4096];
char name[256];
sscanf(buf, "%i %i %i %[^\n]", &rr, &gg, &bb, name);
if (!strcasecmp(name, color))
@ -106,8 +114,9 @@ xpm_parse_color(char *color, uint32_t *pixel)
}
}
}
done:
*pixel = PIXEL_ARGB(0xff, r, g, b);
return PIXEL_ARGB(a, r, g, b);
}
static void
@ -152,23 +161,87 @@ xpm_cmap_lookup(const cmap_t *cmap, int nc, int cpp, const char *s)
return cmap[i1].pixel;
}
static int
xpm_parse_cmap_line(const char *line, int len, int cpp, cmap_t *cme)
{
char s[256], tag[256], col[256];
int i, nr;
bool is_tag, is_col, is_eol, hascolor;
if (len < cpp)
return -1;
is_tag = is_col = is_eol = false;
hascolor = false;
tag[0] = '\0';
col[0] = '\0';
strncpy(cme->str, line, cpp);
for (i = cpp; i < len;)
{
s[0] = '\0';
nr = 0;
sscanf(&line[i], "%255s %n", s, &nr);
i += nr;
is_tag = !strcmp(s, "c") || !strcmp(s, "m") || !strcmp(s, "s") ||
!strcmp(s, "g4") || !strcmp(s, "g");
is_eol = i >= len;
if (!is_tag)
{
/* Not tag - append to value */
if (col[0])
{
if (strlen(col) < (sizeof(col) - 2))
strcat(col, " ");
else
return -1;
}
if (strlen(col) + strlen(s) < (sizeof(col) - 1))
strcat(col, s);
}
if ((is_tag || is_eol) && col[0])
{
/* Next tag or end of line - process color */
is_col = !strcmp(tag, "c");
if ((is_col || !cme->assigned) && !hascolor)
{
cme->pixel = xpm_parse_color(col);
cme->assigned = 1;
cme->transp = cme->pixel == 0x00000000;
if (is_col)
hascolor = true;
DL(" Coltbl tag='%s' col='%s' hasc=%d tr=%d %08x\n",
tag, col, hascolor, cme->transp, cme->pixel);
}
/* Starting new tag */
strcpy(tag, s);
col[0] = '\0';
}
}
return 0;
}
static int
_load(ImlibImage *im, int load_data)
{
int rc;
int rc, err;
uint32_t *ptr;
int pc, c, i, j, k, w, h, ncolors, cpp;
int comment, transp, quote, context, len, done, backslash;
char *line, s[256], tok[256], col[256];
int pc, c, i, j, w, h, ncolors, cpp;
int context, len;
char *line;
int lsz = 256;
cmap_t *cmap;
short lookup[128 - 32][128 - 32];
int count, pixels;
int last_row = 0;
bool comment, quote, backslash, transp;
rc = LOAD_FAIL;
done = 0;
transp = -1;
line = NULL;
cmap = NULL;
@ -183,12 +256,12 @@ _load(ImlibImage *im, int load_data)
j = 0;
w = 10;
h = 10;
transp = false;
ncolors = 0;
cpp = 0;
ptr = NULL;
c = ' ';
comment = 0;
quote = 0;
comment = quote = backslash = false;
context = 0;
pixels = 0;
count = 0;
@ -197,9 +270,8 @@ _load(ImlibImage *im, int load_data)
QUIT_WITH_RC(LOAD_OOM);
len = 0;
backslash = 0;
memset(lookup, 0, sizeof(lookup));
while (!done)
for (;;)
{
pc = c;
c = mm_getc();
@ -209,237 +281,174 @@ _load(ImlibImage *im, int load_data)
if (!quote)
{
if ((pc == '/') && (c == '*'))
comment = 1;
comment = true;
else if ((pc == '*') && (c == '/') && (comment))
comment = 0;
comment = false;
}
if (comment)
continue;
if ((!quote) && (c == '"'))
{
quote = 1;
len = 0;
}
else if ((quote) && (c == '"'))
{
line[len] = 0;
quote = 0;
if (context == 0)
{
/* Header */
sscanf(line, "%i %i %i %i", &w, &h, &ncolors, &cpp);
if ((ncolors > 32766) || (ncolors < 1))
{
E("XPM files with colors > 32766 or < 1 not supported\n");
goto quit;
}
if ((cpp > 5) || (cpp < 1))
{
E("XPM files with characters per pixel > 5 or < 1 not supported\n");
goto quit;
}
if (!IMAGE_DIMENSIONS_OK(w, h))
{
E("Invalid image dimension: %dx%d\n", w, h);
goto quit;
}
im->w = w;
im->h = h;
cmap = calloc(ncolors, sizeof(cmap_t));
if (!cmap)
QUIT_WITH_RC(LOAD_OOM);
pixels = w * h;
j = 0;
context++;
}
else if (context == 1)
{
/* Color Table */
if (j < ncolors)
{
int slen;
int hascolor, iscolor;
iscolor = 0;
hascolor = 0;
tok[0] = 0;
col[0] = 0;
s[0] = 0;
if (len < cpp)
goto quit;
strncpy(cmap[j].str, line, cpp);
for (k = cpp; k < len; k++)
{
if (line[k] == ' ')
continue;
s[0] = 0;
sscanf(&line[k], "%255s", s);
slen = strlen(s);
k += slen;
if (!strcmp(s, "c"))
iscolor = 1;
if ((!strcmp(s, "m")) || (!strcmp(s, "s")) ||
(!strcmp(s, "g4")) || (!strcmp(s, "g")) ||
(!strcmp(s, "c")) || (k >= len))
{
if (k >= len)
{
if (col[0])
{
if (strlen(col) < (sizeof(col) - 2))
strcat(col, " ");
else
done = 1;
}
if (strlen(col) + strlen(s) < (sizeof(col) - 1))
strcat(col, s);
}
if (col[0])
{
if (!strcasecmp(col, "none"))
{
cmap[j].transp = 1;
cmap[j].pixel = 0;
}
else if ((!cmap[j].assigned ||
!strcmp(tok, "c")) && (!hascolor))
{
xpm_parse_color(col, &cmap[j].pixel);
cmap[j].assigned = 1;
cmap[j].transp = 0;
if (iscolor)
hascolor = 1;
}
}
strcpy(tok, s);
col[0] = 0;
}
else
{
if (col[0])
{
if (strlen(col) < (sizeof(col) - 2))
strcat(col, " ");
else
done = 1;
}
if (strlen(col) + strlen(s) < (sizeof(col) - 1))
strcat(col, s);
}
}
if (cmap[j].transp)
transp = 1;
}
j++;
if (j >= ncolors)
{
if (cpp == 1)
for (i = 0; i < ncolors; i++)
lookup[(int)cmap[i].str[0] - 32][0] = i;
else if (cpp == 2)
for (i = 0; i < ncolors; i++)
lookup[(int)cmap[i].str[0] -
32][(int)cmap[i].str[1] - 32] = i;
else
qsort(cmap, ncolors, sizeof(cmap_t), xpm_cmap_sort);
context++;
im->has_alpha = transp >= 0;
if (!load_data)
QUIT_WITH_RC(LOAD_SUCCESS);
ptr = __imlib_AllocateData(im);
if (!ptr)
QUIT_WITH_RC(LOAD_OOM);
}
}
else
{
/* Image Data */
if (cpp == 1)
{
#define CM1(c0) (&cmap[lookup[c0 - ' '][0]])
for (i = 0; count < pixels && i < len; i++)
{
*ptr++ = CM1(line[i])->pixel;
count++;
}
}
else if (cpp == 2)
{
#define CM2(c0, c1) (&cmap[lookup[c0 - ' '][c1 - ' ']])
for (i = 0; count < pixels && i < len - 1; i += 2)
{
*ptr++ = CM2(line[i], line[i + 1])->pixel;
count++;
}
}
else
{
for (i = 0; count < pixels && i < len - (cpp - 1); i += cpp)
{
*ptr++ = xpm_cmap_lookup(cmap, ncolors, cpp, &line[i]);
count++;
}
}
i = count / w;
if (im->lc && i > last_row)
{
if (__imlib_LoadProgressRows(im, last_row, i - last_row))
QUIT_WITH_RC(LOAD_BREAK);
last_row = i;
}
}
}
/* Scan in line from XPM file */
if ((quote) && (c != '"'))
if (!quote)
{
/* Waiting for start quote */
if (c != '"')
continue;
/* Got start quote */
quote = true;
len = 0;
continue;
}
if (c != '"')
{
/* Waiting for end quote */
if (c < 32)
c = 32;
else if (c > 127)
c = 127;
if (c == '\\')
{
if (++backslash < 2)
if (!backslash)
{
line[len++] = c;
backslash = true;
}
else
{
backslash = 0;
backslash = false;
}
}
else
{
backslash = 0;
line[len++] = c;
backslash = false;
}
if (len >= lsz)
{
char *nline;
lsz += 256;
nline = realloc(line, lsz);
if (!nline)
QUIT_WITH_RC(LOAD_OOM);
line = nline;
}
continue;
}
if (len >= lsz)
/* Got end quote */
line[len] = '\0';
quote = false;
if (context == 0)
{
char *nline;
/* Header */
DL("Header line: '%s'\n", line);
sscanf(line, "%i %i %i %i", &w, &h, &ncolors, &cpp);
D("Header: WxH=%dx%d ncol=%d cpp=%d\n", w, h, ncolors, cpp);
if ((ncolors > 32766) || (ncolors < 1))
{
E("XPM files with colors > 32766 or < 1 not supported\n");
goto quit;
}
if ((cpp > 5) || (cpp < 1))
{
E("XPM files with characters per pixel > 5 or < 1 not supported\n");
goto quit;
}
if (!IMAGE_DIMENSIONS_OK(w, h))
{
E("Invalid image dimension: %dx%d\n", w, h);
goto quit;
}
im->w = w;
im->h = h;
lsz += 256;
nline = realloc(line, lsz);
if (!nline)
cmap = calloc(ncolors, sizeof(cmap_t));
if (!cmap)
QUIT_WITH_RC(LOAD_OOM);
line = nline;
}
if ((context > 1) && (count >= pixels))
done = 1;
pixels = w * h;
j = 0;
context = 1;
}
else if (context == 1)
{
/* Color Table */
DL("Coltbl line: '%s'\n", line);
err = xpm_parse_cmap_line(line, len, cpp, &cmap[j]);
if (err)
goto quit;
if (cmap[j].transp)
transp = true;
j++;
if (j < ncolors)
continue;
/* Got all colors */
#define LU(c0, c1) lookup[(int)(c0 - ' ')][(int)(c1 - ' ')]
if (cpp == 1)
for (i = 0; i < ncolors; i++)
LU(cmap[i].str[0], ' ') = i;
else if (cpp == 2)
for (i = 0; i < ncolors; i++)
LU(cmap[i].str[0], cmap[i].str[1]) = i;
else
qsort(cmap, ncolors, sizeof(cmap_t), xpm_cmap_sort);
im->has_alpha = transp;
if (!load_data)
QUIT_WITH_RC(LOAD_SUCCESS);
ptr = __imlib_AllocateData(im);
if (!ptr)
QUIT_WITH_RC(LOAD_OOM);
context = 2;
}
else
{
/* Image Data */
DL("Data line: '%s'\n", line);
if (cpp == 1)
{
for (i = 0; count < pixels && i <= len - cpp; i += cpp, count++)
*ptr++ = cmap[LU(line[i], ' ')].pixel;
}
else if (cpp == 2)
{
for (i = 0; count < pixels && i <= len - cpp; i += cpp, count++)
*ptr++ = cmap[LU(line[i], line[i + 1])].pixel;
}
else
{
for (i = 0; count < pixels && i <= len - cpp; i += cpp, count++)
*ptr++ = xpm_cmap_lookup(cmap, ncolors, cpp, &line[i]);
}
i = count / w;
if (im->lc && i > last_row)
{
if (__imlib_LoadProgressRows(im, last_row, i - last_row))
QUIT_WITH_RC(LOAD_BREAK);
last_row = i;
}
if (count >= pixels)
break;
}
}
if (!im->data || !cmap)