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.
Here's a version that operates directly on the image data:
proc imageinfo {data} { if {[string equal -length 16 $data \211PNG\r\n\32\n\0\0\0\rIHDR]} { set type png binary scan [string range $data 16 23] II width height } elseif {[string range $data 0 5] in {GIF87a GIF89a}} { set type gif binary scan [string range $data 6 9] ss width height } elseif {[string equal -length 2 $data \377\330]} { set type jpeg set pos 2 while {[regexp -start $pos {\377([^\377])(.{2,7})} $data _ mark args]} { if {$mark in {\300 \301 \302 \303 \305 \306 \307 \311 \312 \313 \315 \316 \317}} { binary scan $args x3SS height width break } else { binary scan $args S offset set pos [expr {$pos + ($offset & 0xffff) - 2}] } } } elseif {[string equal -length 2 $data BM]} { set type bmp binary scan [string range $data 18 25] ii width height } if {[info exists type] && [info exists width] && [info exists height]} { list $type [expr {$width & 0xffff}] [expr {$height & 0xffff}] } else { list unknown } }