# Plate 13.2: Tilings from Images ############################################################################ # # File: tilescan.icn # # Subject: Program to select tile from an image # # Author: Ralph E. Griswold # # Date: December 11, 1997 # ############################################################################ # # This file is in the public domain. # ############################################################################ # # This program is designed to assist in locating areas within an image # that, when tiled, produce a desired effect. For example, a background # may consist of a tiled image; this program can be used to find the # smallest tile for the repeat (by "eye-balling"). It's worth noting # that interesting images can be found for other settings. For example, # another interesting use of this program is to produce striped patterns by # selecting a row or column of an image to get a tile that is one character # wide. Sometimes a few rows or columns give an interesting "fabric" # effect. # # There are three windows: # # the VIB control window # the source image window # a repeat window, which shows the selection from the source # image, tiled. # # The selection from the source image is shown as a marquee in the # source image window. When a source image is loaded, the marquee starts # with the entire image. The marquee can be changed by buttons and # arrow-key events on the control window (not the source image window). # # The arrow keys have two modes. With no modifier, they nudge the # location of the marquee. With the meta-key modifier, they nudge # the dimensions of the marquee. # # The reset button resets the marquee to the entire image. # # The following features are provided through keyboard shortcuts, # the File menu and on-board buttons: # # @O open new source image # @R refresh tiling display # @Q quit application # @S save current selection # # The repeat window can be resized by the user, but it is not redrawn # until the marquee is changed or the refresh button is pushed. # ############################################################################ # # Requires: Version 9 graphics. # ############################################################################ # # Links: grecords, interact, tile # ############################################################################ # # Includes: keysyms.icn # ############################################################################ link grecords link interact link tile $include "keysyms.icn" # Globals related to windows: global controls # VIB control window global pattern # repeat window global screen # source image window visible global source # source image window hidden global posx # x position relative to interface window global posy # y position relative to repeat window global sx # marquee location information global sy # Globals related to the selection: global current # current selection record global hmax # maximum height of source image global wmax # maximum width of source image global previous # previous selection record global vidgets # table of interface vidgets procedure main() local atts, x1, y1 atts := ui_atts() put(atts, "posx=10", "posy=10", "gamma=1.0") controls := (WOpen ! atts) | ExitNotice("Cannot open control window.") vidgets := ui() init() repeat { while *Pending(controls) > 0 do ProcessEvent(vidgets["root"], , shortcuts) while *Pending(\screen) > 0 do if Event(screen) === &lpress then draw_marquee() } end # Callback that handles all the buttons that change x, y, w, and h. procedure dimens_cb(vidget, value) if /source then fail case value of { "w max": current.w := (wmax - current.x) "h max": current.h := (hmax - current.y) "w = 1": current.w := 1 "h = 1": current.h := 1 "full": { current.h := hmax current.w := wmax current.x := 0 current.y := 0 } } show() return end procedure draw_marquee() local x1, y1 current.x := &x current.y := &y current.h := current.w := 0 update() repeat { case Event(screen) of { &ldrag : update_marquee() &lrelease : { update_marquee() Raise(controls) return } } } end procedure update_marquee() if &x < 0 then &x := 0 if &y < 0 then &y := 0 if &x > wmax then &x := wmax if &y > hmax then &y := hmax current.w := &x - current.x current.h := &y - current.y show() return end procedure location_cb(vidget, value) if /source then fail case value of { "nw" : current.x := current.y := 0 "ne" : { current.x := wmax - current.w current.y := 0 } "se" : { current.x := wmax - current.w current.y := hmax - current.h } "sw" : { current.x := 0 current.y := hmax - current.h } "center" : { current.x := (wmax - current.w) / 2 current.y := (hmax - current.h) / 2 } } show() return end # Check validity of selection. procedure check() if (0 <= current.w <= (wmax - current.x)) & (0 <= current.h <= (hmax - current.y)) & (0 <= current.x <= hmax) & (0 <= current.y <= wmax) then return else { Alert() fail } end # Copy hidden source window to a visible window. procedure copy_source(label) screen := WOpen( "size=" || WAttrib(source, "width") || "," || WAttrib(source, "height"), "posx=" || posx, "posy=" || posy, "label=" || label, "drawop=reverse", "gamma=1.0", "linestyle=onoff" ) | ExitNotice("Cannot open image window.") CopyArea(source, screen) Raise(controls) wmax := WAttrib(source, "width") hmax := WAttrib(source, "height") EraseArea(pattern) current := rect(0, 0, wmax, hmax) show() return end # File menu callback. procedure file_cb(vidget, value) case value[1] of { "open @O": get_image() "quit @Q": exit() "save @S": save_cb() } return end # Utility procedure to get new source image. procedure get_image() WClose(\source) WClose(\screen) EraseArea(pattern) repeat { (OpenDialog("Open image:") == "Okay") | fail source := WOpen("canvas=hidden", "image=" || dialog_value, "gamma=1.0") | { Notice("Can't open " || dialog_value || ".") next } copy_source(dialog_value) wmax := WAttrib(source, "width") hmax := WAttrib(source, "height") break } return end # These values are for Motif; they may need to be changed for other # window managers. $define Offset1 32 $define Offset2 82 # Initialize the program. procedure init() local iheight posx := WAttrib(controls, "width") + Offset1 iheight := WAttrib(controls, "height") pattern := WOpen("label=repeat", "resize=on", "size=180,100", "gamma=1.0", "posx=" || posx, "posy=10") | ExitNotice("Cannot open pattern window.") posy := WAttrib(pattern, "height") + Offset2 sx := vidgets["text"].ax sy := vidgets["text"].ay Raise(controls) return end procedure update() # Update selection information on interface. WAttrib(controls, "drawop=reverse") DrawString(controls, sx, sy, "marquee: x=" || (\previous).x || " y=" || previous.y || " w=" || previous.w || " h=" || previous.h) DrawString(controls, sx, sy, "marquee: x=" || current.x || " y=" || current.y || " w=" || current.w || " h=" || current.h) WAttrib(controls, "drawop=copy") # Update the selection rectangle. DrawRectangle(screen, (\previous).x, previous.y, previous.w, previous.h) DrawRectangle(screen, current.x, current.y, current.w, current.h) previous := copy(current) return end procedure quit_cb() exit() end procedure refresh_cb() tile(source, pattern, current.x, current.y, current.w, current.h) return end # Callback procedure to allow use of standard tile sizes. procedure select_cb(vidget, value) if /source then fail check(current.w <- current.h <- case value of { " 4 x 4": 4 " 8 x 8": 8 " 16 x 16": 16 " 32 x 32": 32 " 64 x 64": 64 " 72 x 72": 72 " 96 x 96": 96 " 100 x 100": 100 " 128 x 128": 128 " 200 x 200": 200 " 256 x 256": 256 " 400 x 400": 400 " 512 x 512": 512 }) | fail show() return end # Callback to allow setting of specific selection rectangle values. procedure set_cb() repeat { if TextDialog("Set values:", ["x", "y", "w", "h"], [current.x, current.y, current.w, current.h ] ) == "Cancel" then fail check( current.x <- integer(dialog_value[1]) & current.y <- integer(dialog_value[2]) & current.w <- integer(dialog_value[3]) & current.h <- integer(dialog_value[4]) ) | { Notice("Invalid value.") next } show() return } end # Keyboard shortcuts. procedure shortcuts(e) case type(e) of { "string": { if &meta then case map(e) of { # fold case "o": get_image() "q": exit() "r": refresh_cb() "s": save_cb() } } "integer": { if &meta then { # nudge dimensions if check( case e of { Key_Left: current.w <- current.w - 1 Key_Right: current.w <- current.w + 1 Key_Up: current.h <- current.h - 1 Key_Down: current.h <- current.h + 1 } ) then show() else fail } else { # nudge location if check ( case e of { Key_Left: current.x <- current.x - 1 Key_Right: current.x <- current.x + 1 Key_Up: current.y <- current.y - 1 Key_Down: current.y <- current.y + 1 } ) then show() else fail } } } return end # Procedure to handle all that goes with a new selection. procedure show() local x, y, w, h if /source then { Notice("No source image.") fail } x := current.x y := current.y w := current.w h := current.h if w < 0 then x -:= (w := -w) if h < 0 then y -:= (h := -h) tile(source, pattern, x, y, w, h) update() return end # Utility procedure to save current selection. procedure save_cb() return snapshot(\source, current.x, current.y, current.w, current.h) | { Notice("No source image.") fail } end #===<>=== modify using vib; do not remove this marker line procedure ui_atts() return ["size=446,201", "bg=gray-white"] end procedure ui(win, cbk) return vsetup(win, cbk, [":Sizer:::0,0,446,201:",], ["dim:Label:::209,34,70,13:dimensions",], ["dimens:Choice::5:213,56,64,105:",dimens_cb, ["full","w max","h max","w = 1","h = 1"]], ["file:Menu:pull::0,1,36,21:File",file_cb, ["open @O","save @S","quit @Q"]], ["line1:Line:::0,22,445,22:",], ["loc:Label:::120,34,56,13:location",], ["location:Choice::5:113,55,71,105:",location_cb, ["nw","ne","se","sw","center"]], ["refresh:Button:regular::17,88,58,20:refresh",refresh_cb], ["select:Choice::6:309,55,99,126:",select_cb, [" 32 x 32"," 64 x 64"," 72 x 72"," 96 x 96"," 128 x 128", " 256 x 256"]], ["set:Button:regular::17,51,58,20:set",set_cb], ["size:Label:::341,34,28,13:size",], ["text:Button:regularno::17,347,10,20:",], ) end #===<>=== end of section maintained by vib