Keith Vetter 2016-05-25 : here's another utility I wrote a while ago that I thought I'd share here. It's a command line utility that displays the size of images, regardless of image type and not requiring Tk. It's similar to ImageMagick's
identify -format "%i [%m] %w x %h\n img.png' but it's more lightweight and has better handling of filenames. For example, if you invoke it without any arguments, it will find all the image files in the current directory.
% imgSizes
chevrons.xbm [xbm] : 14 x 9
klimb.bmp [bmp] : 50 x 50
logo3.png [png] : 532 x 532
me.jpg [jpg] : 1,024 x 683
quito.jpg [jpg] : 922 x 691
sample.webp [webp] : 256 x 22
seascape.jpg [jpg] : 1,024 x 683
t_and_e.jpg [jpg] : 691 x 922
test.png [png] : 566 x 611
##+##########################################################################
#
# imgSizes.tsh -- command line tool for listing image sizes
# by Keith Vetter, October 28, 2005
#
package require jpeg
package require png
package require tiff
proc GetSize {fname} {
if {[file size $fname] == 0} {return [list $fname - -]}
set bestGuess "try[string tolower [file extension $fname]]"
foreach itype [concat $bestGuess [info procs try.*]] {
if {[info procs $itype] eq ""} continue
set try [$itype $fname]
if {$try ne {}} { return $try }
}
return {? - -}
}
proc try.jpg {fname} {
if {! [::jpeg::isJPEG $fname]} { return {} }
return [concat jpg [::jpeg::dimensions $fname]]
}
proc try.png {fname} {
if {! [::png::isPNG $fname]} { return {} }
array set P [::png::imageInfo $fname]
return [list png $P(width) $P(height)]
}
proc try.tiff {fname} {
if {! [::tiff::isTIFF $fname]} { return {} }
lassign [::tiff::dimensions $fname] w h
return [list tiff $w $h]
}
proc try.gif {fname} {
# http://wiki.tcl.tk/758
set data [ReadN $fname 10]
set sig [string toupper [string range $data 0 5]]
if {$sig ne "GIF87A" && $sig ne "GIF89A"} { return {} }
binary scan [string range $data 6 7] s width
binary scan [string range $data 8 9] s height
return [list gif $width $height]
}
proc try.ico {fname} {
# Note, may contain multiple images so we return a list of sizes
set f [open $fname r]
fconfigure $f -encoding binary -translation binary
binary scan [read $f 6] sss zero type numImages
if {$zero != 0 || ($type != 1 && $type != 2)} { close $f ; return {} }
set d {}
for {set i 0} {$i < $numImages} {incr i} {
set idata [read $f 16]
binary scan $idata cc w h
if {$w == 0} {set w 256}
if {$h == 0} {set h 256}
append d "${w}x$h "
}
close $f
return [list ico $d ?]
}
proc try.ppm {fname} {
# see http://netpbm.sourceforge.net/doc/ppm.html
set data [ReadN $fname 256]
set magic [string range $data 0 2]
if {! [regexp {^P6\s$} $magic]} { return {} }
set n [regexp {^P6\s+(\d+)\s+(\d+)} $data . width height]
if {! $n} { error "bad ppm format" }
return [list ppm $width $height]
}
proc try.xbm {fname} {
# see https://en.wikipedia.org/wiki/X_PixMap#Comparison_with_other_formats
set data [ReadN $fname 256]
set n1 [regexp -line {^\s*\#define\s+[a-zA-Z_09]+_width\s+(\d+)} $data . width]
set n2 [regexp -line {^\s*\#define\s+[a-zA-Z_09]+_height\s+(\d+)} $data . height]
if {! $n1 || ! $n2} { return {} }
return [list xbm $width $height]
}
proc try.bmp {fname} {
# see https://en.wikipedia.org/wiki/BMP_file_format
set data [ReadN $fname 26]
if {[string range $data 0 1] ne "BM"} { return {} }
binary scan [string range $data 18 21] i width
binary scan [string range $data 22 25] i height
return [list bmp $width $height]
}
##+##########################################################################
#
# Webp (weppy)
#
# gallery
# =======
# http://news.cnet.com/8301-1023_3-57580664-93/facebook-tries-googles-webp-image-format-users-squawk/
#
# File Format
# ===========
# https://developers.google.com/speed/webp/docs/riff_container
# VP8 :
# VP8L : https://gerrit.chromium.org/gerrit/gitweb?p=webm/libwebp.git;a=blob;f=doc/webp-lossless-bitstream-spec.txt;hb=master
proc try.webp {fname} {
set data [ReadN $fname 30]
set chunk0 [string range $data 0 11]
set chunk1 [string range $data 12 end]
binary scan $chunk0 "a4ia4" riff size id
if {$riff ne "RIFF" || $id ne "WEBP"} {return {}}
binary scan $chunk1 "a4" vp8
if {$vp8 eq "VP8L"} { return [webp.VP8L $chunk1]}
# We're assuming start code block starts 11 bytes into the VP8 chunk
binary scan $chunk1 "a4cu7cu3cu2cu2" vp8 . startCode widthInfo heightInfo
if {$vp8 ne "VP8 "} { error "unknown VP8 block" }
lassign $startCode b0 b1 b2
if {$b0 != 0x9d || $b1 != 0x01 || $b2 != 0x2a} {
error "missing start code block"
}
set horizScale [expr {[lindex $widthInfo 1] >> 6}]
lset widthInfo 1 [expr {[lindex $widthInfo 1] & 0x3f}]
set vertScale [expr {[lindex $heightInfo 1] >> 6}]
lset heightInfo 1 [expr {[lindex $heightInfo 1] & 0x3f}]
binary scan [binary format cu2cu2 $widthInfo $heightInfo] tt width height
return [list webp $width $height]
}
proc webp.VP8L {chunk1} {
binary scan $chunk1 a4icucu4 vp8 size signature sizeInfo
if {$signature != 0x2f} {
error "bad VP8L signature byte: $signature"
}
lassign $sizeInfo b0 b1 b2 b3
# 10001111000000010100101100010000
# 10001111 00000001 01001011 00010000
# 10001111.000000 01.01001011.0001 0000
# 10001111.000000 1.01001011.0001
# 1_webp_ll.webp
# width: 400px 110001111
# height: 301px 100101100
# 2_webp_ll.webp
# 386x295
set width [expr {1 + ($b0 << 6) + ($b1 >> 2)}]
set height [expr {1 + ($b1 << 12) + ($b2 << 4) + ($b3 >> 4)}]
return [list webp $width $height]
}
proc commify number {regsub -all {\d(?=(\d{3})+($|\.))} $number {\0,}}
proc ReadN {fname n} {
set fin [open $fname rb]
set data [read $fin $n]
close $fin
return $data
}
if {$argv == {}} {
set argv [list *.gif *.jpg *.jpeg *.png *.ico *.webp *.bmp *.tiff *.tif *.ppm *.xbm]
}
set fnames {}
set longestName 0
foreach arg $argv {
regsub -all {\\} $arg {/} arg
foreach fname [glob -nocomplain $arg] {
if {! [file isfile $fname]} continue
lappend fnames $fname
set longestName [expr {max($longestName, 2 + [string length $fname])}]
}
}
if {$tcl_interactive} return
foreach fname [lsort -dictionary $fnames] {
set sizes [GetSize $fname]
lassign $sizes itype w h
set type " \[$itype\]"
if {$itype eq "ico"} {
puts [format "%-*s%s : %s" $longestName $fname $type $w]
} else {
puts [format "%-*s%s : %s x %s" $longestName $fname $type [commify $w] [commify $h]]
}
}
exit