GIF loader bug fix

new topic     » topic index » view thread      » older message » newer message

Hi all!

The GIF loader I posted to this list recently contained a bug that
would in rare cases cause it to crash. I have just fixed this, so
here's the new code:

-------------------------------------------
include machine.e
include file.e

global constant PCX_OPEN_FAILED=1,
                PCX_UNEXPECTED_EOF=2,
                PCX_UNSUPPORTED_FORMAT=3,
                GIF_OPEN_FAILED=1,
                GIF_UNEXPECTED_EOF=2,
                GIF_UNSUPPORTED_FORMAT=3

object junk
sequence image,the_palette,bit_table
integer file_num,exit_flag,palette_at_end
integer x_size,y_size,bits_per_pixel,n_colors
integer background,bytes_per_line,x_min,x_max
integer y_min,y_max,n_planes

bit_table=repeat({},256)
for n=0 to 255 do
    bit_table[n+1]=int_to_bits(n,8)
end for

function read_bytes(integer amount)
    sequence bytes

    bytes=repeat(0,amount)
    for b=1 to amount do
        bytes[b]=getc(file_num)
        if bytes[b]=-1 then
            exit_flag=GIF_UNEXPECTED_EOF
        end if
    end for
    return bytes
end function

function bytes_left()
    integer place,end_place

    place=where(file_num)
    if seek(file_num,-1)=0 then
        end_place=where(file_num)
        if seek(file_num,place)=0 then
            return end_place-place
        else
            return -1
        end if
    else
        return -1
    end if
end function

procedure read_pcx_header()
    if bytes_left()<128 then
        exit_flag=PCX_UNEXPECTED_EOF
        return
    end if
    junk=getc(file_num)
    palette_at_end=0
    if getc(file_num)=5 then
        palette_at_end=1
    end if
    if getc(file_num)!=1 then
        exit_flag=PCX_UNSUPPORTED_FORMAT
        return
    end if
    bits_per_pixel=getc(file_num)
    if bits_per_pixel!=1 and bits_per_pixel!=8 then
        exit_flag=PCX_UNSUPPORTED_FORMAT
        return
    end if
    x_min=getc(file_num)+getc(file_num)*256
    y_min=getc(file_num)+getc(file_num)*256
    x_max=getc(file_num)+getc(file_num)*256
    y_max=getc(file_num)+getc(file_num)*256
    x_size=x_max-x_min+1
    y_size=y_max-y_min+1
    junk=read_bytes(4)
    the_palette={}
    if palette_at_end then
        junk=read_bytes(48)
    else
        for c=1 to 16 do
            the_palette=append(the_palette,read_bytes(3))
        end for
    end if
    if exit_flag then
        return
    end if
    if getc(file_num) then
        exit_flag=PCX_UNSUPPORTED_FORMAT
        return
    end if
    n_planes=getc(file_num)
    bytes_per_line=getc(file_num)+getc(file_num)*256
    junk=read_bytes(6)
    for x=1 to 54 do
        junk=getc(file_num)
        if junk=-1 then
            exit_flag=PCX_UNEXPECTED_EOF
            return
        elsif junk then
            exit_flag=PCX_UNSUPPORTED_FORMAT
            return
        end if
    end for
end procedure

