⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 writegif.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
字号:
#include <u.h>#include <libc.h>#include <draw.h>#include <memdraw.h>#include <bio.h>#include "imagefile.h"enum{	Nhash	= 4001,	Nbuf		= 300,};typedef struct Entry Entry;typedef struct IO IO;struct Entry{	int		index;	int		prefix;	int		exten;	Entry	*next;};struct IO{	Biobuf	*fd;	uchar	buf[Nbuf];	int		i;	int		nbits;	/* bits in right side of shift register */	int		sreg;		/* shift register */};static Rectangle	mainrect;static Entry	tbl[4096];static uchar	*colormap[5];	/* one for each ldepth: GREY1 GREY2 GREY4 CMAP8=rgbv plus GREY8 */#define	GREYMAP	4static int		colormapsize[] = { 2, 4, 16, 256, 256 };	/* 2 for zero is an odd property of GIF */static void		writeheader(Biobuf*, Rectangle, int, ulong, int);static void		writedescriptor(Biobuf*, Rectangle);static char*	writedata(Biobuf*, Image*, Memimage*);static void		writetrailer(Biobuf *fd);static void		writecomment(Biobuf *fd, char*);static void		writegraphiccontrol(Biobuf *fd, int, int);static void*	gifmalloc(ulong);static void		encode(Biobuf*, Rectangle, int, uchar*, uint);staticchar*startgif0(Biobuf *fd, ulong chan, Rectangle r, int depth, int loopcount){	int i;	for(i=0; i<nelem(tbl); i++)		tbl[i] = (Entry){i, -1, i, nil};	switch(chan){	case GREY1:	case GREY2:	case GREY4:	case CMAP8:	case GREY8:		break;	default:		return "WriteGIF: can't handle channel type";	}	mainrect = r;	writeheader(fd, r, depth, chan, loopcount);	return nil;}char*startgif(Biobuf *fd, Image *image, int loopcount){	return startgif0(fd, image->chan, image->r, image->depth, loopcount);}char*memstartgif(Biobuf *fd, Memimage *memimage, int loopcount){	return startgif0(fd, memimage->chan, memimage->r, memimage->depth, loopcount);}staticchar*writegif0(Biobuf *fd, Image *image, Memimage *memimage, ulong chan, Rectangle r, char *comment, int dt, int trans){	char *err;	switch(chan){	case GREY1:	case GREY2:	case GREY4:	case CMAP8:	case GREY8:		break;	default:		return "WriteGIF: can't handle channel type";	}	writecomment(fd, comment);	writegraphiccontrol(fd, dt, trans);	writedescriptor(fd, r);	err = writedata(fd, image, memimage);	if(err != nil)		return err;	return nil;}char*writegif(Biobuf *fd, Image *image, char *comment, int dt, int trans){	return writegif0(fd, image, nil, image->chan, image->r, comment, dt, trans);}char*memwritegif(Biobuf *fd, Memimage *memimage, char *comment, int dt, int trans){	return writegif0(fd, nil, memimage, memimage->chan, memimage->r, comment, dt, trans);}/* * Write little-endian 16-bit integer */staticvoidput2(Biobuf *fd, int i){	Bputc(fd, i);	Bputc(fd, i>>8);}/* * Get color map for all ldepths, in format suitable for writing out */staticvoidgetcolormap(void){	int i, col;	ulong rgb;	uchar *c;	if(colormap[0] != nil)		return;	for(i=0; i<nelem(colormap); i++)		colormap[i] = gifmalloc(3* colormapsize[i]);	c = colormap[GREYMAP];	/* GREY8 */	for(i=0; i<256; i++){		c[3*i+0] = i;	/* red */		c[3*i+1] = i;	/* green */		c[3*i+2] = i;	/* blue */	}	c = colormap[3];	/* RGBV */	for(i=0; i<256; i++){		rgb = cmap2rgb(i);		c[3*i+0] = (rgb>>16) & 0xFF;	/* red */		c[3*i+1] = (rgb>> 8) & 0xFF;	/* green */		c[3*i+2] = (rgb>> 0) & 0xFF;	/* blue */	}	c = colormap[2];	/* GREY4 */	for(i=0; i<16; i++){		col = (i<<4)|i;		rgb = cmap2rgb(col);		c[3*i+0] = (rgb>>16) & 0xFF;	/* red */		c[3*i+1] = (rgb>> 8) & 0xFF;	/* green */		c[3*i+2] = (rgb>> 0) & 0xFF;	/* blue */	}	c = colormap[1];	/* GREY2 */	for(i=0; i<4; i++){		col = (i<<6)|(i<<4)|(i<<2)|i;		rgb = cmap2rgb(col);		c[3*i+0] = (rgb>>16) & 0xFF;	/* red */		c[3*i+1] = (rgb>> 8) & 0xFF;	/* green */		c[3*i+2] = (rgb>> 0) & 0xFF;	/* blue */	}	c = colormap[0];	/* GREY1 */	for(i=0; i<2; i++){		if(i == 0)			col = 0;		else			col = 0xFF;		rgb = cmap2rgb(col);		c[3*i+0] = (rgb>>16) & 0xFF;	/* red */		c[3*i+1] = (rgb>> 8) & 0xFF;	/* green */		c[3*i+2] = (rgb>> 0) & 0xFF;	/* blue */	}}/* * Write header, logical screen descriptor, and color map */staticvoidwriteheader(Biobuf *fd, Rectangle r, int depth, ulong chan, int loopcount){	/* Header */	Bprint(fd, "%s", "GIF89a");	/*  Logical Screen Descriptor */	put2(fd, Dx(r));	put2(fd, Dy(r));	/* Color table present, 4 bits per color (for RGBV best case), size of color map */	Bputc(fd, (1<<7)|(3<<4)|(depth-1));	/* not right for GREY8, but GIF doesn't let us specify enough bits */	Bputc(fd, 0xFF);	/* white background (doesn't matter anyway) */	Bputc(fd, 0);	/* pixel aspect ratio - unused */	/* Global Color Table */	getcolormap();	if(chan == GREY8)		depth = GREYMAP;	else		depth = log2[depth];	Bwrite(fd, colormap[depth], 3*colormapsize[depth]);	if(loopcount >= 0){	/* hard-to-discover way to force cycled animation */		/* Application Extension with (1 loopcountlo loopcounthi) as data */		Bputc(fd, 0x21);		Bputc(fd, 0xFF);		Bputc(fd, 11);		Bwrite(fd, "NETSCAPE2.0", 11);		Bputc(fd, 3);		Bputc(fd, 1);		put2(fd, loopcount);		Bputc(fd, 0);	}}/* * Write optional comment block */staticvoidwritecomment(Biobuf *fd, char *comment){	int n;	if(comment==nil || comment[0]=='\0')		return;	/* Comment extension and label */	Bputc(fd, 0x21);	Bputc(fd, 0xFE);	/* Comment data */	n = strlen(comment);	if(n > 255)		n = 255;	Bputc(fd, n);	Bwrite(fd, comment, n);	/* Block terminator */	Bputc(fd, 0x00);}/* * Write optional control block (sets Delay Time) */staticvoidwritegraphiccontrol(Biobuf *fd, int dt, int trans){	if(dt < 0 && trans < 0)		return;	/* Comment extension and label and block size*/	Bputc(fd, 0x21);	Bputc(fd, 0xF9);	Bputc(fd, 0x04);	/* Disposal method and other flags (none) */	if(trans >= 0)		Bputc(fd, 0x01);	else		Bputc(fd, 0x00);		/* Delay time, in centisec (argument is millisec for sanity) */	if(dt < 0)		dt = 0;	else if(dt < 10)		dt = 1;	else		dt = (dt+5)/10;	put2(fd, dt);	/* Transparency index */	if(trans < 0)		trans = 0;	Bputc(fd, trans);	/* Block terminator */	Bputc(fd, 0x00);}/* * Write image descriptor */staticvoidwritedescriptor(Biobuf *fd, Rectangle r){	/* Image Separator */	Bputc(fd, 0x2C);	/* Left, top, width, height */	put2(fd, r.min.x-mainrect.min.x);	put2(fd, r.min.y-mainrect.min.y);	put2(fd, Dx(r));	put2(fd, Dy(r));	/* no special processing */	Bputc(fd, 0);}/* * Write data */staticchar*writedata(Biobuf *fd, Image *image, Memimage *memimage){	char *err;	uchar *data;	int ndata, depth;	Rectangle r;	if(memimage != nil){		r = memimage->r;		depth = memimage->depth;	}else{		r = image->r;		depth = image->depth;	}	/* LZW Minimum code size */	if(depth == 1)		Bputc(fd, 2);	else		Bputc(fd, depth);	/* 	 * Read image data into memory	 * potentially one extra byte on each end of each scan line	 */	ndata = Dy(r)*(2+(Dx(r)>>(3-log2[depth])));	data = gifmalloc(ndata);	if(memimage != nil)		ndata = unloadmemimage(memimage, r, data, ndata);	else		ndata = unloadimage(image, r, data, ndata);	if(ndata < 0){		err = gifmalloc(ERRMAX);		snprint(err, ERRMAX, "WriteGIF: %r");		free(data);		return err;	}	/* Encode and emit the data */	encode(fd, r, depth, data, ndata);	free(data);	/*  Block Terminator */	Bputc(fd, 0);	return nil;}/* * Write trailer */voidendgif(Biobuf *fd){	Bputc(fd, 0x3B);	Bflush(fd);}voidmemendgif(Biobuf *fd){	endgif(fd);}/* * Put n bits of c into output at io.buf[i]; */staticvoidoutput(IO *io, int c, int n){	if(c < 0){		if(io->nbits != 0)			io->buf[io->i++] = io->sreg;		Bputc(io->fd, io->i);		Bwrite(io->fd, io->buf, io->i);		io->nbits = 0;		return;	}	if(io->nbits+n >= 31){		fprint(2, "panic: WriteGIF sr overflow\n");		exits("WriteGIF panic");	}	io->sreg |= c<<io->nbits;	io->nbits += n;	while(io->nbits >= 8){		io->buf[io->i++] = io->sreg;		io->sreg >>= 8;		io->nbits -= 8;	}	if(io->i >= 255){		Bputc(io->fd, 255);		Bwrite(io->fd, io->buf, 255);		memmove(io->buf, io->buf+255, io->i-255);		io->i -= 255;	}}/* * LZW encoder */staticvoidencode(Biobuf *fd, Rectangle r, int depth, uchar *data, uint ndata){	int i, c, h, csize, prefix, first, sreg, nbits, bitsperpixel;	int CTM, EOD, codesize, ld0, datai, x, ld, pm;	int nentry, maxentry, early;	Entry *e, *oe;	IO *io;	Entry **hash;	first = 1;	ld = log2[depth];	/* ldepth 0 must generate codesize 2 with values 0 and 1 (see the spec.) */	ld0 = ld;	if(ld0 == 0)		ld0 = 1;	codesize = (1<<ld0);	CTM = 1<<codesize;	EOD = CTM+1;	io = gifmalloc(sizeof(IO));	io->fd = fd;	sreg = 0;	nbits = 0;	bitsperpixel = 1<<ld;	pm = (1<<bitsperpixel)-1;	datai = 0;	x = r.min.x;	hash = gifmalloc(Nhash*sizeof(Entry*));Init:	memset(hash, 0, Nhash*sizeof(Entry*));	csize = codesize+1;	nentry = EOD+1;	maxentry = (1<<csize);	for(i = 0; i<nentry; i++){		e = &tbl[i];		h = (e->prefix<<24) | (e->exten<<8);		h %= Nhash;		if(h < 0)			h += Nhash;		e->next = hash[h];		hash[h] = e;	}	prefix = -1;	if(first)		output(io, CTM, csize);	first = 0;	/*	 * Scan over pixels.  Because of partially filled bytes on ends of scan lines,	 * which must be ignored in the data stream passed to GIF, this is more	 * complex than we'd like.	 */Next:	for(;;){		if(ld != 3){			/* beginning of scan line is difficult; prime the shift register */			if(x == r.min.x){				if(datai == ndata)					break;				sreg = data[datai++];				nbits = 8-((x&(7>>ld))<<ld);			}			x++;			if(x == r.max.x)				x = r.min.x;		}		if(nbits == 0){			if(datai == ndata)				break;			sreg = data[datai++];			nbits = 8;		}		nbits -= bitsperpixel;		c = sreg>>nbits & pm;		h = prefix<<24 | c<<8;		h %= Nhash;		if(h < 0)			h += Nhash;		oe = nil;		for(e = hash[h]; e!=nil; e=e->next){			if(e->prefix == prefix && e->exten == c){				if(oe != nil){					oe->next = e->next;					e->next = hash[h];					hash[h] = e;				}				prefix = e->index;				goto Next;			}			oe = e;		}		output(io, prefix, csize);		early = 0; /* peculiar tiff feature here for reference */		if(nentry == maxentry-early){			if(csize == 12){				nbits += bitsperpixel;	/* unget pixel */				x--;				if(ld != 3 && x == r.min.x)					datai--;				output(io, CTM, csize);				goto Init;			}			csize++;			maxentry = (1<<csize);		}		e = &tbl[nentry];		e->prefix = prefix;		e->exten = c;		e->next = hash[h];		hash[h] = e;		prefix = c;		nentry++;	}	output(io, prefix, csize);	output(io, EOD, csize);	output(io, -1, csize);	free(io);	free(hash);}staticvoid*gifmalloc(ulong sz){	void *v;	v = malloc(sz);	if(v == nil) {		fprint(2, "WriteGIF: out of memory allocating %ld\n", sz);abort();		exits("mem");	}	memset(v, 0, sz);	return v;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -