HELP PWMRASTERS                                 Ben Rubinstein, Mar 1987
                                             Revised: Nic Ford, Apr 1987
                                        Revised: Gareth Palmer, Aug 1989

The exchange of bit-image patterns between a PWM surface and POPLOG or
a file.

Keywords: PWM, raster, dump, read, bit-image.

    pwm_make_rasterarray(<list:B>, <integer:D>) -> <array>

    pwm_rasterdepth(<array>) -> <integer>

    pwm_dump_raster(<integer:X>, <integer:Y>, <array>)

    pwm_load_raster(<integer:X>, <integer:Y>,
                        <integer:W>, <integer:H>) -> <array|false>

    pwm_write_rasterfile(<integer:Sx>, <integer:Sy>,
                          <integer:W>, <integer:H>, <string>)

    pwm_read_rasterfile(<integer:Sx>, <integer:Sy>,
                        <integer:W>, <integer:H>, <string>,
                        <integer:Dx>, <integer:Dy>)
    pwm_read_rasterfile(<false>, <string>, <integer:Dx>, <integer:Dy>)

    sunrasterfile(<string>) -> <array>
    <array> -> sunrasterfile(<string>)

    suniconfile(<string>) -> <array>
    <array> -> suniconfile(<string>)

== CONTENTS - (Use <ENTER> g to access required sections) ==

 -- Introduction - representing a raster in POPLOG
 -- Dumping a raster from POPLOG to the screen
 -- Loading a raster from the screen to POPLOG
 -- Writing an image from the screen to a disk file, and vice-versa
 -- Reading and writing rasterfiles from POPLOG
 -- Notes for Common Lisp users

-- Introduction - representing a raster in POPLOG ---------------------

This file assumes that you have read the file HELP *PWMGRAPHICS.  You
should also have read the files HELP *PWMWINDOWS which describes how to
make windows, and HELP *PWMGENERAL which gives general information
needed for programming with the Poplog Window Manager.

A bit-image pattern, known as a raster, is a two dimensional array of
pixels used to represent a pattern which could be seen within a PWM
window. Each pixel is an integer representing the colour which would be
seen on screen. Each pixel is said to have a "depth", which is the number
of bits (BInary digiTS - basically, a sort of numerical switch which can
hold 0 or 1) allocated to each pixel integer, which thus decides the
range of integers each pixel can hold. The depth of a pixel can
therefore be thought of as the number of colours that pixel can choose
from represented as a power of two. A monochrome screen has a depth of
one, so each pixel can have one of a choice of 2**1 = 2 colours
(generally speaking, these are black and white) - likewise, a screen
with a depth of eight has a choice of 2**8 = 256 colours per pixel.
Depths will be one of the numbers 1, 2, 4 or 8, representing 2, 4, 16 or
256 colours per pixel respectively.

A raster array (that is, an array as normally found under POPLOG, but
tailored to represent a raster) can be built by using the procedure
-pwm_make_rasterarray-. This takes two arguments - a boundslist as for a
normal array (see HELP *NEWARRAY) and a depth:

    pwm_make_rasterarray(<list:B>, <integer:D>) -> <array>

For example,

    vars myras = pwm_make_rasterarray([0 15 0 21], 1);

creates and assigns to -myras- an array suitable for representing a
monochrome raster sixteen pixels wide and 22 pixels high, and
initialises all elements (pixels) to zero.

NB. -pwm_screendepth- can be used to find out the correct pixel depth of
the current screen. See HELP * PWMGENERAL for details.

Elements of the array can then be acessed (as with any other array) as
if it were a procedure taking two arguments, the horizontal and vertical
indicies; thus

    myras(7, 0) =>
    ** 0

and the following code would "draw" a box round the edge of the array,
by setting all the pixels around the edge to 1:

    vars x y;
    for x from 0 to 15 do 1 ->> myras(x, 0) -> myras(x, 21) endfor;
    for y from 1 to 20 do 1 ->> myras(0, y) -> myras(15, y) endfor;

    myras(7, 0) =>
    ** 1

