/*% cyntax % -lfb && cc -go # % -lfb
* utah -- convert a utah format picture into a picio file.
* doesn't do enough error checking.
* A utah picture contains:
* size data
* ---- ----
* 2 magic number
* 2 x position
* 2 y position
* 2 x size
* 2 y size
* 1 flags
* 1 # of channels
* 1 # of bits/channel (this program supports only 8 bits/channel)
* 1 # of color map channels
* 1 log2 of # of color map entries
* ncolor background color (padded to even length)
* 2*ncmap*(1<<cmaplen)
* color map entries
* 0 or 2 length of comment (only if flags has COMMENT set)
* ncomment comment data
* indefinite coded image data (see rdimage)
*/
#include <u.h>
#include <libc.h>
#include <libg.h>
#include <stdio.h>
#include <fb.h>
struct utah{
short magic; /* 0146122, all integers are filed lsb first */
short xpos, ypos; /* coordinates of upper-left corner */
short xsize, ysize; /* width, height */
unsigned char flags; /* see definitions below */
unsigned char ncolors; /* # of channels, not counting alpha */
char pixelbits; /* # of bits/channel, must be 8 */
unsigned char ncmap; /* # of channels of color map */
unsigned char cmaplen; /* 1<<cmaplen is # of 2-byte color map entries */
unsigned char *bg; /* background color, if NOBG not set in flags */
short *cmap; /* color map data */
short ncomment; /* length of text annotation */
char *comment; /* comment data */
unsigned char *image; /* image data */
}utah;
#define MAGIC (short)0146122
/*
* flags
*/
#define CLR 1 /* clear the image before reading runs */
#define NOBG 2 /* no background stored */
#define ALPHA 4 /* image contains alpha channel */
#define COMMENT 8 /* image contains text annotation */
/*
* opcodes
*/
#define SKIPLINES 1
#define SETCOLOR 2
#define SKIPPIXELS 3
#define PIXELDATA 5
#define RUN 6
#define ENDFILE 7
#define SHORT 0
#define LONG 0x40
#define LENGTH 0xC0
int rdshort(FILE *f){
int c0, c1;
c0=getc(f);
c1=getc(f);
if(c0==EOF || c1==EOF){
fprintf(stderr, "utah: EOF!\n");
exits("eof");
}
if(c1&128) c1|=~255; /* sign extension */
return c0|(c1<<8);
}
void clear(void){
unsigned char *p=utah.image;
unsigned char *q=utah.image+utah.xsize*utah.ysize*utah.ncolors;
unsigned char *r;
unsigned char *s=utah.bg+utah.ncolors;
while(p!=q){
r=utah.bg;
while(r!=s) *p++=*r++;
}
}
FILE *rdhdr(char *file){
FILE *f=fopen(file, "r");
if(f==0){
perror(file);
exits("can't open input");
}
utah.magic=rdshort(f);
if(utah.magic!=MAGIC){
fprintf(stderr, "%s: bad magic %06o\n", file, utah.magic&0177777);
exits("bad magic");
}
utah.xpos=rdshort(f);
utah.ypos=rdshort(f);
utah.xsize=rdshort(f);
utah.ysize=rdshort(f);
utah.flags=getc(f);
utah.ncolors=getc(f);
utah.pixelbits=getc(f);
if(utah.pixelbits!=8){
fprintf(stderr, "%s: pixelbits!=8\n", file);
exits("not 8 bit");
}
utah.ncmap=getc(f);
utah.cmaplen=getc(f);
if(!(utah.flags&NOBG)){
utah.bg=(unsigned char *)malloc(utah.ncolors+1);
if(utah.bg==0){
NoSpace:
fprintf(stderr, "No space\n");
exits("no space");
}
fread((char *)utah.bg, 1, utah.ncolors, f);
utah.bg[utah.ncolors]=0;
}
if((utah.ncolors&1)==0) getc(f);
if(utah.flags&ALPHA) utah.ncolors++;
utah.image=(unsigned char *)malloc(utah.ncolors*utah.xsize*utah.ysize);
if(utah.image==0) goto NoSpace;
if(utah.ncmap){
utah.cmap=(short *)malloc(utah.ncmap*(1<<utah.cmaplen)*2);
if(utah.cmap==0) goto NoSpace;
fread((char *)utah.cmap, 2, utah.ncmap*(1<<utah.cmaplen), f);
}
if(utah.flags&COMMENT){
utah.ncomment=rdshort(f);
utah.comment=malloc(utah.ncomment);
if(utah.comment==0) goto NoSpace;
fread(utah.comment, 1, utah.ncomment, f);
if(utah.ncomment&1) getc(f);
}
if((utah.flags&(CLR|NOBG))==CLR)
clear();
return f;
}
#define setpixp() (pixp=&utah.image[(line*utah.xsize+pixel)*utah.ncolors+channel])
int rdimage(FILE *f){
unsigned char *pixp;
int i, op, channel=0, line=0, pixel=0, value, length;
while((op=getc(f))!=EOF){
if((op&LENGTH)==LONG) getc(f);
switch(op){
default:
fprintf(stderr, "Illegal opcode %o\n", op);
exits("bad opcode");
case SHORT|SKIPLINES:
line+=getc(f);
pixel=0;
break;
case LONG|SKIPLINES:
line+=rdshort(f);
pixel=0;
break;
case SHORT|SETCOLOR:
channel=getc(f);
if(channel==255) channel=utah.ncolors-1;
pixel=0;
break;
case LONG|SETCOLOR:
channel=rdshort(f);
if(channel==255) channel=utah.ncolors-1;
pixel=0;
break;
case SHORT|SKIPPIXELS:
pixel+=getc(f);
break;
case LONG|SKIPPIXELS:
pixel+=rdshort(f);
break;
case SHORT|PIXELDATA:
length=getc(f)+1;
goto PixData;
case LONG|PIXELDATA:
length=rdshort(f)+1;
PixData:
setpixp();
for(i=0;i!=length;i++){
*pixp=getc(f);
pixp+=utah.ncolors;
}
pixel+=length;
if(length&1) getc(f);
break;
case SHORT|RUN:
length=getc(f)+1;
goto Run;
case LONG|RUN:
length=rdshort(f)+1;
Run:
value=getc(f);
getc(f);
setpixp();
for(i=0;i!=length;i++){
*pixp=value;
pixp+=utah.ncolors;
}
pixel+=length;
break;
case SHORT|ENDFILE:
getc(f);
case LONG|ENDFILE:
return 1;
}
}
return 0;
}
char *chan[]={
0,
"m",
"ma",
"rgb",
"rgba",
"mz...",
"maz...",
"rgbz...",
"rgbaz..."
};
void output(char *name){
PICFILE *f;
int i;
if(utah.ncolors<=0 || 8<=utah.ncolors){
fprintf(stderr, "bad ncolors\n");
exits("bad ncolors");
}
f=picopen_w("OUT", "runcode", utah.xpos, utah.ypos, utah.xsize, utah.ysize,
chan[utah.ncolors], 0, 0);
if(name) picputprop(f, "NAME", name);
if(f==0){
perror("utah2pic");
exits("can't open output");
}
for(i=utah.ysize-1;i>=0;--i)
picwrite(f, utah.image+i*utah.xsize*utah.ncolors);
}
void main(int argc, char *argv[]){
switch(getflags(argc, argv, "n:1[name]")){
default: usage("[file]");
case 1:
rdimage(rdhdr("/fd/0"));
output(flag['n']?flag['n'][0]:0);
break;
case 2:
rdimage(rdhdr(argv[1]));
output(flag['n']?flag['n'][0]:argv[1]);
break;
}
exits(0);
}