📄 readxpm.c
字号:
/* Zgv v3.1 - GIF, JPEG and PBM/PGM/PPM viewer, for VGA PCs running Linux. * Copyright (C) 1993-1998 Russell Marks. See README for license details. * * readxpm.c - XPM loader. * * Since (as far as I can tell) the XPM library seems to be * difficult or impossible to use on a machine without X installed * (which is a possibility with zgv), I reluctantly decided not to * use it. So this is a fairly noddy from-scratch XPM reader. * * XXX one problem with this - >256-col reading is suboptimal for cpp==2. * Could be made quicker by using same sort of speedup used for <=256-col * files when cpp==2, i.e. by using a 65536-entry direct-lookup array * containing ints (well, longs) with rgb values. That would be 256k, * but that wouldn't be too bad to malloc. */#include <stdio.h>#include <string.h>#include <stdlib.h>#include <ctype.h>#include "zgv.h"#include "readpnm.h" /* for dithering routines */#include "rcfile.h"#include "rc_config.h"#include "readxpm.h"/* max chars/pixel we allow; * most files use one, some use two, I've seen a couple which used * three (though not for any good reason!), so this sounds ok I think. */#define MAX_CPP 16/* for aborted_file_xpm_cleanup() */static unsigned char *work_bmap,*work_pal;static FILE *work_in;/* for read_next_string, but outside it in case e.g. a read error * happens halfway through, which would (in theory, if not practice) * screw up further XPM reads. */static int in_comment=0;/* for colchars_bsearch_cmp, but outside it as it's called from * bsearch(). */static int bsearch_cmp_cpp;/* RGB DB array (dynamically allocated) */#define MAX_COLNAME_LEN 64 /* not ideal but simplifies matters */struct rgb_db_tag { int rgb; unsigned char name[MAX_COLNAME_LEN+1]; };static struct rgb_db_tag *rgb_db_array=NULL;/* rgb_db_size must be at least 2*sizeof... (see init_rgb_db() for why) */static int rgb_db_size=64*sizeof(struct rgb_db_tag);static int rgb_db_incr=32*sizeof(struct rgb_db_tag);static int rgb_db_num_entries=0;static struct colchars_tag /* this is used to store colour char codes */ { int idx; unsigned char r,g,b; unsigned char name[MAX_CPP+1]; } *colchars=NULL;/* prototypes */int colchars_cmp(const void *p1,const void *p2);int colchars_bsearch_cmp(const void *p1,const void *p2);unsigned char *read_next_string(FILE *in);unsigned char *next_token(unsigned char *ptr);void get_token(unsigned char *token,int tokenmax,unsigned char *buf);int lookup_named_colour(unsigned char *colname);void init_rgb_db(void);/* output_type returns how many bytes per pixel needed for display */int read_xpm_file(char *filename,hffunc howfarfunc,unsigned char **bmap, unsigned char **pal,int *output_type,PICINFO *pp){static unsigned char smallcol2idx[65536]; /* lookup tbl if cpp<3 */static unsigned char buf[128]; /* only used for magic/tokens so can be short */static unsigned char colname[256]; /* temp. used to store colour name */FILE *in;unsigned char *inpline;int w,h,f,x,y,bytepp,ncols,cpp; /* cpp=chars/pixel */unsigned char *cptr;unsigned char *ptr,*palptr;int incr;*bmap=NULL;*pal=NULL;in_comment=0;bytepp=1;if((in=fopen(filename,"rb"))==NULL) return(_PICERR_NOFILE);if(fgets(buf,sizeof(buf),in)==NULL || strncmp(buf,"/* XPM",6)!=0) CLOSE_AND_RET(_PICERR_BADMAGIC);/* get width/height etc. */if((inpline=read_next_string(in))==NULL) CLOSE_AND_RET(_PICERR_CORRUPT);if(sscanf(inpline,"%d %d %d %d",&w,&h,&ncols,&cpp)!=4) CLOSE_AND_RET(_PICERR_CORRUPT);if(w==0 || h==0) CLOSE_AND_RET(_PICERR_CORRUPT);if(cpp>MAX_CPP) CLOSE_AND_RET(_PICERR_TOOMANYCOLS);if((*pal=palptr=calloc(768,1))==NULL) CLOSE_AND_RET(_PICERR_NOMEM);/* we zero out the palette to avoid possible problems with vkludge in * vgadisp.c if we don't (i.e. random junk colours being used) as there * may well be fewer than 256 colours. */memset(palptr,0,768);if(ncols>256) { bytepp=3; if(*output_type==1 || cfg.jpeg24bit==0) /* dither? */ { int r,g,b; bytepp=1; if(ditherinit(w)==0) CLOSE_AND_RET(_PICERR_NOMEM); ptr=palptr; for(r=0;r<8;r++) for(g=0;g<8;g++) /* colours are 3:3:2 */ for(b=0;b<4;b++) { *ptr=r*255/7; ptr[256]=g*255/7; ptr[512]=b*255/3; ptr++; } } }/* make an effort to always show them on a grey background * by making colour zero grey if possible. * (XXX will need to be changed if the other one is) */incr=0;if(ncols<256) { incr=1; palptr[0]=palptr[256]=palptr[512]=128; }/* read rgb.txt if we haven't already */if(rgb_db_array==NULL) init_rgb_db();/* colchars will usually be NULL at this point, but an error could * well have left it hanging around - nuke it if so. */if(colchars!=NULL) free(colchars);/* alloc colchars array */if(ncols>(1<<24) || (colchars=malloc(ncols*sizeof(struct colchars_tag)))==NULL) CLOSE_AND_RET(_PICERR_NOMEM);/* read in colours. This is REALLY FUN. :-( */for(f=0;f<ncols;f++) { int r,g,b; if((inpline=read_next_string(in))==NULL) CLOSE_AND_RET(_PICERR_CORRUPT); memset(colchars[f].name,0,MAX_CPP+1); memcpy(colchars[f].name,inpline,cpp); colchars[f].idx=f+incr; /* look for "c" token */ cptr=inpline+cpp; *buf=0; while(*cptr && strcmp(buf,"c")!=0) { cptr=next_token(cptr); get_token(buf,sizeof(buf),cptr); } if(strcmp(buf,"c")!=0) { /* no colour visual definition found. * this is pretty rare - in fact, I've not seen *any* cases of it. * for now (XXX), gives a fatal error. */ CLOSE_AND_RET(_PICERR_UNSUPPORTED); } cptr=next_token(cptr); /* so now cptr points to a colour name/number. Unfortunately, * people seem to take amazing liberties with colour names. * They lowercase them (not allowed by the format spec), they * use separate words for colours like "light sky blue" (not * allowed by the format spec)... happy happy joy joy, not. * * The least awful solution, I think, is to add to the next token * (i.e. the one that *should* be the colour) any consecutively * following ones which are more than 2 chars long. Then in any * string comparisons, we use strcasecmp rather than strcmp. * As far as I can see, this scheme should work in all cases. */ get_token(colname,sizeof(colname),cptr); cptr=next_token(cptr); get_token(buf,sizeof(buf),cptr); while(strlen(buf)>2) { /* avoid overruns */ if(strlen(colname)+strlen(buf)+2>sizeof(colname)) break; strcat(colname," "); strcat(colname,buf); cptr=next_token(cptr); get_token(buf,sizeof(buf),cptr); } /* now work out what we've got. if it starts with a `#' it's RGB, * and if it's "None" it's the transparent `colour', otherwise it's a * colour name from rgb.txt. * * However, we also tolerate the old-style "#Transparent" for "None". */ if(*colname=='#' && isxdigit(colname[1])) { /* RGB - length of rest must be 3, 6, 9, or 12. */ int rgb; switch(strlen(colname+1)) { case 3: rgb=strtol(colname+1,NULL,16); r=(rgb>>8)*16+(rgb>>8); g=((rgb>>4)&15)*16+((rgb>>4)&15); b=(rgb&15)*16+(rgb&15); break; case 6: rgb=strtol(colname+1,NULL,16); r=(rgb>>16); g=((rgb>>8)&255); b=(rgb&255); break; case 9: /* 0123456789 * #rr.gg.bb. */ colname[3]=colname[6]=colname[9]=0; r=strtol(colname+1,NULL,16); g=strtol(colname+4,NULL,16); b=strtol(colname+7,NULL,16); break; case 12: /* 0123456789012 * #rr..gg..bb.. */ colname[3]=colname[7]=colname[11]=0; r=strtol(colname+1,NULL,16); g=strtol(colname+5,NULL,16); b=strtol(colname+9,NULL,16); break; default: CLOSE_AND_RET(_PICERR_BADXCOL); } } else /* not RGB number... */ if(strcasecmp(colname,"None")==0 || strcasecmp(colname,"#Transparent")==0) { /* transparent `colour' - we use grey. * This is inconsistent with PNG's current background colour, * true, but it's likely to be what you'd want. * (XXX may want to make configurable though) */ r=g=b=128; } else { /* lookup the colour name. */ int rgb=lookup_named_colour(colname); if(rgb==-1) /* colour not found */ CLOSE_AND_RET(_PICERR_BADXCOL); r=(rgb>>16); g=((rgb>>8)&255); b=(rgb&255); } /* write colour to colchars[] in case we're producing 24-bit output */ colchars[f].r=r; colchars[f].g=g; colchars[f].b=b; if(ncols<=256)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -