jzimagegif.cpp

来自「君正早期ucos系统(只有早期的才不没有打包成库),MPLAYER,文件系统,图」· C++ 代码 · 共 947 行 · 第 1/2 页

CPP
947
字号
/*******************************************************************************
*   GIF decoder
*******************************************************************************/
/* DECODE.C - An LZW decoder for GIF
 * Copyright (C) 1987, by Steven A. Bennett
 * Copyright (C) 1994, C++ version by Alejandro Aguilar Sierra
*
 * Permission is given by the author to freely redistribute and include
 * this code in any program as long as this credit is given where due.
 *
 * In accordance with the above, I want to credit Steve Wilhite who wrote
 * the code which this is heavily inspired by...
 *
 * GIF and 'Graphics Interchange Format' are trademarks (tm) of
 * Compuserve, Incorporated, an H&R Block Company.
 *
 * Release Notes: This file contains a decoder routine for GIF images
 * which is similar, structurally, to the original routine by Steve Wilhite.
 * It is, however, somewhat noticably faster in most cases.
 *
 */

////////////////////////////////////////////////////////////////////////////////

short CJzImageGIF::init_exp(short size)
{
	curr_size = (short)(size + 1);
	top_slot = (short)(1 << curr_size);
	clear = (short)(1 << size);
	ending = (short)(clear + 1);
	slot = newcodes = (short)(ending + 1);
	navail_bytes = nbits_left = 0;

	memset(stack,0,MAX_CODES + 1);
	memset(prefix,0,MAX_CODES + 1);
	memset(suffix,0,MAX_CODES + 1);
	return(0);
}
////////////////////////////////////////////////////////////////////////////////

static const unsigned long code_mask[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F,
                                                                  0x001F, 0x003F, 0x007F, 0x00FF,
                                                                  0x01FF, 0x03FF, 0x07FF, 0x0FFF,
                                                                  0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };

////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////

/* get_next_code()
 * - gets the next code from the GIF file.  Returns the code, or else
 * a negative number in case of file errors...
 */
short CJzImageGIF::get_next_code(CxFile* file)
{
	short i, x;
	DWORD ret;

	if (nbits_left == 0) {
		if (navail_bytes <= 0) {
			/* Out of bytes in current block, so read next block */
			pbytes = byte_buff;
			if ((navail_bytes = (short)get_byte(file)) < 0)
				return(navail_bytes);
			else if (navail_bytes) {
				for (i = 0; i < navail_bytes; ++i) {
					if ((x = (short)get_byte(file)) < 0) return(x);
					byte_buff[i] = (BYTE)x;
				}
			}
		}
		b1 = *pbytes++;
		nbits_left = 8;
		--navail_bytes;
	}

	if (navail_bytes<0) return ending; // prevent deadlocks (thanks to Mike Melnikov)

	ret = b1 >> (8 - nbits_left);
	while (curr_size > nbits_left){
		if (navail_bytes <= 0){
			/* Out of bytes in current block, so read next block*/
			pbytes = byte_buff;
			if ((navail_bytes = (short)get_byte(file)) < 0)
				return(navail_bytes);
			else if (navail_bytes){
				for (i = 0; i < navail_bytes; ++i){
					if ((x = (short)get_byte(file)) < 0) return(x);
					byte_buff[i] = (BYTE)x;
				}
			}
		}
		b1 = *pbytes++;
		ret |= b1 << nbits_left;
		nbits_left += 8;
		--navail_bytes;
	}
	nbits_left = (short)(nbits_left-curr_size);
	ret &= code_mask[curr_size];
	return((short)(ret));
}
////////////////////////////////////////////////////////////////////////////////

/* short decoder(linewidth)
 *    short linewidth;               * Pixels per line of image *
 *
 * - This function decodes an LZW image, according to the method used
 * in the GIF spec.  Every *linewidth* "characters" (ie. pixels) decoded
 * will generate a call to out_line(), which is a user specific function
 * to display a line of pixels.  The function gets it's codes from
 * get_next_code() which is responsible for reading blocks of data and
 * seperating them into the proper size codes.  Finally, get_byte() is
 * the global routine to read the next BYTE from the GIF file.
 *
 * It is generally a good idea to have linewidth correspond to the actual
 * width of a line (as specified in the Image header) to make your own
 * code a bit simpler, but it isn't absolutely necessary.
 *
 * Returns: 0 if successful, else negative.  (See ERRS.H)
 *
 */
