cnv/giftorle.c

Summary

Maintainability
Test Coverage
/*
 * This software is copyrighted as noted below.  It may be freely copied,
 * modified, and redistributed, provided that the copyright notice is
 * preserved on all copies.
 *
 * There is no warranty or other guarantee of fitness for this software,
 * it is provided solely "as is".  Bug reports or fixes may be sent
 * to the author, who may or may not act on them as he desires.
 *
 * You may not include this software in a program or other software product
 * without supplying the source, or without informing the end-user that the
 * source is available for no extra charge.
 *
 * If you modify this software, you should include a notice giving the
 * name of the person performing the modification, the date of modification,
 * and the reason for such modification.
 */
/* +------------------------------------------------------------------+ */
/* | Copyright 1989, David Koblas.                                    | */
/* |   You may copy this file in whole or in part as long as you      | */
/* |   don't try to make money off it, or pretend that you wrote it.  | */
/* +------------------------------------------------------------------+ */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include "rle.h"

#ifndef lint
static char rcsid[] = "$Id: giftorle.c,v 3.0.1.4 1992/04/30 13:56:24 spencer Exp spencer $";
#endif
/*
giftorle()        Tag the file.
*/

#define    MAXCOLORMAPSIZE        256

#define    TRUE    1
#define    FALSE    0

#define CM_RED        0
#define CM_GREEN    1
#define CM_BLUE        2

#define    MAX_LWZ_BITS        12

#define    ReadOK(file,buffer,len)    (fread(buffer,len,1,file)!=0)
#define    EasyFail(str,status)    {fprintf(stderr,str);return(status);}
#define    HardFail(str,status)    {fprintf(stderr,str);exit  (status);}

#define LM_to_uint(a,b)            (((b)<<8)|(a))

int ReadGIF();
int ReadColorMap(), IgnoreExtention(), GetCode(), LWZReadByte();
int ReadRaster();

static rle_map out_map[3*(1<<8)];

CONST_DECL char *MY_NAME = "giftorle";

FILE *outfile;

struct {
    unsigned int        Width;
    unsigned int        Height;
    unsigned char        ColorMap[3][MAXCOLORMAPSIZE];
    unsigned int        BitPixel;
    unsigned int        ColorResolution;
    unsigned int        Background;
} Screen;

static int output_colormap = FALSE;

void
main(argc,argv)
int    argc;
char    **argv;
{
    int    oflag = 0, nfname = 0;
    char *outfname = NULL, **infname = NULL;

    MY_NAME = cmd_name( argv );

    if ( scanargs( argc, argv, "% c%- o%-outfile.rle!s infile.gif%*s",
           &output_colormap, &oflag, &outfname,
           &nfname, &infname ) == 0 )
           exit( 1 );

    outfile = rle_open_f( MY_NAME, outfname, "w" );
    /* Questionable practice.  Modifies default values for headers. */
    rle_addhist( argv, (rle_hdr *)0, &rle_dflt_hdr );

    /* Always read at least one (standard input). */
    if ( nfname == 0 )
    (void)ReadGIF( NULL );

    while ( nfname-- > 0 )
    (void)ReadGIF( *infname++ );
    exit( 0 );
}

int
ReadGIF(filename)
char    *filename;
{
    unsigned char    buf[16];
    unsigned char    c;
    unsigned char    LocalColorMap[3][MAXCOLORMAPSIZE];
    FILE            *fd;
    int                use_global_colormap;
    int                bit_pixel;
    int                count=0;

    fd = rle_open_f( MY_NAME, filename, "r" );

    if (! ReadOK(fd,buf,6))
    EasyFail("error reading magic number\n",TRUE);
    if (strncmp((char *)buf,"GIF87a",6)!=0)
    EasyFail("bad magic number (version mismatch?)\n",TRUE);
    if (! ReadOK(fd,buf,7))
    EasyFail("error reading screen descriptor\n",TRUE);
    Screen.Width           = LM_to_uint(buf[0],buf[1]);
    Screen.Height          = LM_to_uint(buf[2],buf[3]);
    Screen.BitPixel        = 2<<(buf[4]&0x07);
    Screen.ColorResolution = (((buf[4]&0x70)>>3)+1);
    Screen.Background      = buf[5];
    if ((buf[4]&0x80)==0x80) {
    if (ReadColorMap(fd,Screen.BitPixel,Screen.ColorMap))
        return (TRUE);
    }

    while (1) {
    if (! ReadOK(fd,&c,1))
        EasyFail("No image data -- EOF\n",TRUE);
    if (c == ';')
        return FALSE;
    if (c == '!') {
        if (! ReadOK(fd,&c,1))
        EasyFail("No extention function code -- EOF\n",TRUE);
        if (IgnoreExtention(fd))
        return(TRUE);
        continue;
    }
    if (c != ',') {
        fprintf(stderr,"Bogus character ignoring '%c'\n",c);
        continue;
    }
/*
    if (count == 1)
        HardFail("This file contains more than one image! FAILING\n",1);
*/
    count++;

    if (! ReadOK(fd,buf,9))
        EasyFail("Couldn't read left/top/width/height\n",TRUE);
    if ((buf[8]&0x80)==0x80)
        use_global_colormap = FALSE ;
    else
        use_global_colormap = TRUE ;

    bit_pixel = 1<<((buf[8]&0x07)+1);

    if (! use_global_colormap) {
        if (ReadColorMap(fd,bit_pixel,LocalColorMap))
        return TRUE;
    }

    if (ReadRaster((buf[8]&0x40)==0x40, fd,LM_to_uint(buf[4],buf[5]),
               LM_to_uint(buf[6],buf[7]),
               use_global_colormap?Screen.ColorMap:LocalColorMap))
        return TRUE;
    }
}