procedure read_pcx_image()
    sequence one_row,bits,new_row
    integer number,byte,line,count,and_value,place
    integer plane_power,amount

    image=repeat({},y_size)
    one_row=repeat(0,bytes_per_line*n_planes)
    line=1
    while line<=y_size do
        number=1
        while number<=length(one_row) do
            byte=getc(file_num)
            if byte=-1 then
                exit_flag=PCX_UNEXPECTED_EOF
                return
            end if
            if byte>191 then
                count=byte-193
                byte=getc(file_num)
                if byte=-1 then
                    exit_flag=PCX_UNEXPECTED_EOF
                    return
                end if
                one_row[number..number+count]=byte
                number=number+count+1
            else
                one_row[number]=byte
                number=number+1
            end if
        end while
        if bits_per_pixel=1 then
            bits=repeat(0,bytes_per_line*8*n_planes)
            place=1
            for p=0 to n_planes-1 do
                plane_power=power(2,p)
                for t=1 to bytes_per_line do
                    byte=one_row[p*bytes_per_line+t]
                    and_value=256
                    for z=1 to 8 do
                        and_value=and_value/2
                        if and_bits(byte,and_value) then
                            bits[place]=plane_power
                        end if
                        place=place+1
                    end for
                end for
            end for
            amount=bytes_per_line*8
            new_row=bits[1..amount]
            for p=1 to n_planes-1 do
                new_row=new_row+bits[amount*p+1..amount*(p+1)]
            end for
            if length(new_row)>x_size then
                new_row=new_row[1..x_size]
            end if
        else
            new_row=one_row
        end if
        image[line]=new_row
        line=line+1
    end while
    if palette_at_end then
        junk=getc(file_num)
        for c=1 to 256 do
            the_palette=append(the_palette,read_bytes(3))
            if find(-1,the_palette[c]) then
                exit_flag=PCX_UNEXPECTED_EOF
                return
            end if
        end for
    end if
end procedure

procedure read_gif_header()
    object work
    sequence stuff,bits
    integer global_color_map

    if bytes_left()<6 then
        exit_flag=GIF_UNEXPECTED_EOF
        return
    end if
    stuff=read_bytes(3)
    if compare(stuff,"GIF") and compare(stuff,"GIF") then
        exit_flag=GIF_UNSUPPORTED_FORMAT
        return
    end if
    junk=read_bytes(3)
    if bytes_left()<7 then
        exit_flag=GIF_UNEXPECTED_EOF
        return
    end if
    junk=read_bytes(4)
    bits=bit_table[getc(file_num)+1]
    global_color_map=bits[8]
    bits_per_pixel=bits_to_int(bits[1..3])+1
    n_colors=power(2,bits_per_pixel)
    background=getc(file_num)
    junk=getc(file_num)
    the_palette={}
    if global_color_map then
        if bytes_left()<n_colors*3 then
            exit_flag=GIF_UNEXPECTED_EOF
            return
        end if
        for c=1 to n_colors do
            the_palette=append(the_palette,read_bytes(3))
        end for
    end if
    while 1 do
        work=getc(file_num)
        if work=-1 then
            exit_flag=GIF_UNEXPECTED_EOF
            return
        elsif work=',' then
            exit
        end if
    end while
end procedure

