Version 0 of windows icons

Updated 2003-07-15 09:13:24

--AF 15-07-03

reading windows icons:

 proc IconStats {file} {
    set fh [open $file r]
    fconfigure $fh -encoding binary

    if {"[getword $fh] [getword $fh]" != "0 1"} { error "not an icon file" }
    set num [getword $fh]
    set r {}
    for {set i 0} {$i < $num} {incr i} {
        set info {}
        lappend info [scan [read $fh 1] %c] [scan [read $fh 1] %c]
        set bpp [scan [read $fh 1] %c]
        if {$bpp == 0} {
            set orig [tell $fh]
            seek $fh 9 current
            seek $fh [expr {[getdword $fh] + 14}] start
            lappend info [getword $fh]
            seek $fh $orig start
        } else {
            lappend info [int [expr {sqrt($bpp)}]]
        }
        lappend r $info
        seek $fh 13 current
    }
    close $fh
    return $r
 }

 proc getIcon {file index} {
    set fh [open $file r]
    fconfigure $fh -encoding binary

    if {"[getword $fh] [getword $fh]" != "0 1"} { error "not an icon file" }
    if {$index < 0 || $index >= [getword $fh]} { error "index out of range" }

    seek $fh [expr {(16 * $index) + 12}] current
    seek $fh [getdword $fh] start

    seek $fh 4 current
    set w [getdword $fh]
    set h [expr {[getdword $fh] / 2}]
    seek $fh 2 current
    set bpp [getword $fh]
    seek $fh 24 current

    if {$bpp == 4 || $bpp == 8} {
        set colors [read $fh [int [expr {pow(2, ($bpp + 2))}]]]
    } elseif {$bpp == 24 || $bpp == 32} {
        set colors {}
    } else {
        error "unsupported color depth: $bpp"
    }
    set pad [expr {$w % 32}]
    set xor [read $fh [int [expr {($w * $h) * ($bpp / 8.0)}]]]
    set and [read $fh [expr {(($w * $h) + ($w * $pad)) / 8}]]

    close $fh

    set palette {}
    foreach {b g r x} [split $colors {}] {
        lappend palette [format #%02X%02X%02X [scan $r %c] [scan $g %c] [scan $b %c]]
    }

    set and2 {}
    set len [expr {[string length $and] - 4}]
    for {set i 0} {$i < $len} {incr i 4} {
        binary scan [string range $and $i [expr {$i + 3}]] B32 tmp
        if {$w == 16 || ($w == 48 && $i % 8 == 4)} {
            set tmp [string range $tmp 0 15]
        }
        append and2 $tmp
    }
    set img [image create photo -width $w -height $h]

    if {$bpp == 4} {
        set x -2
        set y [expr {$h - 1}]
        incr w -2
        foreach s [split $xor {}] {a1 a2} [split $and2 {}] {
            if {$x == $w} { incr y -1; set x 0 } else { incr x 2 }
            binary scan $s B8 tmp
            if {$a1 == "0"} {
                $img put -to $x $y [lindex $palette [bits2int [string range $tmp 0 3]]]
            }
            if {$a2 == "0"} {
               $img put -to [expr {$x + 1}] $y [lindex $palette [bits2int [string range $tmp 4 7]]]
            }
        }
    } elseif {$bpp == 8} {
        set x -1
        set y [expr {$h - 1}]
        incr w -1
        foreach i [split $xor {}] and [split $and2 {}] {
            if {$x == $w} { incr y -1; set x 0 } else { incr x }
            if {$and == "0"} {
                $img put -to $x $y [lindex $palette [scan $i %c]]
            }
        }
    } elseif {$bpp == 24} {
        set x -1
        set y [expr {$h - 1}]
        incr w -1
        foreach {b g r} [split $xor {}] and [split $and2 {}] {
            if {$x == $w} { incr y -1; set x 0 } else { incr x }
            if {$and == "0"} {
                $img put -to $x $y [format #%02X%02X%02X [scan $r %c] [scan $g %c] [scan $b %c]]
            }
        }
    } elseif {$bpp == 32} {
        set x -1
        set y [expr {$h - 1}]
        incr w -1
        foreach {b g r a} [split $xor {}] and [split $and2 {}] {
            if {$x == $w} { incr y -1; set x 0 } else { incr x }
            if {$and == "0"} {
                $img put -to $x $y [format #%02X%02X%02X [scan $r %c] [scan $g %c] [scan $b %c]]
            }
        }
    }
    return $img
 }


 proc int {n} {
    return [lindex [split $n .] 0]
 }

 proc bits2int {bits} {
    set res 0
    foreach i [split $bits {}] {
        set res [expr {$res*2+$i}]
    }
    return $res
 }

 proc getdword {fh} {
    binary scan [read $fh 4] i* tmp
    return $tmp
 }

 proc getword {fh} {
    binary scan [read $fh 2] s* tmp
    return $tmp
 }

 Usage:

 IconStats myicons.ico

 Output:
 {16 16 4} {16 16 8} {16 16 32} {32 32 4} {32 32 8} {32 32 32} {48 48 4} {48 48 8} {48 48 32}

a list of values each one being a sublist with 3 elements, width height and color depth

 label .l2$x -image [GetIcon myicons.ico 0]

where 0 is an index of the icon you want to display (see IconStats)

Display all icons in a file:

set icons IconStats $argv for {set x 0} {$x < llength $icons} {incr x} {

    # catch in case there are any icons with unsupported color
    catch {
        label .l$x -text [lindex $icons $x]
        label .l2$x -image [getIcon $argv $x]
        grid .l$x .l2$x
    }

}


in the coming weeks i should be adding routines to read from .icl .exe and .dll files. as well as writing to all of the above

--AF 17-15-03