int
ReadColorMap(fd,number,buffer)
FILE            *fd;
int                number;
unsigned char    buffer[3][MAXCOLORMAPSIZE];
{
    int                i;
    unsigned char    rgb[3];

    for (i=0;i<number;i++) {
    if (! ReadOK(fd,rgb,sizeof(rgb)))
        EasyFail("Bogus colormap\n",TRUE);
    buffer[CM_RED][i] = rgb[0] ;
    buffer[CM_GREEN][i] = rgb[1] ;
    buffer[CM_BLUE][i] = rgb[2] ;
    }
    return FALSE;
}

int
IgnoreExtention(fd)
FILE    *fd;
{
    static char        buf[256];
    unsigned char    c;

    while (1) {
    if (! ReadOK(fd,&c,1))
        EasyFail("EOF in extention\n",TRUE);
    if (c == 0)
        return FALSE;
    if (read(fd,buf,(int) c)!=(int) c)
        EasyFail("EOF in extention\n",TRUE);
    }
}

int
GetCode(fd, code_size, flag)
FILE    *fd;
int        code_size;
int        flag;
{
    static unsigned char    buf[280];
    static int                curbit,lastbit,done,last_byte;
    int                        i, j, ret;
    unsigned char            count;

    if (flag) {
    curbit = 0;
    lastbit = 0;
    done = FALSE;
    return 0;
    }

    if ( (curbit+code_size) >= lastbit) {
    if (done) {
        if (curbit>=lastbit)
        EasyFail("Ran off the end of my bits\n",-1);
    }
    buf[0] = buf[last_byte-2];
    buf[1] = buf[last_byte-1];
    if (! ReadOK(fd,&count,1)) {
        EasyFail("Error in getting buffer size\n",-1);
    }
    if (count == 0) {
        done = TRUE;
    } else if (! ReadOK(fd,&buf[2],count))
        EasyFail("Error in getting buffer\n",-1);
    last_byte = 2 + count;
    curbit = (curbit - lastbit) + 16;
    lastbit = (2+count)*8 ;
    }

    ret = 0;
    for( i = curbit, j = 0; j < code_size; i++, j++ )
    ret |= ((buf[ i / 8 ] & (1 << (i % 8))) != 0) << j;

    curbit += code_size;

    return ret;
}

int
LWZReadByte(fd,flag,input_code_size)
FILE    *fd;
int    flag;
int    input_code_size;
{
    static int    fresh=FALSE;
    int        code,incode;
    static int    code_size,set_code_size;
    static int    max_code,max_code_size;
    static int    firstcode,oldcode;
    static int    clear_code,end_code;
    static int    table[2][(1<< MAX_LWZ_BITS)];
    static int    stack[(1<<(MAX_LWZ_BITS))*2],*sp;
    register int i;

    if (flag) {
    set_code_size = input_code_size;
    code_size = set_code_size+1;
    clear_code = 1 << set_code_size ;
    end_code = clear_code + 1;
    max_code_size = 2*clear_code;
    max_code = clear_code+2;

    GetCode(fd,NULL,TRUE);

    fresh=TRUE;

    for (i=0;i<clear_code;i++) {
        table[0][i] = 0;
        table[1][i] = i;
    }
    for (;i<(1<<MAX_LWZ_BITS);i++)
        table[0][i] = table[1][0] = 0;

    sp=stack;

    return 0;
    } else if (fresh) {
    fresh = FALSE;
    do {
        firstcode=oldcode=
        GetCode(fd, code_size, FALSE);
    } while (firstcode == clear_code);
    return firstcode;
    }

    if (sp > stack)
    return *--sp;

    while ((code=GetCode(fd,code_size,FALSE))>=0) {
    if (code == clear_code) {
        for (i=0;i<clear_code;i++) {
        table[0][i] = 0;
        table[1][i] = i;
        }
        for (;i<(1<<MAX_LWZ_BITS);i++)
        table[0][i] = table[1][i] = 0;
        code_size = set_code_size+1;
        max_code_size = 2*clear_code;
        max_code = clear_code+2;
        sp=stack;
        firstcode=oldcode=
        GetCode(fd,code_size,FALSE);
        return firstcode;
    } else if (code == end_code) {
        unsigned char        count;
        unsigned char        junk;

        while (ReadOK(fd,&count,1) && (count!=0))
        while (count-->0 && ReadOK(fd,&junk,1));
        if (count!=0)
        EasyFail("missing EOD in data stream (common occurance)\n",-3);
        return -2;
    }

    incode = code;

    if (code >= max_code) {
        *sp++ = firstcode;
        code = oldcode;
    }

    while (code >= clear_code) {
        *sp++ = table[1][code];
        if (code == table[0][code])
        EasyFail("Circular table entry BIG ERROR\n",-1);
        code = table[0][code];
    }

    *sp++ = firstcode = table[1][code];

    if ((code=max_code)<(1<<MAX_LWZ_BITS)) {
        table[0][code] = oldcode;
        table[1][code] = firstcode;
        max_code++;
        if ((max_code >= max_code_size) &&
        (max_code_size < (1<<MAX_LWZ_BITS))) {
        max_code_size *= 2;
        code_size++;
        }
    }

    oldcode = incode;

    if (sp > stack)
        return *--sp;
    }
    return code;
}

