
        [42m                                            [40m
        [42m   Reading the unreadable : Using ARexx     [40m
        [42m   to extract information from ILBM files   [40m
        [42m                                            [40m   [33;3mby John Collett[0m
        [42m                 PLUS                       [40m     
        [42m                                            [40m
        [42m  Iffy :  A small (and limited) IFF viewer  [40m
        [42m                                            [40m



      [32m:-)  40  :^)  40  :)  40  8)  40  8-)  40  B-)  40  :-O  40  8*)[0m

 
    [42m Contents [40m
    
    1     Extracting brush dimensions from an IFF file
    
    2     Extracting other information
    
    3     Using this information to drive a image displayer
    
    An ARexx program derived from Item 3 is attached, but it is very
    restricted in what it can do, and produces odd results if applied to
    image files which are beyond its limited range.  I've called it all
    sorts of things while I've been writing it.  Finally, I've called it
    'Iffy'.

    
    [42m Acknowledgements [40m
    
    I ran out of knowledge and patience while working on this, but
    the following documents and programs helped :

    · [4mAbout ILBM[0m, Docs on the IFF format, by Carolyn Scheppner 
      (Fish Disk 185)
    
    · [4mAmiga Rom Kernel Reference Manual[0m, Includes and Autodocs, pages I-1
      to I-164.  This is a very curious collection, full of repetitions,
      omissions, and admissions, and on the whole about as clear as mud,
      though a few paragraphs gave vital information.
    
    · [4mWhatIs[0m, by J.Tyberghein (MD UT90)
    
    · [4mNewZAP3.18[0m, by John Hodgson (MD UT151)

    
    [42m Setting the scene [40m

    In the middle of writing an ARexx program, I may want to include a
    function call which will place an image in a particular position
    on the screen.  'Rexxarplib.library' will be on line, and so I can use

          [32mcall IFFImage(port, x, y, width, height, flag)[31m
                         1    2  3    4      5       6
    
    This function call is central to what follows, so we had better clarify
    those six arguments :
    
    1     The name assigned to the port which was created by a
          preceding call to the CreateHost() function.
    2, 3  The x any y coordinates of the upper left corner of the
          proposed image position.
    4, 5  The size of the chunk to be displayed.
    6     From the library docs :
    
    "The colormap of the IFF file, if any, is NOT used on the WorkBench,
    but IS used on custom screens. The 'flag' option can be set to
    'NOCOLOR' (N is sufficient) to prevent modifying the custom screen's
    colors."
    
    All arguments except 1 may be omitted.  The docs say that if any of 2
    to 5 are omitted (just marking their place by a comma if necessary),
    suitable defaults are chosen.

    Items 4 and 5 were the catalyst which set me off on my most recent
    explorations, and eventually led to the writing of the accompanying
    ARexx program 'Iffy'.  The file you are now reading is an account of
    the process.

    When placing an image I usually want to display all of it - no more and
    no less.  So I need to be able to specify the width and height of the
    image accurately, but unless I happen to have noted them at the time
    the image was created, or obtained them by some other method, I have
    only a rough idea of the dimensions, and that information did not
    appear to be readily available.

    
    [42m One solution [40m

    'WhatIs' provides various pieces of information on any file, depending
    on the arguments attached to the command.

     [32mWhatIs <File> [PAGE <pglen>] [WHATIS | SHORT | MEDIUM | FULL | ...[31m 
     [32m   ... DIAG | IDENT | ASCII <len>] [PATH] [NOANSI][31m
    
    The file holding this growing text is currently called 'Article', and
    when, just now, I entered 'WhatIs Article Short', I obtained :

       [32m File         : Article[31m 
       [32m   DirEntryType : -3   [31m 
       [32m   Protect      : rwed [31m 
       [32m   Size         : 2857       NumBlocks : 3     [31m 
       [32m   Last changed : 07-Apr-94 12:43:02 (Thursday)[31m 
       [32m   Comment      :  [31m 
       
       [32m ± 96 % ASCII chars[31m 
    
    If I use 'WhatIs' on an image file, even with the 'Short' argument, I
    get something like the above, and then a whole lot more, of which here
    are the first few lines :

       [32mIFF file                              [31m
       [32mFORM  ILBM   (2646 data bytes)        [31m
       [32m  Interleaved BitMap                  [31m 
       [32mBMHD                                  [31m
       [32m  width   :    70     height  :    96 [31m  <<------
       [32m  x       :     0     y       :     0 [31m
       [32m  nPlanes :     4     Masking :     2 [31m
       [32m  Compression       : 1               [31m 
       [32m  transparent color : 0               [31m 
       [32m  xAspect :     1     yAspect :     1 [31m 
       [32m  PageWid :   640     PageHei :   256 [31m 
                                                  
    And there's even more under the headings of CMAP, GRAB, CRNG, CAMG,
    and BODY.  At the moment we're seeking just 'width' and 'height', and
    there they are, by the arrow : 70 and 96 respectively.
    
    Now I [4mcan[0m obtain that information from within a running program :

    1  Run 'WhatIs' is required, but redirect its output into a file :
    
       [32mMyCommand = 'WhatIs ' || filename || ' Short >Ram:temp' [31m
       [32maddress command MyCommand [31m

    2  Open the 'temp' file, find the line containing 'width' (by whatever
       method, possibly similar to the one described later on for locating
       headers in graphics files), and close the file.
    
    3  Parse the line into its pieces, and retrieve the values we need.
    
    'WhatIs' is handy to have around, and I used it for as thorough a check
    as possible of much of what follows.  Though the above method
    may sound clumsy, I can fall back on it when my own attempts at
    extracting data from image files fail (as they often do - see below).

    But when I am on fairly safe ground, such as handling files which have
    been created or processed via DeLuxe Paint, I can now do it more
    elegantly than via 'WhatIs'.  To achieve that, I needed to review and
    extend my knowledge of IFF files.  I turned to Fish Disk 185, which
    concentrates on that topic, and to NewZAP3.18, which I could use to see
    the contents of files (though the word 'see' should not here be taken
    to mean 'identify and understand at a glance').

    
    [42m From the docs [40m

    To start with, the documents are not completely clear, and full of
    warnings about over-confidence.  And when the only recognised methods
    of representing data are unprintable characters and groups of
    hexadecimal characters which vary in length, you know that trouble lies
    ahead.  The following are some of the main points to come out of the
    documents on Fish Disk 185, and if you think my summary errs on the
    side of vagueness or inaccuracy, you're right.

    [42m [40m The only readily recognisable combinations of characters in a
    graphics file may include some but not necessarily all of the
    following 'headers' : FORM ILBM BMHD CMAP CRNG CAMG BODY.

    [42m [40m Other such headers may be found, but not all that are found are
    orthodox, recognised, documented, or registered.

    [42m [40m Attempts to encourage all makers of graphics files to adopt the same
    structure have been in vain.  You can't rely on anybody doing what they
    have been asked to do.

    [42m [40m Other than the content of the first few bytes, you cannot assume that
    any piece of information in an IFF file will be in a particular
    position.  Even that first few ... well, it's best to check.

    [42m [40m Data between headers should include one item which indicates how
    many bytes there are to the next header.  For example, if a translation
    into plain English reveals 'CMAP0024', the next 24 bytes of 'colour
    mapping' will be eight sets of RGB colour settings (8 * 3 = 24).  You
    know how many colours there are, and can unravel the settings for each
    of them.  

    [42m [40m The CAMG group contains the file's ViewMode, and pigs might fly.
    
    [42m [40m And there's an [4mawful[0m lot more ....
    

    [42m Using an editor to investigate [40m

    To clarify, extend, and perhaps confirm what I had read in the IFF
    docs, I used NewZAP3.18 to look more closely at the contents of IFF
    files.  The main display looks something like this :

    [32m ------------------------------------------------------------------[30m
    [32m|    8 columns of 8 digits, each set of        | 32 characters,    |[30m
    [32m|    digits containing 4 pairs of              | only some of which|[30m
    [32m|    hexadecimal values.                       | are recognisable. |[30m
    [32m.                                              .                   .[30m
    [32m.                                              .                   .[30m    
           ... and it goes on like that, line after line, for ages.

    In the case of a sample which I have just used as a source, the first
    left-hand set of 64 characters looked like this :

    [32m464F524D 00000A56 494C424D 424D4844 00000014 00460060 0000000 04020100
    [31m
    These are in hex, and to satisfy my curiosity I transcribed a few of
    them, a pair at a time, into something more recognisable, by using the
    ARexx 'x2d' function (converts a hex string into a character
    equivalent) :

    The first pair  : [32mx2c(46)[31m       = F
    The first eight : [32mx2c(464F524d)[31m = FORM
    The next eight  : [32mx2c(00000A56)[31m = a carriage return and V ...  (?)
    The third eight : [32mx2c(494C424D)[31m = ILBM ....
    
    Some of it made sense, and agreed with what I saw in the corresponding
    right-hand set :                     |
                                         |[32mFORM   VILBMBHMD    F `[31m
                                         |
    But this first line is not typical, in that some of it is readable at
    sight.  Most of what follows on the right-hand section of the screen is
    either invisible or undecipherable.  On the left-hand side it may not
    be comfortable to read, but it is visible, and can even be changed and
    saved if you are really courageous.  
        
    Rashly, I began to believe that it would be possible to scan a graphics
    file for any header which I expected to be present, and then to examine
    the following set of bytes for specific pieces of information.  I
    started to draw up an ARexx program for the purpose of extracting,
    first, the width and height of an image, and then anything else which
    might be of interest and not too complicated.  Before reaching the
    limits of my patience and understanding, I managed to obtain just a
    [4mlittle[0m bit more than the particular pieces of information I wanted.

    
    [42m Probing with ARexx [40m 

    Let's work our way through some of the steps. These seem to work.
    
    Assume that we are looking into a file called 'thispic'.  

    We shall use the presence in that file of the headers 'FORM' and
    'ILBM' as a fairly reliable indicator that it is a graphics file, and
    we shall also want to locate the strings 'BMHD', 'CMAP' and, later on,
    CAMG.  'FORM' alone may be enough for the first check.  For the next
    level of analysis the second step suggested is barely adequate, and
    there are other headers, but this will do for now.

    The method I use to find a given group of characters in a file is
    to read the whole file into a single string.  The ARexx User's
    Reference Manual says (p.109) that strings are limited to a maximum
    length of 65,535 bytes, a restriction which I have not found
    particularly onerous, though I suppose full-screen interlace pictures
    may well get up into that range.
    
    The file can be read into a string a byte at a time until end-of-file
    is detected, or, if the length of the file is known, in a single move.
    The latter is clearly more efficient, so we need first to obtain the
    size of the file.  The following line parses out the size from the
    information returned by the ARexx function 'statef()'.

      [32mparse value statef(thispic) with type size other[31m
    
    We open the file :       [32mop  = open(p,thispic,'r') [31m    
    Read it into a string :  [32mstr = readch(p,size)  [31m
    And close it :           [32mcl  = close(p)[31m
    
    Check that it is an IFF file.  Despite what was said above about not
    relying on the position of anything, I expect the first four bytes to
    be 'FORM' and the third four to be 'ILBM'.  If such is not the case, I
    conclude that the file is probably not an IFF file. (Since I first
    wrote this I have met a few cases in which this test rejects files
    which I know to be image files!)

      [32mform = index(str,'FORM')          [31m
      [32milbm = index(str,'ILBM')          [31m
      [32mif form ~= 1 & ilbm ~= 9 then ... [31m
                                     ... exit with a message.

    With some help from the docs but mainly by trial and error, I found
    that the following arrangement of bytes contains the width and height
    (ww and hh) of the image : [32mBMHD....wwhh[31m. So, if 'BMHD' is at position
    p, then width will be at p+8 and p+9, and height at p+10 and p+11.  As
    soon as they are found, they can be made readable by the c2d()
    function, which converts a character string into an integer number.    

      [32mbmhd   = index(str,'BMHD')         [31m 
      [32mwidth  = c2d(substr(str,bmhd+8,2)) [31m 
      [32mheight = c2d(substr(str,bmhd+10,2))[31m 
    
    Similarly, the number of colours can be extracted from the four bytes
    following 'CMAP'.  I described earlier how CMAP0024 tells us that
    there are 8 colours.

      [32mcmap  = index(str,'CMAP')[31m
      [32mncols = c2d(substr(str,cmap+4,4)) % 3[31m

    This was already more information than I had set out to find, so I
    started building an easy-to-use interface which would enable me to
    extract what I needed from any image file.    
 
          
    [42m Building a usable module [40m 
    
    Modularity is always being recommended as an aid to programming, and
    so I decided to arrange the above lines of code into a separate
    function that would be easy to pull into any program in which I might
    need it.  The result is the function headed PicParams which is a part
    of the following short program.  Some parameters are already entered,
    and there are no checks on anything.  If you enter the path and name of
    an IFF file, you should get its width and height.  If you enter
    anything else, you'll get garbage.

    The sole purpose of this program is to show how the function works,
    that is, how it obtains the required image data.  To use that data, of
    course, you have to be in the more complex environment of a user-
    designed window created via 'rexxarplib.library'.  We'll come to that
    later.
    
    From CLI, give the command : rx PicModule  (or whatever you call it)                 
    The program assumes that the name of the ControlPort will be 'HO', that
    the upper-left corner values will be 50,30 , and that a 'NOCOLOR' flag
    is not being added.

    [32m/* ARexx Program */                               [32m
    [32msay '0a'x || '           Pic Data Module' || '0a'x[32m
    [32mOptions Prompt "    Enter path and file name : "  [32m
    [32mparse pull fullname            /* Retains upper and lower case  */[32m
    [32m                                                                  [32m
    [32mw_and_h = PicParams(fullname)  /* The function call             */[32m
    [32m                                                                  [32m
    [32mparse var w_and_h w h          /* Divides result into two parts */[32m
    [32marg = "    call IFFImage(HO,'" || fullname || "',50,30,"    [32m
    [32mNewString = arg || w || ',' || strip(h) || ')'              [32m
    [32msay '    The full IFFImage function call will be :' || '0a'x[32m
    [32msay NewString || '0a'x                                      [32m
    [32mexit                                                        [32m
   
    [32mPicParams:       [32m
    [32m thispic = arg(1)[32m
    [32m parse value statef(thispic) with type size other [32m
    [32m op = open(p,thispic,'r') ; str = readch(p,size)  [32m
    [32m cl = close(p)[32m
    [32m bmhd = index(str,'BMHD')            [32m
    [32m width = c2d(substr(str,bmhd+8,2))   [32m
    [32m height = c2d(substr(str,bmhd+10,2)) [32m
    [32m return width || ' ' || height       [32m
    
                    /*   End of module  */
    
    I have just run the above program, and entered 'df1:pic/sixteen' as
    the path-plus-name.  Apart from the colour used here, this was the
    output :
    
       [32m                                                 [31m
       [32m        Pic Data Module                          [31m
       [32m                                                 [31m
       [32m Enter path and file name : df1:pic/sixteen      [31m
       [32m The full IFFImage function call will be :       [31m
       [32m                                                 [31m
       [32m call IFFImage(HO,'df1:pic/sixteen',50,30,70,96) [31m
                                                  |  |
                                             Here they are!  

    The width and height values obtained here happen to be the same as
    those reported above in the 'WhatIs' discussion because I examined the
    same file in each case.  One way of checking results is to confirm that
    they are identical when they should be!

    
    [42m The next phase [40m
   
    Having got this far, I went on to extract the following data from files
    of both full screen and smaller images :

      [32mScreenWidth  ScreenHeight  PicWidth  PicHeight  Depth  ViewMode[31m
    
    It works with the following types of image file :
    
      [32mFullscreen :  Hires, Lowres, Interlace, Ham [31m
      [32mBrush      :  Hires, Lowres, Interlace      [31m
    
    I have to be honest.  Except for the HAM type, all the samples I tried
    out at this stage were created by or processed through DeLuxe Paint
    III.  Pictures obtained from other sources did not deliver [4mall[0m the 
    values sought, and I have spent [4mhours[0m trying in vain to improve this.  
    
    The ViewMode was usually the culprit, and what I found in some of the
    files bore only a fleeting resemblance to anything the docs predicted. 
    In such circumstances, for the time being at least, I can fall back on
    'WhatIs' for help.  
    
    The HAM samples I tried were just as they came on a selection of PD
    disks (my version of DPaint can't handle them), and not all HAM pics
    from that source behaved as expected.

    Though I frequently felt like giving up, I eventually got the
    following set of procedures to work more or less reliably :

    [42m [40m An exploratory program 'GetData', ready to run.
    
    [42m [40m One of each of the seven types of file, with the following titles: 
      highres, lowres, lace, highresbrush, lowresbrush, lacebrush, ham 

    [42m [40m A short script file called 'GetSet':
    
        [32m rx GetData highres     [31m
        [32m rx GetData lowres      [31m
        [32m rx GetData lace        [31m
        [32m rx GetData highresbrush[31m
        [32m rx GetData lowresbrush [31m
        [32m rx GetData lacebrush   [31m
        [32m rx GetData ham         [31m
               
    [42m [40m The CLI command : EXECUTE GetSet
    
    The following is an extract from the actual output from such a run,
    here arranged to suit this medium, but the values have been left
    exactly as they were produced.

                    [31mSize  ScrH  ScrW  PicH  PicW  dpth  mode
    
    1  [32mhighres      7134  256   640   256   640   4     HIRES[31m     
    2  [32mlowres       9682  256   320   256   320   5     LORES[31m     
    3  [32mlace         12264 256   320   512   320   5     LACE[31m      
    4  [32mhighresbrush 2790  256   640   74    170   3     HIRES[31m     
    5  [32mlowresbrush  3256  256   320   85    132   4     LORES[31m     
    6  [32mlacebrush    3482  256   320   89    80    5     LACE[31m      
    7  [32mham          41456 256   320   256   320   6     HAM[31m   
    
    [4mAll as it should be[0m
    Lines 1, 2, and 7: Full screen images, screen and pic dimensions are 
                       the same.
    Line 3:            In Lace mode, PicHeight is twice the ScreenHeight. 
    Lines 4, 5, and 6: Screen size and image size are different, which
                       is how we can recognise them as brush images.

    Even all the modes displayed here are correct, something which at one
    stage I never thought I would see.  I was going to include the 'GetSet'
    program here to document more fully this part of the discussion, but it
    contains little which is not also in the program discussed in the next
    section. 

    
    [42m A sample application [40m       
    
    Some sort of application was the next step, and the result was the    
    attached program 'Iffy', the major components of which are listed here.
    The numbers below more or less match those included in the program
    itself, where you will also find a few further comments.  Some of the
    finer points about running the program are also contained there, so it
    might be worth your while having a look at the program listing, even if
    you would not normally wish to plunge into that jungle.

    [42m 1 [40m Error trapping
    [42m 2 [40m Add required libraries to active list
    [42m 3 [40m If 'PicList' exists, load in its contents, and set 'auto' flag.
    
    If you want a list of pics to be processed, you can create a file
    called 'PicList' containing their names. Those names [4mmust[0m include 
    their path, and 'PicList' should be in the same directory as the
    program. For example : 

            [32mRam:Tree[31m 
            [32mRam:Bush[31m
            [32mdf1:pics/Branch[31m 
            [32mdf1:pics/animals/sloth[31m 
            [32m   and so on.[31m

    If such a list exists, a mouse click anywhere (or a menu selection) is
    all it takes to move on to the next picture.  Without 'PicList', the
    next picture has to be selected each time from a file requester.

    If you are not going to be looking at the parameters which are the main
    topic of this article, but just want to use the program as an IFF
    viewer, then one additional line can be added to 'PicList' containing
    the number of seconds you want each pic to pause before the next one is
    screened.  The presence of such a line turns the 'auto' flag on.  For
    example, a final line of '2' (no quotes) will cause a pause of two
    seconds between the screening of each of the pictures (full-screen
    images or 'brushes').  You can just sit back and watch.

    I know that this can also be done by, for example, 'Display', but it's
    good to be able to produce your own version, knowing that you can
    modify it as you please, and perhaps borrow it and build on it as a
    background for other programs.

    Back to the program outline ...

    [42m 4 [40m Screen first pic as listed, or as selected from File Requester
    [42m 5 [40m Start a loop to handle user actions between pics
    [42m 6 [40m Bypass the wait for user action if 'auto' has been set, otherwise
         deal with :
         A mouse click - Get next picture    ) from the 'PicList' set 
         A menu choice - Get next picture    ) or via a file requester
                         Show pic parameters
                         Show colour settings
                         Quit
    [42m 7 [40m Finish : Close everything 
    [42m 8 [40m 'Syntax' and 'Error' errors dealt with
    
         [4mMain function : Screen the pic[0m
    
    [42m 9 [40m Get the name of the next file and its size
   [42m 10 [40m Check for expected headers
   [42m 11 [40m Pull out the data required 
   [42m 12 [40m Determine the ViewMode 
    
    This is the area in which I had the most difficulty, and the
    difficulty seemed to increase rather than diminish as I worked.  There
    are contradictions in the docs and surprising inconsistencies in the
    file structure in some widely used graphics tools.  Just one example :
    there are (should be) [4mtwo[0m pairs of width/height parameters, one pair 
    for the screen size and another for the image size.  For a full screen
    image these pairs will be the same.  But one PD graphics tool I often
    use puts the [4mimage[0m size in both data locations, making it impossible
    to rely on the presence of, say, 640 or 320 anywhere in those files as
    a quick indicator of screen resolution.  Using 'WhatIs' in such cases,
    for example :

    Instead of  [32m  width   :    70     height  :    96 [31m  
                [32m  .                                   [31m
                [32m  PageWid :   640     PageHei :   256 [31m 
    
    we get      [32m  width   :    70     height  :    96 [31m  
                [32m  .                                   [31m
                [32m  PageWid :    70     PageHei :    96 [31m 
  
   [42m 13 [40m Open a screen   
   [42m 14 [40m Use CreateHost() for a messageport, window, etc. 
   [42m 15 [40m Open the window and the port
   [42m 16 [40m Create a menu     
   [42m 17 [40m At last, call the IFFImage function                                                             /* 17 */
    
        [4mSupporting functions[0m
    
   [42m 18 [40m Check on presence of expected headers in file 
   [42m 19 [40m Show screen width/height, image width/height (if different), and
         number of colours
   [42m 20 [40m Show RGB settings per colour. 
    These are positioned at the right of the picture, and with overlarge
    pictures may be off-screen and therefore invisible. Adjust the value
    of 'sampos' in the program if you want to overcome this. 

   
    [42m Final Notes [40m

    'Iffy' is a ready-to-run ARexx program, mainly menu-driven. It expects
    to find 'rexxarplib.library' in LIBS:.  If the program is to be able to
    do anything, it must also have some IFF images within reach.  It will
    handle LORES, HIRES, LACE and HAM types.

    You have the program.  If you have Arexx, and if you use it, then :
    
        ·   Anything you don't like, change. 
        ·   Anything you can add or improve, do it.  
        ·   Anything I've got wrong, fix it if you can.
        ·   Any complaints or suggestions, write to me.
      
    I readily admit that this is a mere beginning, that there are problems
    and complexities lurking in IFF files which I have not mentioned, and
    even more of which I am not yet aware.  It was just a learning
    exercise which produced one tool to do the intended trivial task and
    another one, as a by-product, which is rather more interesting.

    
    If it is at a level which is useful for you, I am pleased to share it.
    If you find it irritatingly simplistic or pathetically incorrect,
    either ignore it, or take up the challenge to write an improved
    alternative for the next issue of MegaDisc.


    [33;3mHamilton, N.Z.   April, 1994[0m
    
                   -------------------------------


      [32m:-)  40  :^)  40  :)  40  8)  40  8-)  40  B-)  40  :-O  40  8*)[0m

