You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

431 lines
9.0 KiB

/*
* tcxpm2rgb.c
*
* Copyright (C) Tilmann Bitterberg - September 2003
*
* This file is part of transcode, a video stream processing tool
*
* transcode is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* transcode is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Make; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include "transcode.h"
#include "ioaux.h"
#include <ctype.h>
/* ParseColor and QueryColorDatabase are from ImageMagick */
static char *ParseColor(char *data)
{
#define NumberTargets 6
static const char
*targets[NumberTargets] = { "c ", "g ", "g4 ", "m ", "b ", "s " };
register char
*p,
*r;
register const char
*q;
register int
i;
for (i=0; i < NumberTargets; i++)
{
r=data;
for (q=targets[i]; *r != '\0'; r++)
{
if (*r != *q)
continue;
if (!isspace((int) (*(r-1))))
continue;
p=r;
for ( ; ; )
{
if (*q == '\0')
return(r);
if (*p++ != *q++)
break;
}
q=targets[i];
}
}
return((char *) NULL);
}
typedef struct color_t {
int red, green, blue, opacity;
} color_t;
#define TransparentOpacity 255
#define OpaqueOpacity 0
#define BackgroundColor "#ff"
static unsigned int QueryColorDatabase(const char *name,
color_t *color)
{
int
blue,
green,
opacity,
red;
register long
i;
/*
Initialize color return value.
*/
memset(color,0,sizeof(color_t));
color->opacity=TransparentOpacity;
if ((name == (char *) NULL) || (*name == '\0'))
name=BackgroundColor;
while (isspace((int) (*name)))
name++;
if (*name == '#')
{
char
c;
long
n;
green=0;
blue=0;
opacity=(-1);
name++;
for (n=0; isxdigit((int) name[n]); n++);
if ((n == 3) || (n == 6) || (n == 9) || (n == 12))
{
/*
Parse RGB specification.
*/
n/=3;
do
{
red=green;
green=blue;
blue=0;
for (i=n-1; i >= 0; i--)
{
c=(*name++);
blue<<=4;
if ((c >= '0') && (c <= '9'))
blue|=c-'0';
else
if ((c >= 'A') && (c <= 'F'))
blue|=c-('A'-10);
else
if ((c >= 'a') && (c <= 'f'))
blue|=c-('a'-10);
else
return(0);
}
} while (isxdigit((int) *name));
}
else
if ((n != 4) && (n != 8) && (n != 16))
return(0);
else
{
/*
Parse RGBA specification.
*/
n/=4;
do
{
red=green;
green=blue;
blue=opacity;
opacity=0;
for (i=n-1; i >= 0; i--)
{
c=(*name++);
opacity<<=4;
if ((c >= '0') && (c <= '9'))
opacity|=c-'0';
else
if ((c >= 'A') && (c <= 'F'))
opacity|=c-('A'-10);
else
if ((c >= 'a') && (c <= 'f'))
opacity|=c-('a'-10);
else
return(0);
}
} while (isxdigit((int) *name));
}
n<<=1;
color->red=red/(1<<n);
color->green=green/(1<<n);
color->blue=blue/(1<<n);
color->opacity=OpaqueOpacity;
if (opacity >= 0) color->opacity=opacity>>((1 << n));
return(1);
}
return(1);
}
#define EXE "tcxpm2rgb"
#define MAX_BUF 1024
void version(void)
{
// print id string to stderr
fprintf(stderr, "%s (%s v%s) (C) 2003 Tilmann Bitterberg\n",
EXE, PACKAGE, VERSION);
}
static void usage(int status)
{
version();
fprintf(stderr,"\n%s converts a XPM file to rgb24 format\n", EXE);
fprintf(stderr,"Usage: %s [options]\n", EXE);
fprintf(stderr," -i name input file name [stdin]\n");
fprintf(stderr," -o name output file name [stdout]\n");
fprintf(stderr," -v print version\n");
exit(status);
}
int main (int argc, char *argv[])
{
char linebuf[MAX_BUF], target[MAX_BUF], key[16];
FILE *f, *o;
int width, height, colors, bwidth, n, j, linelen, x, y, ch;
char **clist, **keys;
char *p, *q, *line;
char *out = NULL, *d;
char *outfile = NULL, *infile=NULL;
color_t *colormap;
long sret;
while ((ch = getopt(argc, argv, "i:o:vh?")) != -1) {
switch (ch) {
case 'i':
if(optarg[0]=='-') usage(EXIT_FAILURE);
infile = optarg;
break;
case 'o':
if(optarg[0]=='-') usage(EXIT_FAILURE);
outfile = optarg;
break;
case 'v':
version();
exit(EXIT_SUCCESS);
break;
case '?':
case 'h':
default:
usage(EXIT_SUCCESS);
break;
}
}
if (infile) {
f = fopen(infile, "r");
if (!f) {
tc_log_perror(EXE, "fopen infile");
return 1;
}
} else
f = stdin;
if (outfile) {
o = fopen(outfile, "w");
if (!o) {
tc_log_perror(EXE, "fopen outfile");
return 1;
}
} else
o = stdout;
if (!fgets(linebuf, MAX_BUF, f)) {
tc_log_error(EXE, "No more lines");
return 1;
}
if (strncmp ("/* XPM */", linebuf, 9) != 0) {
tc_log_error(EXE, "Not an xpm file 1 (%s)", linebuf);
return 1;
}
if (!fgets(linebuf, MAX_BUF, f)) {
tc_log_error(EXE, "No more lines");
return 1;
}
if (strncmp ("static char", linebuf, 11) != 0) {
tc_log_error(EXE, "Not an xpm file 2");
return 1;
}
fgets(linebuf, MAX_BUF, f);
n = sscanf(linebuf+1, "%d %d %d %d", &width, &height, &colors, &bwidth);
if (n != 4 || (bwidth > 2) || (width == 0) || (height == 0) || (colors == 0)) {
tc_log_error(EXE, "Error reading header");
return 1;
}
//tc_log_info(EXE, "XPM Image: %dx%d; %d colors, %d byte wide", width, height, colors, bwidth);
clist = tc_malloc(colors*sizeof(char *));
if (clist == (char **)NULL) {
tc_log_error(EXE, "Error malloc clist");
return 1;
}
// color lookup table
colormap = tc_malloc(colors*sizeof(color_t));
for (n=0; n<colors; n++) {
int len;
if (!fgets(linebuf, MAX_BUF, f)) {
tc_log_error(EXE, "Error reading color table");
return 1;
}
len=strlen(linebuf);
clist[n] = tc_malloc(len);
memcpy(clist[n], linebuf+1, len-3);
clist[n][len-4] = '\0';
//tc_log_msg(EXE, "[%d] : %s|", n, clist[n]);
}
keys = tc_malloc(colors*sizeof(char *));
for (j=0; j<colors; j++) {
p = clist[j];
keys[j] = tc_malloc(bwidth+1); // a bit stupid since bwidth is usually just 1 or 2
keys[j][bwidth]='\0';
sret = strlcpy(keys[j], p, bwidth+1);
tc_test_string(__FILE__, __LINE__, bwidth+1, sret, errno);
sret = strlcpy(target, "gray", sizeof(target));
tc_test_string(__FILE__, __LINE__, sizeof(target), sret, errno);
q = ParseColor (p+bwidth);
if (q != (char *) NULL)
{
while (!isspace((int) (*q)) && (*q != '\0'))
q++;
(void) strncpy(target,q,sizeof(target)-1);
q=ParseColor(target);
if (q != (char *) NULL)
*q='\0';
}
QueryColorDatabase(target, &colormap[j]);
}
if (j < colors) {
tc_log_error(EXE, "Corrupt XPM image file");
return 1;
}
memset (key, '\0', 16);
linelen = width * bwidth + 16;
line = tc_malloc(linelen);
out = tc_malloc (width*height*3);
if (!out || !line) {
tc_log_error(EXE, "Error malloc line");
return 1;
}
d = out;
j=0;
for (y = 0; y<height; y++) {
int len;
if (!fgets(line, linelen, f)) {
tc_log_error(EXE, "Error reading line %d", y);
return 1;
}
len = strlen(line);
p = line+1;
p[len-4]='\0';
for (x = 0; x<width; x++) {
strncpy(key,p,bwidth);
// can anything be slower?
if (strcmp(key,keys[j]) != 0)
for (j=0; j < colors; j++)
if (strcmp(key,keys[j]) == 0)
break;
*d++ = colormap[j].red&0xff;
*d++ = colormap[j].green&0xff;
*d++ = colormap[j].blue&0xff;
p+=bwidth;
}
}
if ( (n = fwrite (out, width*height*3, 1, o))<0) {
tc_log_error(EXE, "fwrite failed (should = %d, have = %d)", width*height*3, n);
return 1;
}
fclose(o);
//tc_log_msg(EXE, "Wrote %s", outfile);
free(out);
free(line);
for (n=0; n<colors; n++) {
free(clist[n]);
free(keys[n]);
}
free(clist);
free(keys);
free(colormap);
// read };
if (!fgets(linebuf, MAX_BUF, f)) {
tc_log_perror(EXE, "fgets header");
return 1;
}
fclose (f);
return 0;
}