int
ReadRaster(interlace,fd,len,height,cmap)
int interlace;
FILE    *fd;
int    len,height;
char    cmap[3][MAXCOLORMAPSIZE];
{
    unsigned char    c;
    int            v;
    int            xpos=0;
    rle_pixel          **scanline[3];
    rle_pixel           *ptr[3] ;
    rle_hdr        hdr;
    int            i,j;
    int            ypos=0, pass=interlace ? 0 : 4;


    for (j=0;j<(output_colormap?1:3);j++) {
    if ((scanline[j]=(rle_pixel **)
         malloc(height*sizeof(rle_pixel *)))==NULL)
        EasyFail("Unable to malloc space for pixels #1\n",1);
    for (i=0;i<height;i++) {
        if ((scanline[j][i]=(rle_pixel *)
         malloc(len*sizeof(rle_pixel)))==NULL) {
        for (;j>=0;j--)
            for (;i>=0;i--)
            if (scanline[j][i]!=NULL) free(scanline[i]);
        EasyFail("Unable to malloc space for pixels #2\n",1);
        }
    }
    }

    hdr = *rle_hdr_init( (rle_hdr *)NULL );
    rle_names( &hdr, "giftorle", NULL, 0 );

    if (output_colormap) {
    hdr.ncolors =  1;
    hdr.ncmap = 3;
    hdr.cmaplen = 8;
    hdr.cmap = out_map;
    for (i=0;i<(1<<8);i++) {
        out_map[i+(0<<8)] = cmap[CM_RED][i] << 8;
        out_map[i+(1<<8)] = cmap[CM_GREEN][i] << 8;
        out_map[i+(2<<8)] = cmap[CM_BLUE][i] << 8;
    }
    } else {
    RLE_SET_BIT(hdr, RLE_RED);
    RLE_SET_BIT(hdr, RLE_GREEN);
    RLE_SET_BIT(hdr, RLE_BLUE);
    hdr.ncolors =  3;
    }
    hdr.rle_file = outfile ;
    hdr.xmax = len - 1;
    hdr.ymax = height - 1;

    rle_put_setup(&hdr);

    if (! ReadOK(fd,&c,1))
    EasyFail("Bogus image data -- EOF\n",TRUE);
    if (LWZReadByte(fd,TRUE,c)<0)
    return TRUE;

    while ((v=LWZReadByte(fd,FALSE,c))>=0) {
    if (output_colormap) {
        scanline[RLE_RED][ypos][xpos]   = v ;
    } else {
        scanline[RLE_RED][ypos][xpos]   = cmap[CM_RED][v] ;
        scanline[RLE_GREEN][ypos][xpos] = cmap[CM_GREEN][v] ;
        scanline[RLE_BLUE][ypos][xpos]  = cmap[CM_BLUE][v] ;
    }
    xpos++;
    if (xpos==len) {
        xpos = 0;
        switch (pass) {
        case 0:
        case 1:
        ypos += 8; break;
        case 2:
        ypos += 4; break;
        case 3:
        ypos += 2; break;
        case 4:        /* Not interlaced */
        ypos++; break;
        default:
        fprintf( stderr, "%s: Data past end of image.\n", hdr.cmd );
        break;
        }
        if (ypos >= height) {
        pass++;
        switch (pass) {
        case 1:
            ypos = 4; break;
        case 2:
            ypos = 2; break;
        case 3:
            ypos = 1; break;
        default:        /* Shouldn't happen. */
            ypos = 0; break;
        }
        }
    }
    }

    for ( i = height - 1; i >= 0; i-- ) {
    ptr[0] = scanline[RLE_RED][i] ;
    if (! output_colormap) {
        ptr[1] = scanline[RLE_GREEN][i] ;
        ptr[2] = scanline[RLE_BLUE][i] ;
    }
    rle_putrow(ptr,len,&hdr);
    }

    rle_puteof(&hdr);

    for (i=0;i<height;i++) {
    if (scanline[0][i]!=NULL) free(scanline[0][i]);
    if (output_colormap) continue;

    if (scanline[1][i]!=NULL) free(scanline[1][i]);
    if (scanline[2][i]!=NULL) free(scanline[2][i]);
    }
    free(scanline[0]);
    if (! output_colormap) {
    free(scanline[1]);
    free(scanline[2]);
    }

    if (v == -2)
    return FALSE;
    return TRUE;
}