############################################################################ # # File: bitplane.icn # # Subject: Procedures for bitplane manipulation # # Author: Gregg M. Townsend # # Date: May 2, 2001 # ############################################################################ # # This file is in the public domain. # ############################################################################ # # These procedures allow a window to be treated as a series of # overlapping, independent layers, subject to some fairly severe # restrictions. # # AlcPlane(W n) allocates planes. # # FrontPlane(W bp, color) moves a layer to the front. # # BackPlane(W bp, color) moves a layer to the back. # # PlaneOp(W bp, op) initializes layer operations. # # Deplane(W color) restores a window to normal. # ############################################################################ # # These procedures allow drawing and erasing in individual bitplanes of # a window. One way to use bitplanes is to think of them as transparent # panes in front of a solid background. Each pane can be drawn with a # single color, obscuring the panes beyond (and the background). A pane # can also be erased, wholly or selectively, exposing what is beyond; and # a pane need not be visible to be drawn or erased. Panes can be restacked # in a different order, and the color of a pane (or the background) can be # changed. # # For example, the pane in back could be drawn with a city map. The # pane in front of that could be used to lay out bus routes, and the paths # could be erased and rerouted without having to redraw the map. Using a # third plane in front of those, buses could be moved along the routes # without having to redraw either the routes or the map behind them. # # Bitplanes that are allocated together and interact with each other # form a bitplane group. A bitplane group need not fill the window; # indeed, it can be used in discontiguous portions of a window or even # in multiple windows on the same display. On the other hand, multiple # bitplane groups can be used different parts of the same window. # # Bitplanes are implemented using Icon's mutable colors, and they # are gluttonous of color map entries. A set of n bitplanes requires # at least 2^n color map entries, so the practical limit of n is 5 or 6. # On the other hand, sets of 2 or 3 bitplanes are relatively cheap and # using several of them is not unreasonable. # # Each bitplane group is identified by a base index b, which is the # index of the mutable color representing the background. The individual # bitplanes are referenced as b+1, b+2, b+4 etc. using powers of two. # Other indices between b and b+2^n (exclusive) control the colors used # used when multiple bitplanes are drawn. The FrontPlane and BackPlane # procedures provides simple control of these, and more sophisticated # effects (such as making a bitplane partially transparent) are possible # by setting them individually. # # # # AlcPlane([win,] n) -- alc colors for n bitplanes # # AlcPlane allocates a set of 2^n mutable colors chosen to be suitable # for the bitplane manipulations described below. The colors are # consecutively indexed, and the base value b (the most negative index # value) is returned. The base color is initialized to the current # background color, and the others are initialized to the foreground color. # # A sequence of AlcPlane calls with different values of n is more # likely to succeed if the larger sets are allocated first. # # # # FrontPlane([win,] bp, color) -- move indexed plane to "front" # # FrontPlane sets the pixels in a bitplane to the given color and # moves the bitplane in front of the others in the set. The color is # optional. # # bp is the index (base+1, base+2, base+4, or whatever) denoting a # particular bitplane. The move-to-the-front effect is accomplished by # calling Color() for all colors in the bitplane group whose index # after subtracting the base includes the particular bit. # # # # BackPlane([win,] bp, color) -- move indexed plane to "back" # # BackPlane sets the pixels in a bitplane to the given color and # moves the bitplane in back of the others in the set. The color is # optional. # # bp is the index (base+1, base+2, base+4, or whatever) denoting a # particular bitplane. The move-to-the-back effect is accomplished by # calling Color() for all colors in the bitplane group whose index # after subtracting the base includes the particular bit. # # A plane can be effectively rendered invisible by calling # BackPlane(win, bp, base); this moves it to the back and sets # its color to the color of the background plane. # # # # PlaneOp([win,] bp, op) -- set graphics context for plane operation # # PlaneOp initializes the graphics context for drawing or erasing in # a particular bitplane. bp is a bitplane index, as for FrontPlane; # multiple bits can be set to draw or erase several bitplanes # simultaneously. op is usually one of two strings: # # "set" to draw the bits in a bitplane # "clear" to erase the bits in a bitplane # # Subsequent drawing operations will affect only the bits in the selected # bitplane. Foreground operations are used for both drawing and erasure: # use FillRectangle, not EraseArea. # # After calling PlaneOp with "set" or "clear", be SURE to draw only # in portions of the screen previously initialized with pixel values # from the same bitplane group. Drawing anywhere else is liable to # produce strange, unwanted results. Deplane (below) resets the window # for normal operation. # # The op parameter can also be "copy", in which case the previous # contents of the window are immaterial and the drawn pixels are # initialized with the bitplanes specified. # # # Deplane([win,] color) -- restore normal drawop and set foreground # # Deplane is called to restore normal drawing operations after setting # or clearing bits in a particular bitplane. The foreground color can be # changed optionally. # # # # Example: # # b := AlcPlane(win, 3) # get 3 planes # Color(win, b, "white") # background will be white # FrontPlane(win, 1, "gray") # city map will be gray # FrontPlane(win, 2, "navy") # routes will be dark blue # FrontPlane(win, 4, "red") # buses will be red # Fg(win, b) # DrawRectangle(win, x, y, w, h) # initialize background # PlaneOp(win, b+1, "set") # drawmap() # draw map # repeat { # PlaneOp(win, b+2, "clear") # DrawRectangle(x, y, w, h) # clear old routes # PlaneOp(win, b+2, "set") # drawroutes() # draw new routes # while routes_ok() do # runbuses() # run buses using plane b+4 # } # # # # Caveats # # AlcPlane must repeatedly ask for new mutable colors until it gets a # set that is suitable. Unwanted colors cannot be returned or freed, so # some color table entries are usually wasted. # # No more than 7 bitplanes can be requested, and even that is chancy. # # These routines will be confused by multiple displays. Multiple # windows on a single display, or multiple bitplane sets in a window, # are no problem. # # These routines depend on the internals of Icon, specifically the # mapping of window-system pixel values to mutable color indices. # # The use of unusual "and" and "or" drawops makes the code hard to # understand. # ############################################################################ # # Requires: Version 9 graphics # ############################################################################ global Plane_Mask # AlcPlane(win, n) -- allocate 2^n colors for bitplanes and return base b procedure AlcPlane(win, n) #: allocate colors for bitplane local ncolors, mask, b, seqlen, prev, fg, clist if type(win) ~== "window" then { n := win win := &window } if n < 1 | n > 7 then runerr(205, n) fg := Fg(win) ncolors := 2 ^ n mask := ncolors - 1 # need to get ncolors colors in sequence, with the last one having the # low order n bits (of the actual pixel value) set # alternatives on Color are in case current fg/bg would cause failure b := NewColor(win, fg | "black") | fail clist := [b] seqlen := 1 while seqlen < ncolors | iand(-1 - b, mask) ~= mask do { prev := b b := NewColor(win, fg | "black") | fail push(clist, b) if prev - b ~= 1 then seqlen := 1 else seqlen +:= 1 } # discard unwanted colors every 1 to ncolors do pop(clist) if *clist > 0 then { push(clist, win) FreeColor ! clist } # set base color to background and return result Color(win, b, Bg(win) | "white") /Plane_Mask := table() every Plane_Mask [b to b + mask] := mask return b end # FrontPlane(win, bp, color) -- move indexed plane to "front", set color procedure FrontPlane(win, bp, color) #: move bitplane to front local mask, base, bits, i if type(win) ~== "window" then { win :=: bp :=: color win := &window } mask := \Plane_Mask[bp] | runerr(205, bp) base := iand(icom(mask), bp) bits := bp - base /color := bp every i := base to base + mask do if iand(i, bits) = bits then Color(win, i, color) return win end # BackPlane(win, bp, color) -- move indexed plane to "back", set color procedure BackPlane(win, bp, color) #: move bitplane to back local mask, base, bits, i if type(win) ~== "window" then { win :=: bp :=: color win := &window } mask := \Plane_Mask[bp] | runerr(205, bp) base := iand(icom(mask), bp) bits := bp - base Color(win, bp, \color) # set color if specified every i := base to base + mask do if iand(i, bits) = bits & i ~= bp then Color(win, i, ixor(i, bits)) # set color as if plane unset return win end # PlaneOp(win, bp, op) -- set graphics context for plane operation procedure PlaneOp(win, bp, op) #: set context for bitplane operation local mask, base, bits, i if type(win) ~== "window" then { win :=: bp :=: op win := &window } mask := \Plane_Mask[bp] | runerr(205, bp) base := iand(icom(mask), bp) bits := bp - base case op of { "copy": { WAttrib(win, "drawop=copy") Fg(win, bp) } "set": { i := base + bits WAttrib(win, "drawop=and") Fg(win, i) } "clear": { i := base + (mask - bits) WAttrib(win, "drawop=or") Fg(win, i) } default: runerr(205, op) } return win end # Deplane(win, color) -- restore normal drawop and set fg to color procedure Deplane(win, color) if type(win) ~== "window" then { color := win win := &window } WAttrib(win, "drawop=copy") Fg(win, \color) return win end