procedure read_gif_image()
    object work
    sequence strings,bits,output,work_bits,string,stream
    integer interlace,code_size,clear_code,end_of_info
    integer num_bits,code,old,done,pointer,block_length
    integer bit_add

    if bytes_left()<9 then
        exit_flag=GIF_UNEXPECTED_EOF
        return
    end if
    junk=read_bytes(4)
    x_size=getc(file_num)+getc(file_num)*256
    y_size=getc(file_num)+getc(file_num)*256
    work=getc(file_num)
    bits=bit_table[work+1]
    if bits[8] then
        bits_per_pixel=bits_to_int(bits[1..3])+1
        n_colors=power(2,bits_per_pixel)
        the_palette={}
        for c=1 to n_colors do
            the_palette=append(the_palette,read_bytes(3))
        end for
        if exit_flag then
            return
        end if
    end if
    interlace=bits[7]
    image=repeat({},y_size)
    output={}
    if n_colors=2 then
        n_colors=4
    end if
    strings=repeat({},n_colors+2)
    for t=1 to n_colors do
        strings[t]={t-1}
    end for
    code_size=getc(file_num)
    if code_size=-1 then
        exit_flag=GIF_UNEXPECTED_EOF
        return
    end if
    clear_code=power(2,code_size)+1
    end_of_info=clear_code+1
    num_bits=code_size+1
    stream={}
    while 1 do
        block_length=getc(file_num)
        if block_length=0 then
            exit
        end if
        for b=1 to block_length do
            stream=append(stream,getc(file_num))
        end for
    end while
    if find(-1,stream) then
        exit_flag=GIF_UNEXPECTED_EOF
        return
    end if
    stream=append(stream,0)
    stream=append(stream,-1)
    pointer=1
    bits={}
    while length(bits)<26 do
        bits=bits & bit_table[stream[pointer]+1]
        pointer=pointer+1
    end while
    bits=bits[num_bits+1..length(bits)]
    work_bits=bits[1..num_bits]
    code=bits_to_int(work_bits)+1
    output=strings[code]
    old=code
    bits=bits[num_bits+1..length(bits)]
    done=0
    while 1 do
        while length(bits)<26 and done=0 do
            work=stream[pointer]
            pointer=pointer+1
            if work=-1 then
                done=1
                exit
            end if
            bits=bits & bit_table[work+1]
        end while
        if length(bits)<num_bits then
            exit
        end if
        code=1
        bit_add=1
        for b=1 to num_bits do
            if bits[b] then
                code=code+bit_add
            end if
            bit_add=bit_add+bit_add
        end for
        bits=bits[num_bits+1..length(bits)]
        if code=end_of_info then
            exit
        elsif code=clear_code then
            strings=repeat({},n_colors+2)
            for t=1 to n_colors do
                strings[t]={t-1}
            end for
            num_bits=code_size+1
            work_bits=bits[1..num_bits]
            code=bits_to_int(work_bits)+1
            for t=1 to length(strings[code]) do
                output=append(output,strings[code][t])
            end for
            old=code
            bits=bits[num_bits+1..length(bits)]
        else
            if code<=length(strings) then
                string=strings[code]
                for t=1 to length(string) do
                    output=append(output,string[t])
                end for
                work=strings[old] & string[1]
                strings=append(strings,work)
                old=code
            else
                work=strings[old] & strings[old][1]
                for t=1 to length(work) do
                    output=append(output,work[t])
                end for
                strings=append(strings,work)
                old=code
            end if
        end if
        if length(strings)=power(2,num_bits) then
            num_bits=num_bits+1
            if num_bits=13 then
                num_bits=12
            end if
        end if
    end while
    work=repeat({},y_size)
    for r=0 to y_size-1 do
        work[r+1]=output[r*x_size+1..r*x_size+x_size]
    end for
    if interlace then
        image=repeat({},y_size)
        pointer=1
        for l=1 to y_size by 8 do
            image[l]=work[pointer]
            pointer=pointer+1
        end for
        for l=5 to y_size by 8 do
            image[l]=work[pointer]
            pointer=pointer+1
        end for
        for l=3 to y_size by 4 do
            image[l]=work[pointer]
            pointer=pointer+1
        end for
        for l=2 to y_size by 2 do
            image[l]=work[pointer]
            pointer=pointer+1
        end for
    else
        image=work
    end if
end procedure

global function read_pcx(sequence filename)
    exit_flag=0
    file_num=open(filename,"rb")
    if file_num<0 then
        return PCX_OPEN_FAILED
    end if
    read_pcx_header()
    if exit_flag then
        close(file_num)
        return exit_flag
    end if
    read_pcx_image()
    close(file_num)
    if exit_flag then
        return exit_flag
    end if
    the_palette=the_palette[1..power(2,bits_per_pixel*n_planes)]
    return {the_palette,image}
end function

global function read_gif(sequence file_name)
    exit_flag=0
    file_num=open(file_name,"rb")
    if file_num<0 then
        return GIF_OPEN_FAILED
    end if
    read_gif_header()
    if exit_flag then
        close(file_num)
        return exit_flag
    end if
    read_gif_image()
    close(file_num)
    if exit_flag then
        return exit_flag
    end if
    return {the_palette,image}
end function
-------------------------------------------

Regards,
               Michael Bolin

new topic     » topic index » view thread      » older message » newer message

Search



Quick Links

User menu

Not signed in.

Misc Menu