/*
    $VER: Picasso.filer 1.7 (16.12.94)

    Author:
        Michael Böhnisch (billy@uni-paderborn.de)   (mb)
        Matthias Scheler (tron@lyssa.pb.owl.de)     (ms)

    Function:
        ViewGIF,  the  GIF  viewer that accompanies the Picasso II graphics
        board,  often  selects  wrong resolutions when displaying oversized
        pictures.   E.g.   I´ve  got  a  picture,  600×800 in size, ViewGIF
        displays  on  a  1024×768  screen,  cropping off the lower 34 rows.
        Picasso.filer  scans  the  GIF file for the picture size and forces
        ViewGIF into a better resolution.

        ViewJPEG  uses  640×480×24  by default, regardless whether it crops
        the  image  or not.  I prefer seeing the whole image when possible,
        so Picasso.filer allows 16 or 8 bits per pixel resolutions.  Bigger
        pictures  may  take a while to display, especially on unaccelerated
        Amigas.  Stay patient.

        ViewIFF   does   not  show  Multi  Palette  pictures  (PCHG  chunk)
        correctly.  Picasso.filer provides a fallback to an external viewer
        when such pictures are selected.
        Pixels  on  the  Picasso  II board usually are square shaped, or in
        other  words  have  an  X/Y  aspect  ratio  of  1.  This results in
        distorted picture displays for special view modes like  "NTSC:LoRes
        Lace".
        Picasso.filer handles this by supplying one of the keywords DOUBLEX
        or  DOUBLEY  to  ViewIFF  as  appropriate.  For extreme distortions
        (e.g.   pictures for PAL:SuperHighres non-interlaced) a fallback to
        an  external  external  viewer  on  conservative  Amiga  screens is
        choosen.

    Requires:
        Picasso II graphics board
        ViewGIF, ViewJPEG and ViewIFF supplied with Picasso II
        VT  2.0  by Thomas Krehbiel or any other standard IFF viewer.  Must
        support  PCHG  Multi  Palette  pictures  if  you  want to view this
        picture type.

    Call:
        Picasso TYPE FILE

        Where  TYPE is one of GIF, IFF or JPG.  Picasso.filer relies on the
        correctness of this info and does no further file type checking.

    Example for "Filer.RC":
        REXXCLASS "#?","GIF8","Picasso GIF %s"
        REXXCLASS "#?","??????JFIF","Picasso JPG %s"
        REXXCLASS "#?","FORM????ILBM","Picasso IFF %s"

    ToDo:
        IFF pictures with aspect ratios outside the range from 0.35 to 2.83
        are displayed on standard Amiga screens.  Maybe some tool should be
        used  to do a scale operation on the picture first and then forward
        it to the Picasso viewer.

    History:
        06.02.94    1.0 Initial Release                             (mb)
        07.02.94    1.1 IFF & JPEG added                            (mb)
        08.02.94    1.2 fallback for HAM/EHB pictures removed       (ms)
        09.02.94    1.3 removed VT dependency for GIF
                        removed VT dependency for IFF, any viewer
                        wil do now
                        added IFF PCHG fallback
                        added fallback for non-square aspect ratios (mb)
        10.02.94    1.4 used ViewIFF keywords DOUBLEX and DOUBLEY
                        for a wider range of pictures displayed on
                        the Picasso II                              (mb)
        10.03.94    1.5 removed VT dependency for JPEG
                        no need for rexxsupport.library any longer
                        added user confirmation for oversized JPEGs (mb)
        25.11.94    1.6 Bugfix: GIF files with extension block
                        present were not displayed                  (mb)
        16.12.94    1.7 Added some support for nested IFF FORMS     (mb)
*/

/* -------------------------------------------------------------------- */
/* External commands. Adjust these to your individual settings.         */
/* -------------------------------------------------------------------- */

vtcommand = "VT"
vgcommand = "ViewGIF"
vjcommand = "ViewJPEG"
vicommand = "ViewIFF"

/* -------------------------------------------------------------------- */

OPTIONS RESULTS                     /* we need response from filer      */

