tool but then again, it's less than 1/25 the size.KPV 2003-03-06 : added some editing functions like shift, invert, clear and best of all, resize.HJG 2012-04-21 : changed file-extension from .bmp to .bm, to avoid confusing imageviewers. Added hotkeys F1..F8, version-number, set name before saving, demo-bitmap 16x16.HJG 2012-04-22 : Tiled preview 2x2 for bitmaps upto 32x32. It would be nice to also have a 3x3 display, but I don't see how to get the size of the bitmap at that part of the program.Stu 2015-04-20 The changes made by HJG were done to an older version than the bitmap editor included in TkLib.KPV 2016-05-22 : spent some time merging the forked code which Stu noticed. Most notable is that you can stroke lines with the mouse instead of toggling individual pixels. Also added a menu option to choose between 2x2, 3x3 or 4x4 preview like HJG wanted.For other editors of bitmaps, including those useful as .ico Windows icons, see this [1] April 2004 thread, which covers an interesting mix of shareware, Tk-based freeware, ...See also bme.
##+##########################################################################
#
# bitmap.tcl -- simple bitmap editor
# by Keith Vetter (http://wiki.tcl.tk/606)
#
# Revisions:
# KPV Jan 15, 2003 - initial revision
# KPV Mar 06, 2003 - added functions like shift, clear, invert and resize
# KPV Aug 20, 2010 - added mouse stroking, cursor, rename and undo
# HJG 2012-04-21 1.10 : .bmp --> .bm, F1,F2,F3, F5-F8, new bitmap 16x16
# HJG 2012-04-22 1.11 : Tiled preview: 2x2
# KPV May 13, 2016 - merged tklib version with wiki version; added undo for shift
# Syntax: bitmap-editor ?xbm-file?
package require Tcl 8.4
package require Tk
# Data Structures :: 3 global arrays.
#
# S - Application state and configuration
#
# * cell - Size of a pixel cel drawn in the UI
# * tileSize - NxN size for tiled preview
# * prog - Application name
# * version - Application version
# * fname - xbm file currently edited, after loaded/saved
# BM - Bitmap currently being edited.
#
# * b,<row>,<col> - UI cell storage - pixel is set if exists and 1.
# * bits - XBM Parse Result: hexadecimal bit string of pixels.
# * height - XBM Parse Result: bitmap height
# * name - XBM Parse Result: bitmap name
# * raw - xbm data of the bitmap, suitable for use with option
# -data of a Tk bitmap image. Generated from the b,*,*
# keys. See UnparseBMP.
# * width - XBM Parse Result: bitmap width
# * xhot - XBM Parse Result: column of the hot point
# * yhot - XBM Parse Result: row of the hot point
# UNDO - Undo information
#
# * current - current action
# * all - all previous actions
set S(cell) 10
set S(prog) "Bitmap Editor"
set S(version) 1.2
set S(fname) ""
set S(tileSize) 2
set UNDO(current) {}
set UNDO(all) {}
# Table for conversion from hexadecimal nibbles to binary bit
# strings. Used to convert BM(bits) to b,*,* keys in UnpackBits.
array set BITS {
0 0000 1 1000 2 0100 3 1100 4 0010 5 1010 6 0110 7 1110
8 0001 9 1001 a 0101 b 1101 c 0011 d 1011 e 0111 f 1111
}
# Initial bitmap to show upon starting.
set bitmap {
#define bm_width 16
#define bm_height 16
static char bm_bits = {
0x0f, 0xf0, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80,
0xc0, 0x03, 0x20, 0x04, 0x10, 0x08, 0x90, 0x09,
0x90, 0x09, 0x10, 0x08, 0x20, 0x04, 0xc0, 0x03,
0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x0f, 0xf0
}
}
set bitmap_tcl {
#define tcl_width 10
#define tcl_height 8
static char tcl_bits = {
0x00, 0x00, 0x9f, 0x00, 0x84, 0x00, 0xb4, 0x00,
0x94, 0x00, 0x94, 0x00, 0xb4, 0x03, 0x00, 0x00
}
}
set bitmap_bullet {
#define bullet_width 11
#define bullet_height 9
static char bullet_bits[] = {
0x00,0x00, 0x00,0x00, 0x70,0x00, 0xf8,0x00, 0xf8,0x00,
0xf8,0x00, 0x70,0x00, 0x00,0x00, 0x00,0x00
}
}
##+##########################################################################
#
# DoDisplay -- sets up our display
#
proc DoDisplay {} {
wm title . "$::S(prog) $::S(version)"
canvas .c -width 500 -height 500 -bd 2 -relief ridge -highlightthickness 0
.c xview moveto 0 ; .c yview moveto 0
#bind .c <2> [bind Text <2>] ;# Enable button 2 paning
#bind .c <B2-Motion> [bind Text <B2-Motion>]
set button_3 [expr {$::tcl_platform(os) eq "Darwin" ? "2" : "3"}]
bind .c <1> [list Click down ON %x %y]
bind .c <B1-Motion> [list Click move ON %x %y]
bind .c <ButtonRelease-1> [list Click up ON %x %y]
bind .c <${button_3}> [list Click down OFF %x %y]
bind .c <B${button_3}-Motion> [list Click move OFF %x %y]
bind .c <ButtonRelease-${button_3}> [list Click up OFF %x %y]
bind all <Control-z> Undo
bind . <F1> {About}
bind . <F2> {SaveBMP}
bind . <F3> {OpenBMP}
bind . <F5> {BitFunc sleft}
bind . <F6> {BitFunc sright}
bind . <F7> {BitFunc sup}
bind . <F8> {BitFunc sdown}
frame .fctrl -bd 2 -relief ridge
label .info -text "Left button to set Right button to clear" -anchor c \
-bd 2 -relief ridge
grid .c .fctrl -sticky news -row 0
grid .info ^ -sticky ew
grid rowconfigure . 0 -weight 1
grid columnconfigure . 0 -weight 1
DoControl
DoMenus
update
}
##+##########################################################################
#
# DoMenus -- sets up our menus
#
proc DoMenus {} {
. configure -menu [menu .m -tearoff 0]
.m add cascade -menu [menu .m.file -tearoff 0] -label "File" -underline 0
.m add cascade -menu [menu .m.image -tearoff 0] -label "Image" -underline 0
.m add cascade -menu [menu .m.help -tearoff 0] -label "Help" -underline 0
.m.file add command -label "New" -under 0 -command NewBMP
.m.file add command -label "Open" -under 0 -command OpenBMP
.m.file add command -label "Save" -under 0 -command SaveBMP
.m.file add separator
.m.file add command -label "Copy" -under 0 -command CopyBMP
.m.file add separator
.m.file add command -label "2x2 Preview" -under 0 -command {ShowTiledPreview 2}
.m.file add command -label "3x3 Preview" -under 1 -command {ShowTiledPreview 3}
.m.file add command -label "4x4 Preview" -under 2 -command {ShowTiledPreview 4}
.m.file add separator
.m.file add command -label Exit -under 1 -command exit
.m.image add command -label "Clear" -under 0 -command {BitFunc clear}
.m.image add command -label "Invert" -under 0 -command {BitFunc invert}
.m.image add separator
.m.image add command -label "Shift left" -under 6 -command {BitFunc sleft}
.m.image add command -label "Shift right" -under 6 -command {BitFunc sright}
.m.image add command -label "Shift up" -under 6 -command {BitFunc sup}
.m.image add command -label "Shift down" -under 6 -command {BitFunc sdown}
.m.image add separator
.m.image add command -label "Undo" -under 0 -command Undo
.m.image add command -label "Resize" -under 0 -command ResizeBMP
.m.help add command -label About -under 0 -command About
}
##+##########################################################################
#
# DoControl -- draws the control panel
#
proc DoControl {} {
grid rowconfigure .fctrl 0 -minsize 10
grid rowconfigure .fctrl 100 -weight 1
frame .fctop
grid .fctop - - -in .fctrl -row 1
set row 0
foreach a {Name Width Height} {
set a2 [string map {" " ""} [string tolower $a]]
label .l$a2 -text "$a:"
label .e$a2 -textvariable BM($a2) -width 8 -bd 2 -relief sunken
grid .l$a2 .e$a2 -in .fctop -row [incr row]
}
image create bitmap ::img::current
frame .fimg -bd 2 -relief sunken -padx 10 -pady 10
label .limg -image ::img::current -bg white
frame .f_tiled -bd 2 -relief sunken -padx 10 -pady 10
grid .fimg - - -in .fctrl -pady {50 0}
grid .f_tiled - - -in .fctrl -pady {20 0}
pack .limg -in .fimg -expand 1
}
##+##########################################################################
#
# ShowBMP -- computes grid size then draws in the current bitmap
#
proc ShowBMP {} {
global S BM
if {! [info exists BM(raw)]} return
set w [winfo width .c] ; set h [winfo height .c]
set cw [expr {int((($w - 20) / $BM(width)))}]
set ch [expr {int((($h - 20) / $BM(height)))}]
set S(cell) [expr {$cw < $ch ? $cw : $ch}]
if {$S(cell) > 40} { set S(cell) 40}
if {$S(cell) < 5} { set S(cell) 5}
DrawGrid
UnpackBits $BM(bits)
unset BM(bits)
RedrawBits
::img::current config -data $BM(raw) ;# Update current bitmap display
ShowTiledPreview $S(tileSize)
}
##+##########################################################################
#
# ShowTiledPreview -- draws the tiled version of the preview
#
proc ShowTiledPreview {size} {
global BM S
set S(tileSize) $size
set C .f_tiled.c
destroy $C
set w [expr {$size * $BM(width)}]
set h [expr {$size * $BM(height)}]
canvas $C -width $w -height $h -bd 0 -highlightthickness 0
pack $C
for {set row 0} {$row < $size} {incr row} {
set y [expr {$row * $BM(height)}]
for {set col 0} {$col < $size} {incr col} {
set x [expr {$col * $BM(width)}]
$C create image $x $y -image ::img::current -anchor nw
}
}
}
##+##########################################################################
#
# ClearBMP -- clears everything for a new bitmap
#
proc ClearBMP {} {
global BM
.c delete all
::img::current config -data {}
array unset BM
foreach arr [array names BM] { set BM($arr) ""}
set UNDO(current) {}
set UNDO(all) {}
}
##+##########################################################################
#
# DrawGrid -- draws the grid of rectangles--each one with proper bindings
#
proc DrawGrid {} {
global BM
.c delete all
foreach {l t} [CellXY 0 0] break
foreach {b r} [CellXY $BM(height) $BM(width)] break
.c create rect $l $t $b $r -tag outline -width 2
set bg [.c cget -bg]
for {set r 0} {$r < $BM(height)} {incr r} {
for {set c 0} {$c < $BM(width)} {incr c} {
.c create rect [CellXY $r $c] -tag [list grid c($r,$c)] -fill $bg
}
}
.c config -scrollregion [.c bbox all]
}
##+##########################################################################
#
# Click -- handles left and right mouse click in a grid cell
#
proc Click {action onoff x y} {
global BM UNDO
if {$action eq "up"} {
.c config -cursor [lindex [.c config -cursor] 3]
if {$UNDO(current) ne "" && [llength $UNDO(current)] > 2} {
lappend UNDO(all) $UNDO(current)
}
set UNDO(current) {}
return
}
if {$action eq "down"} {
.c config -cursor pencil
set UNDO(current) [list $onoff ":"]
}
# Here for button down and button motion
foreach {row col} [XY2Cell [.c canvasx $x] [.c canvasy $y]] break
if {$row < 0 || $row >= $BM(height) || $col < 0 || $col >= $BM(width)} return
if {$onoff == "ON"} { ;# Set the pixel
if {$BM(b,$row,$col)} return ;# Already set
set BM(b,$row,$col) 1
DrawOnBitForCell $row $col
} else { ;# Clear the pixel
if {! $BM(b,$row,$col)} return ;# Already cleared
set BM(b,$row,$col) 0
.c delete o($row,$col)
}
lappend UNDO(current) $row $col
set BM(raw) [UnparseBMP]
::img::current config -data $BM(raw)
}
##+##########################################################################
#
# Undo -- Implements undo
#
proc Undo {} {
global UNDO BM
set what [lindex $UNDO(all) end]
set UNDO(all) [lrange $UNDO(all) 0 end-1]
if {$what eq ""} return
set data [lassign $what action .]
if {$action eq "BITS"} {
UnpackBits $data
RedrawBits
} else {
set onoff [expr {$action eq "ON" ? "OFF" : "ON"}]
foreach {row col} $data {
if {$onoff == "ON"} { ;# Set the pixel
if {$BM(b,$row,$col)} continue ;# Already set
set BM(b,$row,$col) 1
DrawOnBitForCell $row $col
} else { ;# Clear the pixel
if {! $BM(b,$row,$col)} continue ;# Already cleared
set BM(b,$row,$col) 0
.c delete o($row,$col)
}
}
}
set BM(raw) [UnparseBMP]
::img::current config -data $BM(raw)
}
##+##########################################################################
#
# UnpackBits -- fills in the BM(b,*,*) array
#
proc UnpackBits {rawBits} {
global BM BITS
set row 0
set col 0
array unset BM b,*
foreach byte $rawBits {
foreach {n1 n2} [split $byte ""] break ;# Get each nibble
foreach bit [split "$BITS($n2)$BITS($n1)" ""] { ;# Note the endian
set BM(b,$row,$col) $bit
if {[incr col] >= $BM(width)} { ;# Do we past the last column?
incr row
set col 0
break
}
}
}
}
##+##########################################################################
#
# DrawOnBitForCell -- draws the "on" bit in a specified cell
#
proc DrawOnBitForCell {row col} {
.c create oval [CellXY $row $col] -fill black -tag [list bit o($row,$col)]
}
##+##########################################################################
#
# CellXY -- returns the coordinates of a grid cell
#
proc CellXY {r c} {
global S
set x1 [expr {10 + $c * $S(cell)}]
set y1 [expr {10 + $r * $S(cell)}]
set x2 [expr {$x1 + $S(cell)}]
set y2 [expr {$y1 + $S(cell)}]
return [list $x1 $y1 $x2 $y2]
}
##+##########################################################################
#
# XY2Cell -- returns cell based on canvas position
#
proc XY2Cell {x y} {
set c [expr {(int($x) - 10) / $::S(cell)}]
set r [expr {(int($y) - 10) / $::S(cell)}]
return [list $r $c]
}
##+##########################################################################
#
# ParseBMP -- reads the raw bitmap data into our BM data structure
# NB. face.bmp in demo directory had defines for x_hot and y_hot
#
proc ParseBMP {raw} {
global S BM
ClearBMP
set BM(raw) $raw
while {1} {
if {! [regexp {\#define\s+(.*)_width} $raw => BM(name)]} break
if {! [regexp {\#define\s+.*_width\s*(\d*)} $raw => BM(width)]} break
if {! [regexp {\#define\s+.*_height\s*(\d*)} $raw => BM(height)]} break
regexp {\#define\s+.*_x_hot\s*(\d*)} $raw => BM(xhot)
regexp {\#define\s+.*_y_hot\s*(\d*)} $raw => BM(yhot)
if {! [regexp {(0x.*)\}} $raw => BM(bits)]} break
if {! [regsub -all {0x|,} $BM(bits) { } BM(bits)]} break
if {! [regsub -all {\s+} $BM(bits) { } BM(bits)]} break
set BM(bits) [string tolower $BM(bits)]
return ;# Everything ok, get out
}
ERROR "$S(fname) is not a proper bmp file"
ClearBMP
}
##+##########################################################################
#
# OpenBMP -- opens and reads a BMP file
#
proc OpenBMP {} {
set types { {{BMP Files} {.bmp}} {{All Files} * }}
set fname [tk_getOpenFile -defaultextension ".bmp" \
-initialdir [file dirname $::S(fname)] \
-initialfile [file tail $::S(fname)] \
-filetypes $types]
if {$fname == ""} return
DisplayBMPFile $fname
}
proc DisplayBMPFile {fname} {
global S
if {[catch {set FIN [open $fname r]} emsg]} {
ERROR "Cannot open $fname\n$emsg"
return
}
set raw [read $FIN]
close $FIN
set S(fname) $fname
DisplayBMP $raw
}
proc DisplayBMP {data} {
ParseBMP $data
ShowBMP
}
##+##########################################################################
#
# SaveBMP -- saves the current bitmap to a file
#
proc SaveBMP {} {
global S BM
if {! [info exists BM(raw)]} return
set types {{{BMP Files} {.bmp}} {{All Files} *}}
set fname [tk_getSaveFile -defaultextension ".bmp" \
-initialdir [file dirname $S(fname)] \
-initialfile [file tail $S(fname)] \
-filetypes $types]
if {$fname == ""} return
if {[catch {set FOUT [open $fname w]} emsg]} {
ERROR "Cannot open $fname\n$emsg"
return
}
puts $FOUT $BM(raw)
close $FOUT
set S(fname) $fname
}
##+##########################################################################
#
# UnparseBMP -- converts our internal BM into a proper bitmap data string
#
proc UnparseBMP {} {
global BM
set name $BM(name)
set bmp "\#define ${name}_width $BM(width)\n"
append bmp "\#define ${name}_height $BM(height)\n"
if {[info exists BM(xhot)]} {
append bmp "\#define ${name}_x_hot $BM(xhot)\n"
append bmp "\#define ${name}_y_hot $BM(yhot)\n"
}
append bmp "static char ${name}_bits[] = \{\n"
# set bytes {}
# for {set r 0} {$r < $BM(height)} {incr r} {
# for {set c 0} {$c < $BM(width)} {incr c 8} {
# set byte 0
# for {set cc [expr {$c + 7}]} {$cc >= $c} {incr cc -1} {
# set byte [expr {2 * $byte}]
# if {[info exists BM(b,$r,$cc)] && $BM(b,$r,$cc)} {
# incr byte
# }
# }
# lappend bytes [format 0x%02x $byte]
# }
# }
# append bmp " " [join $bytes ", "]
set bytes [PackBits]
append bmp " 0x" [join $bytes ", 0x"]
append bmp "\n\}"
return $bmp
}
##+##########################################################################
#
# Converts BM(b,*,*) into hex bitmap
#
proc PackBits {} {
global BM
set bytes {}
for {set r 0} {$r < $BM(height)} {incr r} {
for {set c 0} {$c < $BM(width)} {incr c 8} {
set byte 0
for {set cc [expr {$c + 7}]} {$cc >= $c} {incr cc -1} {
set byte [expr {2 * $byte}]
if {[info exists BM(b,$r,$cc)] && $BM(b,$r,$cc)} {
incr byte
}
}
lappend bytes [format %02x $byte]
}
}
return $bytes
}
##+##########################################################################
#
# CopyBMP -- copies current bitmap to the clipboard
#
proc CopyBMP {} {
global BM S
if {! [info exists BM(raw)]} return
clipboard clear
clipboard append $BM(raw)
tk_messageBox -icon info -title "$S(prog) Info" \
-message "Bitmap copied to the clipboard"
}
##+##########################################################################
#
# NewBMP -- creates a blank, new bitmap with sizes specified by the user
#
proc NewBMP {} {
global BM
set n [NewDlg]
if {$n == {}} return
foreach {name width height} $n break
ClearBMP
set BM(name) $name
set BM(width) $width
set BM(height) $height
for {set r 0} {$r < $BM(height)} {incr r} {
for {set c 0} {$c < $BM(width)} {incr c} {
set BM(b,$r,$c) 0
}
}
ParseBMP [UnparseBMP]
ShowBMP
}
proc ResizeBMP {} {
global BMP
set n [NewDlg 1]
if {$n == {}} return
foreach {nName nwidth nheight} $n break
if {$nName ne ""} { set ::BM(name) $nName }
WidenBMP $nwidth
HeightenBMP $nheight
ParseBMP [UnparseBMP]
ShowBMP
}
proc WidenBMP {nwidth} {
global BM
if {$BM(width) == $nwidth} return
if {$nwidth > $BM(width)} {
foreach {low high delete} [list $BM(width) $nwidth 0] break
} else {
foreach {low high delete} [list $nwidth $BM(width) 1] break
}
for {set row 0} {$row < $BM(height)} {incr row} {
for {set col $low} {$col < $high} {incr col} {
set BM(b,$row,$col) 0
if {$delete} {
unset BM(b,$row,$col)
}
}
}
set BM(width) $nwidth
}
proc HeightenBMP {nheight} {
global BM
if {$BM(height) == $nheight} return
if {$nheight > $BM(height)} {
foreach {low high delete} [list $BM(height) $nheight 0] break
} else {
foreach {low high delete} [list $nheight $BM(height) 1] break
}
for {set col 0} {$col < $BM(width)} {incr col} {
for {set row $low} {$row < $high} {incr row} {
set BM(b,$row,$col) 0
if {$delete} {
unset BM(b,$row,$col)
}
}
}
set BM(height) $nheight
}
##+##########################################################################
#
# NewDlg -- asks the user for bitmap parameters
#
proc NewDlg {{resize 0}} {
global S NEW BM
destroy .new
toplevel .new -padx 10 -pady 5
wm title .new "New Bitmap"
if {$resize} {wm title .new "Resize Bitmap"}
wm geom .new "+[expr {[winfo x .] + 150}]+[expr {[winfo y .] + 100}]"
set NEW(ok) 0
set NEW(name) $BM(name)
set NEW(width) $BM(width)
set NEW(height) $BM(height)
frame .new.top -bd 2 -relief raised -padx 10 -pady 10
grid columnconfigure .new.top 1 -weight 1
set widgets {name width height}
foreach a $widgets {
set a1 [string totitle $a]
label .new.l$a -text "$a1:"
entry .new.e$a -textvariable NEW($a)
grid .new.l$a .new.e$a -in .new.top -sticky ew
}
frame .new.buttons
button .new.ok -text Ok -width 10 -command ValidForm
button .new.cancel -text Cancel -width 10 -command {destroy .new}
pack .new.buttons -side bottom -pady 10
pack .new.top -side top -fill x
pack .new.cancel .new.ok -in .new.buttons -side right -padx 10 -expand 1
raise .new
set w ".new.e[lindex $widgets 0]"
focus $w
$w icursor end
$w selection range 0 end
tkwait window .new
if {$NEW(ok)} {
return [list $NEW(name) $NEW(width) $NEW(height)]
}
return {}
}
##+##########################################################################
#
# ValidForm -- validates the NewDlg and then destroys it if it is ok
#
proc ValidForm {} {
global NEW
foreach n {name width height} {
set NEW($n) [string trim $NEW($n)]
if {$NEW($n) == ""} return
}
if {![string is integer $NEW(width)]} return
if {![string is integer $NEW(height)]} return
set NEW(ok) 1
destroy .new
}
proc ERROR {msg} {
tk_messageBox -icon error -title "$::S(prog) Error" -message $msg
}
proc About {} {
set msg "$::S(prog) $::S(version)\n\nby Keith Vetter\nJanuary 2003-2016"
tk_messageBox -title "About $::S(prog)" -message $msg -icon info
}
##+##########################################################################
#
# BitFunc -- handles shifts, inverts and clear bitmap operations
#
proc BitFunc {what} {
global BM
set currentBits [PackBits]
if {$what == "clear"} {
foreach arr [array names BM b,*] {
set BM($arr) 0
}
} elseif {$what == "invert"} {
foreach arr [array names BM b,*] {
set BM($arr) [expr {! $BM($arr)}]
}
} elseif {$what == "sleft"} { ;# Shift left
for {set col 0} {$col < $BM(width)} {incr col} {
set col2 [expr {$col + 1}]
for {set row 0} {$row < $BM(height)} {incr row} {
if {[info exists BM(b,$row,$col2)]} {
set BM(b,$row,$col) $BM(b,$row,$col2)
} else {
set BM(b,$row,$col) 0
}
}
}
} elseif {$what == "sright"} { ;# Shift right
for {set col [expr {$BM(width) - 1}]} {$col >= 0} {incr col -1} {
set col2 [expr {$col - 1}]
for {set row 0} {$row < $BM(height)} {incr row} {
if {[info exists BM(b,$row,$col2)]} {
set BM(b,$row,$col) $BM(b,$row,$col2)
} else {
set BM(b,$row,$col) 0
}
}
}
} elseif {$what == "sup"} { ;# Shift up
for {set row 0} {$row < $BM(height)} {incr row} {
set row2 [expr {$row + 1}]
for {set col 0} {$col < $BM(width)} {incr col} {
if {[info exists BM(b,$row2,$col)]} {
set BM(b,$row,$col) $BM(b,$row2,$col)
} else {
set BM(b,$row,$col) 0
}
}
}
} elseif {$what == "sdown"} { ;# Shift down
for {set row [expr {$BM(height) - 1}]} {$row >= 0} {incr row -1} {
set row2 [expr {$row - 1}]
for {set col 0} {$col < $BM(width)} {incr col} {
if {[info exists BM(b,$row2,$col)]} {
set BM(b,$row,$col) $BM(b,$row2,$col)
} else {
set BM(b,$row,$col) 0
}
}
}
}
lappend ::UNDO(all) [list BITS : {*}$currentBits]
set BM(raw) [UnparseBMP]
RedrawBits
::img::current config -data $BM(raw)
}
##+##########################################################################
#
# RedrawBits -- redraws screen based on BM(b,*,*) data
#
proc RedrawBits {} {
global BM
.c delete bit
for {set row 0} {$row < $BM(height)} {incr row} {
for {set col 0} {$col < $BM(width)} {incr col} {
if {$BM(b,$row,$col)} {DrawOnBitForCell $row $col}
}
}
}
proc MakePhotoImage {} {
global BM
set img [image create photo -width $BM(width) -height $BM(height)]
for {set x 0} {$x < $BM(width)} {incr x} {
for {set y 0} {$y < $BM(height)} {incr y} {
if {$BM(b,$y,$x)} {
$img put black -to $x $y [expr {$x+1}] [expr {$y+1}]
}
}
}
return $img
}
DoDisplay
if {[llength $argv] > 1} {
DisplayBMP $bitmap_bullet
ERROR "Too many files specified, expected only one"
} elseif {[llength $argv] == 1} {
DisplayBMPFile [lindex $argv 0]
} else {
DisplayBMP $bitmap_bullet
}
return