The bounds of the array need not start at zero: this procedure would
create an array of the identical dimensions to the one above, but which
would expect different indices:

    vars myras2 = pwm_make_rasterarray([10 25 1 22], 1);

Printing an array normally just displays its boundslist; if you want to
check the depth of an array you can use the function -pwm_rasterdepth-,
which is defined when the function -pwm_make_rasterarray- is loaded:

    pwm_rasterdepth(<array>) -> <integer>

This function will cause a mishap if the argument array is not of a type
suitable for use with the PWM: any array created by
-pwm_make_rasterarray- will be acceptable, and some others may be also -
see the technical note below for more details.

A special note for Common Lisp users: -pwm_make_rasterarray- with depth
1 is normally compatible with a Common Lisp "bitvector" (but see below),
so you can use the normal Common Lisp functions on an array created by
-pwm_make_rasterarray-.

Note for users of the PWM in version 12: the old routine for dumping a
raster required the raster-data in a specially formatted string.  If
your code has some of these strings around they can be converted to
arrays by using a special form of the -pwm_make_rasterarray- call:

    pwm_make_rasterarray(<list:B>, <integer:D>, <string>)

The string must be the right size for the width and height effectively
specified by the boundslist B.  Note that although this conversion is
reasonably fast, the string is being copied, so change your code to
using arrays as soon as possible.

Technical note, for experts only: -pwm_make_rasterarray- effectively
does the equivalent of -vectorclass- on the depth argument, and then
-newanyarray- on the resulting key and boundslist.  If for some reason
you wish to use your own arrays with the procedures below which accept
arrays created by -pwm_make_rasterarray-, you must note however that the
data in the array is arranged so that each row starts on a byte
boundary.  Thus any array with a -class_spec- of 1, 2, 4 or 8 will work
provided the product of the width and the depth is a multiple of eight.

-- Dumping a raster from POPLOG to the screen -------------------------

To dump a bit-image on to the current graphics surface (-pwmgfxsurface-)
the following function can be used:

    pwm_dump_raster(<integer:X>, <integer:Y>, <array>)

The arguments X and Y specify, in pixels from the top-left corner of the
surface, the point to which the top-left corner of the image will be
dumped.  The current raster-op -pwmrasterop- is used: thus if this
is -PWM_SRC- the image will obliterate anything under it, while if it is
PWM_XOR then wherever a pixel in the image is set, the corresponding
pixel on the surface will be inverted; all other pixels will be
unaffected.  You can dump a raster to any kind of window or to a page.

This function and the PWM can cope with remote logins (i.e. if the PWM
is running on one machine, connected to POPLOG is running on another)
and with rasters of different depth from the screen.  Rasters of depth
greater than the workstation screen will have each pixel shifted down so
that the top bits are used; rasters of depth less than the screen will
have each pixel padded with zeroes at the top end.

Thus a monochrome (depth = 1) raster can be dumped to a screen of any
depth, and each pixel will be either zero or one; and an eight-bit
raster, for example, can be dumped to a monochrome screen, and each
pixel on the screen will be set according to the top bit in the
corresponding pixel from the raster.  This latter facility is of no use
if the raster contained real colour information, but in that case there
would be no sensible way for the PWM to convert to a monochrome image;
but if the raster is actually a grey-level image, it produces a sensible
result. Note that this conversion is done by the PWM: so in the case of
sending a 'colour' raster to a monochrome screen, it is much slower than
sending the monochrome raster that would produce the same image.

-- Loading a raster from the screen to POPLOG -------------------------

You can read a raster from the current graphics surface into an array in
POPLOG:

    pwm_load_raster(<integer:X>, <integer:Y>,
                        <integer:W>, <integer:H>) -> <array|false>


The raster is selected from the top left corner at X,Y in pixels from
the top left corner of the surface, with maximum width and height W and
H in pixels.  (The actual dimensions of the array returned may be less
than this if the rectangle specified by X, Y, W and H extended beyond
the edges of the surface.)