PARSE ARG TYPE PICFILE              /* get arguments                    */
TYPE    = STRIP( TYPE )
PICFILE = STRIP( PICFILE )

/* -------------------------------------------------------------------- */
/* Default to Filer's AReXX port and prevent Filer from quitting        */
/* -------------------------------------------------------------------- */

ADDRESS 'FilerRexx'

LOCKFILER
Key = RESULT

/* -------------------------------------------------------------------- */
/* Get source dircetory name, append "/" if it is not a device name.    */
/* Tag name of file to view.                                            */
/* -------------------------------------------------------------------- */

GETSOURCEPATH
SrcPath = RESULT

IF RIGHT(SrcPath, 1) = ':' THEN
    FileName = SrcPath || STRIP( PICFILE, B, X2C(22) )
ELSE
    FileName = SrcPath || '/' || STRIP( PICFILE, B, X2C(22) )

SELECT

    /* ---------------------------------------------------------------- */
    /* Show GIF picture                                                 */
    /* ---------------------------------------------------------------- */

    WHEN TYPE = 'GIF' THEN DO

        /* ------------------------------------------------------------ */
        /* determine ViewGIF RESOLUTION parameter and call viewer       */
        /* ------------------------------------------------------------ */

        CALL GIFSize
        PARSE VAR RESULT xsize ysize depth
        xsize = STRIP( xsize )
        ysize = STRIP( ysize )
        depth = STRIP( depth )

        res = 1600
        IF ( xsize <= 1280 ) & ( ysize <= 1024 ) THEN res = 1280
        IF ( xsize <= 1152 ) & ( ysize <=  900 ) THEN res = 1152
        IF ( xsize <= 1120 ) & ( ysize <=  832 ) THEN res = 1120
        IF ( xsize <= 1024 ) & ( ysize <=  768 ) THEN res = 1024
        IF ( xsize <=  800 ) & ( ysize <=  600 ) THEN res =  800
        IF ( xsize <=  640 ) & ( ysize <=  480 ) THEN res =  640
        IF ( xsize <=  320 ) & ( ysize <=  240 ) THEN res =  320

        HISTORY 'Picasso: GIF "' || FileName || '"' xsize || '×' || ysize || '×' || depth

        SHELL COMMAND vgcommand RESOLUTION res CENTER FileName '>NIL:'

    END

    /* ---------------------------------------------------------------- */
    /* Show JPG picture                                                 */
    /* ---------------------------------------------------------------- */

    WHEN TYPE = 'JPG' THEN DO

        /* ------------------------------------------------------------ */
        /* Determine JPEG picture size                                  */
        /* ------------------------------------------------------------ */

        CALL JPGSize
        PARSE VAR RESULT xsize ysize depth
        xsize = STRIP( xsize )
        ysize = STRIP( ysize )
        depth = STRIP( depth )

        /* ------------------------------------------------------------ */
        /* choose maximum number of colors available without cropping   */
        /* and call ViewJPG                                             */
        /* ------------------------------------------------------------ */

        dep = '8BIT'
        IF ( xsize <= 1152 ) & ( ysize <=  900 ) THEN dep = '16BIT'
        IF ( xsize <=  800 ) & ( ysize <=  600 ) THEN dep = '24BIT'

        banner = xsize || '×' || ysize || '×' || depth

        HISTORY 'Picasso: JPG "' || FileName || '"' banner '(' || dep || ')'

        /* ------------------------------------------------------------ */
        /* Ask for confirmation if picture is "on the big side"         */
        /* ------------------------------------------------------------ */

        display = 1
        IF xsize * ysize > 1000000 THEN DO
            txt =        "This operation may take a long time.|"
            txt = txt || "      Shall I continue anyway?"

            QUESTBOX txt
            display = RESULT
        END

        IF display = 1 THEN SHELL COMMAND vjcommand dep FileName '>NIL:'

    END

    /* ---------------------------------------------------------------- */
    /* Show IFF ILBM picture                                            */
    /* ---------------------------------------------------------------- */

    WHEN TYPE = IFF THEN DO

        /* ------------------------------------------------------------ */
        /* determine size and depth of the picture                      */
        /* ------------------------------------------------------------ */

        CALL IFFSize
        PARSE VAR RESULT xsize ysize depth ratio
        xsize = STRIP( xsize )
        ysize = STRIP( ysize )
        depth = STRIP( depth )
        ratio = STRIP( ratio )

        banner = xsize || '×' || ysize || '×' || depth

        /* ------------------------------------------------------------ */
        /* Does the IFF file contain a PCHG chunk? These pictures may   */
        /* have a dedicated palette for each scanline and currently are */
        /* not displayed correctly by ViewIFF.                          */
        /* ------------------------------------------------------------ */

        CALL PCHGTest
        test = RESULT

        SELECT

            WHEN test = 'ERRR' THEN DO

                /* ---------------------------------------------------- */
                /* An error occured, inform user and exit               */
                /* ---------------------------------------------------- */

                HISTORY 'Picasso: error processing' FileName

            END

            WHEN test = 'PCHG' THEN DO

                /* ---------------------------------------------------- */
                /* Display Multi-Palette picture                        */
                /* ---------------------------------------------------- */

                HISTORY 'Picasso: IFF "' || FileName || '"' banner '(PCHG)'
                SHELL COMMAND vtcommand FileName '>NIL:'

            END

            /* -------------------------------------------------------- */
            /* do some correction of aspect ratios in number range      */
            /* 0.35 = sqrt(0.5) ... 2.83 = 2 * sqrt(2)                  */
            /* by ViewIFF options DOUBLEX and DOUBLEY                   */
            /* -------------------------------------------------------- */

            WHEN ( ratio < 0.71 ) & ( ratio >= 0.35 ) THEN DO

                /* ---------------------------------------------------- */
                /* Pixels are about half as wide than tall              */
                /* ---------------------------------------------------- */

                HISTORY 'Picasso: IFF "' || FileName || '"' banner '(X:Y =' ratio || ')'
                SHELL COMMAND vicommand CENTER MODE DOUBLEY FileName '>NIL:'
                
            END

            WHEN ( ratio < 1.41 ) & ( ratio >= 0.71 ) THEN DO

                /* ---------------------------------------------------- */
                /* Take this for square, some distortion tolerated      */
                /* ---------------------------------------------------- */

                HISTORY 'Picasso: IFF "' || FileName || '"' banner '(X:Y =' ratio || ')'
                SHELL COMMAND vicommand CENTER FileName '>NIL:'
                
            END

            WHEN ( ratio < 2.83 ) & ( ratio >= 1.41 ) THEN DO

                /* ---------------------------------------------------- */
                /* Pixels are about twice as wide than tall             */
                /* ---------------------------------------------------- */

                HISTORY 'Picasso: IFF "' || FileName || '"' banner '(X:Y =' ratio || ')'
                SHELL COMMAND vicommand CENTER MODE DOUBLEX FileName '>NIL:'
                
            END

            OTHERWISE DO

                /* ---------------------------------------------------- */
                /* Fall back to externalviewer if outside aspect bounds */
                /* This should happen very rarely, e.g. for pictures in */
                /* Superhires without interlace (aspect ratio ~ 4) or   */
                /* Multiscan productivity with interlace (aspect ratio  */
                /* ~ 0.25)                                              */
                /* ---------------------------------------------------- */

                HISTORY  'Picasso: IFF "' || FileName || '"' banner '(X:Y =' ratio || 'FB)'
                SHELL COMMAND vtcommand FileName '>NIL:'

            END

        END

    END

    /* ---------------------------------------------------------------- */
    /* unknown type                                                     */
    /* ---------------------------------------------------------------- */

    OTHERWISE DO

        /* ------------------------------------------------------------ */
        /* Wrong type parameter, inform user                            */
        /* ------------------------------------------------------------ */

        HISTORY 'Picasso: Type' TYPE 'not supported'

    END
            