/* bad_code_count is incremented each time an out of range code is read.
 * When this value is non-zero after a decode, your GIF file is probably
 * corrupt in some way...
 */
short CJzImageGIF::decoder(CxFile* file, CImageIterator* iter, short linewidth, int &bad_code_count)
{
	register BYTE *sp, *bufptr;
	BYTE *buf;
	register short code, fc, oc, bufcnt;
	short c, size, ret;

	/* Initialize for decoding a new image... */
	bad_code_count = 0;
	if ((size = (short)get_byte(file)) < 0)	return(size);
	if (size < 2 || 9 < size)				return(BAD_CODE_SIZE);
	// out_line = outline;
	init_exp(size);
	//printf("L %d %x\n",linewidth,size);

	/* Initialize in case they forgot to put in a clear code.
	 * (This shouldn't happen, but we'll try and decode it anyway...)
	 */
	oc = fc = 0;

   /* Allocate space for the decode buffer */
	if ((buf = new BYTE[linewidth + 1]) == NULL)
        { 
            return(OUT_OF_MEMORY);
        }
   /* Set up the stack pointer and decode buffer pointer */
	sp = stack;
	bufptr = buf;
	bufcnt = linewidth;

   /* This is the main loop.  For each code we get we pass through the
	* linked list of prefix codes, pushing the corresponding "character" for
	* each code onto the stack.  When the list reaches a single "character"
	* we push that on the stack too, and then start unstacking each
    * character for output in the correct order.  Special handling is
	* included for the clear code, and the whole thing ends when we get
    * an ending code.
    */
	while ((c = get_next_code(file)) != ending) {
		/* If we had a file error, return without completing the decode*/
		if (c < 0){
			delete[] buf;
			return(0);
		}
		/* If the code is a clear code, reinitialize all necessary items.*/
		if (c == clear){
			curr_size = (short)(size + 1);
			slot = newcodes;
			top_slot = (short)(1 << curr_size);

			/* Continue reading codes until we get a non-clear code
			* (Another unlikely, but possible case...)
			*/
			while ((c = get_next_code(file)) == clear);

			/* If we get an ending code immediately after a clear code
			* (Yet another unlikely case), then break out of the loop.
			*/
			if (c == ending) break;

			/* Finally, if the code is beyond the range of already set codes,
			* (This one had better NOT happen...  I have no idea what will
			* result from this, but I doubt it will look good...) then set it
			* to color zero.
			*/
			if (c >= slot) c = 0;
			oc = fc = c;

			/* And let us not forget to put the char into the buffer... And
			* if, on the off chance, we were exactly one pixel from the end
			* of the line, we have to send the buffer to the out_line()
			* routine...
			*/
			*bufptr++ = (BYTE)c;
			if (--bufcnt == 0) {
				if ((ret = (short)out_line(iter, buf, linewidth)) < 0) {
					delete[] buf;
					return(ret);
				}
				bufptr = buf;
				bufcnt = linewidth;
            }
		} else {
			/* In this case, it's not a clear code or an ending code, so
			* it must be a code code...  So we can now decode the code into
			* a stack of character codes. (Clear as mud, right?)
			*/
			code = c;

			/* Here we go again with one of those off chances...  If, on the
			* off chance, the code we got is beyond the range of those already
			* set up (Another thing which had better NOT happen...) we trick
			* the decoder into thinking it actually got the last code read.
			* (Hmmn... I'm not sure why this works...  But it does...)
			*/
			if (code >= slot) {
				if (code > slot) ++bad_code_count;
				code = oc;
				*sp++ = (BYTE)fc;
            }

			/* Here we scan back along the linked list of prefixes, pushing
			* helpless characters (ie. suffixes) onto the stack as we do so.
			*/
			while (code >= newcodes) {
				*sp++ = suffix[code];
				code = prefix[code];
            }

			/* Push the last character on the stack, and set up the new
			* prefix and suffix, and if the required slot number is greater
			* than that allowed by the current bit size, increase the bit
			* size.  (NOTE - If we are all full, we *don't* save the new
			* suffix and prefix...  I'm not certain if this is correct...
			* it might be more proper to overwrite the last code...
			*/
			*sp++ = (BYTE)code;
			if (slot < top_slot){
				suffix[slot] = (BYTE)(fc = (BYTE)code);
				prefix[slot++] = oc;
				oc = c;
            }
			if (slot >= top_slot){
				if (curr_size < 12) {
					top_slot <<= 1;
					++curr_size;
				}
			}

			/* Now that we've pushed the decoded string (in reverse order)
			* onto the stack, lets pop it off and put it into our decode
			* buffer...  And when the decode buffer is full, write another
			* line...
			*/
			while (sp > stack) {
				*bufptr++ = *(--sp);
				if (--bufcnt == 0) {
					if ((ret = (short)out_line(iter, buf, linewidth)) < 0) {
						delete[] buf;
						return(ret);
					}
					bufptr = buf;
					bufcnt = linewidth;
				}
			}
		}
	}
	ret = 0;
	if (bufcnt != linewidth)
		ret = (short)out_line(iter, buf, (linewidth - bufcnt));
	delete[] buf;
	return(ret);
}
////////////////////////////////////////////////////////////////////////////////
int CJzImageGIF::get_num_frames(CxFile *fp,struct_TabCol* TabColSrc,struct_dscgif* dscgif)
{
	struct_image image;

                                //printf("  ====== Extension  decoder ========\n");
	long pos=fp->Tell();
	int nframes=0;

	struct_TabCol TempTabCol;
	memcpy(&TempTabCol,TabColSrc,sizeof(struct_TabCol));

	char ch;
	bool bPreviousWasNull = true;

	for (BOOL bContinue = TRUE; bContinue; )
	{
		if (fp->Read(&ch, sizeof(ch), 1) != 1) {break;}

		if (bPreviousWasNull || ch==0)
		{
			switch (ch)
			{
			case '!': // extension
				{
				DecodeExtension(fp);
				break;
				}
			case ',': // image
				{

				//assert(sizeof(image) == 9);
				//log << "Image header" << endl;
				fp->Read(&image,sizeof(image),1);

				//avoid byte order problems with Solaris <candan>
				BYTE *byteData = (BYTE *) & image;
				image.l = byteData[0]+byteData[1]*256;
				image.t = byteData[2]+byteData[3]*256;
				image.w = byteData[4]+byteData[5]*256;
				image.h = byteData[6]+byteData[7]*256;

				if (((image.l + image.w) > dscgif->scrwidth)||((image.t + image.h) > dscgif->scrheight))
					break;

				nframes++;

				// Local colour map?
				if (image.pf & 0x80) {
					TempTabCol.sogct = (short)(1 << ((image.pf & 0x07) +1));
					//assert(3 == sizeof(struct rgb_color));
					fp->Read(TempTabCol.paleta,sizeof(struct rgb_color)*TempTabCol.sogct,1);
					//log << "Local colour map" << endl;
				}

				int bpp; //<DP> select the correct bit per pixel value
				if		(TempTabCol.sogct <= 2)  bpp = 1;
				else if (TempTabCol.sogct <= 16) bpp = 4;
				else						 bpp = 8;
				Create(image.w, image.h, bpp, CXIMAGE_FORMAT_GIF);
				CImageIterator* iter = new CImageIterator(this);
				iter->Upset();
				int badcode=0;
				ibf = GIFBUFTAM+1;

				interlaced = image.pf & 0x40;
				iheight = image.h;
				istep = 8;
				iypos = 0;
				ipass = 0;

				long pos_start = fp->Tell();

				//if (interlaced) log << "Interlaced" << endl;
				decoder(fp, iter, image.w, badcode);
				delete iter;

				if (badcode){
					seek_next_image(fp,pos_start);
				} else {
					fp->Seek(-(ibfmax - ibf - 1), SEEK_CUR);
				}
		
				break;
				}
			case ';': //terminator
				bContinue=false;
				break;
			default:
				bPreviousWasNull = (ch==0);
				break;
			}
		}
	}

	fp->Seek(pos,SEEK_SET);
	return nframes;
}
////////////////////////////////////////////////////////////////////////////////
long CJzImageGIF::seek_next_image(CxFile* fp, long position)
{
	fp->Seek(position, SEEK_SET);
	char ch1,ch2;
	ch1=ch2=0;
	while(fp->Read(&ch2,sizeof(char),1)>0){
		if (ch1 == 0 && ch2 == ','){
			fp->Seek(-1,SEEK_CUR);
			return fp->Tell();
		} else {
			ch1 = ch2;
		}
	}
	return -1;
}
void CJzImageGIF::GifMix(CJzImage & imgsrc2, struct_image & imgdesc)
{


	long ymin = max(0,(long)(GetHeight()-imgdesc.t - imgdesc.h));
	long ymax = GetHeight()-imgdesc.t;
	long xmin = imgdesc.l;
	long xmax = min(GetWidth(), (DWORD)(imgdesc.l + imgdesc.w));

	long ibg2= imgsrc2.GetTransIndex();
    BYTE i2;

	for(long y = ymin; y < ymax; y++){
		for(long x = xmin; x < xmax; x++){
			i2 = imgsrc2.GetPixelIndex(x-xmin,y-ymin);
			if(i2!=ibg2) SetPixelIndex(x,y,i2);
		}
	}


}
////////////////////////////////////////////////////////////////////////////////
void CJzImageGIF::GIFShowFrame( BYTE *pbuffer, struct_image &image, int prevdispmeth)
{
    int i, j, index, dx, dy;
    RGBQUAD  *palette = GetPalette(), color;
    unsigned char *ptar;//

    long ymin = max(0,(long)(GetHeight()-image.t - image.h));
    long ymax = GetHeight()-image.t;
    long xmin = image.l;
    long xmax = min(GetWidth(), (DWORD)(image.l + image.w));

    long jmin = ymin*ivscale/128;
    long jmax = ymax*ivscale/128;
    if( jmax > info.dwimaHeight ) jmax = info.dwimaHeight;
    long imin = xmin*ivscale/128;
    long imax = xmax*ivscale/128;
    if( imax > info.dwimaWidth ) imax = info.dwimaWidth;

    //printf(" ================= GetHeight: %d, ivscale: %d \n",  GetHeight(), ivscale ); 
    //printf(" ======= image.l: %d, image.t: %d, image.w: %d, image.h: %d\n",image.l,image.t,image.w,image.h); 
    //printf(" ================= xmin: %d, xmax: %d, ymin: %d, ymax: %d\n", xmin, xmax, ymin, ymax );
    //printf(" ================= imin: %d, imax: %d, jmin: %d, jmax: %d\n", imin, imax, jmin, jmax );

    //printf(" ================= rescreen_h: %d\n", rescreen_h );
    BYTE *line = pbuffer;
    //line = pbuffer + (rescreen_h-1) * info.dwimaWidth * 4;
    if( prevdispmeth == 2 )
    {
        memset( (void *)line, 0, info.dwimaHeight*info.dwimaWidth*4 );
    }
    dy = ymin;
    for( j = jmin+1; j <= jmax; j++ )
    { 
        line = pbuffer + (info.dwimaHeight-j)* info.dwimaWidth * 4;
        while( ivscale*dy/128 < j ) dy++;
        
        dx = xmin;
        for( i = imin; i < imax; i++ )
        {
            while( ivscale*dx/128 < i )  dx++;
            index = GetPixelIndex(dx,dy); 
            color = palette[index];
            line[i*4+2] = color.rgbRed;            
            line[i*4+1] = color.rgbGreen; 
            line[i*4+0] = color.rgbBlue;  

        }
        //line = line - info.dwimaWidth * 4; 
    }
    ivDeflateGifImage(); 
}  

#endif // CXIMAGE_SUPPORT_GIF




⌨️ 快捷键说明

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