Because this function involves transferring binary data over a tty line,
you may occasionally get garbage, especially on a very large or very
small raster.


-- Writing an image from the screen to a disk file, and vice-versa ----

Dumping or reading an image between POPLOG and a PWM is relatively slow,
because of the narrow channel through which the two communicate.  In
many cases, your program may want to load a pre-defined image into a
window or page without processing it in POPLOG (and perhaps then alter
it by drawing lines etc.).  The PWM has functions which instruct it to
save an image to, or load an image from, a disk file (a "rasterfile") on
the workstation on which it is running.

    pwm_write_rasterfile(<integer:Sx>, <integer:Sy>,
                          <integer:W>, <integer:H>, <string>)

This function instructs the PWM to save the portion of the current
graphics surface specified by top-left corner at (Sx, Sy) and width and
height W and H, into the file identified by the string argument (see
below).  If W is zero, the image is taken to from Sx to the right hand
edge of the surface; similarly if H is zero, it is taken from Sy to the
bottom edge of the surface.  Thus

    pwm_write_rasterfile(0, 0, 0, 0, 'foo')

will always save the whole of the current graphics surface to a file
called 'foo' regardless of the size of the surface.  The image is always
saved exactly, regardless of the value of -pwmrasterop-.

    pwm_read_rasterfile(<integer:Sx>, <integer:Sy>,
                        <integer:W>, <integer:H>, <string>,
                        <integer:Dx>, <integer:Dy>)
    pwm_read_rasterfile(<false>, <string>, <integer:Dx>, <integer:Dy>)

This function instructs the PWM to load an image defined in the file
specified by the string argument into the current graphics surface
-pwmgfxsurface-, using the raster-op -pwmrasterop-, so that its top-left
corner is at position (Dx, Dy) in the surface.

In the first form, the four integer arguments preceding the string
argument specify a portion of the image defined in the file which is to
be copied on to the surface: Sx and Sy specify the top-left corner of
the portion of the image which is to be used, in pixels from (0, 0)
representing the top-left of the image in the rasterfile, and W and H
the width and height of the portion.  If W is zero, the portion extends
to the right-hand edge of the image defined in the rasterfile; if H is
zero the image extends to the bottom edge.  In any case W and H will be
clipped to the boundaries of the image defined in the rasterfile.

The second form specifies that the whole of the image defined in the
rasterfile is to be used: it is thus exactly equivalent to the first
form with arguments Sx, Sy, W and H all zero.

It is important to note, for both the above functions, that the string
specifying the file is interpreted by the PWM, and that logical or
environment variables will not be understood by it. It is thus safest
always to use an absolute pathname for the file, unless you know which
directory the PWM was started in; no reference is made to POPLOG's
notion of the current directory.  If the PWM and POPLOG are running on
the same machine however you can use the POPLOG function -sysfileok- to
expand variables within the string before passing it to one of the
functions mentioned above (use <ENTER>?? sysfileok<RETURN> for details),
thus

    pwm_write_rasterfile(0, 0, 0, 0, sysfileok('~/trash/pic99'));

will write the whole of the current surface into a rasterfile called
"pic99" in the subdirectory "trash" of the current user's top-level
directory - provided the subdirectory does exist, and ONLY if the
POPLOG process is running on the same machine as the PWM.  (If you only
want the file for the duration of a session, and the PWM is running on a
Unix machine, you might find it easiest to use the "/tmp" directory.)

Finally, a perhaps obvious point which it is possible to forget is that
the file is read or written on the workstation on which the PWM is
actually running (e.g. if POPLOG is running on a Vax remotely accessed
through a PWM on a Sun, the file will be read or written on the Sun).

-- Reading and writing rasterfiles from POPLOG ------------------------