END

/* -------------------------------------------------------------------- */
/* re-allow quitting Filer                                              */
/* -------------------------------------------------------------------- */

UNLOCKFILER Key

EXIT

/************************************************************************/
/* JPGSize : subroutine parses a JFIF file and returns the picture's    */
/*           dimensions                                                 */
/************************************************************************/

JPGSize: PROCEDURE EXPOSE FileName

    M_SOF0 = X2C( C0 )      /* SOF code for arithmetic encoding         */
    M_SOF1 = X2C( C1 )      /* SOF code for baseline implementation     */
    M_SOF9 = X2C( C9 )      /* SOF code for non-baseline Huffmann file  */

    IF OPEN( jpg, FileName, 'READ' ) THEN DO

        /* ------------------------------------------------------------ */
        /* skip over SOI marker                                         */
        /* ------------------------------------------------------------ */

        SEEK( jpg, 2, 'BEGIN' )     /* point to JFIF APP0 block         */

        /* ------------------------------------------------------------ */
        /* Loop through data blocks until EOF or a SOF block is found   */
        /* ------------------------------------------------------------ */

        DO UNTIL EOF( jpg )

            IF READCH( jpg ) ~= X2C( FF ) THEN RETURN

            /* -------------------------------------------------------- */
            /* Get block type marker                                    */
            /* -------------------------------------------------------- */

            type = READCH( jpg )

            /* -------------------------------------------------------- */
            /* Test for SOF block                                       */
            /* -------------------------------------------------------- */

            IF type = M_SOF0 | type = M_SOF1 | type = M_SOF9 THEN DO

                /* ---------------------------------------------------- */
                /* Skip to size information words                       */
                /* ---------------------------------------------------- */

                SEEK( jpg, 3, 'CURRENT' )

                /* ---------------------------------------------------- */
                /* read size information and return to calling proc     */
                /* ---------------------------------------------------- */

                hi = C2D( READCH( jpg ) )
                lo = C2D( READCH( jpg ) )
                h  = lo + 256 * hi

                hi = C2D( READCH( jpg ) )
                lo = C2D( READCH( jpg ) )
                w  = lo + 256 * hi

                CLOSE( jpg )

                RETURN w h 24
            END

            /* -------------------------------------------------------- */
            /* No SOF, skip to next marker                              */
            /* -------------------------------------------------------- */

            hi = C2D( READCH( jpg ) )
            lo = C2D( READCH( jpg ) )

            SEEK( jpg, lo + 256 * hi - 2, 'CURRENT' )
        END

        CLOSE( jpg )

    END

    RETURN

