home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: Graphics / Graphics.zip / blendgif.zip / gif_Info.cmd < prev    next >
OS/2 REXX Batch file  |  1999-03-16  |  52KB  |  1,827 lines

  1. /* 25 Feb 1999. Daniel Hellerstein, danielh@econ.ag.gov */
  2. /* This is a simple utility for examining the structure of a GIF file*/
  3. /* to use it, you MUST have the REXXLIB and RXGDUTIL dlls installed */
  4. /* It uses the PARSEGIF.RXX prodedure library */
  5. /* For info on the PARSEGIF.RXX procedcure library, please go to
  6.     http://www.srehttp.org/apps/gif_info/ */
  7.  
  8. parse arg gif_File
  9.  
  10. if gif_file='?' | gif_file='??' then do
  11.   bold='' ; normal='' ; reverse=''; cy_ye=''
  12.     say "GIF_INFO will display the block structure of a GIF file."
  13.     say "Usage: GIF_INFO filename.ext "
  14.     say "or, just enter GIF_INFO, and answer the prompts "
  15.     say
  16.     if gif_File='??' then call shownotes
  17.     exit
  18. end
  19.  
  20.  
  21. call initit
  22.  
  23. ask1:
  24. if gif_file='' then do
  25.    call charout,"Name of .GIF file (? for file listing)? "
  26.    pull gif_file
  27. end /* do */
  28. if gif_File='' then exit
  29. gif_file=strip(gif_file)
  30.  
  31. if left(gif_file,2)="??" then do
  32.    call shownotes
  33.    gif_file=''
  34.    signal ask1
  35. end /* do */
  36.  
  37. if left(gif_file,1)="?" then do
  38.    parse var aa . thisdir
  39.    if thisdir="" then    thisdir=directory()
  40.     say 
  41.     say  ' >> List of .GIF files in: ' thisdir 
  42.     do while queued()>0
  43.     pull .
  44.     end /* do */
  45.    '@DIR /b  '||strip(thisdir,'t','\')'\*.gif | rxqueue'
  46.     foo=show_dir_queue('.GIF')
  47.     say
  48.     say "Hint: enter ?? to display some program notes "
  49.     gif_file=''
  50.     signal ask1
  51. end
  52.  
  53.  
  54. if pos(".",gif_File)=0 then gif_file=gif_file'.gif'
  55. fsize=stream(gif_file,'c','query size')
  56. if fsize="" | fsize=0 then do
  57.    say "No such file: " gif_File
  58.    exit
  59. end /* do */
  60. foo=stream(gif_file,'c','open read')
  61. gifimage=charin(gif_file,1,fsize)
  62. foo=stream(gif_File,'c','close')
  63. nblocks=show_gifcontents(gifimage,1)
  64. exit
  65.  
  66.  
  67. /****************/
  68. shownotes:
  69. say
  70.  say cy_ye"GIF_INFO Notes:"normal
  71. say bold " *"normal" The following abbreviations are used for block names:"
  72.     say "    LSD: Logical Screen Descriptor "
  73.     say "    GCE: Graphical Control Extension "
  74.     say "    IMG: Image Descriptor "
  75.     say "    CMT: Comment Block "
  76.     say "    APE: Application Extension"
  77.     say "    PTE: Plain Text Extension "
  78.     say "    TRM: Terminator "
  79. say bold " *"normal' IMG blocks contain image data (pixel values) and can '
  80. say  "     contain a local color table (both of which you can examine)"
  81. say bold " *"normal' LSD blocks contain a global color table (which you can examime)'
  82. say
  83.  
  84.  return 1
  85.  
  86.  
  87. /***************/
  88. /* initilaize some stuff */
  89. initit:
  90.  
  91. foo=rxfuncquery('sysloadfuncs')     /* load rexxutil library */
  92. if foo=1 then do
  93.   call RxFuncAdd 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs'
  94.   call SysLoadFuncs
  95. end
  96.  
  97. foo=rxfuncquery('rxgdloadfuncs')
  98. if foo=1 then do
  99.   Call RxFuncAdd 'RxgdLoadFuncs', 'RXGDUTIL', 'RxgdLoadFuncs'
  100.   Call RxgdLoadFuncs
  101. end
  102. foo=rxfuncquery('rxgdloadfuncs')
  103. if foo=1 then do
  104.    if verb="" then do
  105.         STRING "Sorry: RXGDUTIL.DLL is not available! Did you copy it to your LIBPATH? "
  106.         return ' '
  107.    end /* do */
  108.    call dosay 'Sorry: RXGDUTIL.DLL is not available! Did you copy it to your LIBPATH? '
  109.    exit
  110. end /* do */
  111.  
  112.  
  113. /* Load up advanced REXX functions */
  114. foo=rxfuncquery('rexxlibregister')
  115. if foo=1 then do
  116.  call rxfuncadd 'rexxlibregister','rexxlib', 'rexxlibregister'
  117.  call rexxlibregister
  118. end
  119. foo=rxfuncquery('rexxlibregister')
  120. if foo=1 then do
  121.     say "Sorry: REXXLIB is not available. Did you copy it to your LIBPATH?"
  122.     exit
  123. end /* do */
  124.  
  125. ansion=checkansi()
  126. if ansion=1 then do
  127.      aesc='1B'x
  128.      cy_ye=aesc||'[37;46;m'
  129.      cyanon=cy_ye
  130.      normal=aesc||'[0;m'
  131.      bold=aesc||'[1;m'
  132.      re_wh=aesc||'[31;47;m'
  133.      reverse=aesc||'[7;m'
  134.  end
  135.  else do
  136.      say " Warning: Could not detect ANSI....  Install will look ugly ! "
  137.       cy_ye="" ; normal="" ; bold="" ;re_wh="" ;
  138.      reverse=""
  139. end  /* Do */
  140.  
  141. return 1
  142.  
  143. /* -------------------- */
  144. /* choose between several alternatives (by default,a yes or no ), 
  145. return 1 if yes (or 0,1,2 for chosen altenative ) */
  146.  
  147. yesno:procedure expose normal reverse bold cy_ye
  148. parse arg amessage , altans,def,arrowok
  149. aynn=' '
  150. if def='' then 
  151.  defans=''
  152. else
  153.  defans=translate(left(strip(def),1))
  154. if altans='' then altans='No Yes'
  155.  
  156. w.0=words(altans)
  157. do iw0=1 to w.0
  158.      w.iw0=strip(word(altans,iw0))
  159.      a.iw0=translate(left(w.iw0,1))
  160.      aa.iw0=substr(w.iw0,2)
  161.      aynn=aynn||bold
  162.      if  a.iw0=defans then aynn=aynn||cy_ye
  163.      aynn=aynn||a.iw0||normal||aa.iw0
  164.      if iw0<w.0 then aynn=aynn'|'
  165. end
  166. if arrowok=1 then aynn=aynn||' [UP]'
  167. do forever
  168.  foo1=normal||reverse||amessage||normal||aynn||' 'normal
  169.  call charout,foo1
  170.  anans=translate(sysgetkey('echo'))
  171.  ianans=c2d(anans)
  172.  if anans='' | ianans=13 | ianans=10 then  anans=defans
  173.  
  174.  if arrowok=1 & ianans=0 then do
  175.      ians=c2d(sysgetkey('noecho'))
  176.      if ians=72 then  do
  177.            say ;say
  178.            return -1  /* -1 : up key */
  179.      end
  180.  end /* do */
  181.  
  182.  do ijj=1 to w.0
  183.     if abbrev(anans,a.ijj)=1 then do
  184.         say
  185.         return Ijj-1
  186.     end
  187.  end /* do */
  188.  call charout,'0d'x
  189. end
  190.  
  191. /* ------------------------------------------------------------------ */
  192.  /* function: Check if ANSI is activated                               */
  193.  /*                                                                    */
  194.  /* call:     CheckAnsi                                                */
  195.  /*                                                                    */
  196.  /* where:    -                                                        */
  197.  /*                                                                    */
  198.  /* returns:  1 - ANSI support detected                                */
  199.  /*           0 - no ANSI support available                            */
  200.  /*          -1 - error detecting ansi                                 */
  201.  /*                                                                    */
  202.  /* note:     Tested with the German and the US version of OS/2 3.0    */
  203.  /*                                                                    */
  204.  /*                                                                    */
  205.  CheckAnsi: PROCEDURE
  206.    thisRC = -1
  207.  
  208.    trace off
  209.                          /* install a local error handler              */
  210.    SIGNAL ON ERROR Name InitAnsiEnd
  211.  
  212.    "@ANSI 2>NUL | rxqueue 2>NUL"
  213.  
  214.    thisRC = 0
  215.  
  216.    do while queued() <> 0
  217.      queueLine = lineIN( "QUEUE:" )
  218.      if pos( " on.", queueLine ) <> 0 | ,                       /* USA */
  219.         pos( " (ON).", queueLine ) <> 0 then                    /* GER */
  220.        thisRC = 1
  221.    end /* do while queued() <> 0 */
  222.  
  223.  InitAnsiEnd:
  224.  signal off error
  225.  RETURN thisRC
  226.  
  227.  
  228.  
  229. /*********/
  230. /* show stuff in queue as a list */
  231. show_dir_queue:procedure 
  232. parse arg lookfor
  233.     ibs=0 ;mxlen=0
  234.     if lookfor<>1 then
  235.        nq=queued()
  236.      else
  237.         nq=qlist.0
  238.     do ii=1 to nq
  239.        if lookfor=1 then do
  240.           aa=qlist.ii
  241.           ii2=lastpos('\',aa) ; anam=substr(aa,ii2+1)
  242.        end /* do */
  243.        else do
  244.           pull aa
  245.           if pos(lookfor,aa)=0 then iterate
  246.           parse var aa anam (lookfor) .
  247.           if strip(anam)='.' | strip(anam)='..' then iterate
  248.        end
  249.        ibs=ibs+1
  250.        blist.ibs=anam
  251.        mxlen=max(length(anam),mxlen)
  252.     end /* do */
  253. arf=""
  254. do il=1 to ibs
  255.    anam=blist.il
  256.    arf=arf||left(anam,mxlen+2)
  257.    if length(arf)+mxlen+2>75  then do
  258.         say arf
  259.         arf=""
  260.    end /* do */
  261. end /* do */
  262. if length(arf)>1 then say arf
  263. say
  264. return 1
  265.  
  266.  
  267.  
  268.  
  269. /************************/
  270. /* Display info from a gif file. A list of the logical "blocks"
  271.    comprising a gif file is displayed, followed by relevant
  272.    information extracted from each block.
  273.  
  274.  This is adapted from the GIFVU.RXX procedure that comes with BLENDGIF.
  275.  
  276.  
  277. Usage:
  278.   status=show_gifcontents(gifimage,dopause)
  279.  
  280. where:
  281.   gifimage: the contents of a gif file; say as read using
  282.             gifimage=charin(gif_file,1,chars(gif_file))
  283.   DOPause : If 1, then pause (wait for ENTER key) after displaying
  284.             info on each block -- and give user chance to
  285.             display more details.
  286. and 
  287.   status = number of blocks in the gif file
  288.  
  289. For example, the following program will display the structure of
  290. a user supplied gif file:
  291.  
  292.  
  293. */
  294. show_gifcontents:PROCEDURE expose bold normal cy_ye reverse
  295. parse arg gifcontents,dopause
  296. talist=read_gif_block(gifcontents,1,'',1)
  297. say bold' The  "block" structure of the gif file is: 'normal
  298. call charout,"  "cy_ye":"normal' '
  299. do mm=1 to words(talist)
  300.      call charout,word(talist,mm)' '
  301.      if (mm//15=0) then do
  302.         say 
  303.         call charout,"  "cy_ye":"normal' '
  304.      end /* do */
  305. end /* do */
  306. if (words(talist)//15)<>0 then say
  307. say
  308. cts.=0
  309. ti=words(talist)
  310. do iJmm=1 to ti
  311.    ainfo=strip(word(talist,iJmm))
  312.    aa='!'ainfo
  313.    ii=cts.aa+1
  314.    cts.aa=ii
  315.    ab=read_gif_block(gifcontents,ii,ainfo,1)
  316.    say reverse"Block "normal||' 'bold||ijmm||normal" (of "ti"): " ainfo ii ", length = "||length(ab)
  317.    select
  318.       when ainfo='CMT' then do
  319.          aa=read_comment_block(ab)
  320.          say "  comment= " aa
  321.       end
  322.       when ainfo='ACE' then do
  323.          niter=read_animation_block(ab)
  324.          parse var niter appname','niter
  325.          if appname="NETSCAPE" then do
  326.              say "  NETSCAPE:  # iters = " niter
  327.           end
  328.           else do
  329.              say "  Appname= " appname
  330.           end
  331.       end /* do */
  332.  
  333.       when ainfo='IMG' then do
  334.          ct_name='CT.'
  335.          img_name='IMG.'
  336.          img.=0
  337.          ct.=0
  338.          stuff=read_image_block(ab,0)           /* 0= do NOT looad IMG. matrix */
  339.          parse var stuff lpos tpos width height lct lctsize interl sort ',' imgdata
  340.  
  341.          say "  Position (l,t): " lpos tpos
  342.          say "  Size (w,h): " width height
  343.          if lct=1 then 
  344.                 say '  Local color with ' lctsize '('ct.0 ') colors.'
  345.          else
  346.                 say "  No local ct (though ctsize = "lctsize
  347.          say "  Interlace, sort flags: " interl ',' sort
  348.          say "  Size of compressed image: " length(imgdata)
  349.       end /* do */
  350.  
  351.       when ainfo='LSD' then do
  352.           ct_name='CT.'
  353.           stuff=read_lsd_block(ab)
  354.           parse var stuff width height gcflag gcsize colres sort bkgcolor aspect
  355.           say "  Image width,height = " width height
  356.           say "  Color resolution, aspect, bkg color " colres aspect bkgcolor 
  357.           if gcflag=1 then
  358.               say "  " gcsize '(' ct.0 ") colors in global color table (sorted="sort
  359.           else
  360.               say "   No global ct (though size = " gcsize
  361.  
  362.       end /* do */
  363.  
  364.       when ainfo='GCE' then do
  365.         stuff=read_gce_block(ab)
  366.         parse var stuff disposal usrinflag tcflag delay tcindex
  367.         say "  Disposal, user input flag, delay : " disposal usrinflag delay
  368.         say "  Transparency flag, index : "tcflag',' tcindex
  369.  
  370.       end /* do */
  371.       when ainfo='TRM' then do
  372.          iterate
  373.      end
  374.      when  ainfo='00' then do  /* junk, remove */
  375.         say " found and ignoring 00  block "
  376.        iterate
  377.      end
  378.      otherwise say " unknown extension "
  379.    end  /* select */
  380.    if dopause=1 then do
  381.        if ainfo="IMG" | ainfo="LSD" then
  382.           aa=yesno(normal"      ..... continue, exit, details?  ","Continue Exit Details ","Continue")
  383.        else
  384.           aa=yesno(normal"      ..... continue, exit, details?  ","Continue Exit ","Continue")
  385.        if aa=0 then do
  386.           say
  387.           iterate
  388.        end /* do */
  389.        if aa=1 then exit
  390.        if aa=2 then do
  391.          select
  392.            when  ainfo="IMG" then do
  393.              do forever
  394.               aa=yesno(normal '    'reverse'Display local Color table, Pixels, Next block? ','ColorTable Pixels Next','NEXT')
  395.               if aa=0 then do
  396.                    if lct=0 then
  397.                          say bold" No local color table specified "
  398.                   else
  399.                     call show_ctable
  400.               end /* do */
  401.               if aa=1 then call show_pixel_values
  402.               if aa=2 then leave
  403.              end
  404.            end
  405.            When AINFO='LSD' then do
  406.               aa=yesno(normal '    'reverse'Display global color table?',,'YES')
  407.               if aa=1 then call show_ctable
  408.            end
  409.            otherwise do
  410.                 say "No extra details available .... "
  411.            end
  412.          end                 /* SELECT */
  413.        end               /* DETAILS */
  414.    end                    /* dopause */
  415. end                      /* blocks */
  416.  
  417. return ti
  418.  
  419.  
  420. /**********/
  421. /* ask for an integer (min value of minval */
  422. ask_integer:procedure expose bold normal
  423. parse arg  amess,defval,minval
  424. if minval='' then minval=0
  425. if amess=''  then amess=' ? '
  426. if defval='' then defval=minval
  427.  
  428. do forever
  429.   call  charout,bold||amess||normal||'('||defval||'):'
  430.   pull aa
  431.   if aa="" then aa=defval
  432.   if datatype(aa)<>'NUM' then do
  433.       say " You must enter an integer greater then or equal to " minval
  434.       iterate
  435.   end /* do */
  436.   if aa<minval then do
  437.       say " You must enter an integer greater then or equal to " minval
  438.       iterate
  439.   end /* do */
  440.   return aa
  441. end
  442.  
  443.  
  444.  
  445.  
  446. /**************/
  447. /* show color table (in ct. stem variable */
  448. show_ctable:procedure expose ct. bold reverse cy_ye normal
  449.  
  450. say
  451. say "# of colors=" ct.0', displayed with their Red, Green, Blue values:'
  452. mm=0
  453. nlines=0
  454. do forever
  455.    oog=0
  456.    if mm>ct.0-1 then leave
  457.    oog=1
  458.    nlines=nlines+1
  459.    if nlines>20  then do
  460.        foo=yesno(normal'  ... more color table values?:',,'Y')
  461.        if foo=0 then leave
  462.        nlines=0
  463.    end /* do */
  464.  
  465.    call charout, left("     " reverse||mm||normal": " ct.!r.mm' 'ct.!g.mm' 'ct.!b.mm,34)
  466.  
  467.    mm=mm+1
  468.    if mm>ct.0-1 then leave
  469.    call charout, left(" | " reverse||mm||normal": " ct.!r.mm' 'ct.!g.mm' 'ct.!b.mm,34)
  470.    
  471.    mm=mm+1
  472.    if mm>ct.0-1 then leave
  473.    call charout,lefT( " | " reverse||mm||normal": " ct.!r.mm' 'ct.!g.mm' 'ct.!b.mm,34)
  474.    mm=mm+1
  475.  
  476.    say
  477. end /* do */
  478. say
  479. if oog=1 then say
  480. return 1
  481.  
  482. /***********/
  483. /* show pixel values */
  484. show_pixel_values:procedure  expose img. bold reverse cy_ye normal ab
  485. img_name='IMG.' ; ct_name='act.'
  486.  
  487. img.=0
  488. stuff=read_image_block(ab,1)
  489.  
  490. nrows=img.!rows ; ncols=img.!cols
  491. irow=0 ; icol=0
  492. do forever
  493. irow0=ask_integer("Enter row (0 to "nrows'):',irow,0)
  494. if irow0>nrows-1 then iterate
  495. icol0=ask_integer("Enter start column (0 to "ncols'):',icol,0)
  496. if icol0>ncols-1 then iterate
  497. icol=icol0 ; irow=irow0
  498. i1=icol
  499. i2=min(ncols-1,icol+10-1)
  500.  
  501. icol=i2+1         /* for next default */
  502.  
  503. call charout, " Row "irow', cols 'i1' - 'i2' = 'bold
  504. do oof=i1 to i2
  505.    apix=c2d(substr(img.irow,oof+1,1))
  506.    call charout,apix' '
  507. end
  508. say  normal
  509. foo=yesno(normal'     dispay more pixel values? ',,'Y')
  510. if foo=0 then do
  511.    say
  512.    return 1
  513. end /* do */
  514. end
  515.  
  516. /************************************************************
  517.                             PARSEGIF
  518.             Procedures to extract information from a  gif file.
  519.  
  520. Notes: 
  521.  * In the descriptions below:
  522.    > ABLOCK is an actual string of bytes; as pulled from gif file,
  523.        or suitable for writing to a gif file.
  524.    > CT_NAME is a string containing the name of the "matrix of 
  525.        color table values" stem variable.
  526.        You MUST set it's value before calling procedures that
  527.        use it.  For example:   ct_name='MY_CT.'
  528.        (note that you MUST include the . at the end of the stem name)
  529.    > STUFF is a space or comma delimited list of variables returned
  530.        by one of these procedures.
  531.    > IMG_NAME is a string containing the name of a "matrix of pixels"
  532.        stem avariable.
  533.        You MUST set it's value before calling procedures that
  534.        use it.  For example:   imgt_name='IMG_NAME.'
  535.        (note that you MUST include the . at the end of the stem name)
  536.  
  537.  *  Use read_gif_block to read  various "blocks" from a GIF file, 
  538.     these blocks may then be used as input to the other 
  539.     For example: 
  540.                  ablock=read_gif_block(a_gif_file,1,'LSD')
  541.                  ablock=read_gif_block(a_gif_file,3,"IMG")
  542.                  ablock=read_gif_block(gifstring,imgnum,'GCE',1)
  543.     
  544.  * Several of these procedures work with color tables. Color tables
  545.    are stored in stem variables, which have the structure:
  546.         ct.0 = # of colors
  547.         ct.!r.n = red value for color n
  548.         ct.!g.n = green value for color n
  549.         ct.!b.n = blue value for color n
  550.     where n =0 ... (ctable.0-1), and ct is the "color table name".
  551.    
  552.     Prior to calling a color table using/returning procedure,
  553.     the "color table name" must be defined. 
  554.     To do this, just set:
  555.          CT_NAME='a_color_table_name.'
  556.     For example:
  557.          CT_NAME='MY_CT.'
  558.          MY_CT.=0
  559.     Note that you MUST include the . after the actual name. Use of MY_CT.=0
  560.     (to set the default value of the MY_CT. "tail" values) is strictly optional.
  561.     
  562.     Example:
  563.          CT_NAME='IMG3_CT.'
  564.          IMG_NAME='IMG_PIX.'
  565.          ablock=read_gif_block(gif_file,3,'IMG')
  566.          stuff=READ_IMAGE_BLOCK(ablock,0)
  567.          (the IMG3_CT. stem variable will contain the local color table
  568.           for the 3rd image of gif_file, assuming one exists).
  569.  
  570.  *  Several of these procedures work with a matrix of pixel values.
  571.     As with color tables, these are stored in stem variables, which
  572.     requires one to assign a value to the IMG_NAME variable. For
  573.     example:
  574.           IMG_NAME='img1.'
  575.           img1.=0
  576.     Note that you MUST include the . after the actual name. 
  577.  
  578.    The structure of this stem variable is (assuming a stem name of img1):
  579.       img1.!rows = # rows
  580.       img1.!cols = # cols
  581.    and each row of the image is in:
  582.       img1.0
  583.         ...
  584.       img1.nrr
  585.    where:
  586.       nrr=# rows-1  
  587.       and each "row" is a string of length img1.!cols.  
  588.           Each character in this string corresponds (is the d2c) for
  589.           a pixel value.  
  590.     Thus, to get the pixel value of the 5 column of the 10th row:
  591.                 avalue=c2d(substr(img1.10,5,1)) 
  592.  
  593. List of Procedures:
  594.  ablock=READ_GIF_BLOCK(giffile,imgnum,infotype,is_string)
  595.  ablock=MAKE_ANIMATION_BLOCK(iter) 
  596.   niter=READ_ANIMATION_BLOCK(ablock)
  597.  ablock=MAKE_COMMENT_BLOCK(a_comment)
  598.   stuff=READ_COMMENT_BLOCK(ablock)
  599.  ablock=MAKE_GCE_BLOCK(tcflag,tcindex,delay,disposal,useinlag)
  600.   stuff=READ_GCE_BLOCK(ablock)
  601.  ablock=MAKE_IMAGE_BLOCK(lpos,tpos,wid,hei,lct,lctsize,inter,sort,imgdata)
  602.   stuff=READ_IMAGE_BLOCK(ablock,to_matrix)
  603.  ablock=MAKE_LSD_BLOCK(width,height,gcflag,colres,sort,bkgcolor,aspect,gcsize)
  604.   stuff=READ_LSD_BLOCK(ablock)
  605.  ablock=MAKE_PTE_BLOCK(tgleft,tgtop,tgwidth,tgheight,ccwidth,ccheight,tfore,tback,amess)
  606.   stuff=READ_PTE_BLOCK(ablock)
  607.  ablock=MAKE_TERMINATOR_BLOCK()
  608.  
  609.  
  610. Description of procedures:
  611.  
  612. ablock=read_gif_block(giffile,imgnum,infotype,is_string)
  613.     Pull a "block" from a gif file.
  614.  
  615.    Where:
  616.         giffile : A file name OR a string containing the contents of a gif file
  617.         nth  : Get block associated with this image, comment, or app block.
  618.         infotype : Type of block to get
  619.         is_string: If 1, then GIFFILE argument is a string, otherwise it's
  620.                    a file name (which read_gif_block will read)
  621.    Values of infotype:
  622.         IMG  -- get the nth "image descriptor" of the imgnum image.
  623.                 To examine: use READ_IMG_BLOCK
  624.         CMT  -- get the nth "comment extension". 
  625.                 To examine: use READ_COMMENT_BLOCK
  626.         ACE  -- get the "application control extension" for the nth application.
  627.                 To examine: use READ_ANIMATION_BLOCK  -- but this is only
  628.                 useful if it's an "animation" block.
  629.         LSD  -- get the "logical control descriptor", including the "GIF89a"
  630.                 (or "GIF87a") header (nth is ignored -- there is only one
  631.                 LSD per file). Note that the LSD is REQUIRED -- all gif files
  632.                 must have start with an LSD. 
  633.                 To examine: use  READ_LSD_BLOCK.
  634.         GCE  -- get the nth "graphic control extension". 
  635.                 To examine: use READ_GCE_BLOCK.
  636.         PTE --  get the nth "plain text extension".
  637.         LST  -- return a spaced delimited list of INFOTYPE codes.
  638.  
  639.   Note that LST is different -- it returns a string. 
  640.   Several additional codes may appear in this "LST" of blocks.
  641.        00 = a '00'x block (a harmless error)
  642.       TRM = terminator -- should ALWAYS be the last code in LST
  643.  
  644.   Note: if an error occurs, ablock will be a string starting with "ERROR",
  645.         and followed by a short error message.
  646.  
  647. ablock=MAKE_ANIMATION_BLOCK(iter) 
  648.       Create an "animation" applications block.
  649.  
  650.       Where:
  651.           iter= # of iterations
  652.  
  653.  
  654. stuff=READ_ANIMATION_BLOCK(ablock(
  655.      Extract # iterations from a "netscape" animation applications 
  656.      control extension (ACE) block.
  657.  
  658.      You can parse stuff with:
  659.         parse var stuff appname','niters
  660.      Where
  661.         appname = name of applicaton block
  662.         niters  = if "NETSCAPE" is the appname, then this is the # of iterations
  663.                   Otherwise, niters=''                                                        
  664.  
  665. ablock=MAKE_COMMENT_BLOCK(a_comment)
  666.    Make a comment block.
  667.  
  668.    Where:
  669.         a_comment = A string containing your comment. Can be any length,
  670.                     and contain CRLFs.
  671.  
  672. stuff=READ_COMMENT_BLOCK(ablock)
  673.    Extract comment from a comment block.
  674.  
  675.    The comment is the only item returned in stuff.
  676.  
  677. ablock=MAKE_GCE_BLOCK(tcflag,tcindex,delay,disposal,useinlag)
  678.    Make a "graphics control extension" block
  679.  
  680.    Where:
  681.         tcflag  = transparent color index flag. If not 1, transparent
  682.                   color still written (Tcindex), but will be ignored by
  683.                   image dipslay programs.
  684.         tcindex = index of the transparent color.
  685.         delay = Delay time (1/100 ths seconds) -- wait this time AFTER
  686.                 displaying image
  687.         dispoal = Disposal method (after delay is over, or userinput taken)
  688.                     0=no action, 1=retain image
  689.                     2=set to background  3=restore to previous
  690.         useinflag = User input flag (1=yes)
  691.  
  692. stuff=READ_GCE_BLOCK(ablock)
  693.   Obtain information from a graphics control extension block.
  694.  
  695.   To get the actual variables, use:
  696.      parse var stuff  disposal usrinflag tcflag delay tcindex
  697.  
  698.   Where the variables are as defined in MAKE_GCE_BLOCK.
  699.  
  700. ablock=MAKE_IMAGE_BLOCK(lpos,tpos,wid,hei,lct,lctsize,inter,sort,imgdata)
  701.     Create an "image descriptor" box.
  702.  
  703.     Where:
  704.         lpos = column number of the left edge of the image (wrt to
  705.                logical screen)
  706.         tpos = row number of the right edge of the image 
  707.         wid= image width in pixels
  708.         hei= image height in pixels
  709.         lct = local color table flag -- set to 1 if a color table 
  710.               to create a local color table
  711.               If LCT=1, then you must "setup the ct_name color table"
  712.               before calling MAKE_IMAGE_BLOCK
  713.       lctsize= size of local color table. if no specified, ct_name.0 is used.
  714.                 If LCT=0, lctsize will still be written (even though
  715.                 no color table is written). This is sort of pointless,
  716.                 but does seem to be a sop.
  717.          inter = interlace flag 
  718.           sort = if 1, indicates that the color table is sorted, with most
  719.                  used color at top. 
  720.       imgdata= If specifed, this should contain:
  721.                    the actual lzw-compressed image data, (including the 
  722.                    "lzw" starting byte)
  723.                If not specified, or if equal to 0, then
  724.                     MAKE_IMAGE_BLOCK will use the contents of the stem variable
  725.                     declared by the IMG_NAME variable (see description above)
  726.                     
  727.     Note: when using a stem variable as the contents of the gif
  728.           image (when imgdata=0), the !cols and !rows "tails" will
  729.           NOT be used -- instead, the width and height variables (specified
  730.           in the argument list) are used. 
  731.           Of course, one would typically make sure that these were equal...
  732.  
  733. stuff=READ_IMAGE_BLOCK(ablock,to_matrix)
  734.     Pull information out of an "image descriptor" block
  735.  
  736.     Where:
  737.        ablock =an image descriptor block; say as retrieved with read_gif_block
  738.        tempfile =  If missing or 0, then
  739.                      ignore
  740.                    If 1, then  
  741.                       write the pixel values of the image to "IMG_NAME"
  742.                       stem variable (see the introductory notes for details).
  743.                       A temporary file, with a name like $TMPnnnn.TMP, 
  744.                       will be temporarily created.
  745.                    If a file name, then
  746.                       Same as 1, but use this filename (instead of a
  747.                       $TMPnnnn.TMP file name) for the temporary file.
  748.               
  749.     The actual information is then obtained by using:
  750.          parse var stuff lpos tpos width hei lct interl sort ',' imgdata
  751.         (see MAKE_IMAGE_BLOCK for a description of these variables).
  752.      and (if to_matrix is appropriately specified)
  753.         by examining the stem variable named by IMG_NAME.
  754.  
  755.     Notes:
  756.          * be SURE to include a ',' before the imgdata (in the parse)
  757.          * if there is any chance the image block includes a local color
  758.            table, be sure to set the value of the CT_NAME variable
  759.            before calling READ_IMAGE_BLOCK
  760.          * if you specify to_matrix, be sure to set the value of the
  761.            IMG_NAME variable before calling READ_IMAGE_BLOCK.
  762.  
  763.  
  764. ablock=MAKE_LSD_BLOCK(width,height,gcflag,colres,sort,bkgcolor,aspect,gcsize)
  765.       Make a logical screen descriptor  block -- including the "GIF89a"
  766.       header (the first 6 six characters in a gif file).
  767.  
  768.       Where:
  769.           width = "logical screen" width (in pixels)
  770.           height= "logical screen" height (in pixels)
  771.           gcflag= set to 1 if a global color table is to be created.
  772.                  If GCFLAG=1, then you must "setup the ct_name color table"
  773.                  beforecalling MAKE_LSD_BLOCK
  774.           colres=2**(colres+1)= color resolution of image creater(rarely used)
  775.           sort = if 1, indicates that the color table is sorted, with most
  776.                  used color at top. 
  777.           bkgcolor = background color index (rarely used)
  778.           aspect = height to width aspect (rarely used)
  779.           gcsize= size of color table. if no specified, ct_name.0 is used.
  780.                  
  781.  
  782. stuff=READ_LSD_BLOCK(ablock)
  783.   Pull information from an logical screen descriptor block
  784.  
  785.     Ablock is an logical screen descriptor block; say as 
  786.     retrieved with read_gif_block.
  787.      
  788.     The actual information is then obtained by using:
  789.         parse var st width height gcflag colres sort bkgcolor aspect
  790.  
  791.      Where the variables are as defined in MAKE_LSD_BLOCK
  792.  
  793. ablock=MAKE_PTE_BLOCK(tgleft,tgtop,tgwidth,tgheight,ccwidth,ccheight,tfore,tback,amess)
  794.    Create a "plain text" extensions block
  795.  
  796.    Where:
  797.         tgleft = pixel column number of left of text grid
  798.         tgtop  = pixel row number of top of text grid
  799.        tgwidth = width of text grid in pixels
  800.       tgheight = height of text grid in pixels
  801.       ccwidth  = width of each cell in pixels
  802.       ccheight = height of each cell in pixels
  803.       tfore    = text foreground color table index (into global color table)
  804.       tback    = text background color table index (into global color table)
  805.        amess   = message string
  806.  
  807. stuff=READ_PTE_BLOCK(ablock)
  808.    Pull information from a plain text extension block.
  809.  
  810.    The actual information can be obtained using:
  811.       parse stuff  tgleft tgtop tgwidth tgheight ccwidth ccheight tfore tback ',' ptext
  812.    Where the variables are as defined in MAKE_PTE_BLOCK
  813.         
  814.  
  815. ablock=MAKE_TERMINATOR_BLOCK()
  816.    Create a "terminator" block.
  817.    No arguments are required (this is simple a constant equal to '3b'x.
  818.  
  819.   
  820. **********************************************************************/
  821.  
  822.  
  823.  
  824. /*******************/
  825. /* make an image block (note use of img_name and ct_name )
  826. Example: 
  827.   ct_name='ct1.' ; img_name='img1.'
  828.   stuff2=make_image_block(lpos,tpos,wid,hei,lct,lcsize,inter,sort,imgdata)
  829. */
  830.  
  831.  
  832. make_image_block:procedure expose (ct_name) (img_name)
  833.  
  834. parse arg lpos,tpos,width,height,lctflag,lcsize,interlace,sortflag,imgdata
  835.  
  836. astuff='2c'x
  837.  
  838. astuff=astuff||dd2c(lpos,2)
  839. astuff=astuff||dd2c(tpos,2)
  840. astuff=astuff||dd2c(width,2)
  841. astuff=astuff||dd2c(height,2)
  842.  
  843. /* create a byte containg several flags */
  844.  
  845. if interlace<>1 then interlace=0
  846. if sortflag<>1 then sortflag=0
  847. if lctflag<>1 then lctflag=0
  848.  
  849. ct0=value(ct_name'0')
  850. if lcsize='' | datatype(lcsize)<>'NUM' then
  851.    isizect=ct0
  852. else 
  853.    isizect=lcsize
  854.  
  855. select          /* 3 bit rep of 2**(sizect+1), rounded up */
  856.    when isizect>128 then do 
  857.          sizect='111' ; is2=256 ;end
  858.    when isizect>64  then do
  859.          sizect='110' ; is2=128 ; end 
  860.    when isizect>32  then do 
  861.         sizect='101' ; is2=64 ; end
  862.    when isizect>16  then do 
  863.         sizect='100' ; is2=32 ;end 
  864.    when isizect>8   then do 
  865.         sizect='011' ; is2=16 ; end ;
  866.    when isizect>4   then do
  867.          sizect='010' ; is2=8 ; end
  868.    when isizect>2   then do 
  869.         sizect='001' ; is2=4 ; end
  870.    otherwise do
  871.         sizect='000' ; is2=2 ; end
  872. end
  873.  
  874. lc=lctflag||interlace||sortflag||'00'||sizect
  875. aa=x2c(b2x(lc))
  876.  
  877. astuff=astuff||aa
  878.  
  879. /* add color table info */
  880. if lctflag=1 then do
  881.   lsd=''
  882.   do mm=0 to min(isizect,ct0)-1
  883.      ii=value(ct_name'!r.'mm)
  884.      lsd=lsd||d2c(ii)
  885.      ii=value(ct_name'!g.'mm)
  886.      lsd=lsd||d2c(ii)
  887.      ii=value(ct_name'!b.'mm)
  888.      lsd=lsd||d2c(ii)
  889.    end /* do */
  890.    if isizect<is2 then do   /* pack the color table */
  891.      do isizect+1 to is2
  892.        lsd=lsd||'000000'x
  893.     end /* do */
  894.   end
  895.   astuff=astuff||lsd
  896. end
  897.  
  898. if imgdata<>'' & imgdata<>'0' then do
  899.   astuff=astuff||imgdata
  900.   return astuff
  901. end
  902.  
  903. /* else, create lzw comppressed image from img_name stem */
  904.  
  905. tempname=imgdata
  906.  
  907. if tempname=1 then do
  908.    usename=systempfilename('$TM1????.TMP')
  909. end
  910. else do
  911.    if pos('?',tempname)>0 then
  912.       usename=systempfilename(tempname)
  913.    else
  914.       usename=TEMPNAME
  915. end
  916.  
  917. ncols=width
  918. nrows=height
  919. messim=rxgdimagecreate(ncols,nrows)
  920. if messim<2 then do
  921.   say "Error Could not create temporary gif image "
  922.   return ''
  923. end
  924.  
  925. pxs.=0
  926. do mr=0 to nrows-1              /* FROM STEM ARRAY TO IMAGE */
  927.    alin=value(img_name||mr)
  928.    do mc=0 to ncols-1
  929.      PXS.MC=c2d(substr(alin,mc+1,1))
  930.    end /* do */
  931.    styled=RxgdImageSetStyle(messim, pxs, ncols)  
  932.    rc=RxgdImageLine(messim, 0,mr,ncols-1,mr,styled)
  933. end
  934.  
  935. DO III=0 TO 255
  936.    FOO=RXGDIMAGECOLORALLOCATE(MESSIM,III,255-III,0)
  937. end /* do */
  938. foo=rxgdimageinterlace(messim,interlace)
  939. foo=rxgdimagegif(messim,usename)
  940. foo=rxgdimagedestroy(messim)
  941.  
  942. oof=charin(usename,1,chars(usename))
  943. if oof="" then  do
  944.  say "Error retrieving temporary gif file"
  945.  return ""
  946. end
  947. foo=stream(USENAME,'c','close')
  948. foo=sysfiledelete(usename)
  949.  
  950. OOF2=read_gif_block(OOF,1,'IMG',1)
  951.  
  952. ct_name='ctmp.'
  953. stuff2=read_image_block(oof2,0)
  954. parse var stuff2 . ',' imgdata
  955. return astuff||imgdata
  956.  
  957.  
  958. /*******************/
  959. /* read an image_block
  960.  
  961. Example:
  962.   ct_name="CT3."
  963.   ct3.=0 ; img_name='img1.'
  964.   ablock=read_gif_block(giffile,1,'IMG')
  965.   stuff=read_image_block(ablock,0)
  966.   parse var stuff leftpos toppos width height lctflag interlaceflag sortflag ','||imgdata
  967.   say " Left top at "leftpos toppos
  968.   say " Width height = " width height
  969.   say " Interlace:" interlaceflag 
  970.   say ' local ct = 'lctflag ' ( sorted = 'sortflag
  971.   if lctflag=1 then do
  972.      say " # colors in lct = " ct3.0 ct3.!r.1 ct3.!g.1 ct3.!b.1
  973.   end
  974.   say " Imgsize = " length(imgdata)
  975.  
  976. and if tomtx is specified (=1 , or equal to a filename), then also
  977. create the IMG_NAME stem variable "matrix of pixel values"
  978.  
  979. */
  980.  
  981. read_image_block:procedure expose (ct_name) (IMG_NAME)
  982.  
  983. parse arg ablock,tomtx
  984.  
  985. il=substr(ablock,2,2)
  986. lpos=c2d(reverse(il))
  987. it=substr(ablock,4,2)
  988. tpos=c2d(reverse(it))
  989. iw=substr(ablock,6,2)
  990. width=c2d(reverse(iw))
  991. ih=substr(ablock,8,2)
  992. height=c2d(reverse(ih))
  993.  
  994. pf=substr(ablock,10,1)
  995.  
  996. pf2= x2b(c2x(pf))
  997. lctflag=substr(pf2,1,1)
  998. interlace=substr(pf2,2,1)
  999. sortflag=substr(pf2,3,1)
  1000.  
  1001. lctsize=right(pf2,3)
  1002. t=right(lctsize,8,0)
  1003.  
  1004. lctsize= x2d(b2x(t))
  1005.  
  1006. lctsize=2**(lctsize+1)
  1007. imgat=11
  1008.  
  1009. if lctflag=1 then do
  1010.    ith=0
  1011.    do m0=1 to (lctsize*3) by 3
  1012.       mm=m0+10
  1013.       aa=value(ct_name'!r.'ith,c2d(substr(ablock,mm,1)))
  1014.       aa=value(ct_name'!g.'ith,c2d(substr(ablock,mm+1,1)))
  1015.       aa=value(ct_name'!b.'ith,c2d(substr(ablock,mm+2,1)))
  1016.       ith=ith+1
  1017.    end
  1018.    imgat=mm+1
  1019. end
  1020.  
  1021. aa=value(ct_name'0',lctsize)
  1022.  
  1023. daimage=substr(ablock,imgat)    /* get rest of stuff in image descriptor block */
  1024.  
  1025. /* note: color table in exposed stem */
  1026. if tomtx="" | tomtx=0 then
  1027.    return lpos tpos width height lctflag lctsize interlace sortflag ','||daimage
  1028.  
  1029. /* else, create the img_name stem var */
  1030.  
  1031. tempname=tomtx
  1032.  
  1033. if tempname=1 then do
  1034.    usename=systempfilename('$TM2????.TMP')
  1035. end
  1036. else do
  1037.    if pos('?',tempname)>0 then
  1038.       usename=systempfilename(tempname)
  1039.    else
  1040.       usename=tempname
  1041. end
  1042.  
  1043.  
  1044. /* make the gif file in memory (very simple version) */
  1045. aa=MAKE_LSD_BLOCK(width,height,0,7,0,0,,)
  1046. aa=aa||ablock||make_terminator_block()
  1047.  
  1048. arf=charout(usename,aa,1)
  1049. if arf<>0 then do  
  1050.    say  "Error writing temporary gif file:" usename
  1051.    return 0
  1052. end
  1053. foo=stream(usename,'c','close')
  1054. /* now read with rxgd */
  1055. dim= RxgdImageCreateFromGIF(usename)
  1056. if dim<=1 then do
  1057.   say " Error reading temporary gif file: " usename
  1058.   oo=sysfiledelete(usename)
  1059.   return 0
  1060. end
  1061.   
  1062. nrows=RxgdImageSY(dim)
  1063. ncols=rxgdimageSx(dim)
  1064. foo=value(img_name'!ROWS',nrows)
  1065. foo=value(img_name'!COLS',ncols)
  1066.  
  1067. ndid=0
  1068. do ny=0 to nrows-1              /* FROM IMAGE TO STEM ARRAY */
  1069.   foo=rxgdimagegetrowpixels(dim,ny,pxels)
  1070.   alin=''
  1071.   do nx=1 to ncols
  1072.      alin=alin||d2c(pxels.nx)
  1073.   end
  1074.   foo=value(img_name||ny,alin)
  1075. end
  1076. foo=rxgdimagedestroy(dim)
  1077. foo=stream(usename,'c','close')
  1078. oo=sysfiledelete(usename)
  1079.  
  1080. return lpos tpos width height lctflag lctsize interlace sortflag ','||daimage
  1081.  
  1082. exit
  1083.  
  1084.  
  1085.  
  1086.  
  1087.  
  1088. /*******************/
  1089. /* make a netscape app block, for animated images, with niter iterations */
  1090.  
  1091. Example:
  1092.   niter=50
  1093.   nu_anim_block=make_animation_block(niter) 
  1094.  
  1095. */
  1096.  
  1097. make_animation_block:procedure
  1098. parse arg niter
  1099. if niter="" then niter=0
  1100. if niter<0 then niter=0
  1101. if niter>65535 then niter=65334
  1102.  
  1103. ablock='21ff0b'x
  1104. ablock=ablock||'NETSCAPE2.0'
  1105. ablock=ablock||'03'x
  1106. ablock=ablock||'01'x
  1107. aiter=dd2c(niter,2)
  1108. ablock=ablock||aiter
  1109. ablock=ablock||'00'x
  1110. return ablock
  1111.  
  1112. /*******************/
  1113. /* read a netscape app block, for animated images, with niter iterations 
  1114.  
  1115. Example:
  1116.  aa=read_animation_block(ablock)
  1117.  
  1118.  You can parse aa with:
  1119.     parse var aa apname','niter
  1120.   
  1121.  If apname='NETSCAPE' then niter will be the iteration count.
  1122.  Otherwise, niter will = ''
  1123.  (that is, if not an animation block, niter='')
  1124. */
  1125.  
  1126. read_animation_block:procedure
  1127. parse arg ablock
  1128.  
  1129. apname=substr(ablock,4,8)
  1130. apauth=substr(ablock,12,3)
  1131. foo=apname||apauth
  1132. if foo<>'NETSCAPE2.0' then return apname
  1133. aiter=substr(ablock,17,2)
  1134. niter=c2d(reverse(aiter))
  1135. return apname','niter
  1136.  
  1137.  
  1138.  
  1139.  
  1140. /*******************/
  1141. /* create a graphics control extension block.
  1142.  
  1143. Example:
  1144.   nu_gce_block=make_gce_block(tcflag,tcindex,delay,disposal,userinputflag)
  1145.  
  1146. */
  1147.  
  1148. make_gce_block:procedure
  1149. parse arg tcflag,tcindex,delay,disposal,userinput
  1150.  
  1151. ablk='21f904'x
  1152.  
  1153. l3='000'
  1154. if disposal='' then disposal=0
  1155. ii= x2b(d2x(disposal))
  1156. ii=right(ii,8,0)
  1157. ii=right(ii,3)
  1158. l3=l3||ii
  1159.  
  1160. if userinput=1 then
  1161.   l3=l3||'1'
  1162. else
  1163.   l3=l3||'0'
  1164.  
  1165. if tcflag<>1 then
  1166.    tcflag='0'
  1167. else
  1168.    tcflag='1'
  1169. l3=l3||tcflag
  1170.  
  1171. l3a=x2c(b2x(l3))
  1172.  
  1173. ablk=ablk||l3a
  1174.  
  1175. if delay='' then delay=0
  1176. delay=dd2c(delay,2)
  1177.  
  1178. if tcindex='' then tcindex=0
  1179. tcindex=dd2c(tcindex,1)
  1180. ablk=ablk||delay||tcindex||'00'x
  1181.  
  1182. return ablk
  1183.  
  1184.  
  1185. /*******************/
  1186. /* make logical screen descriptor 
  1187. Example: (ct2. is a stem containing a color table )
  1188.   ct_name='CT2.'
  1189.   lsd_block=make_lsd_block(width,height,gcflag,colres,sort,bkgcolor,aspect)
  1190.  
  1191. */
  1192.  
  1193. make_lsd_block:procedure expose (ct_name)
  1194. parse arg width,height,gcflag,colres,sort,bkgcolor,aspect,gcsize
  1195.  
  1196. /* organized as:
  1197.  hd= 'GIFxxx' (1-6)
  1198.  width = 2 bytes (7-8)
  1199.  height=  2 bytes (9-10)
  1200. packed = 1 byte (11) -- gcflag (1) colres (3) sort (1) sizect (3)
  1201. bkgcolor =1 byte (12)
  1202. aspect = 1 byte (13)
  1203. colortable = 14 ... 13+ 2**(sizect+1)  bytes (rgbrgbrgb....)
  1204. */
  1205.  
  1206. LSD='GIF89a'
  1207.  
  1208. A2=dD2C(WIDTH,2)
  1209. A3=Dd2C(HEIGHT,2)
  1210.  
  1211. lsd=lsd||A2||A3
  1212.  
  1213. if gcflag=0 | gcflag='' then
  1214.   l3='0'
  1215. else
  1216.   l3='1'
  1217.  
  1218. gcflag=l3
  1219.  
  1220. if colres='' then do
  1221.   colres='111'
  1222. end
  1223. else do
  1224.   colres=x2b(d2x(colres))
  1225.   colres=right(colres,8,0)
  1226.   colres=right(colres,3)
  1227. end
  1228.  
  1229. l3=l3||colres
  1230.  
  1231. if sort='' | sort=0 then
  1232.     l3=l3||'0'
  1233. else
  1234.     l3=l3||'1'
  1235.  
  1236.  
  1237. ct0=value(ct_name'0')
  1238. if gcsize='' | datatype(gcsize)<>'NUM' then
  1239.   isizect=ct0
  1240. else
  1241.   isizect=gcsize
  1242. select          /* 3 bit rep of 2**(sizect+1), rounded up */
  1243.    when isizect>128 then do 
  1244.          sizect='111' ; is2=256 ;end
  1245.    when isizect>64  then do
  1246.          sizect='110' ; is2=128 ; end 
  1247.    when isizect>32  then do 
  1248.         sizect='101' ; is2=64 ; end
  1249.    when isizect>16  then do 
  1250.         sizect='100' ; is2=32 ;end 
  1251.    when isizect>8   then do 
  1252.         sizect='011' ; is2=16 ; end ;
  1253.    when isizect>4   then do
  1254.          sizect='010' ; is2=8 ; end
  1255.    when isizect>2   then do 
  1256.         sizect='001' ; is2=4 ; end
  1257.    otherwise do
  1258.         sizect='000' ; is2=2 ; end
  1259. end
  1260. l3=l3||sizect
  1261.  
  1262. l3a=x2c(b2x(l3))
  1263.  
  1264. lsd=lsd||l3a
  1265.  
  1266. if bkgcolor='' then 
  1267.    lsd=lsd||'00'x
  1268. else
  1269.    lsd=lsd||dd2c(bkgcolor,1)
  1270.  
  1271. if aspect='' then
  1272.    lsd=lsd||d2c(0)
  1273. else
  1274.    lsd=lsd||dd2c(aspect,1)
  1275.  
  1276. /* add color table info */
  1277. if gcflag=1 then do
  1278.   do mm=0 to isizect-1
  1279.     ii=value(ct_name'!r.'mm)
  1280.     lsd=lsd||d2c(ii)
  1281.     ii=value(ct_name'!g.'mm)
  1282.     lsd=lsd||d2c(ii)
  1283.     ii=value(ct_name'!b.'mm)
  1284.     lsd=lsd||d2c(ii)
  1285.   end /* do */
  1286.   if isizect<is2 then do
  1287.      do kkk=isizect+1 to is2
  1288.         lsd=lsd||'000000'x
  1289.      end /* do */
  1290.   end
  1291. end
  1292.  
  1293. return lsd
  1294.  
  1295. /*******************/
  1296. /* make a comment block
  1297. Example:
  1298.   cmt="this is my comment on "||date()
  1299.   nu_cmt_block=make_comment_block(cmt)
  1300. */
  1301.  
  1302. make_comment_block:procedure
  1303. parse arg acomment
  1304. aa='21fe'x
  1305. aa=aa||chunkit(acomment)
  1306. return aa
  1307.  
  1308.  
  1309. /*********/
  1310. read_comment_block:procedure
  1311. parse arg ain
  1312.  iat=2
  1313.  lena=length(ain)
  1314.  amess=''
  1315.  do forever       
  1316.     if iat>lena then return ""   /* no block terminator -- error */
  1317.     iat=iat+1      /* size of block */
  1318.     ii=substr(ain,iat,1) ; ii=c2d(ii)
  1319.     if ii=0 then return amess 
  1320.     iat=iat+1
  1321.     amess=amess||substr(ain,iat,ii)
  1322.     iat=iat+ii-1
  1323.  end /* do */
  1324.  
  1325. /*******************/
  1326. /* plain text stuff */
  1327. read_pte_block;procedure
  1328. parse arg ain
  1329.  
  1330.   l1=substr(ain,1,2)
  1331. tgleft=c2d(reverse(l1))
  1332.   l2=substr(ain,3,2)
  1333. tgtop=c2d(reverse(l2))
  1334.  
  1335.    l1=substr(ain,5,2)
  1336. tgwidth=c2d(reverse(l1))
  1337.    l2=substr(ain,7,2)
  1338. tgheight=c2d(reverse(l2))
  1339.  
  1340.    l1=susbtr(ain,9,1)
  1341.  ccwidth=c2d(l1)
  1342.    l2=substr(ain,10,1)
  1343.  ccheight=c2d(l2)
  1344.  
  1345.   l1=substr(ain,11,1)
  1346.     tfore=c2d(l1)
  1347.   l2=substr(ain,12,1)
  1348.     tback=c1d(l2)
  1349.  
  1350. lena=length(ain);amess=''
  1351.  do forever       
  1352.     if iat>lena then return ""   /* no block terminator -- error */
  1353.     iat=iat+1      /* size of block */
  1354.     ii=substr(ain,iat,1) ; ii=c2d(ii)
  1355.     if ii=0 then leave
  1356.     iat=iat+1
  1357.     amess=amess||substr(ain,iat,ii)
  1358.     iat=iat+ii-1
  1359.  end /* do */
  1360.  
  1361. return  tgleft tgtop tgwidth tgheight ccwidth ccheight tfore tback ','||amess
  1362.  
  1363.  
  1364. /*******************/
  1365. /* plain text stuff */
  1366. make_pte_block;procedure
  1367. parse arg tgleft,tgtop,tgwidth,tgheight,ccwidth,ccheight,tfore,tback,amess
  1368.  
  1369.   ab='2101'x
  1370.   ab=ab||d2c(12)
  1371.   ab=ab||dd2c(tgleft,2)
  1372.   ab=ab||dd2c(tgtop,2)
  1373.   ab=ab||dd2c(tgwidth,2)
  1374.   ab=ab||dd2c(tgheight,2)
  1375.   ab=ab||dd2c(ccwidth,1)
  1376.   ab=ab||dd2c(ccheight,1)
  1377.   ab=ab||dd2c(tfore,1)
  1378.   ab=ab||dd2c(tback,1)
  1379.   ab=ab||chunkit(amess)
  1380.  
  1381.   return ab
  1382.  
  1383. /*************/
  1384. /* convert integer to character, using nb bytes */
  1385. dd2c:procedure
  1386. parse arg ival,nb
  1387. if nb='' then nb=2
  1388. a1=reverse(d2c(ival))
  1389. if length(a1)<nb then do 
  1390.    a1=a1||copies('00'x,nb-length(a1))
  1391. end /* do */
  1392. return left(a1,nb)
  1393.  
  1394.  
  1395. /****************/
  1396. /* convert character to interger */
  1397.  
  1398. /*******************/
  1399.  
  1400. /* make a terminator block -- no arguments needed
  1401. Example:
  1402.   my_trm_block=make_terminator_block()
  1403.  
  1404. */
  1405. make_terminator_block:procedure
  1406.  
  1407. return '3b'x
  1408.  
  1409.  
  1410. /*********************/
  1411. /* parse a graphics control extension block (gce). 
  1412.   Note: Use read_gif_block to get the gce.
  1413.  
  1414. Example:
  1415.   imgnum=1
  1416.   ablock=read_gif_block(giffile,imgnum,'GCE')
  1417.   stu=read_gce_block(ablock)
  1418.   parse var stu disposal userinputflag tcflag delay tcindex
  1419.   say " disposal =  " disposal
  1420.   say " userinput flag = " userinputflag
  1421.   say " transparent color flag = " tcflag
  1422.   say " Delay = " delay
  1423.   say " transparent color index = " tcindex
  1424.  
  1425. */
  1426.  
  1427. read_gce_block:procedure
  1428. parse arg ablock
  1429.  
  1430. l3=substr(ablock,4,1)
  1431. l3=x2b(c2x(l3))
  1432. reserved=left(l3,3)
  1433. disposal=right(substr(l3,4,3),8,0)
  1434. disposal=x2d(b2x(disposal))
  1435. userinputflag=substr(l3,7,1)
  1436. tcflag=substr(l3,8,1)
  1437.  
  1438. delay=c2d(reverse(substr(ablock,5,2)))
  1439.  
  1440. tcindex=c2d(substr(ablock,7,1))
  1441.  
  1442. return  disposal userinputflag tcflag delay tcindex
  1443.  
  1444.  
  1445. /*****************/
  1446. /* read lsd (including global color table), from string containing 
  1447.    logical screen descriptor (lsd)
  1448.    Note: use read_gif_block to get the lsd
  1449.  
  1450. Example of use:
  1451.   ct2.=0
  1452.   ct_name='CT2.'
  1453.   st=read_lsd_block(gifcontents)
  1454.   parse var st width height gcflag colres sort bkgcolor aspect
  1455.   SAY "  # COLORS :" CT2.0
  1456.   say " width " width
  1457.   say " height " height
  1458.   say " gcflag " gcflag
  1459.   say " colres " colres
  1460.   say " sort " sort
  1461.   say " bkgcolor " bkgcolor
  1462.   say " aspect " ASPECT
  1463.   say " # colors = " ct_name.0 
  1464.   do mm=0 to ct_name.0-1
  1465.      say " Color " mm " ct_name.!r.mm ct_name.!g.mm ct_name.!b.mm
  1466.   end 
  1467.  
  1468. */
  1469.  
  1470. read_lsd_block:procedure expose (ct_name)
  1471. parse arg ain
  1472.  
  1473. /* organized as:
  1474.  hd= 'GIFxxx' (1-6)
  1475.  width = 2 bytes (7-8)
  1476.  height=  2 bytes (9-10)
  1477.  packed = 1 byte (11) -- gcflag (1) colres (3) sort (1) sizect (3)
  1478.  bkgcolor =1 byte (12)
  1479.  aspect = 1 byte (13)
  1480.  colortable = 14 ... 13+ 2**(sizect+1)  bytes (rgbrgbrgb....)
  1481. */
  1482.  
  1483. gifver=left(ain,6)
  1484.  
  1485. if abbrev(translate(gifver),'GIF8')=0 then do
  1486.   return 'ERROR bad gif identifier: ' gifver
  1487. end
  1488.  
  1489. l1=substr(ain,7,2)
  1490. width=c2d(reverse(l1))
  1491. l2=substr(ain,9,2)
  1492. height=c2d(reverse(l2))
  1493.  
  1494. l3=substr(ain,11,1)  /* packed fields, used below */
  1495.  
  1496. bkg_color=c2d(substr(ain,12,1))
  1497. aspect=c2d(substr(ain,13,1))
  1498.  
  1499. ctable0=x2b(c2x(l3))
  1500.  
  1501. global_color_flag=left(ctable0,1)
  1502.  
  1503. colres=substr(ctable0,2,3)
  1504. colres=right(colres,8,0)
  1505. colres=x2d(b2x(colres))
  1506.  
  1507. sort=substr(ctable0,5,1)
  1508. ct1=right(ctable0,3)
  1509.  
  1510. ct1=right(ct1,8,0)
  1511. ct1=x2d(b2x(ct1))
  1512. numcolors=2**(ct1+1)
  1513.  
  1514. gcolortable=''
  1515. if global_color_flag=1 then do
  1516.    dcolortable=substr(ain,14,3*numcolors)
  1517.    ith=0
  1518.    do mm=1 to (numcolors*3) by 3
  1519.       aa=value(ct_name'!r.'ith,c2d(substr(dcolortable,mm,1)))
  1520.       aa=value(ct_name'!g.'ith,c2d(substr(dcolortable,mm+1,1)))
  1521.       aa=value(ct_name'!b.'ith,c2d(substr(dcolortable,mm+2,1)))
  1522.       ith=ith+1
  1523.    end
  1524. end
  1525. aa=value(ct_name'0',numcolors)
  1526. return  width height global_color_flag numcolors colres sort bkg_color aspect
  1527.  
  1528.  
  1529. /**************************
  1530. read_gif_block is called as:
  1531.  
  1532.      stuff=read_gif_block(gif_file,imgnum,infotype,is_string)
  1533.  
  1534. Parameters:
  1535.  
  1536.     GIF_FILE: Required. A fully qualified file name.
  1537.                   OR
  1538.               The contents of a gif_file (say, as read with 
  1539.                   gif_file=charin(afile,1,chars(afile))
  1540.  
  1541.          nth: # of image, etc. to get information about. If not specified,
  1542.               a value of 1 is assumed.
  1543.  
  1544.     infotype: Which type "descriptor block" to read (may be image specific)
  1545.               Actually, get the "nth" occurence of this infotype.
  1546.               Valid INFOTYPES are: LSD (nth will be ignored), GCE, IMG, PTE
  1547.               ACE, and CMT
  1548.  
  1549.     is_string: if 1,then gif_file is the "string" containing a gif file,
  1550.                 otherwise, gif_file is a file name.
  1551.  
  1552. Returns:
  1553.   A block from the gif file; or a string beginning with ERROR.
  1554.   Or, if infotype='', a list ob blocks in the gif_file.
  1555.  
  1556. Technical info:  For gif89a specs, please see
  1557.                  http://member.aol.com/royalef/gif89a.txt
  1558.  
  1559. */
  1560.  
  1561. read_gif_block:procedure
  1562. parse  arg afile,nth,infotype,is_string
  1563.  
  1564. infotype=translate(infotype)
  1565.  
  1566. if nth='' then nth=1
  1567.  
  1568. archy='LSD'    /* list of blocks found -- first is ALWAYS LSD block */
  1569. chewerr=0     /* flag set when error in chew_data */
  1570.  
  1571. /* read gif file ? */
  1572. if is_string<>1 then do
  1573.  fqn=stream(afile,'c','query exists')
  1574.  if fqn='' then do
  1575.     if chkerr=0 then return ''
  1576.     return 'ERROR no such file: ' afile
  1577.  end
  1578.  oo=stream(afile,'c','close')
  1579.  filesize=chars(afile)
  1580.  ain=charin(fqn,1,filesize)
  1581.  oo=stream(afile,'c','close')
  1582. end
  1583. else do         /* string provided */
  1584.    ain=afile
  1585. end
  1586.  
  1587. /* check for proper header */
  1588. gifver=left(ain,6)
  1589. if abbrev(translate(gifver),'GIF8')=0 then do
  1590.   if chkerr=0 then return ''
  1591.   return 'ERROR bad gif identifier: ' gifver
  1592. end
  1593.  
  1594.  
  1595. /* is there a global color table? */
  1596. l3=substr(ain,11,1)
  1597. ctable0=x2b(c2x(l3))
  1598. global_color_flag=left(ctable0,1)
  1599. ct1=right(ctable0,3)
  1600. ct1=right(ct1,8,0)
  1601. ct1=x2d(b2x(ct1))
  1602. numcolors=2**(ct1+1)
  1603.  
  1604. iat=13          /* 11 bytes used for intro info */
  1605.  
  1606. if global_color_flag=1 then do
  1607.    iat=iat+(3*numcolors)  /* iat is the Last byte used */
  1608. end
  1609.  
  1610. if infotype='LSD' then return substr(ain,1,iat)
  1611.  
  1612.  
  1613. /* if here, we need top scan file looking for other blocks */
  1614.  
  1615. desc.1='2c'x   /*'image' */
  1616. desc.2='21'x  /*'extension'*/
  1617. desc.3='3b'x   /*trailer' */
  1618.  
  1619. ext.1='f9'x ; /*graphic control'*/
  1620. ext.2='fe'x ; /*'comment'*/
  1621. ext.3='01'x ; /*'plain text'*/
  1622. ext.4='ff'x ; /*application'*/
  1623.  
  1624. nimgs=0         /* set counters */
  1625. ngcs=0
  1626. ncmts=0
  1627. napps=0
  1628. nptxts=0
  1629.  
  1630. lengif=length(ain)
  1631.  
  1632. do forever              /* ------------ scan the gif file */
  1633. iat=iat+1       
  1634.  
  1635. if iat>lengif then leave /* end of file contents (should not happen)*/
  1636.  
  1637. blockid=substr(ain,iat,1)       /* get next block type */
  1638. iat_b=iat               /* iat at beginning of this block */
  1639.  
  1640. select
  1641.  
  1642.    when blockid='00'x then do  /* ignore this relatively harmless error */
  1643.        ares=''
  1644.        ARCHY=archy' 00'
  1645.    end /* do */
  1646.  
  1647.    when blockid=desc.1 then do  /* it's an image */
  1648.       nimgs=nimgs+1
  1649.       call do_image
  1650.       ares=result
  1651.       archy=archy' IMG'
  1652.       if nimgs=nth  & infotype='IMG' then 
  1653.            return substr(ain,iat_b,(1+iat-iat_b))
  1654.    end
  1655.  
  1656.    when blockid=desc.2 then  do      /* extension */
  1657.        iat=iat+1                /* get extention type */
  1658.        extype=substr(ain,iat,1)
  1659.  
  1660.        select                   /* several types of "extensions */
  1661.  
  1662.           when extype=ext.1 then do     /*graphics control */
  1663.             ngcs=ngcs+1
  1664.             call graphics_control
  1665.             ares=result
  1666.             archy=archy' GCE'
  1667.             if infotype='GCE' & nth=ngcs then 
  1668.                return substr(ain,iat_b,(1+iat-iat_b))
  1669.           end
  1670.  
  1671.           when extype=ext.3  then do    /*plain text */
  1672.               nptxts=nptxts+1
  1673.               call plain_text  
  1674.               ares=result
  1675.               archy=archy' PTE'
  1676.               if nptxts=nth & infotype='PTE' then       /* check this image */
  1677.                     return substr(ain,iat_b,(1+iat-iat_b))
  1678.           end /* plain text */
  1679.  
  1680.           when extype=ext.2 then do     /*comment */
  1681.              ncmts=ncmts+1
  1682.              call is_comment
  1683.              ares=result
  1684.              archy=archy' CMT'
  1685.              if ncmts=nth &  infotype='CMT' then
  1686.                 return substr(ain,iat_b,(1+iat-iat_b))
  1687.           end
  1688.  
  1689.           when extype=ext.4 then do     /* application */
  1690.              napps=napps+1
  1691.              call application_block
  1692.              ares=result
  1693.              archy=archy' ACE'
  1694.              if nth=napps & infotype='ACE' then 
  1695.                 return substr(ain,iat_b,(1+iat-iat_b))
  1696.           end /* do */
  1697.  
  1698.           otherwise  do
  1699.              return 'ERROR Bad extension code: '||c2x(extype)
  1700.           end
  1701.        end      /* extype select */
  1702.    end          /* extention descriptor */
  1703.  
  1704.    when blockid=desc.3 then do
  1705.       archy=ARCHY' TRM'
  1706.       leave      /* terminator -- must be end of real gif stuff */
  1707.    end
  1708.  
  1709.    otherwise do
  1710.       return 'ERROR Bad descriptor code: ' blockid
  1711.    end
  1712.  
  1713. end  /* select */
  1714.  
  1715. if ares<>'' then do     /* ERROR DETECTED */
  1716.    if chkerr=0 then return ''
  1717.    return 'ERROR 'ares
  1718. end
  1719.  
  1720. end     /* forever */
  1721.  
  1722. /* if here, end of file and either nothing found, or found list of blocks */
  1723. if infotype='' then return archy
  1724. return ''                       /* blank means " not found " */
  1725.  
  1726.  
  1727. /************/
  1728. do_image:                             
  1729.       l3=substr(ain,iat+9,1)
  1730.       ctable0=x2b(c2x(l3))
  1731.       lcl_ct_flag=left(ctable0,1)
  1732.       t1=right(ctable0,3) ; t1=right(t1,8,0)
  1733.       lcl_ct_size=x2d(b2x(t1)) ; lcl_ct_size=2**(lcl_ct_size+1)
  1734.  
  1735.       skip=lcl_ct_flag*lcl_ct_size*3
  1736.       iat=iat+9+skip    /* iat is now just before the table based image */
  1737.  
  1738. /* chew up the data block */
  1739.        iat=iat+1        /* skip the lzw bits variable */
  1740.        img_data=chew_data()
  1741.        if imgdata="" then return 'ERROR Bad Image Data '
  1742.        return ""
  1743.  
  1744. /*********/
  1745. graphics_control:
  1746.        iat=iat+6
  1747.        term=x2d(c2x(substr(ain,iat,1)))
  1748.        if term<>0 then return 'Bad Graphics Control Extension '
  1749. return ""
  1750.  
  1751. /*********/
  1752. application_block:
  1753. iat=iat+1
  1754. app_blocksize=x2d(c2x(substr(ain,iat,1)))
  1755. if app_blocksize<>11 then do
  1756.     return 'Bad application block size '
  1757. end /* do */
  1758.  
  1759. iat=iat+11
  1760. app_data=chew_data()
  1761. if app_data="" then return 'Bad application block data '
  1762.  
  1763. return ""
  1764.  
  1765. /***********/
  1766. plain_text:
  1767. iat=iat+1
  1768. pt_data=''
  1769. app_blocksize=x2d(c2x(substr(ain,iat,1)))
  1770. if ptextblocksize<>12 then do
  1771.     return 'Bad Plain Text Block Size '
  1772. end /* do */
  1773.  
  1774. iat=iat+13
  1775. pt_data=chew_data()
  1776. if pt_data="" then return 'Bad Plain Text Data '
  1777. return ""
  1778.  
  1779. /*********/
  1780. is_comment:
  1781. cmt_data=chew_data()
  1782. if chewerr=1 then return 'Bad Comment Data '
  1783. return ""
  1784.  
  1785. /*********/
  1786. chew_data:procedure expose iat ain amess filesize chewerr
  1787. parse arg averbose
  1788.        chewerr=1
  1789.        amess=''
  1790.        do forever       /* data blocks */
  1791.          if iat>filesize then do
  1792.              return ""
  1793.          end /* do */
  1794.          iat=iat+1      /* size of block */
  1795.          ii=substr(ain,iat,1) ; ii=c2d(ii)
  1796.          if ii=0 then do 
  1797.              leave
  1798.          end /* do */
  1799.          iat=iat+1
  1800.          amess=amess||substr(ain,iat,ii)
  1801.          iat=iat+ii-1
  1802.        end /* do */
  1803. chewerr=0
  1804. return amess 
  1805.  
  1806. /***********/
  1807. /* make a chewable chunk of data */
  1808. chunkit:procedure
  1809. parse arg astr,klen
  1810. if klen='' then klen=250
  1811.  
  1812. mkit=''
  1813. lenstr=length(astr)
  1814. do mm=1 to lenstr by 250 
  1815.    iget=min(250,1+lenstr-mm)
  1816.    a1=substr(astr,mm,iget)
  1817.    a0=d2c(iget)
  1818.    mkit=mkit||a0||a1
  1819. end
  1820. mkit=mkit||'00'x   
  1821. return mkit
  1822.  
  1823.  
  1824.  
  1825.  
  1826.  
  1827.