Version 0 of Reading image type and dimensions

Updated 2010-12-23 20:56:01 by AMG

AMG: I collected code from other Wiki pages on reading image dimensions of various formats (GIF, PNG, JPEG, and BMP) and made a single proc that can determine the type and dimensions all at once.

The argument is the name of the channel connected to the image data.

The image is expected to begin at the current channel seek position, and the channel must have "binary" translation.

The returned value is a one- or three-element list. The first element is the type, which is gif, png, jpeg, bmp, or unknown. The second and third elements are the image width and height in pixels; they're omitted if the first element is unknown.

proc imageinfo {chan} {
    set tell [chan tell $chan]
    if {[chan read $chan 16] eq "\211PNG\r\n\32\n\0\0\0\rIHDR"} {
        set type png
        binary scan [chan read $chan 8] II width height
    } elseif {"[chan seek $chan $tell][chan read $chan 6]" in {GIF87a GIF89a}} {
        set type gif
        binary scan [chan read $chan 4] ss width height
    } elseif {"[chan seek $chan $tell][chan read $chan 2]" eq "\377\330"} {
        set type jpeg
        while {![chan eof $chan]} {
            while {[chan read $chan 1] ne "\377"} {}
            while {[set byte [chan read $chan 1]] eq "\377"} {}
            if {$byte in {\300 \301 \302 \303 \305 \306 \307
                          \311 \312 \313 \315 \316 \317}} {
                binary scan [chan read $chan 7] x3SS height width
                break
            } else {
                binary scan [chan read $chan 2] S offset
                chan seek $chan [expr {($offset & 0xffff) - 2}] current
            }
        }
    } elseif {"[chan seek $chan $tell][chan read $chan 2]" eq "BM"} {
        set type bmp
        binary scan [chan read $chan 24] x16ii width height
    }
    if {[info exists type] && [info exists width] && [info exists height]} {
        list $type [expr {$width & 0xffff}] [expr {$height & 0xffff}]
    } else {
        list unknown
    }
}

I designed it for use with the SQLite [$db incrblob] command, which produces a channel providing incremental access to a database blob. It's also suitable for use with ordinary file I/O. Remember to close the channel when you're done with it.

See also