/************************************************************************/
/* GIFSize : subroutine parses a GIF file and returns the picture's     */
/*           dimensions                                                 */
/************************************************************************/

GIFSize: PROCEDURE EXPOSE FileName

    IF OPEN( gif, FileName, 'READ' ) THEN DO

        /* ------------------------------------------------------------ */
        /* extract colour depth information                             */
        /* ------------------------------------------------------------ */

        SEEK( gif, 10, 'BEGIN' )    /* point to resolution flag reg.    */

        rf = C2D( READCH( gif ) )   /* resolution flag register         */
        r  = ( rf // 8 ) + 1        /* extract bits/pixel               */

        SEEK( gif, 13, 'BEGIN' )    /* point behind screen descriptor   */

        /* ------------------------------------------------------------ */
        /* skip global colour map if present                            */
        /* ------------------------------------------------------------ */

        IF rf >= 128 THEN SEEK( gif, 3 * 2**r, 'CURRENT' )

        /* ------------------------------------------------------------ */
        /* skip extension block if present                              */
        /* ------------------------------------------------------------ */

        IF READCH( gif ) = '!' THEN DO          /* read header          */

            SEEK( gif, 1, 'CURRENT' )           /* skip function code   */
            DO UNTIL len = 0                    /* up to last block     */
                len = C2D( READCH( gif ) )      /* get data block size  */
                SEEK( gif, len, 'CURRENT' )     /* skip data block      */
            END
            SEEK( gif, 1, 'CURRENT' )           /* skip next header     */

        END
            
        /* ------------------------------------------------------------ */
        /* The image descriptor block is reached, at last. Now let's    */
        /* look for the actual image size...                            */
        /* ------------------------------------------------------------ */

        SEEK( gif, 4, 'CURRENT' )               /* skip topleft coords  */

        /* ------------------------------------------------------------ */
        /* all numbers are stored in INTEL format, lo-byte first        */
        /* ------------------------------------------------------------ */

        lo = C2D( READCH( gif ) )               /* get width            */
        hi = C2D( READCH( gif ) )
        w  = lo + 256 * hi

        lo = C2D( READCH( gif ) )               /* get height           */
        hi = C2D( READCH( gif ) )
        h  = lo + 256 * hi

        CLOSE( gif )

        RETURN w h r

    END

    RETURN

/************************************************************************/
/* IFFSize : subroutine parses an IFF ILBM file and returns the picture */
/*           size and pixel aspect ratio                                */
/************************************************************************/

IFFSize: PROCEDURE EXPOSE FileName

    IF OPEN( iff, FileName, 'READ' ) THEN DO

        /* ------------------------------------------------------------ */
        /* File starts with FORM????ILBM.                               */
        /* ------------------------------------------------------------ */

        len = 12                    /* offset for first chunk in file   */

        DO UNTIL type = 'BMHD'

            /* -------------------------------------------------------- */
            /* seek to next chunk                                       */
            /* -------------------------------------------------------- */

            SEEK( iff, len, 'CURRENT' )

            /* -------------------------------------------------------- */
            /* parse out chunk type and length                          */
            /* -------------------------------------------------------- */

            type = READCH( iff, 4 )

            IF type = 'FORM' THEN
                len = 8;
            ELSE DO
                len  =             C2D( READCH( iff ) )
                len  = len * 256 + C2D( READCH( iff ) )
                len  = len * 256 + C2D( READCH( iff ) )
                len  = len * 256 + C2D( READCH( iff ) )
            END

            IF len // 2 = 1 THEN len = len + 1  /* add 1 for odd length */

        END

        /* ------------------------------------------------------------ */
        /* We're now positioned on a BMHD chunk.                        */
        /* ------------------------------------------------------------ */

        hi = C2D( READCH( iff ) )   /* read image width                 */
        lo = C2D( READCH( iff ) )
        w  = lo + 256 * hi

        hi = C2D( READCH( iff ) )   /* read image height                */
        lo = C2D( READCH( iff ) )
        h  = lo + 256 * hi

        SEEK( iff, 4, 'CURRENT' )   /* seek to nPlanes entry            */

        d  = C2D( READCH( iff ) )   /* number of bitplanes (depth)      */

        SEEK( iff, 5, 'CURRENT' )   /* seek to aspect ratio entries     */

        xa = C2D( READCH( iff ) )   /* xAspect                          */
        ya = C2D( READCH( iff ) )   /* yAspect                          */

        NUMERIC DIGITS 3            /* sometimes less is more...        */
        a  = xa / ya                /* Aspect ratio                     */
        NUMERIC DIGITS

        CLOSE( iff )                /* clean up                         */

        RETURN w h d a
    END

    RETURN

/************************************************************************/
/* PCHGTest : Returns true if IFFFile contains a PCHG chunk and thus,   */
/*            is not correctly displayed by ViewIFF.                    */
/************************************************************************/

PCHGTest: PROCEDURE EXPOSE FileName

    IF OPEN( iff, FileName, "READ" ) THEN DO

        len = 12

        DO UNTIL ( Type = 'PCHG' ) | EOF( iff )

            SEEK( iff, len, 'CURRENT' )

            type = READCH( iff, 4 )

            IF type = 'FORM' THEN
                len = 8;
            ELSE DO
                b1   = C2D( READCH( iff ) ) * 2**24
                b2   = C2D( READCH( iff ) ) * 2**16
                b3   = C2D( READCH( iff ) ) * 2**8
                b4   = C2D( READCH( iff ) )
                len  = b1 + b2 + b3 + b4
            END

            IF len // 2 = 1 THEN len = len + 1  /* add 1 for odd length */

        END

        CLOSE( iff )

        IF Type = 'PCHG' THEN
            RETURN 'PCHG'
        ELSE
            RETURN 'NORM'

    END

    RETURN 'ERROR'