The format of the rasterfiles which the PWM can read or write is not a
part of the PWM specification: rather it is intended that the PWM for a
particular workstation will use any format that is standardly defined
for that workstation.  Thus the Sun PWM (at the time of writing
the only one supporting full graphics facilities) uses the Sun-defined
format documented in MAN 5 RASTERFILE, so it is compatible with e.g. the
"screenload" command.  There is a library which allows a POPLOG program
to read or write raster-arrays directly to files in this format: when
other PWM's have been produced which use different formats, it is
intended that similar libraries will be produced for these formats.

    sunrasterfile(<string>) -> <array>
    <array> -> sunrasterfile(<string>)

Although this library is especially useful on a Sun, when it can be used
to read into POPLOG an image written by a PWM, or vice-versa, it is also
a perfectly good way to save a raster-array from POPLOG on any machine.

On Sun's only, there is also a library (in $popsunlib) which can read or
write a raster in the format used by Sun's "icontool":

    suniconfile(<string>) -> <array>
    <array> -> suniconfile(<string>)

Finally, an efficiency note for Sun users.  If you are running both
POPLOG, and the PWM through which it is being accessed, on the same
machine, then because of the narrow channel through which the PWM and
POPLOG communicate, writing a very large raster to disk with
-sunrasterfile-, and then instructing the PWM to read it off the disk
and onto a surface with -pwm_read_rasterfile- can be very considerably
faster than dumping it directly with -pwm_dump_raster-.  In the future
it is expected that special arrangements for the "local PWM" case
will make -pwm_dump_raster- faster still; but for the present, you
could find procedures like the following could be useful:

    define fast_local_dump(x, y, array);
        lvars x y array;
        lconstant tmpfilename = '/tmp/pictemp' >< poppid;
        array -> sunrasterfile(tmpfilename);
        pwm_read_rasterfile(false, tmpfilename, x, y);
    enddefine;

    define fast_local_load(x, y, w, h) -> array;
        lvars x y w h array;
        lconstant tmpfilename = '/tmp/pictemp' >< poppid;
        pwm_write_rasterfile(x, y, w, h, tmpfilename);
        sunrasterfile(tmpfilename) -> array;
    enddefine;

Using this facility makes the program considerably less portable, and
also depends on there being enough space in the "/tmp" directory of your
machine (and requires that you have aprropriate access permissions etc.)
It will also make your programs less future-proof, since as noted above
it is expected that developments in the near future will make the
ordinary -pwm_dump_raster- routine faster than the -fast_local_dump-
routine defined above: so you should probably only make use of this if
you have very large rasters to dump, or are especially concerned about
speed.

-- Notes for Common Lisp users ----------------------------------------

The arrays created by -pwm_make_rasterarray- store their data in column
major order, which means that elements of a row are stored in adjacent
memory locations. Common Lisp arrays (as created by MAKE-ARRAY etc), use
the opposite format, row major order, where the elements of a column are
stored in adjacent memory locations. Therefore Common Lisp array
processing functions will not always work as expected on raster arrays.
This will only matter if it is necessary to process the rasters in
Common Lisp. If so, it is possible to to make raster arrays that use the
same format as Common Lisp, at the cost of decreased efficiency and
convenience. For this reason, users are strongly advised not to bother,
and to do their array processing in POP11.

To ensure Common Lisp format raster arrays, the POP11 variable
'poparray_by_row' must be set <false> whenever an array is created.
For example:

    (defun make-pwm-raster-array (&rest args)
        (let ((pop11::poparray_by_row pop11::false))
            (declare (special pop11::poparray_by_row))
            (apply #'pop11:pwm_make_rasterarray args)))

Similar 'wrapper' procedures could be defined for reading rasters from
disk, and reading rasters from a graphics surface.

There is no need to set -poparray_by_row- <false> when dumping to disk
or to the screen. Note: a Common Lisp format array which has been
written to disk can *not* be dumped directly to a window, because the
PWM routine that handles this assumes column major format.


--- C.pwm/help/pwmrasters --------------------------------------
--- Copyright University of Sussex 1989. All rights reserved. ----------
